wrapper_3.5.51_src/build32.sh100755 0 0 324 14333053647 13137 0ustar 0 0 #!/bin/sh BUILDFILE="`pwd`/`dirname $0`/build.xml" echo "--------------------" echo "Wrapper Build System" echo "using $BUILDFILE" echo "--------------------" "$ANT_HOME/bin/ant" -f "$BUILDFILE" -Dbits=32 $@ wrapper_3.5.51_src/build64.sh100755 0 0 324 14333053647 13144 0ustar 0 0 #!/bin/sh BUILDFILE="`pwd`/`dirname $0`/build.xml" echo "--------------------" echo "Wrapper Build System" echo "using $BUILDFILE" echo "--------------------" "$ANT_HOME/bin/ant" -f "$BUILDFILE" -Dbits=64 $@ wrapper_3.5.51_src/build/ 40755 0 0 0 14333053647 12357 5ustar 0 0 wrapper_3.5.51_src/doc/ 40755 0 0 0 14333053650 12017 5ustar 0 0 wrapper_3.5.51_src/src/ 40755 0 0 0 14333053647 12047 5ustar 0 0 wrapper_3.5.51_src/src/bin/ 40755 0 0 0 14333053650 12611 5ustar 0 0 wrapper_3.5.51_src/src/c/ 40755 0 0 0 14333053652 12265 5ustar 0 0 wrapper_3.5.51_src/src/conf/ 40755 0 0 0 14333053652 12770 5ustar 0 0 wrapper_3.5.51_src/src/java/ 40755 0 0 0 14333053647 12770 5ustar 0 0 wrapper_3.5.51_src/src/java/org/ 40755 0 0 0 14333053647 13557 5ustar 0 0 wrapper_3.5.51_src/src/java/org/tanukisoftware/ 40755 0 0 0 14333053647 16625 5ustar 0 0 wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/ 40755 0 0 0 14333053652 20301 5ustar 0 0 wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/ 40755 0 0 0 14333053652 21225 5ustar 0 0 wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/ 40755 0 0 0 14333053652 22171 5ustar 0 0 wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/event/ 40755 0 0 0 14333053652 21422 5ustar 0 0 wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/jmx/ 40755 0 0 0 14333053652 21077 5ustar 0 0 wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/security/ 40755 0 0 0 14333053652 22150 5ustar 0 0 wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/ 40755 0 0 0 14333053652 21260 5ustar 0 0 wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test2/ 40755 0 0 0 14333053652 21342 5ustar 0 0 wrapper_3.5.51_src/src/test/ 40755 0 0 0 14333053647 13026 5ustar 0 0 wrapper_3.5.51_src/src/test/common/ 40755 0 0 0 14333053647 14316 5ustar 0 0 wrapper_3.5.51_src/src/test/org/ 40755 0 0 0 14333053647 13615 5ustar 0 0 wrapper_3.5.51_src/src/test/org/tanukisoftware/ 40755 0 0 0 14333053647 16663 5ustar 0 0 wrapper_3.5.51_src/src/test/org/tanukisoftware/wrapper/ 40755 0 0 0 14333053652 20337 5ustar 0 0 wrapper_3.5.51_src/README_de.txt100644 0 0 14770 14333053653 13551 0ustar 0 0 ------------------------------------------------------------------------------- Java Service Wrapper Community Edition 3.5.51 Copyright (C) 1999-2022 Tanuki Software, Ltd. All Rights Reserved. https://wrapper.tanukisoftware.com ------------------------------------------------------------------------------- Zusammenfassung: 1. Was ist der Java Service Wrapper? 2. Dokumentation 3. Installation 4. Lizenzoptionen 5. Wie zu erwerben? 6. Aktualisieren 7. FAQ 8. Support 9. Systemvoraussetzungen 1. Was ist der Java Service Wrapper? ------------------------------------------------------------------------------- Der Java Service Wrapper ist eine Anwendung, die aus dem Wunsch heraus erstellt worden ist, eine Vielzahl von Java bedingten Problem und Einschränkungen zu beheben. Einige der Möglichkeiten des Wrappers sind: * Eine Java-Anwendung als Windows-Dienst und Unix Daemon zu starten * Erhöhung der Zuverlässigkeit von Javaanwendungen. * Automatische Crash-, Stillstand- und Deadlock-Erkennung und Wiederherstellung * On-Demand-Neustarts * Standard-, Out-of-the-Box-Scripting * Flexible Cross-Platform-Konfiguration * Vereinfachung der Installation von Java-Anwendungen * Logging * Und vieles mehr ... Bitte gehen Sie auf unsere Downloadseite für mehr Details: => https://wrapper.tanukisoftware.com/doc/german/download.jsp Für weitere Informationen gehen Sie bitte auf: => https://wrapper.tanukisoftware.com/doc/german/introduction.html 2. Dokumentation ------------------------------------------------------------------------------- Bitte die vollständige Dokumentation finden Sie auf unserer Webseite. Hier sind ein paar Informationen für den Anfang: * Die vollständige Dokumentation finden Sie online: => https://wrapper.tanukisoftware.com/doc/german/ * Wie Sie Ihre Anwendung mit dem Java Service Wrapper integrieren: => https://wrapper.tanukisoftware.com/doc/german/integrate.html * Konfigurationseigenschaften: => https://wrapper.tanukisoftware.com/doc/german/properties.html * HOWTOs: => https://wrapper.tanukisoftware.com/doc/german/howto.html * Javadocs, für fortgeschrittene Benutzer: => https://wrapper.tanukisoftware.com/doc/german/javadocs.html * Tanuki Software, Ltd. Firmenseite: => https://www.tanukisoftware.com/ 3. Installation ------------------------------------------------------------------------------- Wenn Sie das hier lesen, heißt das, dass Sie unsere Software bereits erfolgreich entpackt haben. Die Standard und Professional Editionen des Java Service Wrappers beinhalten bereits eine zeitlich beschränkte, aber vollfunktionstüchtige Testlizenz, die es Ihnen erlaubt den Wrapper unbegrenzt oft zu starten und jeweils für 15 Minuten zu laufen. Dies ist für einfache, ungezwungene erste Tests gedacht. Sie können ebenfalls kostenlos eine einmonatige Testlizenz beantragen, die wie eine Serverlizenz funktioniert und für diesen Zeitraum auf einem Server Ihrer Wahl ohne die zeitliche Begrenzung von 15 Minuten pro Anwendung verwendet werden kann. Testlizenzen können auf folgender Seite beantragt werden: => https://wrapper.tanukisoftware.com/doc/german/requestTrial.jsp Dauerhafte Lizenzen können auf der folgenden Seite erworben werden: => https://wrapper.tanukisoftware.com/doc/german/accountLicenses.jsp The Community Edition benötigt keinerlei Lizenz! Sollten Sie bereits eine Lizenz erworben haben, können Sie den Lizenzschlüssel auf der folgendenen Seiten erstellen und herunterladen: Für Serverlizenzen: => https://wrapper.tanukisoftware.com/doc/german/accountServerLicenses.jsp Für Entwicklerlizenzen: => https://wrapper.tanukisoftware.com/doc/german/accountDevLicenses.jsp Abhängig von der Art der Java-Anwendung, bedarf es verschiedener Arten, wie Sie die Anwendung mit dem Wrapper integrieren. Die Integrationsschritte finden Sie auf folgender Seite: => https://wrapper.tanukisoftware.com/doc/german/integrate.html 4. Lizenzoptionen ------------------------------------------------------------------------------- Der Java Service Wrapper ist unter 3 verschiedenen Lizenzen verfügbar: * Entwicklerlizenz-Vereinbarung (Kommerziell) * Serverlizenz-Vereinbarung (Kommerziell) * Communitylizenz-Vereinbarung (kompatibel mit GPL2 und GPL3) Für mehr Informationen besuchen Sie bitte die Lizenzübersicht auf unserer Seite: => https://wrapper.tanukisoftware.com/doc/german/licenseOverview.html 5. Wie zu erwerben? ------------------------------------------------------------------------------- Sollten Sie an den Funktionen der Standard bzw. Professional Edition interessiert sein, können Sie diese hier online erwerben: => https://wrapper.tanukisoftware.com/doc/german/accountLicenses.jsp Sollten Sie eine Banküberweisung bevorzugen, kontaktieren Sie uns bitte unter sales@tanukisoftware.com und wir werden Ihnen ein ausführliches Angebot mit den Zahlungsdaten zukommen lassen. Eine Händlerliste finden Sie auf folgender Webseite: => https://www.tanukisoftware.com/en/distributors.php 6. Aktualisieren ------------------------------------------------------------------------------- Sie finden die aktuellste Version des Wrappers immer auf unserer Downloadseite: => https://wrapper.tanukisoftware.com/doc/german/download.jsp Für die Anleitung gehen Sie bitte auf diese Seite: => https://wrapper.tanukisoftware.com/doc/german/howto-upgrade.html 7. FAQ ------------------------------------------------------------------------------- Bitte gehen Sie auf den FAQ-Bereich unserer Seite: => https://wrapper.tanukisoftware.com/doc/german/faq.html Desweiteren haben wir auch eine Troubleshooting-, HOWTOs- und Fragen- & Antwortenseite: => https://wrapper.tanukisoftware.com/doc/german/troubleshooting.html => https://wrapper.tanukisoftware.com/doc/german/howto.html => https://wrapper.tanukisoftware.com/doc/german/qna.html => https://wrapper.tanukisoftware.com/doc/german/debugging.html 8. Support ------------------------------------------------------------------------------- Informationen zum Support finden Sie auf unserer Supportseite: => https://wrapper.tanukisoftware.com/doc/german/support.jsp 9. Systemvoraussetzungen ------------------------------------------------------------------------------- Eine vollständige Liste der unterstützten Plattformen finden Sie auf der folgenden Seite: => https://wrapper.tanukisoftware.com/doc/german/supported-platforms.html ------------------------------------------------------------------------------- Copyright (C) 1999-2022 Tanuki Software, Ltd. All Rights Reserved. wrapper_3.5.51_src/README_en.txt100644 0 0 13757 14333053653 13567 0ustar 0 0 ------------------------------------------------------------------------------- Java Service Wrapper Community Edition 3.5.51 Copyright (C) 1999-2022 Tanuki Software, Ltd. All Rights Reserved. https://wrapper.tanukisoftware.com ------------------------------------------------------------------------------- Summary: 1. What is the Java Service Wrapper? 2. Documentation 3. Installation 4. License Options 5. How to Purchase 6. Getting Updates 7. FAQ 8. Support 9. System Requirements 1. What is the Java Service Wrapper? ------------------------------------------------------------------------------- The Java Service Wrapper is an application that has evolved out of a desire to solve a number of problems common to many Java applications. Some of the Wrapper's features are: * Run a Java application as a Windows Service or Unix Daemon * Java Application Reliability * Automatic detection and recovery of Crashes, Freezes and Deadlocks * On-Demand Restarts * Standard, Out-of-the Box Scripting * Flexible Cross-Platform Configuration * Ease Application Installations * Logging * Many more... See our Product Features page for a more detailed feature list: => https://wrapper.tanukisoftware.com/doc/english/product-features.html For more information, please visit: => https://wrapper.tanukisoftware.com/doc/english/introduction.html 2. Documentation ------------------------------------------------------------------------------- Please visit our website for the full documentation. Here are some ways to get you started. * Complete documentation can be found online: => https://wrapper.tanukisoftware.com/ * How to integrate the Java Service Wrapper with an Application: => https://wrapper.tanukisoftware.com/doc/english/integrate.html * Configuration Properties: => https://wrapper.tanukisoftware.com/doc/english/properties.html * HOWTOs: => https://wrapper.tanukisoftware.com/doc/english/howto.html * Javadocs, for advanced users: => https://wrapper.tanukisoftware.com/doc/english/javadocs.html * Tanuki Software, Ltd. Corporate site: => https://www.tanukisoftware.com/ 3. Installation ------------------------------------------------------------------------------- If you are reading this, it means you have successfully unpacked this software. The Standard and Professional Editions of the Java Service Wrapper ship with a time-limited but full-featured trial license key, which allows you to run the Wrapper as many times as you want for up to 15 minutes. This is meant for quick, no-hassle testing. You can also request a FREE 1-month trial license that allows you to run the Wrapper for the validity of the license (one month) on a single server without the 15-minute limit per session. Trial licenses can be obtained at the following URL: => https://wrapper.tanukisoftware.com/doc/english/requestTrial.jsp Permanent licenses can be purchased at the following URL: => https://wrapper.tanukisoftware.com/doc/english/accountLicenses.jsp The Community Edition does not require a license key. If you have already purchased a license, you can generate and download your license key by logging on and viewing your License Management Page. For Server Licenses: => https://wrapper.tanukisoftware.com/doc/english/accountServerLicenses.jsp For Development Licenses: => https://wrapper.tanukisoftware.com/doc/english/accountDevLicenses.jsp Depending on the specific type of Java application that will be run with the Wrapper, there are a few integration options available: => https://wrapper.tanukisoftware.com/doc/english/integrate.html 4. License Options ------------------------------------------------------------------------------- The Java Service Wrapper is made available under three types of licenses. * Development License Agreement (Commercial) * Server License Agreement (Commercial) * Community License Agreement (Compatible with GPL2 or GPL3) For more information, please browse our license overview: => https://wrapper.tanukisoftware.com/doc/english/licenseOverview.html 5. How to Purchase ------------------------------------------------------------------------------- If you are interested in the features available with our Standard or Professional Editions, licenses can be purchased online: => https://wrapper.tanukisoftware.com/doc/english/accountLicenses.jsp If you prefer to pay via wire transfer, please contact us at sales@tanukisoftware.com and we will send you a detailed quote with payment information. For a list of distributors, please consult the page below: => https://www.tanukisoftware.com/en/distributors.php 6. Getting Updates ------------------------------------------------------------------------------- You can always find the latest Wrapper release on our download page: => https://wrapper.tanukisoftware.com/doc/english/download.jsp We also offer an upgrade guide: => https://wrapper.tanukisoftware.com/doc/english/howto-upgrade.html 7. FAQ ------------------------------------------------------------------------------- Please see our general FAQ page: => https://wrapper.tanukisoftware.com/doc/english/faq.html We also offer Troubleshooting, HOWTOs, and a Question & Answers page: => https://wrapper.tanukisoftware.com/doc/english/troubleshooting.html => https://wrapper.tanukisoftware.com/doc/english/howto.html => https://wrapper.tanukisoftware.com/doc/english/qna.html => https://wrapper.tanukisoftware.com/doc/english/debugging.html 8. Support ------------------------------------------------------------------------------- Please see the following page for support options: => https://wrapper.tanukisoftware.com/doc/english/support.jsp 9. System Requirements ------------------------------------------------------------------------------- A full list of supported platforms can be found online: => https://wrapper.tanukisoftware.com/doc/english/supported-platforms.html ------------------------------------------------------------------------------- Copyright (C) 1999-2022 Tanuki Software, Ltd. All Rights Reserved. wrapper_3.5.51_src/README_es.txt100644 0 0 14730 14333053653 13564 0ustar 0 0 ------------------------------------------------------------------------------- Java Service Wrapper Community Edition 3.5.51 Copyright (C) 1999-2022 Tanuki Software, Ltd. All Rights Reserved. https://wrapper.tanukisoftware.com ------------------------------------------------------------------------------- Resumen: 1. ¿Qué es el Java Service Wrapper? 2. Documentación 3. Instalación 4. Opciones de Licencia 5. Cómo Comprar 6. Actualizaciones 7. FAQ 8. Soporte Técnico 9. Requisitos del Sistema 1. ¿Qué es el Java Service Wrapper? ------------------------------------------------------------------------------- El Java Service Wrapper es una aplicación que ha evolucionado con el deseo de resolver una seria de problemas comúnes encontrados en aplicaciones Java. Alguna de las características del Wrapper son: * Ejecutar aplicaciones Java como Servicio de Windows o Demonio de Unix * Fiabilidad en Aplicaciones Java * Detección y recuperación automática de fallas, congelamientos o bloqueos * Reinicios de la JVM en demanda * Scripting estándar y listo para usar * Configuración multiplataforma flexible * Instalaciones más fáciles * Registros de datos * Y muchas más... Por favor visite nuestra página de Características del Producto para una lista más detallada: => https://wrapper.tanukisoftware.com/doc/spanish/product-features.html Para más información, por favor visite: => https://wrapper.tanukisoftware.com/doc/spanish/introduction.html 2. Documentación ------------------------------------------------------------------------------- Por favor visite nuestro sitio web para ver toda nuestra documentación. Aquí hay algunas maneras de empezar. * La documentación completa puede ser encontrada en línea en: => https://wrapper.tanukisoftware.com/ * Cómo integrar el Java Service Wrapper con una aplicación: => https://wrapper.tanukisoftware.com/doc/spanish/integrate.html * Propiedades de Configuración: => https://wrapper.tanukisoftware.com/doc/spanish/properties.html * HOWTOs: => https://wrapper.tanukisoftware.com/doc/spanish/howto.html * Javadocs, para usuarios avanzados: => https://wrapper.tanukisoftware.com/doc/spanish/javadocs.html (Sólo Inglés) * Sitio de la compañia - Tanuki Software, Ltd.: => https://www.tanukisoftware.com/es/ 3. Instalación ------------------------------------------------------------------------------- Si está leyendo esto, significa que ha descomprimido correctamente este software. Las ediciones Estándar y Profesional del Java Service Wrapper son distribuidas con una clave de licencia de prueba de tiempo limitado, con todas las características habilitadas, que le permite ejectuar el Wrapper tantas veces quiera por hasta 15 minutos por vez. Esto está diseñado para realizar pruebas rápidas y sin complicaciones. También puede solicitar una licencia de prueba GRATUITA de 1 mes, que le permite ejecutar el Wrapper en un solo servidor durante la validez de la licencia (un mes), sin el límite de 15 minutos por sesión. La licencia de prueba puede ser adquirida en: => https://wrapper.tanukisoftware.com/doc/spanish/requestTrial.jsp Las licencias permanentes se pueden comprar en el siguiente enlace: => https://wrapper.tanukisoftware.com/doc/spanish/accountLicenses.jsp La edición comunidad (Community Edition) no require una clave de licencia. Si ya ha comprado una licencia, puede iniciar sesión y generar su clave de licencia desde su Página de Administración de Licencias. Para Licencias para Servidor: => https://wrapper.tanukisoftware.com/doc/spanish/accountServerLicenses.jsp Para Licencias de Desarrollo: => https://wrapper.tanukisoftware.com/doc/spanish/accountDevLicenses.jsp Dependiendo del tipo específico de aplicación Java que se ejecutará con el Wrapper, hay opciones diferentes de integración disponibles: => https://wrapper.tanukisoftware.com/doc/spanish/integrate.html 4. Tipos de Licencia ------------------------------------------------------------------------------- El Java Service Wrapper está disponible bajo tres licencias. * Licencia de Desarrollo (Comercial) * Licencia para Servidor (Comercial) * Licencia para la Comunidad (Compatible con GPL2 o GPL3) Para más información, por favor visite nuestra página de licencias: => https://wrapper.tanukisoftware.com/doc/spanish/licenseOverview.html 5. Cómo Comprar ------------------------------------------------------------------------------- Si está interesado en las funciones disponibles con nuestras ediciones Estándar o Profesional, las licencias se pueden comprar en línea: => https://wrapper.tanukisoftware.com/doc/spanish/accountLicenses.jsp Si prefiere pagar mediante transferencia bancaria, contáctenos en sales@tanukisoftware.com y le enviaremos un presupuesto detallado con la información de pago. Para ver la lista de distribuidores, consulte la página siguiente: => https://www.tanukisoftware.com/es/distributors.php 6. Actualizaciones ------------------------------------------------------------------------------- Siempre es posible encontrar la versión más reciente en: => https://wrapper.tanukisoftware.com/doc/spanish/download.jsp Nosotros también ofrecemos una guía de actualización: => https://wrapper.tanukisoftware.com/doc/spanish/howto-upgrade.html 7. FAQ ------------------------------------------------------------------------------- Para ver nuestra página de Preguntas Frequentes general, por favor visite: => https://wrapper.tanukisoftware.com/doc/spanish/faq.html Solución de problemas, HOWTOs o nuestra sección de Preguntas y Respuestas: => https://wrapper.tanukisoftware.com/doc/spanish/troubleshooting.html => https://wrapper.tanukisoftware.com/doc/spanish/howto.html => https://wrapper.tanukisoftware.com/doc/spanish/qna.html => https://wrapper.tanukisoftware.com/doc/spanish/debugging.html 8. Soporte Técnico ------------------------------------------------------------------------------- Por favor visite nuestra página de soporte para más opciones: => https://wrapper.tanukisoftware.com/doc/spanish/support.jsp 9. Requisitos del Sistema ------------------------------------------------------------------------------- La lista completa de plataformas soportadas puede ser encontrada en línea: => https://wrapper.tanukisoftware.com/doc/spanish/supported-platforms.html ------------------------------------------------------------------------------- Copyright (C) 1999-2022 Tanuki Software, Ltd. All Rights Reserved. wrapper_3.5.51_src/README_ja.txt100644 0 0 17256 14333053653 13555 0ustar 0 0 ------------------------------------------------------------------------------- Java Service Wrapper Community Edition 3.5.51 Copyright (C) 1999-2022 Tanuki Software, Ltd. All Rights Reserved. https://wrapper.tanukisoftware.com ------------------------------------------------------------------------------- 概要: 1. Java Service Wrapper とは? 2. ドキュメンテーション 3. インストール 4. ライセンスの種類 5. ライセンス購入方法 6. アップデート方法 7. よくある質問 FAQ 8. サポート 9. システム要件 1. Java Service Wrapper とは? ------------------------------------------------------------------------------- Java Service Wrapper は、多くの Java アプリケーションによくある問題を 解決したいという多くの要望に応えて登場したアプリケーションです。 主な Wrapper の特徴: * Windows サービスや Unix デーモンとして Java アプリケーションを動かす * Java アプリケーションの信頼性 * クラッシュ、フリーズ、デッドロックなど自動検知および自動リカバリー * オンデマンドによる再起動 * 簡単にすぐ使えるスクリプト付き * クロスプラットフォームに対応した柔軟なコンフィギュレーション * アプリケーションの簡単なインストール * ログ記録 * その他色々 さらに詳しい特徴などは機能の比較ページをご覧ください。 => https://wrapper.tanukisoftware.com/doc/japanese/product-features.html さらに詳しくはこちらもご覧ください。 => https://wrapper.tanukisoftware.com/doc/japanese/introduction.html 2. ドキュメンテーション ------------------------------------------------------------------------------- 完全なドキュメンテーションをご覧になりたい場合にはウェブサイトをご覧ください。 「始めるには?」を簡単にご紹介します。 * オンラインで完全なドキュメンテーションをご覧になれます。 => https://wrapper.tanukisoftware.com/doc/japanese/ * Java アプリケーションを Java Service Wrapper とインテグレーションする方法 => https://wrapper.tanukisoftware.com/doc/japanese/integrate.html * コンフィギュレーションプロパティ一覧 => https://wrapper.tanukisoftware.com/doc/japanese/properties.html * HOWTO(ハウツー) => https://wrapper.tanukisoftware.com/doc/japanese/howto.html * Javadocs(上級レベルのユーザー向け) => https://wrapper.tanukisoftware.com/doc/japanese/javadocs.html     (英語のみ) * タヌキソフトウェア有限会社サイト => https://www.tanukisoftware.com/ja/ 3. インストール ------------------------------------------------------------------------------- このファイルを読んでいるということは、本ソフトウェアが正常に解凍されたことを意味します。 Java Service Wrapper のスタンダード版やプロフェッショナル版には、時間制限つきの 全機能を利用できるトライアルライセンスキーを同梱してあり、1回の稼働が最長15分までに 制限されていますが、回数に関係なく何度でも利用が可能です。 簡単にクイックテストなど便利にご利用いただけます。 さらに長時間の利用を希望であれば、セッションごとに15分の制限なしで1台のサーバー上で 使える1カ月の無料トライアルライセンスもご用意しています。 1カ月トライアルライセンスは次のページからリクエストすることができます。 => https://wrapper.tanukisoftware.com/doc/japanese/requestTrial.jsp 永久ライセンスは次のURLよりご購入できます。 => https://wrapper.tanukisoftware.com/doc/japanese/accountLicenses.jsp Java Service Wrapperコミュニティー版にはライセンス・キーは不要です。 既にライセンスを購入済みの方は、サイトにログインして、ライセンス管理ページで ライセンスキーを生成することができます。 サーバーライセンス管理ページ => https://wrapper.tanukisoftware.com/doc/japanese/accountServerLicenses.jsp 開発ライセンス管理ページ => https://wrapper.tanukisoftware.com/doc/japanese/accountDevLicenses.jsp Java Service Wrapper で動かす Java アプリケーションの種類により、 いくつかインテグレーション方法があります。 => https://wrapper.tanukisoftware.com/doc/japanese/integrate.html 4. ライセンスの種類 ------------------------------------------------------------------------------- Java Service Wrapper の利用には3つのライセンスがあります。 * 開発ライセンス契約 (商用ライセンス) * サーバーライセンス契約 (商用ライセンス) * コミュニティライセンス契約 (GPL2 または GPL3 と両立) さらに詳しくは、ライセンス概要をご覧ください。 => https://wrapper.tanukisoftware.com/doc/japanese/licenseOverview.html 5. ライセンス購入方法 ------------------------------------------------------------------------------- Java Service Wrapper スタンダード版あるいはプロフェッショナル版の機能を ご利用になるためにオンラインでライセンスを購入することが可能です。 => https://wrapper.tanukisoftware.com/doc/japanese/accountLicenses.jsp 銀行振り込みを希望される場合は sales@tanukisoftware までお問い合わせ願います。 支払い方法の詳細を記載した見積書を送付いたします。 代理店、ディストリビューター経由からの購入も可能です。   => https://www.tanukisoftware.com/ja/distributors.php 6. アップデート方法 ------------------------------------------------------------------------------- 最新の Java Service Wrapper リリースはいつでもダウンロードページで入手できます。 => https://wrapper.tanukisoftware.com/doc/japanese/download.jsp 最新版へのアップグレード手順ガイド => https://wrapper.tanukisoftware.com/doc/japanese/howto-upgrade.html 7. よくある質問 FAQ ------------------------------------------------------------------------------- よくある質問 FAQ ページも便利に活用いただけます: => https://wrapper.tanukisoftware.com/doc/japanese/faq.html その他、トラブルシューティング、HOWTO、質問と回答など各ページもご活用ください。 => https://wrapper.tanukisoftware.com/doc/japanese/troubleshooting.html => https://wrapper.tanukisoftware.com/doc/japanese/howto.html => https://wrapper.tanukisoftware.com/doc/japanese/qna.html => https://wrapper.tanukisoftware.com/doc/japanese/debugging.html 8. サポート ------------------------------------------------------------------------------- 皆様が快適にユーザーサポートを受けられるよう、いくつかオプションをご用意 しています。サポートの詳細については弊社のサポートページをご覧ください。 => https://wrapper.tanukisoftware.com/doc/japanese/support.jsp 9. システム要件 ------------------------------------------------------------------------------- サポートされているプラットフォームの完全なリストをオンラインで ご用意していますのでご参照ください。 => https://wrapper.tanukisoftware.com/doc/japanese/supported-platforms.html ------------------------------------------------------------------------------- Copyright (C) 1999-2022 Tanuki Software, Ltd. All Rights Reserved. wrapper_3.5.51_src/build-tests.xml100644 0 0 513400 14333053650 14373 0ustar 0 0 wrapper.java.classpath.1=../lib/wrapper.jar wrapper.console.format=PM wrapper.console.format=PM wrapper.console.format=PM ]]> wrapper.java.additional.1= wrapper.java.additional.1=-verbose:gc wrapper.java.additional.1= ]]> wrapper.syslog.loglevel=NONE wrapper.syslog.loglevel=NONE ]]> ]]> wrapper.java.additional.1= wrapper.java.classpath.2=../lib/wrapper.jar wrapper.java.classpath.2=../lib/wrapper.jar wrapper.syslog.loglevel=NONE wrapper.syslog.loglevel=NONE wrapper.java.additional.1= wrapper.java.classpath.1=../lib/wrapper.jar wrapper.java.classpath.1=../lib/wrapper.jar wrapper.java.classpath.1=../lib/wrapper.jar ]]> ]]> ]]> ABC DEF GHI ]]> "Hello World." ]]> "Hello World." ]]> #encoding=UTF-8 ABC ]]> # This is a comment. ABC # This is another comment. DEF##param2 "#GHI" ]]> %WRAPPER_LANG% "%WRAPPER_BIN_DIR%" %WRAPPER_BITS% -Dparam1=param1 -Dparam2=param2 -Dparam3=param3 -Dparam="Hello World." -Dparam="Hello World." #encoding=UTF-8 -Dparam=param # This is a comment. -Dparam1=param1 # This is another comment. -Dparam2=##param2 -Dparam3="#param3" -Dparam1=%WRAPPER_LANG% -Dparam2="%WRAPPER_BIN_DIR%" -Dparam3=%WRAPPER_BITS% wrapper.java.additional.1= wrapper.java.additional.1= wrapper.java.additional.1= ]]> wrapper.java.classpath.2=../lib/wrapper.jar wrapper.java.classpath.2=../lib/wrapper.jar setlocal @echo off rem Use a relative path to the wrapper %~dp0 is location of current script under NT echo PassThroughMode=START Test echo The '****PASSTHROUGH****' argument should appear after 'StartParam' echo. %~dp0\..\bin\wrapper.exe -c ..\test\passthrough.conf wrapper.java.additional.1=-Dorg.tanukisoftware.wrapper.WrapperStartStopApp.passthroughMode=START -- "****PASSTHROUGH****" echo. echo PassThroughMode=STOP Test echo The '****PASSTHROUGH****' argument should appear after ''StopParam' echo. %~dp0\..\bin\wrapper.exe -c ..\test\passthrough.conf wrapper.java.additional.1=-Dorg.tanukisoftware.wrapper.WrapperStartStopApp.passthroughMode=STOP -- "****PASSTHROUGH****" echo. echo PassThroughMode=BOTH Test echo The '****PASSTHROUGH****' argument should appear after 'StartParam' and 'StopParam' echo. %~dp0\..\bin\wrapper.exe -c ..\test\passthrough.conf wrapper.java.additional.1=-Dorg.tanukisoftware.wrapper.WrapperStartStopApp.passthroughMode=BOTH -- "****PASSTHROUGH****" echo. echo PassThroughMode=IGNORE Test echo The '****PASSTHROUGH****' argument should not appear at all. echo. %~dp0\..\bin\wrapper.exe -c ..\test\passthrough.conf wrapper.java.additional.1=-Dorg.tanukisoftware.wrapper.WrapperStartStopApp.passthroughMode=IGNORE -- "****PASSTHROUGH****" echo. echo Default PassThroughMode Test echo The '****PASSTHROUGH****' argument should not appear at all. echo. %~dp0\..\bin\wrapper.exe -c ..\test\passthrough.conf -- "****PASSTHROUGH****" echo. wrapper.app.parameter.1=<YourMainClass> wrapper.app.parameter.1=<YourMainClass> wrapper.app.parameter.1=<YourMainClass> wrapper.app.parameter.1=<YourMainClass> wrapper.app.parameter.1=<YourMainClass> wrapper.app.parameter.1=<YourMainClass> wrapper.app.parameter.1=<YourMainClass> wrapper.app.parameter.1=<YourMainClass> wrapper.app.parameter.1=<YourMainClass> the needs of your application #! /bin/sh echo "PassThroughMode=START Test" echo "The '****PASSTHROUGH****' argument should appear after 'StartParam'" echo "" ../bin/wrapper -c ../test/passthrough.conf wrapper.java.additional.1=-Dorg.tanukisoftware.wrapper.WrapperStartStopApp.passthroughMode=START -- "****PASSTHROUGH****" echo "" echo "PassThroughMode=STOP Test" echo "The '****PASSTHROUGH****' argument should appear after ''StopParam'" echo "" ../bin/wrapper -c ../test/passthrough.conf wrapper.java.additional.1=-Dorg.tanukisoftware.wrapper.WrapperStartStopApp.passthroughMode=STOP -- "****PASSTHROUGH****" echo "" echo "PassThroughMode=BOTH Test" echo "The '****PASSTHROUGH****' argument should appear after 'StartParam' and 'StopParam'" echo "" ../bin/wrapper -c ../test/passthrough.conf wrapper.java.additional.1=-Dorg.tanukisoftware.wrapper.WrapperStartStopApp.passthroughMode=BOTH -- "****PASSTHROUGH****" echo "" echo "PassThroughMode=IGNORE Test" echo "The '****PASSTHROUGH****' argument should not appear at all" echo "" ../bin/wrapper -c ../test/passthrough.conf wrapper.java.additional.1=-Dorg.tanukisoftware.wrapper.WrapperStartStopApp.passthroughMode=IGNORE -- "****PASSTHROUGH****" echo "" echo "Default PassThroughMode Test" echo "The '****PASSTHROUGH****' argument should not appear at all." echo "" ../bin/wrapper -c ../test/passthrough.conf -- "****PASSTHROUGH****" echo "" ]]> #wrapper.license.debug=TRUE wrapper_3.5.51_src/build.xml100644 0 0 331465 14333053650 13244 0ustar 0 0 wrapper.check.deadlock.interval=60 wrapper.java.classpath.1=../lib/wrapper.jar # of the the file. wrapper.java.classpath.1=../lib/wrapper.jar # Out Of Memory detection. ********************************************************** * An intermediate pre-release distribution has been * * created in the dist directory. This file can be * * expanded on any system to quickly produce a native * * release by running "build<64/32> release". * * * * Alternately a release can be generated for the current * * platform by running "build<64/32> release" now. * * * * Intermediate pre-release distributions: * * ${dist.dir}/${prerelease.file}.zip * ${dist.dir}/${prerelease.file}.tar.gz ********************************************************** ********************************************************** * You must first either run the pre-release task in a * * full source distribution or run within an intermediate * * pre-release distribution. * ********************************************************** # of the the file. wrapper.java.classpath.1=../lib/wrapper.jar # Out Of Memory detection. ********************************************************** * A native release distribution has been created in the * * dist directory. * * * * Release distribution: * * ${dist.dir}/${releasefile}${release-unsigned-tail}.zip ********************************************************** # of the the file. wrapper.java.classpath.1=../lib/wrapper.jar # Out Of Memory detection. ********************************************************** * A native release distribution has been created in the * * dist directory. * * * * Release distribution: * * ${dist.dir}/${releasefile}${release-unsigned-tail}.tar.gz ********************************************************** wrapper.check.deadlock.interval=60 wrapper.java.classpath.1=../lib/wrapper.jar # Wrapper Properties wrapper.java.classpath.1=../lib/wrapper.jar ********************************************************** * A delta-pack release distribution has been created in * * the dist directory. * * * * Release distributions: * * ${dist.dir}/${deltareleasefile}.zip * ${dist.dir}/${deltareleasefile}.tar.gz ********************************************************** wrapper_3.5.51_src/build32.bat100644 0 0 366 14333053647 13276 0ustar 0 0 @echo off setlocal set BUILDFILE=%~dp0%build.xml echo -------------------- echo Wrapper Build System echo using %BUILDFILE% echo -------------------- call "%ANT_HOME%\bin\ant.bat" -f "%BUILDFILE%" -Dbits=32 %1 %2 %3 %4 %5 %6 %7 %8 wrapper_3.5.51_src/build64.bat100644 0 0 363 14333053647 13300 0ustar 0 0 @echo off setlocal set BUILDFILE=%~dp0%build.xml echo -------------------- echo Wrapper Build System echo using %BUILDFILE% echo -------------------- call "%ANT_HOME%\bin\ant.bat" -f "%BUILDFILE%" -Dbits=64 %1 %2 %3 %4 %5 %6 %7 %8 wrapper_3.5.51_src/default.properties100644 0 0 6333 14333053650 15116 0ustar 0 0 # ------------------------------------------------------------------- # B U I L D P R O P E R T I E S # ------------------------------------------------------------------- # Specifies default property values # Overridden ant.properties # Not user-editable; use ant.properties files instead name = wrapper Name = Wrapper long.name = Java Service Wrapper Community ${version.root} Version = 3.5.51 version = ${Version} version.root = 3.5.51 year = 2022 # NOTE - The version property gets corrupted by the use of Xalan # so Version is used instead. app.name = testwrapper app.caps.name = TestWrapper app.long.name = Test Wrapper Sample Application app.desc = Test Wrapper Sample Application Description # Settings used to configure compile environment build.debug = on build.optimize = off build.deprecation = off build.compress = false junit.failonerror = true # Minimum version of Java required to build the Wrapper javac.target.version=1.4 # Location of java to build the pre-release javac.dir=E:\\j2sdk1.4.2_19 # location of intermediate products build.dir = ${basedir}/build build.classes = ${build.dir}/classes build.headers = ${build.dir}/headers build.testclasses = ${build.dir}/testclasses build.tests = ${build.dir}/tests dist.dir = ${basedir}/dist signtool.dir=C:\\Program Files (x86)\\Windows Kits\\8.1\\bin\\x64 cert.dir= # Set the properties for existing directories bin.dir = ${basedir}/bin conf.dir = ${basedir}/conf src.dir = ${basedir}/src lib.dir = ${basedir}/lib logs.dir = ${basedir}/logs doc.dir = ${basedir}/doc jdoc.dir = ${basedir}/jdoc test.dir = ${basedir}/test context.dir = ${src.dir}/documentation tools.dir = ${basedir}/tools # -------------------------------------------------- # REQUIRED LIBRARIES # -------------------------------------------------- # List of possible locations for the visual studio setup scripts vcvars.v8_32_1=c:\\Program Files (x86)\\Microsoft Visual Studio 9.0\\VC\\bin\\vcvars32.bat vcvars.v8_32_2=d:\\Program Files (x86)\\Microsoft Visual Studio 9.0\\VC\\bin\\vcvars32.bat vcvars.v8_32_3=c:\\Program Files (x86)\\Microsoft Visual Studio 8\\VC\\bin\\vcvars32.bat vcvars.v8_32_4=d:\\Program Files (x86)\\Microsoft Visual Studio 8\\VC\\bin\\vcvars32.bat vcvars.v8_32_5=C:\\Program Files\\Microsoft Platform SDK for Windows Server 2003 R2\\SetEnv.Cmd vcvars.v8_32_5.arg.1=/XP32 vcvars.v8_32_5.arg.2=/RETAIL vcvars.v8_32_6=C:\\Program Files\\Microsoft Platform SDK\\SetEnv.Cmd vcvars.v8_32_6.arg.1=/XP32 vcvars.v8_32_6.arg.2=/RETAIL vcvars.v8_x86_64_1=c:\\Program Files (x86)\\Microsoft Visual Studio 9.0\\VC\\bin\\vcvars64.bat vcvars.v8_x86_64_2=c:\\Program Files\\Microsoft Visual Studio 9\\VC\\bin\\amd64\\vcvarsamd64.bat vcvars.v8_x86_64_3=c:\\Program Files\\Microsoft Platform SDK for Windows Server 2003 R2\\SetEnv.Cmd vcvars.v8_x86_64_3.arg.1=/XP64 vcvars.v8_x86_64_3.arg.2=/RETAIL vcvars.v8_x86_64_4=c:\\Program Files\\Microsoft Platform SDK\\SetEnv.Cmd vcvars.v8_x86_64_4.arg.1=/XP64 vcvars.v8_x86_64_4.arg.2=/RETAIL vcvars.v8_ia_64_1=c:\\Program Files\\Microsoft Platform SDK\\SetEnv.Cmd vcvars.v8_ia_64_1.arg.1=/SRV64 vcvars.v8_ia_64_1.arg.2=/RETAIL # -------------------------------------------------- # OPTIONAL LIBRARIES # -------------------------------------------------- wrapper_3.5.51_src/doc/index.html100644 0 0 703 14333053650 14071 0ustar 0 0 Java Service Wrapper Documentation Complete documentation can be found online:
http://wrapper.tanukisoftware.org

Java docs are available online as well:
http://wrapper.tanukisoftware.org/jdoc/index.html wrapper_3.5.51_src/doc/revisions.txt100644 0 0 1140107 14333053650 14762 0ustar 0 0 Java Service Wrapper Revision History. -------------------------------------- 3.5.51 * Fix a bug where some cleanup tasks related to the backend pipe or socket (usually performed between a JVM shutdown and the next restart) were being skipped. This was only a problem if the JVM had crashed, was frozen or if the Wrapper process was abnormally delayed. Since 3.5.47. * Fix installation failure of Windows Services when using a managed service account, a group-managed service account, or a virtual account. Also improve the detection of built-in service accounts, which do not require authentication. * Fix an issue on Windows where the 'file.encoding' system property, if specified in the wrapper.java.additional. properties, was overridden. * Improve the behavior when the command used to request the Java version fails. For any failure while waiting for the process to complete, an automatic restart will be scheduled (as this could simply be due to the system being temporarily under high load). If, in addition, the process cannot be killed, the Wrapper will stop. * Fix status message not showing when the JVM was continued (with SIGCONT) on macOS. * Fix a timing issue where some remaining JVM output was sometimes printed after confirming that the JVM process had completed. * Increase the width of the 'W' and 'J' columns of the log format to fit the size of the maximum PID value. * Change the link order of the Make files for the Community Edition, so that the libraries appear after the sources. This is done to be more compatible with some linkers, which only accept this order. * (Standard) Fix 'wrapper -h' command not returning any hostIds on Microsoft Azure or Hyper-V Virtual Machines. * (Standard) Improve messages printed when there is a problem with the license, and indicate the property name of the key id currently used to help identify the problematic property set when the license key file contains multiple keys. * (Standard) Fix a minor logging problem where the 'D' format was displaying "unknown" for messages being logged by the main Wrapper instance when wrapper.single_invocation.notify was set to TRUE. 3.5.50 * (Standard) Fix an issue where the Wrapper would sometimes fail to validate the license HostId on some Windows installations. * Fix a log rolling bug where old files were not being purged on Wrapper startup if the mode was WRAPPER, JVM, SIZE_OR_WRAPPER, or SIZE_OR_JVM. Since 3.5.45. * Update the MacOS and Windows installer titles to include the architecture. This is to avoid confusion between x86 (or universal for MacOS) and arm installers. * Add wrapper.check_certificate.default.loglevel property to control the log level of non-critical errors detected when performing trust verification on the certificate chain built by the Wrapper. Change the default log level to DEBUG. * Enhance security when running on old versions of Windows where only SHA-1 hash algorithm is supported. The Wrapper will shutdown if the signature of the certificate cannot be verified, as is the case for SHA-2 certificates. * Stop the Wrapper when the counter signature of the code signing timestamp was not valid. * Fix a hung issue on Windows caused by the certificate validation being slower than the main thread's execution. This could happen on machines with incomplete certificate installation, and when the Wrapper stopped soon after it started (e.g when the configuration is incorrect). * Improve behavior on exit to wait for threads that are still running while the Wrapper still handles CTRL-C signals, and before displaying the final "Wrapper stopped" message. * Fix wrapper.startup_thread.timeout being a few milliseconds shorter than configured. * Add missing '-fPIC' compilation option for FreeBSD 32-bit (lack of this option could cause some errors in the execution of the native library). * Remove redundant log message when the path to the log file is either incorrect or doesn't have sufficient permissions. * Remove misleading status message "JVM process is gone" which was actually for the JVM instance launched to get the Java version (UNIX only). * Improve display of the 'J' column in the log format to only show the PID of the Java process when it's existing. Otherwise "-----" is displayed. * Improve behavior when requesting a thread dump while the Java process doesn't exist: skip sending a signal, and simply log a message stating that the JMV is not running. * (Standard) Improve the messages printed when there is a problem with the license, and indicate the location of the license key file currently used to help when it needs to be replaced. * (Standard) Remove unnecessary debug message and skip some configuration loading when launching the Wrapper with a command that doesn't involve a JVM. * (Profesional) Fix false positive on checking when the JVM process is continued if an event command completed while the JVM process was in the stopped state. * Fix potential issue where a SIGCHLD signal from the JVM could be received by other running threads of the Wrapper and ignored. This would result in a slower detection of the JVM shutdown. * (Standard) Fix memory issues when using wrapper.timezone or wrapper.java.timezone. Since 3.5.46. * (Standard) Fix incorrect calculation of the offset when using a timezone with 'UTC' format if minutes where specified. The detailed timestamps printed with wrapper.timezone.debug_timestamps, which showed the offset of the system time on Windows, are also corrected. * (Standard) When wrapper.java.timezone uses (or defaults to) a value with 'UTC' it is now passed to the command line using the corresponding 'GMT' notation to be compatible with Java. 3.5.49 * Fix high CPU usage observed on certain Unix platforms when running as a console application. The Windows platform, as well as daemon installations on Unix are not affected. Since 3.5.47. Applications using version 3.5.48 that do not need to handle stdin may circumvent the issue by adding wrapper.disable_console_input=TRUE to their configuration file. This workaround is not recommended for 3.5.47 because of another issue (see below). * Reintroduce Solaris SPARC releases in the Delta Pack. * Fix an issue where the Linux armel and armhf (32-bit) architectures were not correctly loading the native library. This was only a problem when using the Delta Pack. * Fix Windows installers that were ignoring the installation directory if it was changed by the user. 3.5.48 * Fix a critical bug on UNIX where the Wrapper could get stuck and fail to restart if a JVM restart was requested by the JVM or Wrapper. This would happen when wrapper.disable_console_input was set to TRUE, which is the default when running as a daemon. Since 3.5.47. * Fix a bug on UNIX where console messages would sometimes be truncated, causing the next message to start on the same line. This was only a display problem and did not affect the operation of the Wrapper. The log file was not affected. Since 3.5.47. * Windows versions were unchanged from 3.5.47. 3.5.47 * (Standard) Fix a bug where setting wrapper.share..startup.premapped or wrapper.share..startup.failure to 'SHUTDOWN' would cancel the action specified by wrapper.on_exit.. Since 3.5.30. * Fix 'Invalid permission eventType: "serviceremote control"' exception when using a Security Manager. Security Manager has, however, been deprecated since Java 17 and usage is no longer recommended. * Fix incorrect return code of Windows Batch Scripts when there is a problem. * On Windows, Oracle Java version 12 and above start using the default code page of the UI language (instead of the default ANSI Windows code page). The Wrapper will add the 'file.encoding' system property to the command line to be able to print the Java output correctly regardless of the Java version. * Rework the initialization of the Unix Shell Script: improved validation of startup arguments, better control of user and permissions, etc. * Improve the Shell Script so it now updates its SELinux context (type to 'bin_t') during the installation as a systemd daemon, and restores it when removing the unit. This is required to be authorized to start, and only done when SELinux is enabled. * Improve message to indicate the Wrapper log file(s) when the Wrapper is launched from the Shell Script and fails to start. * Deprecate the SU_BIN parameter of the Unix Shell Script. The command to substitute the user is now resolved to 'runuser' when present on the system, and falls back on 'su' otherwise. This behavior is regardless the start type (console or daemon). In addition, the RUN_AS_USER feature will now always check that the root user is used. * Improve handling of signals sent to the process group of the script so that they are forwarded to the Wrapper and handled in sync with it. This works both when running normally or as a substitute user. In particular, this makes it possible to send interruption requests with CTRL-C on the shell when running as a user, but also ensures that the script never terminates leaving the Wrapper process running in the background. When not targeting a process group, it is advised to send the signals directly to the Wrapper process. * Launch Java in a new process group. This allows the Wrapper to handle signals more consistently and solves issues such as the double thread dump happening when pressing CTRL-\ or sending a QUIT signal to the process group (UNIX). * (Professional) Add new wrapper.event..command.new_process_group properties to control whether event commands should be launched in new process groups. * (Professional) Add a new setAutoCloseInputStreams() method to the WrapperProcessConfig class to make it possible to control the mode to read pipes of child processes. The default is now "true" for UNIX. It was effectively "false" for previous versions which caused the input stream to block if sub-child processes were still using the pipes, or in some cases where the direct child process would crash. * (Professional) More accurate timeouts when using WrapperProcess.destroy() and better performances for WrapperProcess.waitFor(). * (Professional) Fix exception being thrown on Windows when launching a child process if the working directory was changed. * Improve the cleanup of monitored child process groups when their group leader is terminated but other members are still running. The Wrapper will make sure that the group's status is at least checked every 5 seconds. * Fix a problem where the TestWrapper application in console mode would keep prompting an action in an infinite loop if the input stream was closed. * (Professional) The z/OS distributions are not available for this release. Please contact us if you need them, as we may be able to provide them at a later date. 3.5.46 * Add a new signed installers for Windows. * Add new Apple silicon "aarch64" releases. * Add new FreeBSD 64-bit ARM (aarch64) releases. Built on FreeBSD 13.0. * Change the architecture name to "arm" for linux 64-bit. "armhf" is still used for the 32-bit distributions to differentiate with "armel". * Stop providing new releases for Solaris SPARC. No usage have been reported recently. Please contact us if there is a need for this platform. * Add additional checks for os.arch on MacOS so only supported "ppc", "ppc64", "x86", and "x86_64" will be mapped to the "universal" architecture. * (Standard) Add new property wrapper.java.timezone to make it possible to override the value of wrapper.timezone and configure a different timezone for the Java application. Also add the 'SYSTEM' value to let these properties be set with the local timezone of the system. * (Standard) Always make sure to restore the value of the TZ environment variable set by the Wrapper prior to launching the JVM. * Fix an advice wrongly reported when using 'UTC' for the value of wrapper.timezone. * (Professional) Add a new setNewProcessGroup() method to the WrapperProcessConfig class to make it possible to control whether or not child processes will be assigned to a new process group (PGID). The default is now true for UNIX, it was effectively false for previous versions which made it easy to end up with orphaned processes which are launched indirectly, such as via an intermediate bash process. * (Professional) The shutdown of child processes on Wrapper exit, as well as when calling WrapperProcess.dispose() were waiting for the soft shutdown timeout after attempting to send a CTRL-C to a console child proceess. Due to the way the Windows API works, these CTRL-C signals will never be seen by the child process, and the timeout will always be used. To avoid this delay, the Wrapper now immediately terminates console processes as that was always the end result anyway. This will be revisited if a way to send the CTRL-C signal can be implemented. Windows processes are asked to shutdown nicely, and then the timeout is used to wait for them to exit cleanly. * (Professional) Add WrapperProcessConfig.getSoftShutdownTimeout() method for consistency. * (Professional) Fix memory leak when launching a child process with POSIX_SPAWN. Also improve the FORK_EXEC and VFORK_EXEC methods to cleanly terminate the child process in case an error occurs while creating it. * (Professional) Add support of POSIX_SPAWN start type for child processes on FreeBSD 8+. * (Professional) Fix log output of child processes not being printed on z/OS and improve performances on other platforms. * (Professional) Fix a warning message on UNIX platforms where the Wrapper would report that an event command exited unexpectedly if wrapper.event..command.block.timeout was 0. The command would otherwise complete correctly. Positive timeouts were working correctly. * Fix jar files being signed with an older code signing certificate (expiring on 2020-12-15 but with a timestamp valid until 2027-06-27). The new certificate is the same as the one used to sign Windows binaries and expires on 2023-11-19 with a timestamp valid until 2031-01-06. * Fix wrong path to the id command in the Shell Script. That was mainly causing an issue when using RUN_AS_USER on Solaris. The srcmstr daemon must be reinstalled after upgrading the Wrapper. * (Standard) Fix a problem where no hostIds were listed when using NIC teaming on Windows Server. * Normalize paths used in the Unix Shell Script. Non-normalized paths were causing certain systemd commands to fail. * Fix exit code not being set to an error (value of wrapper.exit_code.error) when the JVM is being killed because it is unresponsive while stopping. * Fix GUI of the Demo application not being scaled correctly with Java 9+ on Windows. If you were using WrapperManager.nativeGetDpiScale(), you may need to precede its call with WrapperManager.nativeGetDpiAwareness() to retrieve the dpi awareness of the process, and WrapperManager.nativeSetDpiAwareness() to set it to a different value. * On Windows, automatically switch to monitoring the Java process when it is redirected from the process launched by the Wrapper. Use wrapper.java.monitor=LAUNCHED to restore the previous behavior and monitor the launched process instead. * Add wrapper.java.monitor.redirect.loglevel to control the log level of messages printed if a redirection of the java process is detected and when the monitored process changes. * Fix the log level of some property warnings. Use the value of wrapper.property_warning.loglevel. * Fix an issue where the Wrapper could crash when trying to format a message. This was observed when the JVM returned a negative exit code. * (Professional) Fix 'wrapper -h' returning "No valid HostId(s)" on z/OS. * Improved German translations. 3.5.45 * Fix a problem where Windows binaries were not being signed correctly in the 3.5.44 release. Added additional checks. * Fix a parsing error causing the Wrapper to hang on startup if the file 'etcetera' (part of the IANA tz database) was present at the location specified by wrapper.timezone.folder. Removing this file would solve the issue. * Add support for encodings added in Java 11 (ISO-8859-16, x-Big5-HKSCS-2001, x-IBM1129, x-IBM1166, x-IBM1364, x-IBM833, x-MS932_0213, x-MS950-HKSCS-XP). * Fix a problem where the Wrapper would fail to start if the shell script was on a path which contained multiple consecutive spaces. (Bug #311) * When building tests (Community Edition), check the presence of a Javascript engine instead of assuming its availability based on the Java version. If no built-in engine is found, an external engine can be specified by setting the 'external.js.engine.dir' property when calling Ant. (Bug #313) * Add a new _WRAPPER_TIMEOUT setting in the Windows batch scripts which makes it possible to control the maximum number of seconds to pause on exit when there is a problem. (Feature #136) * Make it possible to build the Community Edition with Java 10+ by automatically using 'javac -h' instead of 'javah'. (Bug #312) * Use the value of javac.target.version in default.properties (Ant file) as the minimum version of Java supported by the Wrapper instead of a hard-coded value (useful when compiling the Community Edition). * No longer show a warning when Java is launched via a script. Instead the Wrapper will check that the Java PID is as expected to confirm that Java has been launched correctly. * Fix a problem where the Wrapper would show an error on startup if the stdin pipe was not tied to a console window. This can happen when called within other programs. Since 3.5.43. * Modify the DemoApp so its internal Wrapper instance no longer logs to the main wrapper.log file as that was causing confusion. Fix an NPE in the DemoApp if the Wrapper was shutdown external to the DemoApp dialog. * Fix a log rolling issue if the current log file was manually deleted or if it did not exist because the logging was temporarily turned off. The WRAPPER, JVM, SIZE_OR_WRAPPER, SIZE_OR_JVM modes were still rolling the old files and the most recent one ended up having a roll number of "2". Now the rolling occurs only when a log file without roll number exists. * Fix an issue where some early messages were logged before the log file was being rolled on startup. * Fix duplicate messages generated while loading the configuration: when the path to the configuration file or the working directory can't be resolved, when the Wrapper is elevated to control a Windows Service, or in some cases when properties are not set correctly. * Use the configured log file, if possible, when an invalid command line property is found or when the Wrapper fails to load resource properties (not final). Before a default log file was created in the working directory. * Fix the log level of some property warnings (wrapper.*.group, wrapper.*.action, wrapper.event..email.attach_log). Use the value of wrapper.property_warning.loglevel. * Fix an issue where the pid file was wrongly detected as stale and removed by the Shell Script on some Linux systems. This was happening when the path to the working directory exceeded a certain amount of characters. * Make it possible to query permissions of a service with WrapperW. The output will be displayed in the dialog window. * Print the path of the log file when the Wrapper is launched from the Shell Script and fails to start. * Make sure the console output is completely disabled when running as a background process on UNIX systems. Previously certain messages generated right after startup or while loading the configuration were printed before the output was being disabled. * Fix a bug when the Java version output contained '%' characters. In normal situations this should not happen, but there might be such issue when wrapper.java.command is not set correctly. * Print the output of the command used to request the Java version at the INFO level if it fails. * Disable filters for Java version output as they should only apply to the JVM output printed by the Java application. * Fix wrapper.java.command.resolve being ignored when querying the Java version (Unix). This is critical because it could cause the Wrapper to stop. Since 3.5.44. * Fix issue on UNIX platforms where jdb was not being detected if used directly in the value of wrapper.java.command. See wrapper.java.detect_debug_jvm. * When jdb or javaw are used as the Java command, their output can't be used to parse information such as the Java version or vendor. The Wrapper now tries to find the 'java' command in the same directory (or simply 'java' if the command has to be resolved using the system PATH), and uses it to query information. * Fix several messages in the script that were not translated because of single and double quotes not being escaped correctly. Some also had syntax errors. * The z/Linux distributions are now built on a IBM z14 machine with RedHat Linux 7.8. 3.5.44 * Add a log message when a wildcard classpath, defined with a wrapper.java.classpath. property, does not match any files. The log level is controlled with the wrapper.java.classpath.missing.loglevel property. * Add a new wrapper.java.pid.loglevel property which makes it possible to control when the PID of a newly launched JVM is logged. * Modify the way the -q, -qs and -qp arguments work to query the status or permissions of a Windows Service. The configuration file is now required. This change is not compatible with the implementation in 3.5.43. * Update the LSB Init Block in the default shell script so it works more smoothly with older Linux systems using update-rc.d. * Improve logging when the value of wrapper.java.command is not a Java binary. * Add property wrapper.ntservice.account.logon_as_service to control whether or not the Wrapper should add the 'Log on as a service' privilege to the configured account during installation of the Windows Service. * Fix issue where the authentication attempt performed during installation of a Windows Service would fail if the configured wrapper.ntservice.account did not have the 'Log on as a service' privilege. Since 3.5.42. * Fix a problem where special service accounts (LocalService, NetworkService and LocalSystem) could not be used by wrapper.ntservice.account when installing a service. Since 3.5.42. * Fix a timing problem where the Wrapper would sometimes claim that the JVM exited unexpectedly if the JVM requested a stop while starting. * Sign the binaries in the macosx tar.gz distributions as well as in the delta pack distributions. * Add a new signed and notarized package distribution for macosx. * Stop providing new releases for Windows and Linux Itanium. The Itanium architecture is discontinued and no usage have been reported recently. Please contact us if there is a need for these platforms. * Build the z/OS distribution on z/OS 2.4. Previously z/OS 1.8 was used. 3.5.43 * Rename sh.script.in to App.sh.in in the src/bin directory. * Create 'App.shconf.in' in the src/bin directory. This template contains a copy of all settings of the Shell Script. It is recommended to customize this file rather than the Shell Script to simplify Wrapper upgrades. * Improve customization of the Wrapper binaries to limit failures when the target executable is temporarily locked by an external process (often an antivirus). * Fix misleading error messages during customization of the Wrapper binaries. * Fix bug where calls to WrapperManager.signalStarting() were overriding the disabling of wrapper.startup.timeout (when its value is set to 0). This was also happening with WrapperManager.signalStopping() and wrapper.sthutdown.timeout=0. * Add new '--conf-optional' option to be used when customizing the Wrapper. It allows the target executable to run without configuration file if the configuration has been embedded as a binary resource. Without this option, the configuration file will be required. Previously, Wrapper with embedded configuration were allowed to continue without configuration file, but could potentially fail at a later stage. * Improve logging when the configuration file fails to load, and make sure the Wrapper always stops if a file was specified but could not be found. * Add the ability to query the status of a service by specifying its name instead of the configuration file (e.g. 'wrapper -q=testwrapper', 'wrapper -qs=testwrapper'). * Add new property wrapper.console.quickedit to enable or disable the QuickEdit Mode of the console attached to the Wrapper process. The default value will disable the QuickEdit Mode as it can cause the Java application to freeze. * Fix a problem running the Demo Application with the delta pack. * Add a new WrapperManager.generateDetailedNativeBaseName(baseName) method which lets user code generate a base file name based on the current platform. * Fix a crash that happened when the Wrapper was handling control signals during its shutdown process. It had no serious implications because the Wrapper was almost stopped and already disposed, but on Windows it caused to generate an empty dump file in the working directory. * Fix a problem where WrapperManager.stop() failed if the native library was not loaded successfully. * Fix misleading debug messages describing the JVM and Wrapper exit codes. * Raise to STATUS the log level of notifications about the JVM exit whenever it ends with an exit code resulting of a crash on Windows. * Fix 'demoapp' script file not being executable in the bin folder (UNIX). * Fix some malformed/corrupted messages in German. * Fix cases where the JVM was detected as 64-bits on 32-bit systems (for debug only). * Fix dialog window not showing if an error occurred while controlling a Windows Service with WrapperW. 3.5.42 * Add the ability to query and edit permissions granted to users or groups on Windows Services. With appropriate permissions, the Wrapper can control a service without prompting the user for Administrator credentials. * Fix Wrapper not waiting for Network Interfaces when using license keys prefixed with HostIds (wrapper.wait_for_hostid=TRUE was ignored). * Fix an issue where Wrapper instances used to control the Wrapper as a Windows Service were writing the output in wide characters (UTF-16). When the Wrapper output was piped, the console was reading the pipe output in its code page, thus causing a bad character translation. * Fix output issues when the Wrapper is elevated to control a Windows Service: - if wrapper.console.direct is set to TRUE (default), force using only stdout to preserve the output order. - if wrapper.console.direct is set to FALSE, handle stdout and stderr separately (previously everything was redirected to stdout). - no longer block when messages are too large. * No longer prompt for information required to install a Windows Service if the service already exists. * Check that the account and password provided are correct during the installation of a Windows Service, rather than on startup. Also, check that the account has the service privilege enabled. * Handle the Windows Service password more securely and load it in memory only when required. * Add new properties wrapper.monitor_exit and wrapper.java.monitor_exit, which are used by the --setup (-su) command on Windows to allow the Event Log to monitor the Wrapper or Java processes whenever they exit. This is useful to diagnose cases where the Wrapper or Java are being killed by another process. * Change the way HostIds are generated on Windows and allow HostIds tied to wireless network interfaces. * No longer automatically stop a Windows Service prior to updating it. If the service is running while the user attempts to update it, the command will fail. * Change the default value of wrapper.java.version.timeout to 30 seconds (previously it was 10 seconds). * Fix cases where the Java version was not resolved to its default value if it couldn't be retrieved from the JVM output (for example when wrapper.java.version.timeout elapsed). This was causing the Wrapper to stop. * Add a debug message logged whenever stdout or stderr are reassigned by the Java application. This helps understand cases where the standard streams are no longer forwarded to the Wrapper output. * Fix a problem where the code of the Shell Script resolving the status of a daemon was checking the presence of some commands which were actually not used. This was causing an incorrect status reported when the PATH environment variable did not contain the location of the commands. * Improve the way a daemon is installed and removed using the tools available on the different Linux distributions. * Remove support for Linux PPC-BE. This architecture is discontinued for lack of usage. 3.5.41 * Fix java.lang.IllegalArgumentException occuring when printing the usage of the WrapperSimpleApp, WrapperJarApp, WrapperStartStopApp integration methods. * Ignore wrapper.java.additional.auto_bits and wrapper.java.additional.auto_bits. for Java 9 and above. This means that the "-d32" or "-d64" options will no longer be added to the Java command line. These options were deprecated in Java 9 and their usage started to cause a fatal exception after being removed in Java 10. * Add new environment variable 'WRAPPER_RUN_MODE', which is set to 'console' when running as a console, or 'service' when running as a Windows Service / Unix Daemon. * Add new environment variable 'WRAPPER_BASE_NAME', which is set to the binary name without the extension, and without the OS, architecture and bits (if included in the name). * Change the name of default configuration file to be the value of 'WRAPPER_BASE_NAME' suffixed with '.conf'. This means that the default configuration file when using the delta pack will no longer include the OS, architecture and bits. * No longer require for a network interface to be active for its HostId to be used by the Wrapper. * Fix delay on startup which may occur when the IP address of the machine is resolved via DNS that is not configured correctly (Unix). * Fix a bug where the Wrapper failed to parse the version of Java and stopped when wrapper.javaio.use_thread was set true. Since 3.5.35. * Fix a bug where the Java version failed to be parsed on certain JVM implementations (observed on Raspbian with openjdk 9+). * Fix an issue where threads were not safely used on HPUX (Itanium and PA-RISC), and inside libwrapper.so (Linux and FreeBSD). For instance, this was causing repeated log errors when the Java IO thread (wrapper.javaio.use_thread=TRUE) was reading no byte from the pipe. * Fix rare cases where the WrapperW log dialog was displayed without message or with garbled characters. * Add output in the log for a crash dump to make it clear if the Wrapper binary had been customized. * Modify the debug message when a language resource fails to be loaded. * Fix a crash on Windows when wrapper.lang.windows.encoding was set and the JVM output contained certain C formatting sequences like '%s'. Since 3.5.35. * Improve detection of Linux distributions in the Shell Script and fix Amazon Linux not being recognized. * Fix a crash when the Wrapper prompts the user for a password during the installation of a Windows Service. Since 3.5.33. * Fix an issue where non-ASCII characters were not read correctly in the password input by the user during the installation of a Windows Service. * Deprecate wrapper.max_hostid_timeout in favour of wrapper.wait_for_hostid.timeout, and change the default value to 15 which is the maximum number of seconds that the Wrapper should wait for Network Interfaces to come up. Previously the default value was 0 to wait indefinitely. * Add new property wrapper.wait_for_hostid.strict to force waiting for a specific hostId up to the value of wrapper.wait_for_hostid.timeout. The default value if FALSE which tells the wrapper to stop waiting once at least one network interface is found. * Always flush each log output to the log file on startup until the Wrapper enters its cycle to monitor the application. 3.5.40 * Fix a segmentation fault on UNIX systems when the user of the Wrapper process is not part of the group specified by a wrapper.*.group property. * Add Full RELRO to all Linux Wrapper binaries (except zLinux and Linux ia64). RELRO is a security measure which protects against types of GOT-overwrite memory corruption attacks. * Enhance security with FORTIFY_SOURCE level 2 (buffer overflows detection) for Linux (except zLinux, Linux x86 32-bit and Linux ia64) and MacOSX. * Prevent parallel installations of the Wrapper as a daemon using different service management tools. * Fix wrong status reported when the daemon was installed with systemd and the SERVICE_MANAGEMENT_TOOL variable was changed to a different system. This was changing the behavior when installing, removing, or printing the status of a daemon. Wrong usage or false positive on checking script arguments were also observed. * Fix potential memory corruption when printing the certificate information on Windows. * Add the ability to suspend & resume timeouts while the JVM is running. These two actions can be triggered by filters, timers, on a deadlock check, on a ping timeout, using the command file, or from the Java code by using the new WrapperManager.suspendTimeouts() and WrapperManager.resumeTimeouts() methods. This gives a way to tell the Wrapper not to worry about the JVM being unresponsive while the Java application is performing a long blocking task. * Improve the command file by trimming trailing spaces before processing the command. Trailing spaces caused to interpret the last argument of some commands as empty instead of NULL. * Fix a rounding issue causing the timeouts of the JVM states to be slightly inaccurate (less than one second). * Fix sh-incompatible syntax issues in the shell script introduced in version 3.5.38, and version 3.5.36 when RUN_AS_USER is set. 3.5.39 * Add the ability to set the wrapper.*.group properties with a group id. * Improve logging messages when wrapper.*.group properties are set to invalid values. * Fix an issue where WRAPPER_JAVA_VERSION, WRAPPER_JAVA_VERSION_MAJOR, WRAPPER_JAVA_VERSION_MINOR, WRAPPER_JAVA_VERSION_REVISION were not set if the Java version failed to be parsed and was resolved to the value of wrapper.java.version.fallback or the minimum supported version. * Fix an issue introduced in 3.5.38 where a daemon installed with systemd would fail to start and stop on a system boot or when using systemctl. 3.5.38 * Add support for 64-bit ARMHF platforms (aarch64). Built on SUSE Linux Enterprise Server 12 SP4 (glibc 2.22). * Fix a problem where the Wrapper, running as a console application, and the JVM were not cleanly shutdown on Windows Vista, Server2008 and above when the user logged off. * Add new property wrapper.user_logoffs.message to change the message displayed by Windows when the Wrapper is stopping and blocking a user log off. * Fix a problem on UNIX where the Wrapper could hang trying to read from the output pipe of a JVM if the fork to launch the JVM process failed. This was happenning when the maximum number of processes available for the user (ulimit -u) was hit perfectly. * No longer show debug output "active log file changed" when the configuration is loaded. * Fix misleading messages when setting wrapper.lang to an unknown locale name. * Stop printing warnings about missing mo files. Only show messages if there was an error while loading a language file that exists but is invalid. * Fix a bug where the Wrapper considered that the wrapperw process had an attached console. This was causing several small issues: performance decay in case of intensive logging, irrelevant warning about QuickEdit mode being enabled, and inability to use certain locales when the OEM encoding, usually used for consoles, was not compatible. * Improve the error message logged when all of the ports in the configured port range are already in use. * Fix a crash in WrapperManager.nativeGetPortStatus() caused by an unhandled return code while checking on the status of the port to be used by the JVM to connect to the Wrapper (Windows only). This problem was introduced in version 3.5.27, but has only been seen once. The crash is only possible immediately after the JVM is launched. It can also be worked around by using a pipe rather than a socket with the wrapper.backend.type property. * Add new set of properties wrapper.group and wrapper.*.group to change the group of several files created by the Wrapper on Unix systems. * Fix a bug introduced in 3.5.37 where Shell Script invocations during the Wrapper execution were deleting the PID file set in the configuration file. The status command, especially, was not only deleting the PID file, but then also returned a wrong status assuming the Wrapper process was gone. * Fix a bug where the Java version failed to be parsed on HPUX Itanium. The suffix "-hp-ux" being attached to the version was causing the failure. * Add new property wrapper.java.version.fallback which will be used in case the Wrapper fails to parse the output of 'java -version'. If this property is not set, the Java version will be resolved to the lowest supported version, but the Wrapper will stop when certain properties requiring the Java version are used. * No longer stop the Wrapper when failing to write the lockfile on Windows, and allow it to be written by Windows Services. These changes were made to match with the behavior on Unix. The lockfile is not used by the Windows system, but is made available for external applications that would make use of it. * Add 'WRAPPER_PERCENTAGE' to represent '%' characters in the values of configuration properties. Usage of this variable solves a syntactic ambiguity arising when two or more % characters are used, causing the content in between to be interpreted as an environment variable. * Fix '.shconf' file not being loaded when the Shell Script has a '.sh' extension. * Fix a crash when installing the Wrapper as a Windows Service, happening if the binaries contained embedded configuration and '-' was used in place of the configuration file in the command line used to launch the Wrapper. * Fix an issue where wrapper.console.flush and wrapper.internal.namedpipe were copied to the command line of the Windows Service when the Wrapper was first launched for installation without Administrator privileges. This would affect performance of interactive services if a console was attached and visible. * Fix FIXED_COMMAND being ignored when set in the .shconf file. * Add value 'both' for the PASS_THROUGH variable of the Shell Script & Windows Batch files to allow both passing script arguments as Wrapper properties and application parameters. See the description in the scripts for usage. * Restrict usage of arguments passed to the Shell Script & Windows Batch files, for only the commands that make use of them, and when PASS_THROUGH is not disabled. On Windows, commands that can be used with arguments are 'console', 'install', 'installstart' and 'update'. On UNIX, only the 'console' command and, if not installed as a UNIX Daemon, the 'start', 'restart', 'condrestart' commands. Unlike on Windows, arguments are not stored when installing a UNIX Daemon, so this restriction was added to avoid cases where the script can be manually called with arguments, and automatically launched without arguments on a system boot. * Several small corrections in the Shell Script & Windows Batch files, including improved warnings and error messages, and a better help shown in case of a wrong command input. * Add a 'README.txt' file in the bin folder on the Windows releases to explain about the popup "Windows protected your PC" that might show up when executing the batch files. In production, this readme file can be removed. 3.5.37 * Load the wrapper.ntservice.preshutdown.timeout property during the installation of a Windows service and no longer when starting the service. The preshutdown timeout value is stored in the service configuration, which can only be changed by a user with sufficient permissions, and not all users who start Windows Services have these permissions. * Make it possible to use system locales with an encoding notation that is not directly supported by iconv, if the canonical alias for that encoding is supported. * Fix a memory corruption happening when the configured locale encoding is not supported by iconv. * No longer show a warning when the configured locale matches a system locale with a default encoding. * Fix a locale issue when launching the Wrapper with RUN_AS_USER set in the Unix shell script. Depending on the OS and how the Wrapper is launched, either the 'runuser' or 'su' commands can be used, but until now, only 'su' was using the system locale for the forked process. Now, the system locale is explicitly passed to the forked process when using runuser to ensure the same behavior with both commands. * Fix the initial value of the time passed since the last JVM output (displayed with the 'R' column of the log format), which was '99999999' when launching the JVM instance used to get the Java version. It now displays the time since the JVM has started. * Fix heading and tailing tab characters not being trimmed when parsing the lines of the configuration file and timezone file. This was causing properties and directives preceded by a tab to be ignored. * Fix a problem where the configuration file could not be read if it contained characters that were not supported by the encoding of the context in which the Wrapper was launched. It is now possible to change the encoding by using wrapper.lang..encoding and thus give the Wrapper a second chance to read the configuration file and convert it in the correct encoding. * Fix a problem where include configuration files containing corrupted characters were skipped instead of causing a fatal error. * Add new environment variable: WRAPPER_JAVA_VENDOR. This variable is set after the configuration is loaded, when parsing the 'java -version' output. * Improve handling of the customize command line and logging of errors in case it is malformatted. * Make it possible to skip the configuration file by specifying '-' in the command line used to launch the Wrapper if the binary has been customized to embed the configuration. This allows the user to append command lines properties after the '-' and override the default configuration. * Add the ability to register a native exception handler in Windows versions of the Java process by passing the -Dwrapper.register_native_handler=TRUE system property to the JVM. This can be useful in tracking down the cause of a JVM crash. * Add new raiseExceptionNative() and raiseFailFastExceptionNative() methods in the WrapperManager class which can be useful in testing JVM crash recovery. * Make it possible to combine log rolling by date with any other existing modes (SIZE, WRAPPER, JVM, SIZE_OR_WRAPPER, SIZE_OR_JVM). * Change the default value of wrapper.logfile.purge.sort to 'NAME_SMART' for all rolling modes. Before the default value was 'TIMES' except when rolling by date. 'NAME_SMART' will purge the log files sorting them using the dates and indexes included in their names. With this mode, the files will always be purged in the correct order without being affected by manual edits which cause their last modified dates to be changed. * Add a warning on startup when the Wrapper is running in a console with QuickEdit mode enabled (Windows), and a property wrapper.console.quickedit.loglevel to control its log level. The QuickEdit make it easier to select text in the console, but should be used with caution as selecting text will freeze the Wrapper and block the Java application. * When running the delta pack on a 64-bit OS, the script launching the Wrapper will first send a request to resolve whether the license is 32-bit or 64-bit, then launch the appropriate Wrapper binaries. Before the binaries matching the OS bits was used. * Add an optional source file which can contain the customized configuration of the UNIX shell script. This file will be executed after setting the variables on the top of the script, giving the user a chance to override any of them. The file must have the same basename as the script and suffixed with a '.shconf' extension. This should allow the user to keep the shell script in its original state, and make it easier to upgrade it. * Set the default values of the APP_NAME and APP_LONG_NAME variables in the UNIX shell script. APP_NAME will default to the name of the shell script, and then APP_LONG_NAME will default to the value of APP_NAME. * Add a property wrapper.forced_shutdown.delay to control the minimum amount of time required between two CTRL-C or TERM signals to initiate a forced shutdown. The default value is 2 (200ms) to prevent double signals from forcibly shutting down the application. 3.5.36 * Add new properties wrapper.java.additional..java_version.min and wrapper.java.additional..java_version.max to allow passing different options to the JVM depending on the Java version. * Allow WRAPPER_JAVA_HOME to be updated on each JVM restart. This variable is set when the Java command is located using the Windows registry and cleared when using wrapper.java.command. Previously, this variable was final, meaning that once it was set it could not be changed, causing potential mismatch with the location of the Java command being used. * Ensure that a variable cannot be set by the user if it is also used by the Wrapper internally. Before, this was only done for certain variables but not those set after the configuration is loaded (such as the WRAPPER_EVENT_* and the WRAPPER_JAVA_HOME variables). For any conflict, a warning message will be logged and the value set by the user will be ignored. * Add new environment variables: WRAPPER_JAVA_VERSION, WRAPPER_JAVA_VERSION_MAJOR, WRAPPER_JAVA_VERSION_MINOR and WRAPPER_JAVA_VERSION_REVISION. These variables are set after the configuration is loaded, when parsing the 'java -version' output. * Add new environment variables WRAPPER_VERSION and WRAPPER_EDITION. * Deprecate wrapper.environment.dump in favor of wrapper.environment.dump.loglevel and generate a new dump each time the configuration is reloaded if wrapper.restart.reload_configuration is set to TRUE. * Add new properties wrapper.java.additional_file.required and wrapper.app.parameter_file.required. * Allow the Wrapper to be launched without specifying a configuration file if the binaries have been customized with embedded properties. * Fix insufficient buffer size causing the Wrapper to crash when printing large messages in UTF-8 on Windows. * Change the default value of the #properties.on_overwrite.loglevel directive to 'AUTO' which is resolved to: - 'WARN': - When a property is overridden within the same configuration file or in a file with a lower include depth than the file of the previous definition; - When a final property embedded during customization is overridden. - 'DEBUG': in all other cases. * Fix an issue where the Wrapper ignored systems properties surrounded by double quotes and referenced in the wrapper.java.additional. properties (Windows only). Instead of giving priority to the properties of the configuration file, the Wrapper was creating duplicates in the Java command line when using the same system properties internally. * Fix an issue where environment variables were expanded too early while embedding configuration properties into the Wrapper binaries. Now they are left untouched and expanded at runtime. During customization, the properties and variable definitions will be stored as a binary resource in the original order of the configuration file to ensure that variables will be expanded with the same rules as when loading from the file. * Fix a bug where the Wrapper failed to parse the Java version if some system messages were inserted before the JVM output. * Fix a bug causing the Wrapper to sometimes freeze or crash when launching a child process on UNIX systems other than Linux (observed on AIX). * Enable the wrapper.console.title property to be reloaded when wrapper.restart.reload_configuration is set to TRUE. * Use the '-K PIC' option instead of '-K pic' when compiling the Wrapper 64-bit binaries (but not for the native library) on Solaris SPARC. This was required as the size of the binaries increased, but should not affect the Wrapper behavior. * Add a new property wrapper.registry.java_version, which makes it possible to target a specific version of Java when locating the command using the Windows registry. If this property is not set, the Wrapper will use the CurrentVersion string of the registry. Note that Java 9 and above use a different location in the Windows registry, so by default the Wrapper will first search in this new location to prefer recent JVMs. * Fix a bug where a daemon installed with systemd would fail to start if the path to the shell script contained spaces. * Fix corrupted messages logged by the native library if the system encoding and the JVM encoding were different. This was most noticeable when using localization on Windows (on UNIX systems, this would only occur if 'file.encoding' was manually added to the JVM options). * Add a check to make sure that the UNIX shell script can be accessed using an absolute path. If not, the permissions to each intermediate folder will be listed and the script will stop. * Other small fixes, performance improvements and corrected log messages. 3.5.35 * Fix a bug where the icon of the hidden console allocated by wrapperw reappeared in the taskbar whenever the Java application opened a full screen window. If this would occur the icon will be quickly hidden, but a flicker may be observed on the taskbar as the icon will still briefly show up. * Fix a bug introduced in 3.5.31 where setting wrapper.logfile to a blank value was not disabling file logging. Instead the Wrapper was showing a warning and fell back using the default log file in the working directory. This version restores the ability to disable file logging with a blank value. * Handle the backend communication (via pipe or socket) from the native Wrapper to the JVM in UTF-8. Before, the encoding of the current locale was used, but some encodings could not support all characters when the Java side of the Wrapper was reading the configuration. * Fix string conversion issues on the native library happening when the JVM was using a default encoding that did not match the system encoding. This resulted in unreadable JVM outputs. * Add the ability to parse any encoding specified in the JVM arguments (via file.encoding, sun.stdout.encoding or sun.stderr.encoding) and read the JVM output using the same encoding. * Deprecate wrapper.lang.encoding in favor of wrapper.lang.unix.encoding and add wrapper.lang..encoding to specify an encoding per platform. While Unix systems often use the same syntax for encodings, Windows uses code pages instead of names. The new created set of properties helps setting the encoding used by the Wrapper in a cross-platform way. The specified value will be both used to encode the JVM output and the Wrapper log output. * Improve portability of wrapper.lang.unix.encoding. If its value doesn't match any encoding of the locales installed on the system, the Wrapper will search for a match with a different notation of the same encoding (without case sensitivity, dashes, etc.). * Add wrapper.jvm.encoding to control the default encoding of the JVM. Usage of this property requires Java 8 and above, and should be limited to cases where the encoding of the JVM has to be different from the Wrapper output encoding. Otherwise wrapper.lang.[windows|unix].encoding is preferred. * Improve the way the encoding is passed to the JVM on the different platforms: - On Windows, since version 3.5.8 (Professional and Standard Editions), file.encoding was added to the JVM arguments to ensure that the Wrapper always reads the JVM output using a suitable encoding for its own language regardless of the OS language. However it forced the Java application to use an encoding that may not be appropriate. From now on, the encoding is passed to the JVM only if wrapper.lang.windows.encoding or wrapper.jvm.encoding are set, or if the version of Java is older than 7 (recent versions of Java have their default encoding set to the default ANSI Windows code page, like the Wrapper, but that was not always the case on older versions of Java). - On MacOSX, JVMs older than version 7 have their default encoding set to "MacRoman" regardless the current locale. This caused some encoding issues because the Wrapper uses the encoding of the locale to read the JVM output. To correctly read the output of those JVMs, the Wrapper sets file.encoding to the locale encoding (unless it is already present among the JVM arguments). - On all other UNIX platforms, the JVM encoding is automatically resolved from the current locale, so it is only added to the JVM arguments if wrapper.jvm.encoding specifies a different value. * On Windows, user.language and user.country will be set and passed to the JVM whenever wrapper.lang differs from the language and country of the UI. Previously only user.language was passed. * Make it possible to use wrapper.lang.folder, wrapper.lang.domain, wrapper.lang.window.encoding and wrapper.lang.unix.encoding without setting wrapper.lang. This can be useful if any of this configuration needs to be changed while still using the default language of the OS. On Windows, if wrapper.lang is set to 'DESKTOP' or 'DEFAULT', the encoding must be set to 'ANSI', 'OEM' or 'UTF-8' to ensure a correct encoding through the different platforms. * Fix garbled messages when exceptions are raised from the native library on UNIX. * Fix an issue where the Wrapper failed to load a language pack (*.mo) other than those provided by default (German and Japanese). * Fix wrong exit code (0) returned in case the Wrapper would fail to launch a JVM. * Allow wrapper.exit_code.error to be used at an earlier stage if the Wrapper has to stop while loading the configuration (for example when duplicate properties are found with #properties.on_overwrite.exit being set to TRUE). * Allow the CPU affinity of the Wrapper process to be changed when the configuration is reloaded. * Add wrapper.java.version.min and wrapper.java.version.max to control the versions of Java for which the Wrapper is allowed to run. Out of the specified range, the Wrapper will stop. * Fix wrapper.console.title being ignored with wrapperw.exe and wrapper.ntservice.console set to TRUE. * Add four customization options (properties-default, property-file-default, properties-final, property-file-final) to include configuration properties into the Wrapper binaries (Windows). The properties specified with the 'default' options can be overridden from the configuration file or the command line, whereas those specified with the 'final' options are marked as final and cannot be overridden. * Add a new property, wrapper.properties.dump.format, to print various kinds of information when dumping configuration properties. Note that the default formatting changes from 'NAME=VALUE' to 'SOURCE | FINAL | NAME | VALUE'. * Add a new property wrapper.test.no_jvm. By setting it to TRUE, the Wrapper will run until it is about to launch a JVM and then will shutdown. This can be useful to test a configuration, confirm the license status, check properties or environment variables dumps, etc. without starting the Java application. * If wrapper.timezone is set, -Duser.timezone will be added to the Java command line to synchronize the time between the Java application and the Wrapper. It is possible to pass a different timezone using one of the wrapper.java.additional. properties, in which case the Wrapper will not add its own timezone to the Java command line. * Improve Linux distribution detection in the shell script by checking that update-rc.d is present on the system before installing the daemon. This corrects an installation failure on Amazon Linux AMI. * No longer use IBM Advanced Toolchain when building the Wrapper for Linux PPC-LE. Advanced Toolchain was introduced in v. 3.5.34, but since then some users reported that the Wrapper binaries could not be executed on their machine. * Fix a bug happening on Red Hat Enterprise Linux, CentOS, Amazon Linux AMI and Fedora, where the logfile was not rolling if the ROLLNUM token was used inside the value of wrapper.logfile. * Always make sure to process queued log messages before the Wrapper exits on UNIX platforms (previously this was only done on Windows). 3.5.34 * Fix a memory access violation when the Wrapper uses localization files. * Fix minor memory leaks (after validating the certificate and when launching child processes). * Improve logging when establishing connection to an SMTP server and when sending emails. * Dual sign the Wrapper binaries with SHA-1 and SHA-2 hash algorithms and timestamping to allow stronger verification at the OS level while keeping compatibility with older versions of Windows that don't support SHA-2. * Add parameter "--silent" to never show the log dialog when customizing wrapperw.exe. * Return an exit code of 1 whenever the customization of wrapper.exe failed. Previous versions were always returning 0. * Improve the detection of the location of several commands used by the shell script when performing actions like detecting the service management tool. * Remove the ID_BIN variable defined on the top of the script file, as it was ignored on most platforms. The location of the id command is now automatically resolved. * Fix methods of the shell script to restart a daemon installed with launchd or upstart. They behaved like 'condrestart', which means they exited with an error code of 1 if the service was not running. * Fix the condrestart command of the shell script to use built-in restart methods of upstart, systemd, launchd and src, instead of performing a stop followed by a start. * Improve messages when starting/stopping/restarting the Wrapper daemon with the different service management tools available on Unix systems. * Fix errors when querying the status of a service on AIX without being root. * Add a new property, SYSTEMD_KILLMODE, in the shell script to control how systemd should kill the Wrapper child processes on shutdown. When using the Professional edition, it may be useful to set this property to 'process'. This will prevent systemd from killing detached child processes that should normally be left running on shutdown. * Many improvements in the Japanese and German translations. 3.5.33 * Fix a false positive when detecting Upstart on Linux. * Fix a bug where it was required to register the Wrapper to the Event Log on Windows in order to send messages to a remote syslog server via UDP. * Fix a memory leak when sending messages to a remote syslog server. Since 3.5.27. * Fix wrong exit code when the Wrapper was configured to shutdown on a ping timeout. The exit code was always 1 whereas it should be the value of the wrapper.exit_code.error property. * Check that the configured service management tool is available before installing a daemon on Linux and return an exit code of 1 whenever the installation fails. * Add new properties wrapper.ulimit.data.{soft|hard} to limit the size of the data segment of any process started by the Wrapper. It is similar to running the command "ulimit -d X" in a terminal. * Improve the processing of the command file by ignoring blank lines and any spaces before the command. * Correct invalid exit code that may be written in the command file along with a STOP command. If the exit code is out of the range 0-255, it will be resolved to the value of the wrapper.exit_code.error property. * Improve messages when the Wrapper is paused or resumed. * Fix a bug where, on certain UNIX platforms, the locale was not correctly set from the environment at startup. * Fix a bug on FreeBSD where conversions of JVM outputs to a different encoding (specified with wrapper.lang.encoding) systematically failed because the iconv library was not loaded by the native library. * On FreeBSD, it has been observed that certain versions of iconv fail to convert some characters like backslashes when the SJIS encoding is specified. In such case the Wrapper will consider SJIS as unsupported and fall back using the encoding of the console which launched the Wrapper. * Fix garbled question marks that appeared in messages logged while performing actions (install, start, pause, etc.) on a Windows service when the Wrapper language was different than the system language. * On Windows, instead of printing system errors in the language of the UI, the Wrapper will now first try to use the language specified in the configuration (English for the community edition). * Enhance security by adding a linker option to enable ASLR (Address Space Layout Randomization) on Windows Vista and above (Windows Itanium not supported), and by attempting to enable DEP (Data Execution Prevention) right after the Wrapper is launched (Windows XP SP3 and above). DEP enforcement will only affect 32-bit versions as Windows automatically enables DEP for 64-bit binaries. * Fix double thread dump issue when pressing CTRL-\ while the Wrapper is running as a console on Linux. The problem remains on other UNIX platforms. * Fix a problem where backslashes in the path of the log file, configured with the wrapper.logfile property, were not being handled correctly resulting in the Wrapper being unable to create the log file (Unix only). 3.5.32 * Fix a bug introduced in 3.5.31 where the Wrapper did not clean up child processes when shutting down. * Fix a bug on z/OS where commands were ignored if the command file contained an EBCDIC newline character. * In the script file, stop using "runuser" when running the Wrapper in console mode and use "su" instead to handle signals properly. * In the script file, it is now possible to pass options to the command "runuser" with $SU_OPTS. * Fix an issue where some messages coming from Wrapper invocations used to control the Wrapper as a service were not marked with the "wrapperm" logging source. * Fix a memory fault happening when the Wrapper exits after failing to write the pid file. This was causing to return 139 instead of the normal exit code. Since 3.5.31. * Fix a minor memory leak when sending emails. 3.5.31 * Fix script file to resolve the architecture name correctly on Z/Linux. * Change the default behavior of the script file to automatically detect the service management tool on Linux and AIX. See 'SERVICE_MANAGEMENT_TOOL' in the script file. * Add new properties wrapper.ulimit.nofile.{soft|hard} to limit the use of open files. It is similar to running the command "ulimit -n X" in a terminal. * Fix a bug introduced in 3.5.30 where the log file was opened for outputs with a log level lower than the value specified by wrapper.logfile.loglevel. This could cause a performance issue if the wrapper.logfile.close.timeout property was set to a low value as it would cause to reopen the file regularly. * Fix a bug where messages generated shortly after the Wrapper starts could not be redirected to the configured log file. This was happening when using a custom working directory and when the path to the log file was set relative to it. * Fix issues when processing queued log messages which could result in minor memory leaks and dysfunction in the way log file change events are fired. * Fix a problem where the log rolling by date stopped working if at some point the Wrapper could not access the configured log file and switched to the default log file. From now on, the rolling will continue as soon as the configured log file becomes available. As for the default log file, it is always set up to roll with another single file, each file having a size limit of 5Mb. * Fix misleading error messages when the Wrapper fails to write in the log file or fails to terminate the JVM process. The system error codes and their descriptions were incorrect. * Several improvements when the Wrapper switches to a different log file. When the log file was not accessible, if its path was changed and the configuration reloaded, the Wrapper failed to resume into the new log file. Also improved log notifications whenever the log file changes. * Improve how the Wrapper checks if there is a source for the Windows Event Log. Before, this was done on the first message being logged. Now, this check is done at launch time for better performance and readability. If there is no source registered, the logging to the Event Log will be disabled, with the exception of critical messages such as when the log file changed. * Several improvements in the log outputs such as wrong entries in the property dump, double outputs, thread messages being inserted in the middle of multi-line messages, etc. * Fix a bug where the Wrapper did not stop even though the action specified by wrapper.event.wrapper_pause.command.on_exit.=SHUTDOWN was triggered. * Several improvements and bug fixes related to time zones, especially when parsing the IANA time zone file. * Add new WrapperManager.getOS() and WrapperManager.getArch() methods to make it easy to access the same resolved OS and architecture used by the Wrapper. * Fix a buffer overflow problem happening when collecting HostIds if the number of adapters on the local machine exceeded 10. Also improved the display of HostIds when calling 'wrapper -h'. * When launching the Wrapper in console mode, the UNIX shell script will now return the exit code of the Wrapper. When using the start action, the script will not wait for the Wrapper to finish and will not return its exit code. * Add a new property, wrapper.exit_code.error, which allows the user to specify the exit code to use when the Wrapper stops with an error. This can be used to distinguish the Wrapper exit codes from other codes that the Java application may return. Note that this will only take effect after the configuration has been loaded. * Correct the exit code of a child process launched by WrapperManager.exec() on UNIX platforms when it exited due to a crash. * Several bug fixes and improvements when setting the language and encoding. If the configuration doesn't match with any locale installed on the system, the Wrapper will try using a similar locale ignoring case and modifiers. See the Internationalization/Localization page for details. * Stop setting the LANG environment variable on Windows. * Fix a problem where wrapper.lang.encoding was ignored if the LC_ALL environment variable was set on the terminal that launched the Wrapper. To solve this issue, LC_ALL is now set by the Wrapper with the same value as LANG. This only applies to UNIX platforms when wrapper.lang is set. * Due to changes in the way locales are set, the value of the WRAPPER_LANG environment variable may be affected. This variable was and is still set with the 2 first characters of the locale used by the Wrapper, but it is now always lowercase. This makes it match with the suffix used in the language file. On the community edition, the value is always 'en'. * After a ping timeout takes place, the Wrapper will no longer wait for wrapper.shutdown.timeout to elapse and will immediately kill the JVM if a RESTART or SHUTDOWN action was specified. A hung JVM process can't be shut down cleanly, so there is no need to wait for an additional timeout. * Deprecate the use of the wrapper.event..email.attach_log properties in favor of wrapper.event..email.maillog. These new properties make it possible to control if the logs should be sent as an attachment and/or directly in the body of the Notification emails. Also note that, unlike the deprecated properties, logs inclusion can now be set for individual events without activating it for the "default" event type. * Add new actions PAUSE, RESUME and CLOSE_LOGFILE to the signal properties wrapper.signal.mode.hup, wrapper.signal.mode.usr1 and wrapper.signal.mode.usr2. CLOSE_LOGFILE can be used to force the Wrapper to recreate a new log file after it has been moved or renamed. * Add the possibility to pause and resume the Wrapper from the UNIX shell script. Two modes were added and can be activated with the PAUSABLE_MODE variable. The first mode, which is the default, uses the SIGUSR1 and SIGUSR2 signals to request pause and resume. The second mode uses the command file. By default the script will not allow pausing or resuming unless the PAUSABLE variable is explicitly set. When this feature is activated be aware that depending on the mode, wrapper.signal.mode.usr1, wrapper.signal.mode.usr2, or wrapper.commandfile will be passed as command properties to the wrapper and thus override the corresponding properties of the configuration file. * Add event handler methods to the Demo application to show how to handle the service control event sent by the Wrapper when wrapper.pausable.stop_jvm is set to FALSE. * Stop allowing the Wrapper to run when versions of the Wrapper executable, native library, and jar file do not match. The Wrapper had always shown a warning then attempted to continue, but this would often lead to unpredictable behaviour and some features simply not working. Please be sure to upgrade all Wrapper files when upgrading the Wrapper. * Deprecate the use of the wrapper.command.poll_interval and wrapper.anchor.poll_interval properties in favor of wrapper.commandfile.poll_interval and wrapper.anchorfile.poll_interval. * Fixed a rounding issue causing the intervals between two pings or two deadlock checks to be 900ms shorter than the value specified in the configuration. 3.5.30 * Improve the logging messages when using the IANA timezones introduced in version 3.5.29 and add a new property, wrapper.timezone.debug_timestamps, to display additional information (offset to GMT and whether DST is in effect) in the timestamps of the log outputs. * Improve the behavior when the Wrapper fails to parse the timezone file or fails to resolve daylight saving time. Any error on startup will cause the Wrapper to fall back to system time. If an error would occur during a time change, the Wrapper will continue with the current time. * Add new properties wrapper.ntservice.preshutdown and wrapper.ntservice.preshutdown.timeout to allow Windows services with a long shutdown time to stop cleanly before the system starts its shutdown process. The first property specifies that the service will accept PRESHUTDOWN notifications. The second property tells the service control manager how long to wait until the service stops. These properties are not supported on Windows Itanium or Windows XP and lower. * Fix a memory leak on Fedora 21 and lower. This issue was fixed for RHEL, CentOS and Amazon Linux AMI on version 3.5.28, but was still happening on Fedora when rolling the log file in size mode. * Fix a bug where, under certain conditions, a white square appeared briefly on the screen just before the splashscreen was drawn (wrapperw). * Change name of the architecture for Linux PPC. Since 3.5.29 the Wrapper is available on Linux PPC little-endian and big-endian. To avoid confusion 'linux ppc' is now 'linux ppcbe' and 'linux ppc64le' becomes 'linux ppcle'. * In the script file, add the variable FILES_TO_SOURCE which should contain a list of files to source prior to executing any commands. * Add two tokens, 'W' and 'J', to the logging format to display the Wrapper process id and the Java process id. * Fix a bug where the Wrapper stopped to log messages coming from the Java application, or one of its child process, when it encountered a null character (\0). It also resulted in a memory leak if the Java application was logging messages at a frequency higher than the value specified with wrapper.log.lf_delay.threshold. * Fix a wrong implementation of the property 'wrapper.log.lf_delay.threshold'. The amount of time specified by this property was counted from the last log output received instead of the last line feed received. * If the Wrapper is unable to write to the configured wrapper.logfile, it would fall back to a default log file in the working directory. If this would also fail, the log file will be disabled and logging will only continue in the console, syslog or EventLog. From 3.5.30, the Wrapper will resume logging into the configured file as soon as it is available again. If the log file had been disabled and the configured log file would still be locked, then it will try to resume the logging in the default log file. * Fix a bug where a RESTART or a PAUSE action specified with wrapper.on_exit. (or wrapper.on_exit.default) would abort a SHUTDOWN triggered by an event, a filter, a timer, a deadlock, a ping timeout or failures of mounted shares. * Improve the logging messages when the action 'KILL_RESTART' is specified for the 'wrapper.event..command.block.action' property. Depending on the event, RESTART is not always applicable, but the Wrapper always notified that a new JVM was going to be launched. Now it will clearly inform when the restart is not applicable. * Improve the output when querying the status of a service on Unix. It will show whether the service is installed or not, and which system is used if there are several possibilities for the current OS (init.d, systemd, etc.). * Fix a bug where the daemon was not reloaded when the Wrapper was installed using systemd and a custom service file. * When installing a daemon with systemd or upstart, the Wrapper will now check for a previous install like it does for the other init systems. This implies that the service should first be removed to force reinstallation. * Add the ability to log the properties each time the Wrapper loads or reloads the configuration file. A new wrapper.properties.dump.loglevel property was created to control at which level they will be logged. * fixed a bug happening when launching multiple instances of the shell script in a short time interval. If one instance was starting the Wrapper while another attempted to stop it, the PID file of the new Wrapper process might eventually be deleted. Running without a PID file could cause some issues and would leave open the possibility for a second instance of the Wrapper to run simultaneously even though wrapper.pidfile.strict would be set to TRUE. 3.5.29 * Fix two problems when wrapper.lang.folder was passed in the command line to the JVM. The path was limited to 78 characters and was not put in quotes on Windows. * Add new supported platform: Linux PPC64LE 64-bit. Built on a RedHat 7.1 LE machine, GNU libc 2.17. * Fix a bug happening on startup where timers set with an interval less than wrapper.timer.max_catchup were executed several times. * Add a dynamic variable 'WRAPPER_SYSMEM_

' where

is a percentage of the physical memory available on the system. This variable can be used in the wrapper.java.additional. properties to set the values of JVM options like -Xmn, -XX:PermSize or -XX:MaxMetaspaceSize. * Fix a bug on AIX where the properties wrapper.java.maxmemory.percent and wrapper.java.initmemory.percent did not work because the Wrapper failed to get the physical memory size of the system. * Fix script (AIX only) to allow users to start and stop the Wrapper without using SRC and fix syntax error in a message when validating the size of APP_NAME. * Add the possibility to specify a timezone of the IANA tz database in which the Wrapper will be executed. Although the old timezone abbreviations are still available for backward compatibility, usage of the new IANA identifiers is preferred because it solves several issues, especially regarding daylight saving time. By default, summer/winter time changes are automatic and will update timers, mail settings and time-stamps of all log outputs. * Fix script for MacOSX where users could only start the daemon if it was installed. Now it is not necessary to install it. Introduced in 3.5.28. * Improve a workaround to a bug in a libc function on AIX which causes memory errors when messages containing Japanese characters are logged. There might still be some errors if the Java application logs very long messages through the Wrapper, but in this case a special configuration is possible as a workaround. * Fix a bug on FreeBSD 10.x where the libiconv library could not be loaded. This caused the Wrapper to stop at startup. * Fix a scaling issue on the GUI of the Demo application. The main window was too small on high-DPI displays. 3.5.28 * Fix a memory leak on Red Hat Enterprise Linux, CentOS and Amazon Linux AMI. It occurred when activating log rolling in size mode. This memory leak came from an issue in the GNU C Library (glibc) which was fixed on version 2.21. However, a workaround was added so that the Wrapper can run without issues on any version of glibc. * Fix an issue on z/OS that if the path to the Wrapper binaries was longer than 79 characters, the script could not execute the 'start' and 'stop' commands properly. It reported a stale PID file when starting the daemonized process and later failed to stop it because it could not find that PID file. * Fix the console flicker bug that was still happening when launching wrapperw.exe from a Windows shortcut. * Add the possibility to configure Processor affinity for AIX, FreeBSD, HP-UX and Solaris. For HP-UX and Solaris it is only possible to bind one CPU per process. * Fix a problem where boolean configuration properties were not assigned their default values if a wrong value was set. The Wrapper always resolved unknown values to FALSE even though the default value should be TRUE. * Add a new event 'WrapperSecondInvocationEvent' that can be consumed in the Java Application on Windows. This event will be fired whenever a second instance of the Wrapper tries to start when single invocation mode has been specified. This enables you to execute custom tasks like showing a message or bringing a window of your application to the front. * With single invocation mode, it is now possible to specify that the Wrapper will focus the foreground window of the running Java Application instead of launching it a second time. If no window can be found, the focus will be set to the Wrapper monitoring the application. This can be used in combination with the event handling to activate any window that was brought to the front. * Fix bug on Linux where RUN_AS_USER was ignored when running the Wrapper as a service controlled by systemd. The service was always running as root. * Fix bug on AIX where running the Wrapper as a service would not use the System Resource Controller (SRC) and ignore RUN_AS_USER value. Thanks to Miroslaw for pointing this out and for his contributions to the shell script. * Add a new bit flag (0x8000) to indicate that an error occurred when querying the status of a Windows service. If the query is silent (-qs option), wrapperw.exe will no longer display error messages in the dialogbox, but these errors will still be logged in the log file. * Add new system property to WrapperStartStopApp (integration method #2) to handle passthrough parameters. These parameters can be passed to the start and/or stop method of your Java application. By default the passthrough parameters are ignored (see the javadocs of the WrapperStartStopApp class for details). * On Windows, the Wrapper will now check that the code page of the console supports the specified language. If it doesn't, it will fall back to English. * Add two new values for the wrapper.lang property: 'DESKTOP' and 'SYSTEM'. These values are used on Windows and ignored on other systems. The first, which is the default, specifies that the language of the Wrapper will be resolved according to the UI language. The second will cause it to use the system language. Before, the Wrapper used the language of the Region Format settings. * Fix a bug where messages in Japanese coming from the JVM were badly encoded. This occurred on Windows when the language for non-unicode programs was not set to Japanese. * Improve the error message if there are any problems initializing the backend pipe or socket. * Add 'installstart' command to the UNIX shell script in order to install and then automatically start the Wrapper as a daemon process. * On AIX and HPUX Itanium, the Wrapper will no longer have the GNU version of libiconv as a dependency. This dependency was introduced in version 3.5.26 for AIX and 3.5.27 for HPUX itanium, but caused some compatibility issues. * Add a new '--teardown' option (and TeardownApp.bat.in) to the Windows version which can be used to reverse changes done with the '--setup' option. Administrative credentials will be requested. As of version 3.5.28, this will only unregister the application/service from the event log system. * Add a new property, wrapper.syslog.ident.enable, which tells the Wrapper to create or delete an identification (also known as source) for the event log. Starting with Windows Vista or Windows Server 2003, this action requires administrative credentials. When running on a lower permission, the Wrapper will ignore this property and start normally. * Fix a bug where actions specified by wrapper.ping.timeout.action were only performed the first time the JVM became hung. All subsequent hangs were ignored. With the default configuration, this would mean the Wrapper could only restart a hung JVM once. Since 3.5.16. * The Wrapper is now signed with a SHA-2 certificate. SHA-2 provides stronger security than the previously used SHA-1 algorithm. The decision to change the certificate was made in compliance with Microsoft's SHA-1 deprecation plan which states that Windows 7 and higher will no longer support SHA-1 after January 1st, 2016. It should be noted that Windows XP SP2 and lower, as well as Windows Server 2003, don't support SHA-2 and the new certificate will not apply on these platforms. The binaries provided for Windows Itanium will also no longer be signed. * Fix memory errors happening on AIX when the system uses some localizations like Japanese. 3.5.27 * Fix a misleading error message when there is a problem processing a system error message on Windows. * Handle system exit codes with descriptions (Windows only). * Add new configuration file directives (#properties.on_overwrite.loglevel and #properties.on_overwrite.exit) to better control how the Wrapper should behave when detecting duplicate property definitions. * Fix: No longer tries to resume a Windows service that is already running, or pause a service that is already paused. * Improve accuracy of status messages when installing, removing, starting, stopping, resuming and pausing a Windows service. * Improve the display of the available HostIds when calling 'wrapper -h'. * Fix the console flicker bug. Happened when wrapper.ntservice.generate_console was set to TRUE, which is required for thread dumping. * Fix a problem on Windows with the consoleless version of the Wrapper. If a splash screen was set at launch time, and if the Wrapper eventually failed to start normally, the splash screen did not disappear and remained on the top of the log window. * Fix a problem on Windows platforms that caused the log window to overlap all the other forms even when the focus was set on an other application. * Add the ability to define CPU affinity for the Wrapper and JVM processes (Windows and Linux systems, professional edition). * The wrapper can now be executed with a '--setup' option in order to install components under elevated mode on Windows. During the installation, the wrapper will register the application to the Windows Event Log system. After the Wrapper is executed with the '--setup' option, the Event Viewer will no longer report that the installation was corrupted. * wrapper.exe and wrapper.dll are now manifested for compatibility with newer versions of Windows (8.1 and above). * Modify the Community Edition license to allow the Wrapper to be integrated and distributed with Products released under either the GPL version 2 or GPL version 3 licenses. * Include the PID of the Wrapper and Java processes in debug output. * Fix a problem where the wrapper.jvm.port.min and wrapper.jvm.port.max properties were being ignored. This meant that the defaults were always being used. Since 3.5.26. * Fix a very infrequent issue in which the Wrapper would produce an out of memory error or fail when trying to get the path to the Wrapper on Windows XP. * Modify the wrapper.pausable and wrapper.pausable.stop_jvm properties so they are no longer reloaded when wrapper.restart.reload_configuration=TRUE. * Fix a bug when the child process printing the JVM version became a zombie after completion (problem known on CentOS). Add property wrapper.java.version.timeout to let the user set a timeout for the child process to print the JVM version before being terminated. * Avoid 4227 events in the Windows LogBook when using SOCKET values for the wrapper.backend.type property. We now try to check the port status in native code prior to opening it in Java. The Wrapper worked correctly, but a Windows issue was causing warnings in the EventLog if the port had an existing socket in the TIME_WAIT state from a previous JVM invocation. * Fix a problem on AIX with OpenJRE where the Wrapper failed to load the native library. The native library now has a '.so' extension like on other UNIX platforms. * Fix a problem on HP-UX when listing HostIds. If several HostIds were available on the machine, the Wrapper could only get the first one and reported DLPI errors when attempting to get the others. * Fix path to remove files when uninstalling the Wrapper daemon process on HPUX. * Added Linux Itanium binaries in the "Delta Pack". It was removed since version 3.5.18. * Fix an encoding conversion error when sending email on HP-UX. * On MacOSX, set the launchd KeepAlive key to "false" by default as recommended by Apple. However, it is possible to set it to "true" by editing the new MACOSX_KEEP_RUNNING variable in the script file. 3.5.26 * Improve the log messages when a JVM restart is requested when the Wrapper is in a state that it will be ignored. * (Standard, Professional) Add an additional debug message when a deadlock sweep completes. No functional change but this was added to make it easy to tell how long the sweep takes on a given application. * Clean up the internal tracking of where environment variables were set. No functional changes. * When detecting Cygwin with 'uname -o' in the script file, some OS's (Solaris, MacOS, etc.) show an error message because '-o' is not a valid parameter. Now we hide this error message to avoid confusion and we can guess that Cygwin is not running. * Add the ability to control the service with systemd using the script file. * On Mac OS X, fix a typo, add another alternative way to find Java to set the JAVA_HOME environment variable, and use the full path to execute sysctl in case it's not in the path. Thanks to Dannes Wessels for pointing this out. * Fix a problem on newer versions of Windows when customizing the Wrapper. The certificate was not removed when creating the target. This was leading to an error when trying to sign the target with a new certificate. * Add missing support in the shell script for restarting/stopping the Wrapper service with launchctl on Mac OS X. * Add missing support in the shell script for restarting/stopping the Wrapper service with Upstart. * Add the ability to set the path of the 'su' command as well as the ability to specify additional arguments in the Wrapper shell script using new SU_BIN and SU_OPTS variables. * Fix a problem in the WrapperSimpleApp, WrapperStartStopApp, and WrapperJarApp helper classes where command line problems that resulted in the JVM exiting on startup looked like unexpected exits. This meant that the Wrapper would try to relaunch the JVM again rather than giving up on the first attempt. * Modified the WrapperSimpleApp, WrapperStartStopApp, and WrapperJarApp helper classes so that their usage banners will only now be shown for command line related problems. Issues with the specified classes, methods, or jar files are still logged as errors, but showing the user the usage in these cases was confusing. * Correct the WrapperResources.getString() method that has no replacement arguments so that its processing of the raw string is handled the same as methods which have replacement arguments. Now all strings are processed by the MessageFormat class before being returned. Previous versions were not consistent about the need to escape single quotes. * Added optional key validation to make sure that strings passed in to the WrapperResources.getString() method are formatted correctly. See the WrapperResources class for information how to enable. * Clean up escaping of single quotes in a few messages. * Add new options for property wrapper.backend.type: AUTO, SOCKET, SOCKET_IPv4, SOCKET_IPv6, PIPE. When set to AUTO, the Wrapper will try to open a socket ipv4 and fallback to ipv6 and to pipe in case of error. * Fix bug when converting from multibyte to wide char without setting the locale at startup (Unix only). Thanks to Bert. * Add a 'CLOSE_BACKEND' alias to 'CLOSE_SOCKET' as a test command for the wrapper.commandfile property. This is because there have been multiple options for the backend connection for some time and the name is more portable. * Fix a problem on Mac when running OS X Yosemite where the script was failing to correctly compare versions '10.10' to '10.5.0'. This was leading to the script thinking that only 32-bit binaries could be run. This was only an issue if the delta pack naming of the wrapper binaries was being used. * Add a new wrapper.java.classpath.missing.loglevel property which makes it possible to control how the Wrapper logs warnings about missing classpath elements. Previously this was always logged as debug output and could not be disabled. * If there are any low level problems launching the Java process that are not related to configuration or the JVM itself, the Wrapper will no longer try again as such retries were meaningless. * Windows system APIs have a problem that cause a process to crash if it attempts to set an environment variable over 32767 characters in length. The Wrapper now checks for this and logs an error rather than setting the variable. This was a problem if very large classpaths were used when the wrapper.java.classpath.use_environment property was TRUE. * Windows does not allow a command line to be longer than 32766 characters in length. Different versions of Windows handle it differently when such a long command is provided. The Wrapper now does its own check and shows an error in a consistent way. * Improve the error message on UNIX platforms when the command line is longer than the system maximum. This maximum varies greatly by platform and environment and is difficult to calculate reliably, so the maximum length is not currently shown in the message. * Fix a problem on UNIX platforms where a very large configuration file could cause the WrapperManager to fail to startup. This was caused by the partial transfer of the configuration file to the WrapperManager via the backend connection. This would happen regardless of the backend type used. The size of the configuration file that would cause a problem varied greatly based on the specific system. * Fix a problem on Windows platforms where a very large configuration file would fail to be sent to the WrapperManager on startup when the wrapper.backend.type property was set to PIPE. The only problem was that the WrapperManager.getProperties() method would be empty. * (Professional) Add checks to the WrapperManager.exec() methods on Windows for the maximum command line length of 32766, and maximum environment variable name-value pair length of 32767. * (Professional) Fix a problem where a free of a NULL pointer was being done as part of the cleanup process of a WrapperManager.exec() call. This is not known to have caused any issues. * Added getStdOut(), getStdErr(), and getStdIn() methods to the WrapperProcess class as aliases to the getInputStream(), getErrorStream(), and getOutputStream() methods to help avoid confusion. * Fix a problem on Windows 7 where long log lines got corrupted after 31397 characters when wrapper.console.direct was true. This seems to be a problem with the underlying Windows API and was only a display issue. Reduced the maximum number of characters that can be written with a single API call to 30000 to work around the issue. This change is internal and there is still no limit on the length of logged lines. * Fix a deadlock if the Wrapper failed to write to the backend when the wrapper.backend.type property was PIPE. Found by code review and is not known to have actually happened. * From Mac OSX 10.5.0, the script file will use the key 'KeepAlive' in the launchctl configuration file. Prior versions of Mac OSX will continue to use the same key 'OnDemand'. Thanks to Robin for pointing this out. * (Standard, Professional) Disallow the use of hostids tied to Apple Bluetooth devices when running on a virtualized Windows system hosted on an OS X system. * Fix a problem where WrapperManager.log() was not working correctly when multi-byte messages were logged. * In the debug output the full configuration properties packet is suppressed from the log output because it is huge and can contain sensitive data. Add a size to the abbreviated output to assist with debugging. * Fix a memory leak on UNIX platforms whenever an error message was reported. This was only a problem if the message was logged repeatedly. * Correct a couple other potential memory leaks found through code review. Not known to have been causing any actual problems. 3.5.25 * (Professional) Improve the wrapper.timer..interval property so it is now possible to specify ranges and lists of values as well as offsets for interval values to more precisely control when timers are fired. * (Professional) Fix a problem with the wrapper.timer..interval property where timers would not fire during an interval the system time was set back. Also fixed a problem where timers would stop firing permanently if the system time was set forward by more than the value of the wrapper.timer.max_catchup property and a timer had been scheduled to be fired during that interval. Both of these issues were most likely during daylight savings time. * Fix a problem where signals received by the JVM were not being logged in debug output correctly if the wrapper.ignore_signals property is set to true. We now also log debug output even if a user event listener consumes the signal event. * Fix a problem on Gentoo Linux where the shell script was not correctly detecting the system architecture. This may also be a problem on other distributions whose 'uname -p' returns a detailed architecture. * In the shell script, when the flag to use systemd (USE_SYSTEMD) is set, the shell script generates a ".service" file in /etc/systemd/system/ when installing the Wrapper as a daemon. * In the shell script, add a function to validate the size of APP_NAME when installing or removing the daemon on AIX. * It was possible to disable the logging of the Java command line even when debug output was enabled by setting the wrapper.java.command.loglevel property to NONE. This made it difficult to debug problems and is no longer possible. * When the wrapper.java.version.output property is set to true, add debug log output to show the actual command line used. * Fix a problem on Windows when the wrapper.java.version.output property is true where it was possible that java executable being run to get the version could be different than that used to run the application if the java executable was being located on the default system PATH as well as the PATH defined in the environment. The Wrapper now looks once and uses the same fully resolved path in both places. For clarity, both java command lines are now included in debug log output when the version is requested. (Bug #288) * Change the timing of the logging of the Java command line on UNIX so it is consistent with Windows. * Improve the error message text thrown when a native feature is not available from Java to be more clear about whether the problem is that the native library could not be loaded versus the wrong edition being used. * On Windows, detect when the Wrapper is running under Cygwin and set the default value for wrapper.console.flush to TRUE. On other platforms, the script will display a message and stop running. * (Professional) Add support for WRAPPER_EVENT_TIME_* and WRAPPER_EVENT_RAND_* variable references so event times can be used when events are fired. * Fix a buffer overflow problem on AIX which caused crashes or deadlocks on startup for some users. This had been an issue since 3.5.0 but only reported recently. * Remove output debug messages on UNIX when the wrapper.port.address property was set. * Clean up code when converting multibyte characters to wide characters. Some error checks were not implemented correctly. Found during a code review and is not known to have actually caused any problems. 3.5.24 * Fix a problem where the message source of remote syslog messages from the JVM were being logged as "jvm %d" rather than "jvm 1". * Add a new wrapper.syslog.split_messages property which controls whether or not multi-line messages will be logged as is or first split into individual lines. * Fix a problem on Windows Vista and above where the wrapper.single_invocation property was not correctly identifying Wrapper instances running in different sessions under some circumstances. 3.5.23 * Clean up the error messages logged when the Wrapper failed to elevate itself on Windows platforms. They are now more informative. * (Professional) Fix a handle leak on Windows in WrapperProcess.isAlive(). * (Professional) Modify the exception thrown when WrapperManager.exec is called while the Wrapper is shutdown so it now says that rather than saying that the native library is not loaded. * (Processional) The Wrapper is now more careful to make sure that the backend is never closed while a child process is being launched to ensure that the Wrapper knows about all child processes launched. * (Professional) Add a warning message in case the Wrapper is not notified of a launched child process due to the backend connection being closed. * (Professional) Fix a potential NPE that could be thrown while reading stdout or stderr output from a child process while the Wrapper was shutting down and after the child process exited. * (Professional) Fix a problem on UNIX platforms where we were getting stuck trying to cleanup after a process which was currently blocking on a read from stdout or stderr. * (Professional) Fix a problem on UNIX platforms where a timeout attempting to obtain an internal lock of the child process list was causing an internal counter to get out of sync, leading to a other terminated child processes being left as defunct zombies until the Java process terminated. * (Professional) Fix a problem on UNIX platforms where pipe file descriptions used to communicate with child processes were being incorrectly passed to all child processes. They were not growing over time. * (Professional) Fix a potential synchronization problem calling WrapperProcess.waitFor() or WrapperProcess.exitValue(). * Add additional debug log output showing the various timeout values to help with debugging user issues. * Fix a problem where the shell script was not correctly detecting the OS version with Mac OSX Maverick. * Add warnings about undefined environment variables in include file references in the configuration file. * Add support for environment variable expansion in files specified with the wrapper.java.additional_file and wrapper.app.parameter_file properties. * Correct the integer formatting in the WrapperUNIXGroup.toString() method so it is consistent with the rest of the Wrapper. * Fix a problem where the iconv library requirement couldn't be satisfied in FreeBSD 10. We now locate the library dynamically by looking for /usr/local/lib/libiconv.so, /usr/local/lib/libbiconv.so, or /lib/libkiconv.so.4 in that order. * Fix a the WrapperPrintStream.println method so strings containing linefeeds will correctly have each line prepended with the configured header. * (Standard, Professional) When an unknown license type is encountered, include the unknown type in the error message to help with debugging. * (Standard, Professional) Fix a problem on FreeBSD systems where the wrapper.lang.encoding was not resolving to UTF-8 correctly when the LANG environment variable was not set. * (Professional) Fix a memory corruption problem on z/OS when the language was set to a double byte locale like Japanese. * Go through and make the Wrapper much more durable when badly encoded double byte characters are encountered. 3.5.22 * (Standard, Professional) Fix a crash in native code behind WrapperResources instances which could happen if the system was very low on memory. Since version 3.5.7. * (Professional) Fix a couple slow memory leaks in the native code involved with launching and checking on the status of child processes. * (Professional) Fix a problem where an attempt to throw a WrapperJNIError within native code on Z/OS would result in a ClassNotFoundException being thrown. * Reviewed the native JNI code and fixed a few potential crashes and memory leaks which could occur under low memory conditions. * Modify the way the wrapper.console.direct property works so the Wrapper will now always downgrade itself to using piped console writing as soon as it has determined that a physical console does not exist. In 3.5.21 we tried to predict when the console would not exist and disabling it in advance. There were cases where this was not correct, resulting in error messages in the console output. * Fix a problem where operations like installing a service on Windows 7 and above which need to be elevated were resulting in an error that the Wrapper could not write to the console. The actual operation was working correctly but the console output was incorrect. Since 3.5.21. * Move the check that the jar and wrapper versions match earlier in the startup process so we guarantee that a warning will always be displayed. There were some errors which could abort the startup process before this warning was logged. * (Standard, Professional) Fix a problem where the value of wrapper.lang.folder was not being recognized if a wrapper.lang value was not set. * (Standard, Professional) Fix a small memory leak resolving the language to run the Wrapper with. * (Professional) Fix a potential buffer overflow reading data from a child process stderr or stdout if the amount of data available in the pipe is larger than the buffer length passed in to WrapperProcessInputStream. read(byte b[], int off, int len). * (Professional) Fix a problem where reads from a WrapperProcessInputStream were consuming too much CPU while blocking for data on Windows. Now correctly being done asynchronously. * (Professional) Fix a problem where JVM cleanup, including cleanup of child processes, was not always being done completely if the user requested an immediate shutdown by pressing CTRL-C twice. 3.5.21 * Add a new 'R' log format which will track the number of milliseconds since the previous JVM log output. Useful for simple performance checks. See the wrapper.console.format, wrapper.event.default.email.maillog.format, wrapper.logdialog.format, and wrapper.logfile.format properties. * When the ACCESS_VIOLATION testing command of the wrapper.commandfile was used to kill the Wrapper some log output was not making it into the wrapper.log file. Add a forced flush just before crashing the Wrapper to make sure everything makes it into the log file. * Add a new wrapper.javaio.buffer_size property which makes it possible to control the size of the internal buffer used to process console output from the JVM. Also increased the default from the system default to 65536 bytes. * Renamed the wrapper.use_javaio_thread property to wrapper.javaio.use_thread. The old property still exists as a deprecated alias. * Added a new wrapper.console.direct property on Windows which tells the Wrapper to use Windows APIs to render log output to the console rather than writing to stdout and stderr. This results in a major speed improvement. The drawback is that this removes the ability to pipe the output to another program on the command line. We chose to enable this by default so the majority of users can take advantage of the speed improvement. If your application takes advantage of piping on Windows, you will need to disable this property to regain the old behavior. * Add a new "wrapperm" logging source on Windows to help differentiate which log entries are coming from Wrapper invocations used to control the Wrapper as a service such as starting, stopping, installing, etc. Previously the log entries from the Wrapper service process and launching process both used a "wrapper" logging source, which could be confusing. * On UNIX, modify the way we keep track of whether the process is daemonized or not. This was done to clean up a bit, but should not effect how the Wrapper works. * On Windows, change the timing of when PID files are created when running as a service so any failures creating them will correctly be reported as a service error. If the Wrapper was unable to write to a PID file because it was read-only then the user was presented with a confusing message about the service timing out on startup. This was in addition to the correct error being logged. * Fix a potential problem where internally queued log entries could fail to be logged on shutdown. The log queue is now always processed as part of the shutdown process. * Modify UNIX shell script to remove all existing run level rc files for the application on both install and remove, rather than only the ones for the currently configured run level to avoid unexpected behavior due to old rc files being left around. This only affects platforms which make use of rc files. * Add a new RUN_LEVEL variable to the UNIX shell script to make it easy to configure the run levels when installing as a daemon on various platforms. * Add new wrapper.logfile.close.timeout and wrapper.logfile.flush.timeout properties, and deprecate the wrapper.logfile.inactivity.timeout property, to increase control over when and how the logfile is flushed and closed. * Add a new PIDFILE_CHECK_PID setting in the UNIX shell script which makes it possible to control whether or not the script double checks that the pid in an existing pidfile is actually the pid of the application's Wrapper. This property makes it possible to disable this check so multiple applications can be tied to the same pid file. * Go through and make sure that none of the Wrapper native JNI methods are called after the WrapperManager shutdown has completed. Doing so was causing a JVM crash on some Linux JVMs. This was happening when a WrapperResources instance was finalized by the garbage collector on shutdown. * (Professional) Fix a problem where processes created by calling the WrapperManager.exec functions could fail to be registered with the Wrapper for cleanup if the call was made as the JVM was shutting down. This was resolved by making sure that JNI calls can not be called after the WrapperManager shutdown has completed. * Modify the internal WrapperManager tick thread so it is allowed to complete once the WrapperManager has fully shutdown. This was the only remaining reference to the WrapperManager class. * Add a new wrapper.property_warning.loglevel property which controls the log level at which the Wrapper will log warnings about undefined but referenced environment variables, as well as invalid boolean or integer values. * Update the way environment variable references within property values are parsed to make it possible to report those which are not defined. Each pair of '%'s is now expected to be part of a variable reference. For example "%AAA%BBB%CCC%", previously if "AAA" was not defined, "BBB" would be located, but now only "AAA" and "CCC" will be recognized as variables. * Fix a problem on Windows where a non-existent wrapper.working.dir directory was causing multiple error messages in the log file. * Modify the way the wrapper.environment.dump property works so it will now log at the INFO level with the rest of Wrapper output when set to true. When false however, the output will be logged as DEBUG output if enabled. Previous versions always logged the output to the INFO log level in either case. * Fix a problem on Linux IA64 where the WrapperActionServer was throwing an IOException when the JVM was shutdown by calling System.exit. It did not cause any problems other than the message in the log. * (Professional) Added new "jvm_ping_timeout", "jvm_ping_slow", and "jvm_ping_response_slow" events to help respond to ping related issues. * Fix a problem where a value of 0 for wrapper.ping.alert.threshold was not correctly disabling ping threshold alerts. * (Professional) Fix a problem where the thread that handles events would permanently get stuck if the event queue filled up. This was very unlikely but not impossible if a large number of filter events were triggered in a very short time. Corrected the problem so it now recovers correctly, but also increased the queue size to make the overflow even more unlikely. * (Standard, Professional) Fix a problem where console output of a wrapperw.exe processes launched when the wrapper.ntservice.console property was TRUE was not being shown correctly. This issue was introduced in 3.5.19. 3.5.20 * Further improvements to the memory footprint of the Wrapper to minimize the memory required when logging JVM output consisting of very long lines. * Fix a minor potential buffer overflow, which could occur if the path of the first classpath element is larger than 1024 characters. This overflow was detected during a code review and we have no reports that it actually ever caused any problems. * Improve the error message displayed when the Wrapper's configuration file could not be loaded so that it now includes the name of the file. * Work around a libc system library bug on some HPUX platforms in which calls to vswprintf can cause a crash if the expanded string does not fit into the buffer. Worked around the problem with the help of HP support by making sure the buffer length followed a rule so that its length was 1+N where N is a multiple of 8. * Fix a problem on HPUX platforms where the JVM would sometimes get stuck on shutdown, when the shutdown was initiated within the JVM, leading to the Wrapper having to forcibly kill it. This was caused by the Wrapper attempting to close the backend socket when another read was blocking attempting to read from the same socket. This was introduced in version 3.5.8. * Fix a potential log corruption when queued log messages were larger than the internal buffer size. Found during a code review and is not known to have actually caused any problems. * Fix a typo in the shell script which was breaking the 'install' command on Solaris platforms. * Fix a potential crash on HPUX platforms if the value of the wrapper.port.address property had an encoding problem. 3.5.19 * Fix a problem in the batch file, where a space in the path to the Wrapper.exe file would cause the script to locate the Wrapper.exe file. Introduced in 3.5.18. * When running as Windows service with hiding the console of the Wrapper will cause the Wrapper to disable unnecessary logging to the console in order to enhance performance. 3.5.18 * Fix a problem, where an unclosed percentage character '%' was opening the chance of a dangling pointer in the additional java parameters. The '%' character is a special character, specifying an environment variable. * Added variable _WRAPPER_DIR the batch files to make it possible to specify any other directory where the Wrapper binary file is located. Until now the batch file and exe file had to be in the same location. Thanks and credits go to Geoff. * Added property wrapper.port.address, which makes it possible to specify a different address to bind the socket to when using the socket backend connection between the Wrapper and the JVM. Until now, the socket was always bound to the localhost loopback interface. * The script will from now on also use the update-rc.d tool for installing an application as daemon on Debian. Thanks and credits go to Haifeng. * Whenever the Wrapper is causing the JVM to be forcibly terminated, the Wrapper will make sure the JVM has actually been terminated. If it wasn't after wrapper.jvm_terminate.timeout seconds, a pending restart will be canceled and the Wrapper exit. * Reworked the way the Wrapper is processing output from the JVM, in order to increase performance on huge output on a single line and also reduce memory usage. 3.5.17 * Add a new wrapper.java.additional.default.stripquotes property to make it possible to specify the default value of wrapper.java.additional..stripquotes * Fix a bug where the timer failed to calculate the fire time when that time was more than one week in the future. This was possible for weekly timers which spanned a daylight savings time change which rolled the time back by an hour in the fall. * Fix problem in the shell script, where it might fail to remove an installed daemon after the location of the script has been changed. * Add additional advice messages when a Windows service fails to be started due to file access problems involving the Wrapper binary, configuration, or log files. * Fix a problem where the dynamic library on MacOSX was not able to load it's functions. * Added wrapper.app.parameter_file property, which works similar to the wrapper.java.additional_file property * Reduce CPU-consumption of WrapperProcess.waitFor() function 3.5.16 * (Standard, Professional) Retry failed share mappings if the target host or network is unreachable as that may be a temporary problem. * (Professional) There was a problem where the IO-redirection of a child process which got created with the WrapperManager.exec API and used the feature to run the child process in the logged on users desktop was only allowing to create a process once per second. * Include information about the base configuration file in the debug output when debugging of cascading configuration files has been enabled. * Add a check in the UNIX script to output a more descriptive error message, when the user specified in the RUN_AS_USER variable doesn't exist. * (Standard, Professional) Fix a problem where console log output was not being displayed correctly when running with the WrapperW.exe binary with the wrapper.ntservice.console property was set to true. * (Standard, Professional) Implement the wrapper.ntservice.generate_console property when using the WrapperW.exe binary so it is now possible to disable the creation of the hidden console. * Modify the way the wrapper.ntservice.generate_console property works so it is now easier to disable the generation of the console using just this property. * Improve the message logged when the Wrapper attempts to perform a thread dump without a valid console being available. * Add new wrapper.ping.alert.threshold and wrapper.ping.alert.loglevel properties which make it much easier to debug ping timeout issues by asking the Wrapper to log messages about ping responses which were shorter than the registered wrapper.ping.timeout, but longer than the threshold. * Add a new WrapperManager.appearSlow method which makes it easier to test how the Wrapper behaves when the JVM is being slow to respond to commands. * Add a new wrapper.disable_tests property which can be used to disable all of the testing methods of the WrapperManager class. It has always been possible to control their access with a SecurityManager, but this is simpler for most applications. * Update the default wrapper configuration file template so a restart due to a matched OutOfMemoryError filter will no longer be triggered by default if the user enables -verbose:class output. * Fix a problem on UNIX platforms where the Wrapper would fail to start if it was located on the system PATH. This had been a problem since version 3.3.0 but had gone unnoticed as the Wrapper is not usually referenced in this way. * Rework the internal flags governing the generation and hiding of the backend console on Windows so we are able to almost always obtain the console's window handle. * Cleanup some startup code to reduce duplication and make sure that more debug and warning messages are logged after the "Wrapper Started" message. * Add new wrapper.java.additional_file and wrapper.java.additional_file.stripquotes properties to make it possible to specify JVM parameters in a file. * Add support for Linux on ARM systems. * Re-Enabled the forced reloading of the SYSTEM (and if set to a specific account, the user) registry before launching the Wrapper as a service on Windows XP and 2003. This has been originally disabled for Windows XP and 2003 since version 3.5.5. * (Standard, Professional) Fix a problem where the instance class names logged when a deadlock involving ReentrantLock instances were corrupted. The actual deadlock detection was working correctly, but this could have lead to other problems caused by the corruption. A workaround was to set the wrapper.check.deadlock.output property to SIMPLE. * (Standard, Professional) Make it possible to completely disable the details of a deadlock by setting the wrapper.check.deadlock.output property to NONE. * (Standard, Professional) Object Ids in thread dump reports were not correctly being logged as 64-bit ids on 64-bit JVMs in some cases. * Fix a problem where the source code values returned by the WrapperServiceActionEvent.getSourceCode() method were incorrect. The constant values were incorrect and have been corrected from this release. * Add new WrapperServiceActionEvent.getSourceCodeName() and WrapperServiceActionEvent.getSourceCodeName(int actionSourceCode) methods which returns a localized name of the source where the event originated. * Fix a minor problem where a couple uncommon backend packet codes were not being correctly identified by name in the debug log output. Functionally they were all working correctly. * Added property wrapper.ping.timeout.action, which will let you specify an action in case the timeout triggers. So far the only action was to restart the JVM. * Fix a problem where a JVM process was not stopped completely on a UNIX platform and stayed defunct after a forced kill until the Wrapper process itself stopped. This was especially noticeable if the JVM is frozen and the JVM is being killed forcibly. * Add additional debug log output showing the various timeout values to help with debugging user issues. 3.5.15 * Add a new _WRAPPER_CONF_OVERRIDE setting to the Wrapper dedicated command batch files on Windows so it is now possible to control whether or not the first parameter is the configuration file name. The ability to specify an alternate configuration file is now disabled by default as it was confusing for users who tried to pass other parameters to the JVM. * Correct a couple log messages in the WrapperManager class which were missing the correct prefix identifying where they originated. * Remove some old reflection code needed for Java 1.2.x support as we have required Java 1.4 since version 3.4.0. * Remove some code to try to reconnect the backend socket from Java. It has never been possible to do so without restarting the JVM, and the related messages were confusing. * Add a new wrapper.disable_forced_shutdown property to make it possible to disable the feature to forcibly kill the JVM on shutdown if CTRL-C was pressed twice. * Reduce the number of times thread priorities are changed within the WrapperManager class to simplify the startup and shutdown process. * Fixed a dangling pointer problem, which could cause undefined behaviour whenever a property contained an unset environment variable. * Fix a race condition in the timer thread, which could cause a sigkill being propagated through the whole process group rather than the timer thread. This can only happen during the shutdown of the Wrapper. * When a child process, which got launched by WrapperManager.exec() failed to start due to a runtime-error (such as missing privileges), the forked heap persisted and the child process never finished until shutdown/restart of the JVM. The error only appears on Unix platforms when using the FORK_EXEC start-type. * Change log level and message if a certificate check returned a problem, which is not directly caused by the signature of the Wrapper, but the signature chain. * Fix a problem when the silent query command wasn't returning the correct exit code on windows Vista (and later) when the command was run from an unelevated console. Thanks to Darren for pointing this out. * The java system property wrapper.backend.so_timeout was ignored if it was set to 0, making it not possible to explicitly set the timeout to be indefinitely. * Added the properties wrapper.java.additional.auto_bits. to individually turn on/off the feature for the supported platforms. * Fix a problem where the script was trying to use the 64-bit binaries on Mac OSX even if the CPU was only a 32-bit architecture. This only affected versions of Mac OSX greater 10.5.0, the vast majority of those machines are already 64-bit CPU's. * The Wrapper when reloading the configuration file, was trying to access data from the call stack of a function which was actually outside of the memory range of the stack. This access violation might yield a segmentation fault. This issue was introduced in 3.5.5. Thanks to Lincoln for helping finding this problem. 3.5.14 * Fix a problem in the AppCommand.bat.in file where a parenthesis in the file name of the Wrapper binary would have caused a "PATH was unexpected at this time" error. * (Standard, Professional) Fix a problem when using a localized version of the Wrapper on Windows 64-bit platforms where the Wrapper would continue to use the default system language even wrapper.lang was used to specify a different language. Introduced in 3.5.12. * Fix a problem in the Windows AppCommand.bat.in command based batch file where the 'status' command was incorrectly being reported as 'query' in the usage output. The 'status' command had always worked correctly if used. * Fix a problem on UNIX platforms where some asynchronous messages were causing a warning message "Coding Error..." to be logged in place of the intended message. This could be seen if the configured log file did not have write permissions. Other than the incorrect log message, the Wrapper worked correctly. Introduced in 3.5.2. * Fix a problem in the UNIX script where running with Upstart wasn't working correctly when RUN_AS_USER was set. * Relax security checks when running the 'status' command against the UNIX shell script so it now allows any user running the script to perform the read-only check of the pid file. * Fix a problem with the UNIX script where the 'remove' command was trying to stop a running application even when the application had not been installed. * Fix a buffer overflow which could potentially cause a crash during the installation of a Windows Service when wrapper.ntservice.account was specified. This was introduced in 3.5.12. * Fix a heap corruption which could occur on startup and potentially cause a crash. Only Windows systems, which use the System Event logs, were affected. Discovered from a code review, there had never been any reports of this causing problems for users. This could happen if the configured wrapper.log could not be written to as the Wrapper always tries to write to the Event Log in such cases. Introduced in 3.5.12. * Add a new version comparison between the UNIX shell script and Wrapper to start showing a warning in case of a version mismatch. The check will only work if the shell script and Wrapper are each of at least version 3.5.14. * Added a new wrapper.pidfile.strict property which will tell the Wrapper not to start if the pid file already existed. Defaults to false for backwards compatibility. * Make the Java side of the backend socket more resilient in case of a read or write timeout. The backend socket does not have a timeout set by default so this should not have been an issue. A couple users reported problems on specific systems however which led to this fix. * To aid in the testing of the backend socket timeout, a new wrapper.backend.so_timeout system property was added to make it possible to configure the backend socket to use a timeout. See the Javadocs of the WrapperManager class for details. 3.5.13 * Fix a typo in the script where the environment variable 'TR_BIN' should actually be 'TREXE'. This was causing the "install" command on UNIX platforms to fail. Introduced in 3.5.12. * Fix a heap corruption which could lead to a crash that would occur the second time an internal buffer used for logging was expanded. The buffer would be expanded the first time a log line over 2048 characters in length was encountered. Then the second expansion would happen when a line at least 1024 characters longer was encountered. Introduced in 3.5.11. Bug ID #3423108 3.5.12 * Put more descriptive Text in case the Wrapper is using integration method 4, but the jar file deos not specify the Main-Class correctly in its meta information. * Fix a bug when failing to grant the LogOnAsService permission to a domain user. * Fix a bug where the ident for the syslog on Unix platforms was broken since 3.5.0. This was because when opening the syslog, the Wrapper was freeing the memory for pointing to ident. However the string pointer ident will be retained internally by the Syslog routines. And must not free the memory that ident points to. Bug #3404978. * Add a check on the script to make sure the 'tr' command exists on Unix platforms. * Improve the parsing of log formats so that format tokens are recocognized even if they are lower case. This affects the wrapper.console.format, wrapper.event.default.email.maillog.format, wrapper.logdialog.format, and wrapper.logfile.format properties. * The Wrapper parses log formats by looking for known tokens, any invalid tokens are simply ignored. If the entire format is made up of invalid tokens then this resulted in the Wrapper logging an empty line, which was not very useful and caused confusion when encountered. The Wrapper now reverts to the default log format in such cases. This affects the wrapper.console.format, wrapper.event.default.email.maillog.format, wrapper.logdialog.format, and wrapper.logfile.format properties. * Improve the debug output while loading native libraries to avoid confusion about the meaning of the warning logged about attempts to load alternately named native library files. * Fix a problem on Unix platforms where the default umask was being set to 0000 rather than inheriting it from the parent process when running as a daemon process. This can be a security problem as log and other files have global read/write access. Introduced in 3.5.8. Can be worked around by setting the wrapper.umask property to a safe value. 3.5.11 * Fix a potential crash on Windows caused by a buffer overflow. This has been a problem since version 3.5.0 and affects configurations which define more than one wrapper.ntservice.dependency.. Depending on what was in memory, this did not always result in a crash. It has very reproducible behavior for a given configuration file. * Fix a problem on Windows where the Wrapper was taking 15 seconds or longer to startup on some systems because the WinVerifyTrust system call was having problems updating the CRL. This had been a problem since the Wrapper binaries started being signed in version 3.5.7. If the WinVerifyTrust call takes longer than the configured wrapper.startup_thread.timeout then the Wrapper will continue to startup without further delay. * (Standard, Professional) Explicitly remove the certificate of the customized binary during customization. There were problems resigning the binary with another certificate otherwise. * If the Wrapper is unable to write to the configured wrapper.logfile for any reason then we always fall back to a default log file and then log a message about the failure. If the default also fails then that is also logged but the messages would only be logged to the console in most cases. Modify the Wrapper so we now always send both messages to the syslog or EventLog regardless of what the wrapper.syslog.loglevel is set to. This is important to help track down the cause of logfile access problems. * Starting with version 3.5.0, it was internally possible to print out multi-line log messages so that all of the lines would always be grouped together in the log file. This version modifies the logging code slightly so that such multi-line log output is now logged as a single message in the UNIX sylog or Windows EventLog. * Fix a problem where very long lines of output from the JVM would cause the Wrapper to appear to hang for a while. The first time a single line of output containing several hundred thousand characters was logged, an internal buffer was being incrementally increased by 100 characters per cycle. The Wrapper now increases the size based on last known size to greatly reduce the number of cycles needed to choose a new buffer size. * Modify the PAUSE_THREAD command so it is now possible to wait indefinitely. Only useful for testing the Wrapper. * Add a new PAUSE_LOGGER command to make it possible to pause the next log entry. Only useful for testing the Wrapper. * On UNIX, the stdout/stderr pipe between the JVM and Wrapper was not being cleaned up correctly. This resulted in a small leak but was otherwise harmless. The pipes are now cleaned up and initialized for each JVM instance. * Fix a problem where the Wrapper could fail to restart the JVM when the restart request originated in the JVM if the system was experiencing very heavy IO resulting in long disk IO queues. This was causing the Wrapper's main loop to block on the write and miss the restart request, causing the Wrapper to shutdown rather than restart. This could affect all platforms. On Windows, it could also be reproduced by making a selection in the console to freeze output and then making a request from within the JVM to restart. * Add a new WrapperPropertyUtil helper class to make it easer to access Wrapper property values from within the JVM. * Fix a bug on some platforms where java log output could get corrupted due to misuse of a strncpy system function. This function warns that some implementations do not support overlapping memory copies. The problem could only be reproduced on a single Linux test machine in lines following an empty line of output. This problem has existed since 3.4.0. 3.5.10 * Setting wrapper.jvm.port to '0' (zero) will make the JVM to assign an available port for the backend socket by itself. * Add warnings in the log file if an integer configuration property value contains a non-numerical value. Previously, the Wrapper would silently ignore the problem and use the value of 0 if the number started with an invalid character, it will now return the default value. If the property value started with valid numerical characters then those were, and still will be, used to generate a value, but the invalid characters will be trimmed. The later is being kept this way to avoid breaking old configurations. * Add warnings in the log file if a boolean configuration property has any value other than TRUE or FALSE. It will still return a value of FALSE for other values to avoid breaking old configurations. * Add a warning if an invalid value is specified for the wrapper.on_exit. property. * Add a new wrapper.log.lf_delay.threshold property which makes it possible to control how long a partial line of Java log output will be allowed to be buffered until it is completed with a line feed. If the threshold is exceeded then the partial line will be logged as a full line resulting in an extra line feed in the log output. All previous versions would do this within 1-2 ms. The default is now 500ms. * (Standard, Professional) Make it possible to customize the manufacturer through the customize options. * (Professional) Fix a problem where the Wrapper was sending a CTRL-BREAK rather than a CTRL-C signal to child processes launched with WrapperManager.exec when destroying them on Windows. For most child processes this was not a problem, but if the direct child process was a JVM then the CTRL-BREAK was triggering a thread dump rather than asking the JVM to exit. The Wrapper was then timing out and forcibly killing the JVM child process. * (Standard, Professional) Fixed a bug, where the timezone ICT when set by the wrapper.timezone property got misinterpreted as IST. * (Standard, Professional) Fixed a problem where the UTC offset value in the wrapper.timezone property was not being parsed correctly on UNIX platforms. This led to the Wrapper to using an offset UTC0000. * Take out the warning about unset LANG environment variable on Linux and AIX systems. On system startup, some systems fail to set the LANG environment variable. This is not really a problem for the Wrapper and the warning was causing confusion for users so we decided to remove it. 3.5.9 * (Standard, Professional) Fix a problem on Windows where network adapters whose names contained "PRO/1000" were being removed from the list of hostids displayed when "wrapper.exe -h" was run. This did not affect existing server license key files generated for hostIds reported by 3.5.7 or earlier, or development license keys. But it did cause the Wrapper to report that no valid hostIds could be found when the Wrapper was started without a license file. This was caused by some test code added in 3.5.8 that we failed to remove. * Fix a problem where the Wrapper was not correctly yielding control back to its main loop when very large amounts of continuous output was being logged from the JVM. Introduced in version 3.4.0. In versions prior to 3.5.8, this could have caused the JVM to timeout and restart itself. That particular issue was resolved but the Wrapper process in 3.5.8 would still have been unresponsive when this was happening. The Wrapper will now always yeild back to its main loop after 250 milliseconds of continuous logging. * Fix a problem where the WrapperManager could block trying write debug output if the current user application was writing very large amounts of output to the console as well. In extreme circumstances this led to the Wrapper thinking that the JVM was frozen. This was only an issue if debug output was enabled. * Restructured the shell script so all editions now use the same script again. 3.5.8 * (Standard, Professional) Starting with version 3.5.5, we invalidated the use of all 00ff* hostids on Windows to avoid problems with changing hostids when users have a Juniper Network Connect network adapter on their system. This turned out to be too restrictive as Guest OSs running under Parallels also make use of this hostid range. The Wrapper is now more careful to only invalidate actual Juniper Network Connect hostids. * (Standard, Professional) Improve the message shown to the user when the Wrapper is unable to locate any hostids for a system. * Fixed a problem with the wrapper script on Solaris, where the option -F was not available for grep. * Added Windows version information on the wrapper debug output. * Added a wrapper.log.warning.threshold property which makes the Wrapper show a warning whenever it detects that the Wrapper took a long time to record a log message. This was added to test a reported issue caused by slow IO on very heavily loaded systems. * Added a new 'G' log format to log the time in milliseconds of the previous log message. See documentation with the wrapper.log.warning.threshold property. Added to the wrapper.console.format, wrapper.logfile.format, and wrapper.logdialog.format properties. * Fix a problem where a filter that requested the JVM to restart would be ignored if the JVM exited on its own immediately. The Wrapper is now more consistent so that restart requests from within the JVM or filters will always take priority over such exit requests. External shutdown requests, or those from other actions will still behave as they did in the past and continue to shutdown the Wrapper. The Wrapper also logs messages in debug output if an outstanding restart request is being preserved or ignored. * Fixed a problem in the AppCommand.bat batch file which could occur on some Windows platforms with certain multi-byte system encodings. The script has been rewritten and questionable parts have been simplified. The functionality of the script has been preserved. * Added the environment variable WRAPPER_CONF_DIR, which can be used for the configuration properties. Feature Request #3160644. * Made the script exit with the right exit code received when running the script as different user, specified in RUN_AS_USER. Bug #3185281. * Fix an access violation which could happen when reporting that the code signing certificate has failed to been verified. * Log an error if the backend socket is forcibly closed externally. It had been getting logged at a debug log level. The message is "An existing connection was forcibly closed by the remote host. (0x2746)". Because the message was only logged if debug output was enabled, the JVM would be restarted with no clear explanation as to what happened. The source of the socket closure is under investigation. * (Professional) Added the Java call fireUserEvent to the WrapperManager API. This enables to fire user event mails, actions without the filter trigger. * Fix a warning on Mac versions if the configured java command was not a universal binary. A check was added in 3.4.0 to make sure that the wrapper.java.command pointed directly to an executable to avoid unexpected behavior when running a script. The message is only a warning and the Wrapper continues regardless. Standard ppc, ppc_64, x86_64, i386, as well as the universal binaries will now all work correctly without a warning. * The default value of the wrapper.*.umask properties is using the current umask the process has. Before the default value was always 0022. * Add a new wrapper.backend.type property that is used to control whether the Wrapper communicates with the JVM using the traditional "SOCKET" or new experimental "PIPE". This was added as a workaround to a rare problem where some Windows machines are closing the socket at an OS level. This was only ever seen on Windows 2003, but could exist on other Windows versions as well. * Add a new experimental wrapper.use_javaio_thread property which causes the Wrapper to handle all java console output in a dedicated thread. * Add a new WrapperManager.isNativeLibraryOk() method which lets user code easily test whether or not the native library was loaded and initialized on startup. * Add a new PAUSE_THREAD command to the wrapper.commandfile property which makes it possible to test how the Wrapper behaves when various threads block or freeze. This was used to simulate and reproduce issues on heavily IO bound servers. * Improve the way the Java side of the Wrapper behaves when the Wrapper fails to ping the JVM for an extended period of time. The JVM used to exit to let itself resync itself with the JVM. This was causing problems on systems which were heavily IO bound because the Wrapper could block for a while while trying to write to the log file and the JVM was exiting. The JVM will now never exit under such circumstances. The JVM will never become orphaned because it will still exit almost immediately if the backend socket or pipe with the Wrapper is ever closed. * Deprecate the WrapperManager.appearOrphan() method as it is used to simulate a failure mode which is no longer possible with the Wrapper. * Changed the way the Wrapper is handling certificate errors regarding the code signing/timestamping certificate. The Wrapper will now only shutdown itself if the signature of the binary was not successfully verified because the binary or signature has been malformed but not if any problem with the counter-signer has been found. Starting with 3.5.7, the Windows Wrapper binaries are signed. Some users with locked down Windows 2008 systems had problems with the Wrapper refusing to start because the Comodo certificate had been disabled on their system. * Add a new wrapper.java.detach_started property which makes it possible to use the Wrapper as a simple tool to launch Java applications. When enabled, the Wrapper terminates immediately and the JVM is left to run on its own. Also add new wrapper.java.outfile and wrapper.java.errfile properties which make it possible to redirect the stdout and stderr of the JVM to files when detached. * When running the Wrapper as a specified User Account, through the wrapper.ntservice.account property, the Wrapper will add permission for the account to log on as service automatically upon install. Feature Request #3286491. * Fixed a problem binding the backend socket on Windows. If another process bound a port inside the wrapper.port.min and wrapper.port.max range using the SO_EXCLUSIVEADDRUSE option, the Wrapper would stop at this port report an Access Permission problem and omits binding any further port in the range. This problem existed ever since the Wrapper was released. 3.5.7 * Changed the way the script is installing the daemon gets installed on an AIX system. The script now uses inittab & SRC. * Fix a problem in the shell script that was preventing the script from starting the Wrapper correctly if a file or directory existed in the current working directory which was one character in length. This was only a problem when the delta-pack naming of the Wrapper was used. This was easy to reproduce on AIX systems on system restart because a "u" directory exists in the root directory by default. This had been a problem since 3.4.0 when it was introduced as a fix to a Solaris problem. The root cause was a missing set of quotes in the tr command. * Fix a problem in the shell script that was preventing the script from finding the running wrapper process when it was started when the working directory was in the same directory as the wrapper binary, but queried later from another location. It would also fail if it was started from another location, but then queried from the location of the Wrapper. This was introduced in version 3.5.6 when the script stopped changing the working directory in the script. * Add a new GC action that will cause the JVM to immediately perform a full garbage collection sweep. See the wrapper.commandfile, wrapper.filter.action., wrapper.check.deadlock.action, and wrapper.timer..action properties for details. * (Professional) Modify the wrapper.event..command.block.action property slightly so it will now correctly warn if an unknown action is encountered. It had been defaulting to CONTINUE silently. * Modify the timing of the message shown when an #encoding directive is missing from the top of a configuration file. It was only being logged if debug output was enabled. It will now also be logged if the #include.debug directive is specified. * Fix the indentation of warning messages about encoding or include problems in configuration files. * (Standard, Professional) Fix a problem where include file problems could cause the shell script to have errors due to invalid translated output from the Wrapper. * Add a warning when the maximum include depth is reached and include debugging is enabled. The Wrapper had always just silently skipped the include. * Fix a problem where #include.required directive was not correctly preventing startup if the include file was missing but the required include was in a nested include. * Fix a problem where the cause of some fatal include problems were not being logged correctly, resulting in a simple, non-informative message only that the configuration file failed to be loaded. This was a problem since 3.5.5. * Fix a Windows problem where the Wrapper could fail to start as a service if a defined environment variable would expand to a length larger than the 32k limit specified in the ExpandEnvironmentStrings system function. This was a problem on all Windows platforms prior to version 3.5.5, but only on Windows 2000 since then, when the code used to reload the environment from the registry was disabled for newer versions of Windows. We now simply skip the expansion of the problem variable and continue with a warning. Bug #3103776. * Add a set of optional system properties that the WrapperSimpleApp, WrapperStartStopApp, and WrapperJarApp helper classes are aware of to tell them to ignore uncaught exceptions thrown within the main methods of the user application. The exceptions will still be logged, but they can now be configured so that the main method is just allowed to end without causing the Wrapper to shutdown in an error state. Java on its own will stay running in such a case as long as it has launched at least one non-daemon thread prior to the uncaught exception being thrown. This does not affect most users, but an application was found that was having problems because of this difference in behavior. See the javadocs of the helper classes for details. * (Professional) Fix a problem when looking for the correct exit code to use for the wrapper.event..command.on_exit. property. The Wrapper now searches for a value as follows: wrapper.event..command.on_exit., wrapper.event..command.on_exit.default, wrapper.event.default.command.on_exit., then wrapper.event.default.command.on_exit.default. The third pattern had been getting skipped in previous versions since it was added in version 3.3.0. * (Professional) Add logic to report a warning if an unexpected value is specified for the wrapper.event..command.on_exit. or wrapper.event..command.block.action properties. * (Professional) Clean up the message log levels so the output is as expected when using the wrapper.event..command.loglevel property. * (Professional) Improve the wrapper.event..command.on_exit. property so the configured action will now work even if the command exits after the block time out has expired. In previous versions, there was no way to make the Wrapper do anything other than continue. * (Professional) Fix a problem where the Wrapper was failing to detect a JVM exit correctly if an event command had been fired first. The only problem was that the Wrapper was always reporting a JVM exit code of 0 rather than the actual exit code. * Fix a buffer overflow on Windows when either installing as a service, or updating an existing service. The problem only occurred when properties containing spaces, or Java passthrough arguments containing spaces were specified on the command line. The running service did not have any problems. This was introduced in 3.5.0. * (Standard, Professional) Improve the error message logged when an unlicensed version of the Wrapper's native library is used. * (Standard, Professional) Fix a buffer overflow problem on Windows when creating a customized Wrapper binary if the target binary name did not include the ".exe" extension. This problem existed since its intruduction in version 3.3.7. * The wrapper.exe, wrapperw.exe and wrapper.dll binaries are now being signed on Windows making it possible to verify that valid Tanuki Software binaries are being used. * Implemented a way to install, remove, start, stop, etc., the Wrapper as a Windows service from a non-elevated (UAC) console. The Wrapper is elevated transparently using a child process. This is needed starting with Windows Vista and 2008 for smooth interaction with the Windows Service Manager. * (Standard, Professional) Fix a problem where the wrapperjni_*.mo localized files were not being loaded correctly. These messages are only shown when debug output is enabled. Application and Wrapper localization was working fine. Introduced in 3.5.5. * (Standard, Professional) Enhanced the ability to run with localizations other than the system language on Windows. The Wrapper process locale was not being set correctly. So Japanese text was not always showing correctly if the wrapper.lang property was set when the OS was English or German. The Java process still has an issue where it will always start with the system default value for the file.encoding system property. This can still cause problems writing Japanese text when the file.encoding is German for example. * Added support in the shell script for starting/installing the wrapper on system boot with Upstart. * Fix a problem in the shell script where it would fail to recognize a running Wrapper if the Wrapper command line or path contained a square bracket. * Modify the way we test for the existance of the temp directory so we now generate our own file name rather than using File.createTempFile. On some systems createTempFile was taking a long time because it requires that Java initialize its internal entropy. We discovered that large numbers of files in the java.tmpdir directory makes Java's entropy initialization code very slow. This has been a potential issue since 3.5.0. * Fixed a problem on Windows where passthrough arguments after a "--" which contained spaces were not being passed through to the JVM intact, they were being split at the spaces into multiple arguments. * Fix a problem on Windows where the Wrapper could sometimes crash on shutdown if more than one thread got into the cleanup code at the same time. This did not affect running applications and was only an issue on shutdown. It was more likely if a language pack was loaded. Introduced in 3.5.3. 3.5.6 * Fix a problem on UNIX platforms where the log file path was being calculated incorrectly if an absolute path was specified for wrapper.logfile, and the file did not already exist. A warning message was being displayed but the Wrapper would continue using the default log file. There was a problem in the message displayed which caused a crash on some platforms include Mac OSX. Introduced in version 3.5.5. * Fix a problem on Windows platforms where the Wrapper would crash if it could not access the directory of the configured wrapper.logfile. Introduced in version 3.5.5. Bug #3087424. * Improve the way warnings are logged when there are problems writing to the configured wrapper.logfile so that the message will now be logged into the log file that the Wrapper ends up using in case it is successful in falling back to a default log file. * Fix a problem on Windows platforms where wrapper.java.additional. properties that were specified on the command line, and contained spaces, were not being requoted correctly when building up the Java command line. Introduced in version 3.3.6. * Fix a problem where the warning message logged for invalid values of the wrapper.java.additional. property, contained corrupted text. Introduced in version 3.3.6. * Fix a problem on UNIX platforms where an invalid value for the wrapper.java.additional. property was correctly being reported and then skipped, but the resulting command line to launch the JVM had a blank value that was causing the JVM to fail to launch. An invalid value is any value that does not begin with a "-" character. * Add a new WRAPPER_INIT_DIR environment variable which can be used to reference the working directory from which the Wrapper was launched. This is needed for certain applications because the Wrapper always changes its working directory to the location of the Wrapper binary. * Modify the UNIX shell script so it no longer changes the current working dir to the location of the script. This is no longer needed because the Wrapper has been changing the working directory to its own location since version 3.2.0. * Add a new wrapper.request_thread_dump_on_failed_jvm_exit.delay property to control how long the Wrapper will wait after doing a thread dump before killing the Java process. This delay has always been hardcoded to 5 seconds. * Clean up the text of several warning messages about invalid configuration values to make them more consistent. * (Professional) Add a new wrapper.jvm_kill.delay property which makes it possible to control the amount of time to allow between the jvm_kill event being fired and the JVM actually being killed. Useful if an external event command is fired that needs to do something with the JVM process first. * (Professional) Fix a problem where the output of the wrapper.event..message and wrapper.event..loglevel properties were not displaying correctly on UNIX platforms. * (Professional) Fix a problem on UNIX platforms where the Java side of the Wrapper was not being correctly notified if a large number of child processes that had been launched with WrapperManager.exec, exited at the same instant. Some of them were going into a defunct state until the next child exited, at which point they would be cleaned up. This was mainly an issue on JVM shutdown if the user code was calling WrapperProcess.waitFor() as part of its shutdown process. WaitFor calls at any point were getting stuck and would remain so until another child exited. As part of this fix, there were also several changes to the Windows implementation to bring the two code bases into sync. * Fix a problem on Windows when multiple threads were creating Childobjects, Handles could have been unintendedly get inherited by another Child Process, causing problems on reading/writing to the Input/Output/Errorstream. * Fix a problem on solaris and AIX when errno calls were not thread safe due to a compiler switch. * Fix a problem where debug level warning output while loading the Wrapper configuration was being displayed on startup. This could be fixed because the Wrapper actually loads the configuration twice, and such output is now only logged on the second call. * (Standard, Professional) Remove the undocumented ability to define a single file share mapping without the index. This would cause confusion if used, and complicated the code. * (Standard, Professional) Fix a byte alignment problem caused by a bad compiler directive on Windows platforms. It was known to cause a crash when defining mapped drives on 64-bit Windows versions. The problem was in the source since version 3.3.7, but is not known to cause any other issues. * (Standard, Professional) Modify the messages displayed when network shares are mapped or fail for some reason. Also add messages about them being unmapped on shutdown. * On some Windows platforms, a failure to delete a rolled log file was not being reported correctly. The system function to delete a file was returning success even if it had failed. We now double check. * Fix a deadlock in the code that is used to send data to the Java process. It was only possible if debug level output was enabled and log file rolling was enabled. Introduced in 3.3.7. * Fix a problem where the Wrapper was not notifying the JVM whenever the log file was rolled and the new name was the same as the previous one, as it is when wrapper.logfile.rollmode is anything other than NONE or DATE. * Fix a problem where the WrapperManager.getWrapperLogFile() was not returning the accurate log file name until the first time the log file was rolled after each JVM invocation. This was only noticeable if the wrapper.logfile contained either the "ROLLNUM" or "YYYYMMDD" tokens. * Correct an error message that was displayed on UNIX platforms when the configured java binary could not be accessed. The message referenced a buffer whose contents were undefined on some platforms. * Fix a problem on z/OS where due a difference in the API used to lock a mutex compared to all other UNIX platforms, the mutex's locking and unlocking code were effectively being ignored. This means that multiple threads were able to access code which was not thread safe and could lead to a crash of the Wrapper. This is a problem that has been in the code since the first z/OS release and is not known to have actually caused any problems. Starting with 3.5.1, this was only an issue if debug output was enabled. Versions 3.3.9 through 3.5.0 could have also had problems whenever the Wrapper received a system signal. 3.5.5 * Add new wrapper.filter.trigger. action, "SUCCESS". If this gets triggered then the Wrapper will treat the current JVM invocation as a success, and reset its internal failed invocation counter. This is useful for applications that need to be restarted frequently. * (Standard, Professional) Ignore Juniper Network Connect hostIds as they change on each reboot and are thus unreliable as hostIds. * Added a PASS_THROUGH setting to the UNIX shell script and Windows AppCommand.bat.in files which tells them to pass any extra arguments directly on to the JVM when it is launched. * Added a FIXED_COMMAND setting to the UNIX shell script and Windows AppCommand.bat.in files to make it possible to run either without specifying a command. Mainly useful in conjunction with PASS_THROUGH. * (Standard, Professional) Added a --passthrough option to the exe customization, in order to tell the wrapper to precede the whole command line through to the application in the JVM. * (Standard, Professional) Added a --conf option to change the default conf file, the wrapper tries opening when no conf file was explicitly specified. * Added wrapper.ntservice.account.prompt. If set to TRUE the wrapper will prompt for all account details (domain, account name, password). * Fix a minor issue in #include file declarations where a leading space was not required. * Add a new #include.required directive which works the same as the #include directive except that it will output an error and prevent the loading of the configuration if the included file does not exist. Normally include files are optional by design. * Modify the error messages displayed when configuration files fail to load so they now provide more information about where specifically the problem was. * Disabled the forced reloading of the SYSTEM (and if set to a specific account, the user) registry before launching the Wrapper as a service on Windows. This was done originally in Windows NT because changes to the configured environment were not being reflected when running a service unless the system was first rebooted. Microsoft appears to have solved this problem in Windows XP and 2003. In Windows 7 and 2008, this was actually causing a problem because the SYSTEM registry contains a setting "USERNAME=SYSTEM" by default that was overwriting the USERNAME when run as specific user. It was decided to disable this registry loading for Windows versions starting with XP and 2003. Of the supported versions, only 2000 is now reloading its environment. The only difference from version 3.5.4 and earlier that could be found is that when running as the SYSTEM user on Windows 7 or 2008, the USERNAME environment variable will now be set to the host name followed by a dollar sign rather than SYSTEM. This is actually how all other services work. But just in case this is a problem, it can we resolved by adding a "set.USERNAME=SYSTEM" property into the Wrapper configuration file. Bug #3061490. * (Standard, Professional) Fix a problem for Solaris and HP-UX where the socket timeout properties for the email notifications were ignored. * (Standard, Professional) Added wrapper.ntservice.recovery. properties to define system level actions in the event that the Wrapper process itself has a failure. * (Standard, Professional) Fixed a problem in the WrapperProcess.waitFor() and WrapperProcess.exitValue() call, where it would fail to return when called after the Wrapper had initiated the shutdown of the JVM. * (Standard, Professional) Add WrapperProcessConfig.setSoftShutdownTimeout(int) method to tell the Wrapper how long to wait after nicely asking the child process to shutdown cleanly when calling WrapperProcess.destroy(). Once the timeout has ellapsed, the child process will be forcibly terminated. This timeout had been hard coded to 5 seconds in earlier versions. * Add more detailed usage output to the UNIX shell script. * Make it possible to 'pause' and 'resume' the JVM from the UNIX shell and Windows batch scripts. * (Professional) Fix a minor memory memory leak while initializing timers. * Fix a memory leak which could happen if there were any invalid strings in localization resources. * (Professional) Fix a bug where the wrapper.event..command.argv. properties were not correctly parsed on Windows. This issue was introduced in version 3.5.0. * (Professional) Add the ability to define wrapper.event.default.command.argv. properties that will be used if the event specific specific commands are not defined. Mainly useful for testing. * Fix a problem occuring when the wrapper failed to roll the log file causing to write to the wrapper's default log (wrapper.log) file rather than continuing to write to the current logfile. * (Standard, Professional) Fix a put problem in the internal hash map implemenation used for localization where values could be lost. This was not a visible issue because of the data used. * Add new wrapper.filter.allow_wildcards. property and make it possible to specify wrapper.filter.trigger. patterns which contain '*' and '?' wildcards. * Add a commented alternative in the default OutOfMemoryError filter configuration to make it more specific to only trigger on uncaught exception stack traces. This is to avoid output like that from the -XX:+PrintClassHistogram JVM argument from causing the JVM to restart with a false out of memory warning. See the wrapper.filter.trigger. OutOfMemoryError example for more details. * Localize the default filter message. * Added ISO-8859-* encoding support and a few other encodings. * (Standard, Professional) Fix a problem on UNIX versions, parsing dates in license keys that was introduced in version 3.5.0. Windows verisons did not have this problem. All license upgrade terms and lease terms which contained "08" or "09" as either a month or day were being parsed incorrectly. This was leading the Wrapper to interpret those date components as "00". If the number was the date, then this would cause the date to be set to the last day of the previous month. If it was the month however, it would cause the date to be set to December of the previous year. For example "2010-08-20" was being interpreted as "2009-12-20", and "2010-10-08" was being interpreted as "2009-09-30". This would have prevented some licenses from being able to start because the upgrade terms were in effect prior to the Wrapper's release date. Some trial licenses could also have stopped early because their lease term end was earlier that it should may have been. For normal licenses, his will have no effect on installations once they are up and running because they do not use the lease term. * Fix a problem on Windows when a service received several service control codes in rapid succession. Since version 3.5.1, the Wrapper was only to process a single control code in each cycle of its main loop. This was resulting in messages like "Previous control code (4) was still in queue, overwriting with (4)." in the logs. The Wrapper can now handle up to 25 control codes per 10ms cycle. * Fix a problem where it was not possible to send passthrough arguments to the JVM when installing or updating a Windows Service. Passthrough using the "--" argument was added in 3.5.2. * Add a new wrapper.pause_on_startup property which makes it possible to tell the Wrapper to go directly into a paused state without ever launching a JVM. * Fix a problem where the STOP command set in a command file was being ignored if the Wrapper was currently in a paused state. * Make it possible to specify DEFAULT for the configuration file encoding. This will cause the file to be loaded using the default system encoding. We added this by request, but recommend using a portable encoding like UTF-8 to ensure that the configuration file will load correctly on all systems. * Added a WRAPPER_LANG environment variable which makes it possible to reference the current locale language code in the configuration file. One common use is to do localization using inclues. (e.g. #include ../conf/wrapper-%WRAPPER_LANG%.conf) 3.5.4 * Add optional support for custom public static methods in the WrapperSimpleApp and WrapperStartStopApp helper classes. Feature Request #2812276. * Add a new special configuration file directive "#properties.debug" which enables debug output about the properties as they are loaded by the configuration file. This can be useful to tell if and why certain properties are being overwritten. Feature Request #3042959. * Fix a minor problem where the "#include.debug" configuration file directive was sticky so it would be enabled when the configuration file was reloaded even if the reloaded configuration file no longer had the directive set. This was only an issue if the wrapper.restart.reload_configuration property was set. * Messages about missing included configuration files that were output when the #include.debug" configuration file directive was active were being logged at the ERROR level even though they were not problems. * Fix a minor problem where the WRAPPER_JAVA_HOME environment variable was not correctly being set to final when it was set internally by Wrapper. This could lead to unexected results if the user overwrote it later in their configuration file. * Fix a problem on AIX and z/OS, when running the Wrapper without any arguments. The Wrapper was attempting to use the default wrapper.conf file but the check for the file was failing causing the Wrapper to continue even though the file did not exist. This caused a confusing error message to be displayed, but was otherwise harmless. * Clean up some debug code associated with sleeping where log output was being queued when it did not need to be. * (Standard, Professional) Consolidate some redundant code associated with waiting for interfaces on startup. * (Professional) Fix a problem in the email feature of the Wrapper where a subject of more than 27 bytes in length when encoded as UTF-8. This was caused by a miscalculation in the Base64 conversion of the subject. * (Professional) Fix a problem when the WrapperManager.exec method which takes an array of command elements was called on Windows. The command elements need to be combined into a single command line, but if any of the elements contained spaces, the resulting command line was not being correctly quoted. * Add a new wrapper.java.command.resolve property to control whether or not the Wrapper tries to resolve any symbolic links in the Java command, specified with the wrapper.java.command property. Historically, it has always done so, but some jvm started applications like run-java-tool on Gentoo will fail if it is run directly as they have a check to make sure it is launched via a symbolic link. * Fix a problem on Windows versions where a path to the Wrapper binary, including the Wrapper binary itself, which was more than 100 characters would cause a buffer overflow when installing the service. A second similar problem would happen if the same path was more than 128 characters, whenever the Wrapper was launched. These were both very old issues and only happened on 32-bit versions of Windows XP and 2000. Microsoft documentation says that the issue should also exist on the 64-bit versions, but we were unable to reproduce it there. Newer versions of Windows are all fine. 3.5.3 * Fix a typo in the UNIX shell scripts that was causing "command not found" errors to be shown when running the Community Edition. * Add new wrapper.console.fatal_to_stderr, wrapper.console.error_to_stderr, and wrapper.console.warn_to_stderr properties to control whether the output at the FATAL, ERROR, and WARN log levels go to stdout or stderr. In the past they all went to stdout. With this change, FATAL and ERROR log levels now default to stderr output. * Fix a problem where the shell script would produce unexpected results if the Standard or Professional Edition shell scripts were used with the Community Edition Wrapper. Fix was in Wrapper binary by changing the default ERROR and FATAL log level console output to stderr rather than stdout. * (Standard, Professional) Fix a problem where script error message output was not being shown if the wrapper.conf file specified in the script did not exist. * Fix a problem where errors from failed forks on Windows were always being flushed immediately rather than honoring the value of the wrapper.console.flush property. * Fix a problem on Windows 2000 systems where a new feature added in 3.5.2 was preventing the Wrapper from running because the API used was too new. * Change the font of the wrapperw dialog in order to have prettier output of multibyte characters. * Add a line feed after the first message when starting the Wrapper from the UNIX script. * Add a note in the debug output so the configured java temporary directory is always logged to help with debugging. * Add a workaround for a bug in both Sun and IBM JVMs which cause an invalid exception to be thrown when a socket is already bound. It had been causing the Wrapper to report: "Unexpected exception opening backend socket: java.net.SocketException: Unrecognized Windows Sockets error: 0: JVM_Bind": http://bugs.sun.com/view_bug.do?bug_id=6965962 * Add the encoding of the subjects in the event mails to be always UTF-8 Base-64 encoded. * Add new wrapper.event..email.smtp.auth.type, wrapper.event..email.smtp.auth.userid, and wrapper.event..email.smtp.auth.password properties which make it possible to do LOGIN and PLAIN connection authorizations. Currently SSL (STARTTLS) connections to the mail are server are not yet supported. * Fix a buffer overflow while loading the configuration file on Mac OSX versions. Introduced in 3.5.0. * Fix a several memory leaks on UNIX versions that were added in 3.5.0, as well as a few others on all platforms, some from quite early versions. * Fix some places where a resolved path of exactly MAX_PATH characters in length could have resulted in a buffer overflow. * (Standard, Professional) Fix a memory leak disposing language packs. * Go through and increase the consistency of text strings. * Fix a problem on HP-UX where the Wrapper was logging a warning that the configured JVM was invalid if it was a PA-RISC 2.0 binary. Bug #3037317. * Fix a problem where the WrapperManager was failing to trap and hide errors initializing the MBean server on some JVMs that did not support it. 3.5.2 * Added new command line argument "--" . All arguments following will be preceded and passed to the java application. The arguments are attached after the arguments used in wrapper.app.parameter. * Fixed a problem in the shell script which could lead to report failed starts of a daemon incorrectly on the command line. * Implemented some small logic in the wrapper script which tries to change the permissions of the wrapper binary to executable if it wasn't set. * The Demo Application had problems locating the right conf file on Unix platforms and failed to launch the internal demonstration wrapper process. * Improved the error message logged if the Windows version of the Wrapper exits with an internal error. It now logs more information about the internal state of the Wrapper as well as saving a mini dump file which can be sent to support to make it easier to diagnose the cause of the problem. * Fix a problem where the names and displayNames in WrapperWin32Service instances were corrupted. List affected the WrapperManager.listServices() and WrapperManager.sendServiceControlCode() methods. There was a similar problem with the WrapperManger.getUser(), WrapperManager.getInteractiveUser() and WrapperManager.setConsoleTitle() methods. Introduced in 3.5.0. * Fix a problem on Windows where wildcards would sometimes fail to be resolved or cause the Wrapper to crash. This affected the generation of classpaths and logfile rolling. Introduced in 3.5.0. * Fix a problem on UNIX platforms where some error messages related to a failed fork to launch the JVM were not being logged correctly. Introduced in 3.5.0. * Fix a problem where invalid characters in configuration files that did not declare an encoding could cause the Wrapper to crash on startup. This could be issue for users upgrading from versions prior to 3.5.0 as older versions did not do any character set translations and would not have had a problem. * Fix a problem in code to check whether a drive was a mapped network drive or not was failing. This would sometimes lead to a false warning that the drive could not be identified. Introduced in 3.5.0. * Add a new ACCESS_VIOLATION command to the wrapper.commandfile property to test how the Wrapper and external systems behave if the Wrapper were to crash. Only enabled when the wrapper.commandfile.enable_tests property is enabled. 3.5.1 * Start using a system function to determine whether or not a character in the configuration file is a control character or not. This way it works on non-ASCII platforms. * (Standard, Professional) Fix a crash on Windows platforms caused by freeing up unallocated memory while loading the Wrapper's configuration. * Add debug output describing the system and current code pages on Windows versions to help understand encoding and mojibake issues. * Add a Japanese localized src/conf/wrapper.conf.in_ja template configuration file to assist Japanese users. * Fix an potential deadlock on UNIX platforms if the JVM exits while logging is taking place and the wrapper.debug or undocumented wrapper.state_output properties were set to TRUE. * Fix a problem where a failed JVM invocation that happened immediately after being launched would cause the Wrapper to immediately give up and shutdown. This should not have been possible with normal JVM configurations, but was turned up in testing. * Fix a problem where some startup output of the tick timer debug output was corrupted. This was only an issue if the wrapper.tick_output property was set. * (Standard, Professional) Rework the way text is localized so that mallocs are all handled on initialization. 3.5.0 had problems with occassional freezes and crashes caused by mallocs being made within signal handlers. The problems were rare unless debug output was enabled. * Greatly simplify signal handlers on all platforms so they now do as little as possible, actions are now queued and then processed by the main thread. * (Standard, Professional) Fix a problem where the hostname in the wrapper.syslog.remote.host couldn't resolve a hostname (IP Address was working). * (Standard, Professional) Add debug output on Windows versions to help debug localization and code page issues. * (Standard, Professional) Fix a localization problem on Windows where the Wrapper was using the system-wide language rather than that of the current process. This was resulting in mojibake if the Wrapper was launched in a new console if that new console did not support the required code page. This was only an issue if the user had changed the active code page from the default for their Windows desktop. * Fix a problem on Windows platforms where the JVM child output was being logged with an extra line feed in some cases. * Fix a problem running the DemoApp from some Network shares on Windows. * Add a new WrapperManager.isWindows() and WrapperManager.isMacOSX() methods to make it easy to write such conditional code as both platforms can require non standard code. * (Standard, Professional) Make the GetHostId.bat file more robust so it can be executed using a relative reference from the command line. It also now supports Delta-pack style Wrapper binary naming and falling back to use which binaries are available. * Change the timing of when the wrapper.working.dir is set so that any error messages generated while loading the configuration will be logged to the correct log file. It is also needed for Standard and Professional Editions to ensure that the language pack is loaded from the correct location so that all such messages will be in the correct locale. * Fix a problem on UNIX platforms where the status command was failing with an error due to the localization feature added in 3.5.0. 3.5.0 * (Standard) Added the ability to detect and react to thread dead locks in the JVM. Requires at Java 1.5. Added wrapper.check.deadlock, wrapper.check.deadlock.interval, wrapper.check.deadlock.action, and wrapper.check.deadlock.output properties to configure the detection. * (Professional) Add a new jvm_deadlock event which is fired in response to a thread deadlock being detected within the JVM. * Fix a problem where the intervals like wrapper.ping.interval were all rounding down causing them to function at a rate one second shorter than configured. * Fix a minor memory leak calling WrapperManager.exec. * Add a new wrapper.filter.message. property which can be used to control the message which is logged when a wrapper.filter.trigger. is matched. * Rework the way actions are fired for the wrapper.filter..action and wrapper.timer..action properties so there is no longer any duplicate code. * Modify the way the wrapper.filter..action and wrapper.timer..action properties work so it is now possible to specify a list of actions which will happen in the order specified. This makes it possible to do a thread dump and then restart in response to an error. * Add the DEBUG action to the wrapper.filter..action property for consistency. * (Professional) Implement the ability to fire user defined events as actions of the wrapper.filter..action, wrapper.timer..action, and wrapper.check.deadlock.action properties. * Fix a problem in the WrapperProcess.destroy() function, when a child process couldn't have been forcibly killed. * Add GetHosdID.bat file for Windows platforms. This file will open a dialog displaying the HostId of the machine. * Fix a problem in the shell script which, on a rc based OS, could lead to problems starting the Wrapper when the APP_NAME in the script contained capital letters. * (Standard, Professional) Added a set of wrapper.share..* properties which makes it possible to configure the Wrapper to map specific network resources before launching the JVM on Windows. * Corrected the way the Wrapper is installing itself as a Windows service when the binary and/or conf file are located on a Mapped Drive. * Add new wrapper.pausable and wrapper.pausable.stop_jvm properties to control whether the JVM can be paused or not, and deprecate the use of the Windows specific wrapper.ntservice.pausable and wrapper.ntservice.pausable.stop_jvm properties. * Modified the wrapper.commandfile property so PAUSE and RESUME commands are now supported on all platforms at any time. * Modified the wrapper.filter.action. property so PAUSE, RESUME, and DEBUG actions are now supported. * (Professional) Modified the wrapper.timer..action property so PAUSE and RESUME actions are now supported. * (Professional) Modified the wrapper.event..command.block.action property so PAUSE and RESUME actions are now supported. * Added new WrapperServicePauseEvent and WrapperServiceResumeEvent classes to make it possible to notify an application that it has been paused or resumed. To date, this was only possible when the Windows service was paused or resumed * (Professional) Add new "wrapper_pause" and "wrapper_resume" event types that are fired whenever the Wrapper is paused or resumed. * Enhance the command line parsing for the WrapperManager.exec() command. The single commandLine version of the exec call will handle quotes more sophisticated. * Modified the wrapper.syslog.ident property so its value is now silently trimmed to 32 characters in length. This is needed to avoid problems with syslog servers. (See RFC 3164 section 4.1.3) * (Standard, Professional) Add new wrapper.syslog.remote.host, wrapper.syslog.remote.port, and wrapper.syslog.local.host properties which can be used to redirect all syslog (UNIX) or Event Log (Windows) output to a remote syslog server. * Add support for the wrapper.syslog.facility property on Windows so remote syslog entries can be sent from Windows versions of the Wrapper. * Add debug output from the WrapperManager class which shows information about the current wrapper.jar, including its size, location, and MD5 hash. * Add a check for wrapper.java.classpath. entries containing quotes. The quotes are now stripped and a debug warning logged. * (Standard, Professional) Increase the size of the wrapperw.exe error dialog so it is a little easier to read. Also changed the font for the same reason. * (Standard, Professional) Modify the title of the wrapperw.exe error dialog so it now includes the value of the wrapper.displayname property. * Add a new check for the writability of the configured Java temporary directory. When running on Vista, it will not be writable and can lead to errors. * Added new wrapper.java.tmpdir.required and wrapper.java.tmpdir.warn_silently properties to control whether the temporary directory is required. * Add a note in Wrapper conf template showing users how to configure a specific Java version to use. * Add a new CLOSE_SOCKET command to the wrapper.commandfile property and added a new wrapper.commandfile.enable_tests property to control whether it can be used. * Fix a debug warning while shutting down caused by one thread closing the backend socket when another expected it to be open. * If the backend socket between the JVM and Wrapper is somehow closed, make the JVM respond promptly by restarting the JVM immediately to resync with the Wrapper. Added log information to make it clear when this happens. * Add a new wrapper.use_tick_mutex property to control whether or not the internal tick timer is protected by a mutex. (Experimental) * (Standard, Professional) Add support for zLinux 32 and 64-bit on s390 and s390x systems. * Change the internal encoding of the wrapper from native characters to UNICODE. This will allow the wrapper to handle correctly any characters in its configuration file and internally. * (Standard, Professional) Implement a new WrapperResources class which makes it possible for user applications to localize their applications using the same methods common to native applications. The Wrapper uses this internally to provide localized versions of the Wrapper. * Remove the old org.tanukisoftware.wrapper.resources package as it is no longer being used. * (Standard, Professional) Add Japanese language pack to localize the Wrapper to Japanese. * Fix a problem in the WrapperManager class where unexpected exceptions thrown in the main socket loop were being thrown away without being logged. * Make the Wrapper more robust against failures removing the Wrapper's shutdown hook on shutdown. It had been causing the clean shutdown of the JVM to fail as an unexpected exit. * Fixed a problem in the wrapper customize code where customized multi layer icons may get scaled incorrectly by Windows. Bug #3015848 * Modify the wrapper.on_exit. property so it is now possible to PAUSE the Wrapper when a JVM exits. This delays the restart of the JVM until the Wrapper is resumed. * Add a new log format, 'U', which logs the approximate time in seconds since the Wrapper was launched. It is based on the internal tick timer and will be a few percent lower than the actual number of seconds that have elapsed. Added to the wrapper.console.format, wrapper.logfile.format, and wrapper.logdialog.format properties. * Fix a problem where deleting the configured anchor file was not recognized if the JVM was not running at the time. The Wrapper was not noticing that it was missing until the next JVM was launched. * (Standard, Professional) Add a new NOTICE log level which is used to log license related message. These will only show up trial license log output. * (Standard, Professional) Add a new default 15 minute trial license which can be used out of the box on any machine without the need to register and obtain a trial license or purchase a license. * Add a new wrapper.environment.dump property which will dump all of the environment variables to the log, along with where the variables originated. * Force the Wrapper to flush all logfile output for each line until the configuration file has been loaded. There is normally no output up to this point anyway, but this ensures that any errors are logged in a consistent location. * Move the registration of the main thread with the logging system up a bit to make it possible to perform low level debug logging earlier. * Add a set of checks to display an error and prevent the Wrapper from starting if the batch or shell scripts being used are based on the TestWrapper demo application. It is important that the user start with the default template files in the distribution src/bin and src/conf directories to make sure the integration goes smoothly. 3.4.1 * Fix a problem where the wrapper.app.parameter.1 property was always being set to an empty string when launched from launchd on Mac OSX. It worked correctly with the testwrapper application, but would fail for most user applications. * (Professional) Fix a potential synchronization problem in the log output of the tick timer, event command and event email code. This had been in the code since 3.3.0 without any reports of problems. * Improve the error message displayed when the configured wrapper.java.command can not be found on the path. * Log the name of the signal in the logs when a SIGSEGV is detected from the JVM. It had been logged as Unknown. * Add some comments to the wrapper.conf.in template to help users get started with a new license key file. * Add some default properties to the wrapper.conf.in template file to make it easier to get up and running with out of memory checks, as well as sending out alert emails. * Add a small debug message when running wrapperw.exe so it is clear which wrapper binary is being used. * Fix a problem where the wrapper.logfile.maxfiles was not being applied on startup when the current log file did not yet exist. This was true for any wrapper.logfile value which contained a unique value like a PID. * (Professional) Fix a problem in the WrapperProcess.destroy() function, where the function was failing to forcibly kill the child process. * (Standard, Professional) Add a new GetHostID.bat file for Windows platforms. This file will open a dialog displaying the HostId of the machine to help new users get up and running. * Add a new WrapperManager.appearOrphan() method which can be used to test how the WrapperManager shuts down the JVM in the event that the Wrapper process ever crashes or freezes. This was not due to any particular problem and was added to fill out our test coverage. * (Professional) Fix a problem where event commands were being left in a defunct state after execution on UNIX platforms. * Fix a potential problem on 64-bit versions where the internal tick timer was not rolling over in the same way as the 32-bit versions. This has been in all previous versions but is not known to have caused any problems. It was fixed for consistency. * Modify the WrapperManager class so it will now request a JVM hard exit only after 4-times the ping timeout, in seconds, since the last contact with the JVM. This had been 90 seconds more than the ping timeout, which was not always long enough when the wrapper.ping.timeout was very large. * Shift the initial start point of the tick counter so it will roll over 50 seconds after startup. This shouldn't be noticeable to the user, but makes sure that this roll over is much better tested as it always happens the first time on startup. * Add a new wrapper.java.classpath.use_environment property which makes it possible to set the classpath using the CLASSPATH environment variable rather than including it on the Java command line. * Fix a problem where requests to shutdown the Wrapper when in the state where it was just about to launch a JVM would being ignored. This was fixed in all editions, but was only an issue in the Professional Edition when the wrapper.event.jvm_prelaunch.command.on_exit.=SHUTDOWN property was being used with the wrapper.event.jvm_prelaunch.command.block=TRUE property and the command exited with a code that triggered the SHUTDOWN. * (Professional) Add a new wrapper.event..command.block.action property to control how the Wrapper behaves when a command does not complete execution within the configured timeout. Accepted values are CONTINUE, KILL, KILL_SHUTDOWN and KILL_RESTART. Default value is CONTINUE. * (Professional) Made it possible to use the "default" event type with the wrapper.event..command.loglevel, wrapper.event..command.block, wrapper.event..command.block.timeout, wrapper.event..command.on_exit., and wrapper.event..command.on_exit.default properties. * Fixed a single byte buffer overflow which takes place just before the JVM is launched on Windows platforms. This was introduced in 3.4.0. * Add a message pointing the user to the Wrapper's log file when the service fails to start after it has been launched. * Update the debug message displayed when attempting to open the backend socket if the port is unavailable so it doesn't seem like a problem. It will retry with another port. * Work around an issue on some Solaris problems where the shell script would fail if /usr/ucb/ps wasn't installed. * Fix a problem on UNIX versions where the Wrapper would fail to resolve the correct location of java if it was located using the system PATH, and that PATH element pointed to a binary via a relative symbolic link. This was introduced in 3.4.0. 3.4.0 * Increased the minimum supported Java version from 1.2 to 1.4. * Removed the reflection code used to manage the Wrapper's shutdown hook. The Runtime methods are now called normally. * Add a note in the TestWrapper shell script to make it clear that the user should always use the scripts in the WRAPPER_HOME/src/bin/ directory when generating scripts for their own applications. This became more of an issue because of changes to the TestWrapper script starting with 3.3.8. Bug #2902843. * (Professional) Added new WrapperManager.exec methods and a new WrapperProcess class to allow the launching and management of child processes through the Wrapper rather than using the standard Runtime class. This makes it possible to clean up child processes when the JVM crashes or is restarted, as well as solves severe memory issues with the way processes are forked on some UNIX platforms. * (Professional) Added a new wrapper.child.status.loglevel property to make it easier to debug the status of child processes. * (Professional) Added a new wrapper.child.count.interval property to control the interval at which the number of waiting child processes is logged after a JVM is shutdown. * (Professional) Added a new wrapper.jvm_cleanup.timeout property to control the amount of time that managed child processes still running after the JVM has exited are given to shutdown before being forcibly killed. * Fix a problem with WrapperManager.getUser() and getInteractiveUser() methods which was preventing us from using the latest compiler on 32-bit Windows. (The compiler used to build 32-bit Windows versions was rolled back to the version used through the 3.3.5 release in 3.3.9 to work around the problem that was causing these methods to always return null.) The change in this version should not affect how the Wrapper functions. * The old compiler used in 3.3.9 for Windows 32-bit builds was causing a false hit on Symantec security software. The new compiler causes the Wrapper to once again pass without any warnings. * Added a warning while starting up the JVM which will be displayed if the Java command in wrapper.java.command does not point to a valid java binary file. * [Changed the scripts and batch files to try to load the wrapper executable according to the system, i.e. on 64-bit systems the 64bit binary of the wrapper is attempted to be executed first and after this fails the 32 bit gets attempted to be loaded. This behaviour is primaly useful with the delta-pack of the wrapper.] * Add some debug output logging the current timezone to assist supporting time related problems. * (Standard, Professional) Added a new wrapper.license.debug property which will log information about the license key(s) being tested on startup. * (Standard, Professional) Fix a problem where license validation was failing at certain times of the day for servers with timezones east of Japan. Development license keys which have their update times obfuscated were not affected. This was a problem introduced in 3.3.7. * Fixed a bug in the wrapper shell script which occurred when running the script on a Solaris within a non singlebyte locale. Bug #2910327 * Fixed a potential bug in the wrapper script where requesting a Thread Dump, the shell is sending the wrapper console to the background and returning with a prompt. * Fix a problem where Java log output would sometimes get extra line feeds when under heavy load. * Fix a problem which was leading to a resource not found error when trying to start a service, if it was installed on a Mapped Network Drive under Windows. * Added some advice messages recommending the use of UNC paths if a resource located on a Mapped Network Drive is used in the configuration file on Windows versions. * (Standard, Professional) Fix a bug in the wrapper binary customization, which occurred when the source binary file was set to read only. * Fixed a null string problem in the error message if a classpath value wasn't found. * (Professional) Added the option to include a logfile into the wrapper's EmailNotification mails via wrapper.event.default.attach_log=TRUE * Improve the message displayed when a user calls WrapperManager.requestThreadDump() in JVM which does not have console window on Windows. This should never be an issue when run with the Wrapper, but can happen if running standalone without the wrapper binary. * Update the Windows batch scripts so they now take AMD64 and IA64 architectures into account when deciding which version of the Wrapper to run when using the delta pack. * Update the UNIX shell script so it now does a much better job of resolving the ideal platform bits to run the correct version of the Wrapper when using the delta pack. Thanks to Leo Leung for the patch. * Add install and remove commands to the UNIX shell script so it is now much easier to install and remove the Wrapper on many UNIX plattforms to start and stop on system startup and shutdown. Thanks to Leo Leung for the patch. * Update the Windows batch scripts so it is now easier to change the location of the Wrapper configuration file or rename the Wrapper binary when using the scripts. * Added a new QueryApp-NT.bat.in template script which can be used to query the current running status of the service on Windows. * (Standard, Professional) Add a new "-u" or "--update" command to the Wrapper which allows you to effectively reinstall the Wrapper as a service without having to provide the account credentials if the service is running as a specific user. This is very useful for installers upgrading an application to a new version. * Add a new "update" command to the Windows AppCommand.bat.in batch script. * Go through and clean up the messages displayed when controlling the Wrapper as a service so the messages are consistent and more meaningful to the user. * Add wrapper.wait_for_hostid and wrapper.max_hostid_timeout property. This properties set if and how long the wrapper shall wait when starting up until the hostid is available. This is important to make sure that server licenses are validated correctly on UNIX platforms as the OS is booting up. * (Professional) Fix a problem where environment variables referenced in property values were not being expanded correctly the first time they were referenced if the property's value was a default value. The underlying fix was in all editions, but this was only causing a problem in the Professional Edition where the WRAPPER_HOSTNAME variable was not being expanded in the subject and body of emails sent out for the "wrapper_start" event if the defaults were used. * (Professional) Fix a problem where backslashes in the body of emails, configured with the wrapper.event..email.body property, were not being handled correctly when displaying things like paths from environment variable references. * Fix a problem on UNIX platforms where the Wrapper was displaying an error about not being able to locate the configuration file when the Wrapper was run without any arguments. * (Standard, Professional) Improve the message shown when a License Key is not found. * Add a new message to the Community Edition when the user requests a HostId. * Add WAIT_FOR_STARTED_STATUS and WAIT_FOR_STARTED_TIMEOUT to the script. This lets the script wait (up to timeout sec.) when starting a daemonized process until the process has been started. Thanks to Dobes V. Feature Requests #2917391. * Improve the error message displayed when the user tries to run the Wrapper with the internal -s or --service commands. * Fix a problem where the WrapperSystemPropertyUtil.getBooleanValue() method was not correctly returning the specified default value when the looked up property was missing. Also added a new getStringValue() method. * Improve the error message displayed when the user tries to install and remove the wrapper as Service on Windows Versions after Windows Vista. * Add an advice message when MacOSX applications launched with launchd encounter a "Returning nil _server" error when displaying a GUI. 3.3.9 * Modify the way JNI functions are called from within the native library so they work correctly on platforms which are not ASCII based. * (Professional) Add support for IBM z/OS 390 servers. This is still an alpha release and will be made available on request. * Improve the message displayed when a server license key is used on a different server. * Add a minimum max file size of 1024 bytes to the wrapper.logfile.maxsize property to avoid the log file rotating too often if the user enters a very small value. * Add a message that shows up in the console when the Wrapper fails to write to the configured log file. As with previous versions, it will then fall back to writing to wrapper.log in the current working directory. * On UNIX platforms, automatically set the wrapper.disable_console_input property when wrapper.daemonize is set. * Fix a problem introduced in 3.3.8 where relative configuration file includes were failing to resolve correctly when the wrapper.working.dir and wrapper.daemonize properties were used together. The wrapper.daemonize property causes the configuration to be loaded twice on startup and the working directly was not being reset correctly before it was loaded the second time. * Fix a problem introduced in 3.3.8 where wildcard selection of files on Windows failed in some cases. * Fix a problem introduced in 3.3.8 where setting the wrapper.logfile.maxfiles property to 0 was causing all log files to be deleted rather than none when the ROLLNUM file pattern was used. * Revert the way rolled log files are deleted when using the ROLLNUM file pattern to the functionality used in versions 3.3.7 and earlier. Files such as wrapper.log.bak or other files manually created with the same log file base name were also being deleted with 3.3.8. * (Standard, Professional) Fix a problem where the close window button in the title of the WrapperW log dialog was not correctly cancelling the dialog. * (Standard, Professional) Fix a problem where the WrapperW log dialog would sometimes fail to show itself on top of other windows if the splashscreen had been displayed on startup. * Fix a problem on 32-bit Windows versions where starting with he Wrapper, the WrapperManager.getUser() and getInteractiveUser() methods were always returning null. This problem was introduced in version 3.3.6. * (Professional) Fix a buffer overflow when sending alert emails to multiple addresses. This would sometimes cause crashes in versions 3.3.7 and 3.3.8 when sending alert emails to even a single recipient. Because of the nature of the overflow, in most cases did not cause any problems. 3.3.8 * Add the new start type DELAY_START for NT Services, which startes the service after other auto-start services are started plus a short delay. * Fix a problem where the Wrapper's PID file was not being set to the correct PID when the wrapper was launched daemonized. With the shell scripts that ship with the Wrapper, this means that it would not be possible to stop the Wrapper using the script because the expected PID did not exist. This was a problem introduced in 3.3.7. * Changed the timing for the wrapper's splashscreen when the splash screen mode was set to JVM_STARTING. Now the splashscreen will disappear when the JVM has been initialized. * Fix a problem where the splashscreen was being shown when starting a service or performing other command line operations. * Remove some extra debug output on startup for Mac versions. * Fix a crash in the Community Edition on PPC platforms of the Mac OSX version. This crash has been reproduced in all versions starting with 3.3.1. The OSX distribution is a universal binary but does not appear to result in a crash on x86 servers. Standard and Professional Editions were not affected. * Fix a problem on Windows versions where problems accessing the registry were not reporting the correct error message. This did not cause any problems in and of itself, but it made it difficult to track the down the cause. * When wildcards are used in the classpath elements, the list of matching jar files included in the classpath are now sorted to ensure that their order is consistent across installations. Normally it would not matter, but if certain classes or resources are redefined in multiple jars this will ensure that the application now always works the same. * Fix a problem where wrapper.logfile.maxfiles was being ignored when wrapper.logfile.rollmode=JVM was set. * Changed the way the wrapper.logfile.maxfiles property works with the ROLLNUM token. Now when the log files are rolled, all files greater than maxfiles count will be deleted. Previously, the Wrapper would roll within the maxfiles count and ignore extra files if they existed. This would cause extra files to be left around if the maxfiles property value was decreased. * Add new wrapper.logfile.purge.pattern and wrapper.logfile.purge.sort properties which make it possible to limit the number of log files in some advanced cases. Be sure to read the documentation before playing with them. * Fix a potential crash when referencing non-existent environment variables in the value of wrapper.logfile. * Modify the way all properties used to define file names behave so that undefined environment variable references will now have their '%' characters replaced by '_'. This is to avoid problems caused by file names containing '%' characters on some platforms. * Fix a problem introduced in 3.3.6 where the windows shown by the JVM or its child processes could not be displayed when running in iteractive mode. * Rework the TestWrapper application a bit so it can now be run in headless mode for testing. * Fix a problem on some UNIX platforms where the shell script was showing an extra '-n' when run with the "start" command. * Fix a problem for FreeBSD which could cause the wrapper being unable to stop the wrapper daemon if the ps command buffer size (kern.ps_arg_cache_limit) was too small to contain the wrapper command line. 3.3.7 * (Professional and Standard) Added the ability to customize the wrapper.exe and wrapperw.exe binaries on Windows with a user specified icon and splash screen. * (Professional and Standard) Added a new wrapper.splashscreen.mode property to control how long the splashscreen is visible. * Fix a problem on SELinux where a strict security policy could lead the Wrapper fail to load the libwrapper library. Thanks to Jean for the hint. * Fixed a problem in the obfuscated license date, which caused a license to be reported as invalid if run in a timezone west of Japan. This feature was implemented in 3.3.6, but disabled on the site until the release of version 3.3.7. Thanks to Leo for the hint. * Added a new WRAPPER_PID environment variable. Feature Request #2870136. * Added a new WrapperManager.getWrapperLogFile() method and WrapperLogFileChangedEvent class that can be used to receive notifications of changes to the Wrapper log file currently in use. Feature Request #2870133. * (Profesional) Improved the wrapper.event..email.to property so it now supports the ability to specify multiple recipients. 3.3.6 * Introduce the wrapper.timezone property. This property allows to set the timezone in which the wrapper will be executed. This property is available in the standard and the professional Edition of the Java Service Wrapper. * Fix a potential problem on Windows platforms where a failure to register the control handler was not being logged. If this happened, user logoffs would not be trapped correctly but no other problems would have occurred. * Fixed a problem in the shell script on Solaris platforms where a deep directory structure would cause the script to incorrectly report that the Wrapper was not running. That could lead to multiple zombie copies of the Wrapper and its application running in memory. Bug #1664303. * Fixed a problem in the shell script on HP-UX platforms where a deep directory structure would cause the script to incorrectly report that the Wrapper was not running. That could lead to multiple zombie copies of the Wrapper and its application running in memory. Bug #2812207. * Improve the error message displayed when there are problems initializing the main class specified for the WrapperSimpleApp, WrapperStartStopApp, or WrapperJarApp helper classes. * (Professional) Add new wrapper.event..loglevel and wrapper.event..message properties which can be used to output a customizable message like "Event: " to the logs at an arbitrary log level. * Add debug output in the Windows version to log the full path of the native library. * Add a new wrapper.java.detect_debug_jvm property which will control whether or not the Wrapper disabled timeouts when java's debugger is in use. The Wrapper has automatically disabled timeouts in this case since version 3.2.1. Thanks to Ruslan Gainutdinov for the patch. * Fix a buffer overflow problem for values of wrapper.ntservice.name. * Fix a problem with where the wrapper.syslog.ident property was not working as documented. It had been necessary to use wrapper.ntservice.name in its place. * Add a new wrapper.ignore_sequence_gaps property which makes it possible to have non sequential numbered property values. This affects the wrapper.app.parameter., wrapper.event..command.argv., wrapper.filter.trigger., wrapper.java.additional., wrapper.java.classpath., wrapper.java.library.path., wrapper.ntservice.dependency., and wrapper.timer..interval, properties. The default value maintains past functionality. Feature Request #1930298. * (Professional) Fix a problem where the Event Handler Variables were not being set correctly in the values of the wrapper.event..command.argv. properties. * (Professional) Fix a potential access violation if memory allocations fail while sending event mails or executing event commands. * Add a new WRAPPER_HOSTNAME default environment variable as an alias of the existing WRAPPER_HOST_NAME variable. * (Professional) Deprecate the use of the wrapper.event..set_environment property. It will remain in the product for compatibility but its use is discouraged as it does not always work correctly when the configuration file is reloaded. See the property's documentation for more details. * Add a new DUMP action to the wrapper.filter.action. property to make it possible to invoke a thread dump in response to a specific string in the JVM's console output. * Fix a problem where the WrapperManager.stopAndReturn method was dead locking if called when the JVM was being run in standalone mode without the Wrapper. Bug #2711872. * (Standard, Professional) Modify the way the licensing times work so the Wrapper compares the License Upgrade Term to an official release date rather than the build date. This makes it possible to release additional platforms at a later date while keeping the use of a license consistent. * (Standard, Professional) Make it possible to obfuscate the upgrade term in License Key files for development licenses so it is not visible to an end user what the development license holder's upgrade term is. This feature is accessed from the License Management Page when downloading a new or existing License Key file. 3.3.5 * Fix a problem on some UNIX platforms introduced in 3.3.4 where the Wrapper would crash on startup if the configured JVM could not be found. * Fix a problem introduced in 3.3.2 where the Wrapper could crash if the system host name was longer than 27 characters in length. * Fix a potential problem with the way thread ids were being compared on UNIX systems. * Add a new wrapper.java.additional.auto_bits property which will automatically add the -d32 or -d64 arguments to the JVM for platforms whose JVMs typically expect the argument. * (Professional) Fix a problem with the Date field of outgoing event emails. 3.3.4 * (Standard, Professional) Update the development license to version 1.1 so that a new copyright notice file can be shipped with user applications rather than the full license text. * The Community Edition may be licensed under either the GPL2 based Community License, or the Development License. The source for the Standard and Professional Editions of the Wrapper are proprietary. http://wrapper.tanukisoftware.org/doc/english/licenseOverview.html * Fix a problem introduced in 3.3.2 where querying a the status of a Windows service with the -q or -qs commands resulted in an access violation. The running status of the service was reported correctly but additional configuration information was failing. Bug #2644515. * Add a new wrapper.disable_restarts.automatic property to disable only restarts caused by JVM timeouts, crashes, etc. Manual or configured restarts will still be allowed. * Switch to using make for HPUX IA 32/64 builds. * Add Advice comments when the Wrapper fails to launch the JVM process. * Fix a problem on UNIX platforms where log entries made by the forked Wrapper process would result in two blocks of log entries in the log file because the parent Wrapper process would think they were console output from the JVM process. * Add a set of new wrapper environment variables that can be referenced in the wrapper.conf file to generate random numbers, or timestamps for use in generating unique file names, etc. See the Default Environment Variable definitions section for more details. * (Standard, Professional) Fix a problem with Development licenses being able to authorize applications using Integration Method #4 which was added in version 3.3.3. * Add the number of bits of the current Wrapper to the startup banner to aid in support requests. * (Standard, Professional) Fix a crash problem on HPUX versions which would reliably happen on some machines at startup. 3.3.3 * Modify the wrapper.ignore_signals property so it now takes the values WRAPPER, JAVA, and BOTH, in addition to TRUE and FALSE. * Modify the WrapperManager so it is now careful never to poll the native library once the JVM is flagged as being ready to shutdown. This is to make sure that the JVM never exits in the middle of a call as that could lead to the JVM crashing. * Add a pair of methods to allow threads to request and release locks which will prevent the Wrapper from letting the JVM exit while certain operations are in progress. See WrapperManager.requestShutdownLock() and WrapperManager.releaseShutdownLock(). * Fix a problem where interactive services would sometimes leave a console window visible when after starting up. The Wrapper is now more resilient about closing the window even if it fails to do so initially. * Add a new integration method (#4), which makes it easy to configure the Wrapper to execute an executable jar file. * Fix a problem where the random generator was not being seeded correctly before naming the Wrapper's console window when running as a Windows service. This was leading to problems identifying the wrapper's console when more than one service was running on the same machine. 3.3.2 * Add a file information record to the wrapper.exe and wrapperw.exe binaries so the Version tab will be displayed correctly in the Properties dialog of the file. * (Standard, Professional) Fix a problem with the wrapperw.exe binary where the log dialog was not being displayed correctly when the Wrapper was launched with the "-?", "-v", or "-h" arguments. * (Standard, Professional) Fix a problem with the wrapperw.exe binary where a message was being displayed in the dialog with the location of the full log file even if there had not been any entries written to the log. * (Professional) Improve the debug output produced when sending event emails when the wrapper.event..email.debug property enabled. * (Professional) Add wrapper.event..email.send.timeout and wrapper.event..email.receive.timeout properties to avoid the wrapper hanging waiting for a response from a remote mail server. Bug #2064885. * (Professional) Fix a problem where the Wrapper would get stuck and fail to send emails with some mail servers. Problems was being caused by incorrect linefeeds in the body of the email. * Add a warning if the leading '#' is missing from an '#include' in the configuration file to assist users with include file problems. * Added a new wrapper.ntservice.generate_console property which will cause the Wrapper to always generate a console when running as a service and then hide it immediately. This will cause a slight flicker but is needed to support thread dumps. Bug #2060181. * Fix a problem in the Windows version where the console window would sometimes be left visible when running as an interactive service even when it was configured to be hidden. * Add support for PowerEvents so Windows services can respond to suspend and resume events. * Fix a problem where the wrapper.key system property passed to the JVM was being generated incorrectly randomly, 1 in 2048 times the JVM was launched. This would result in the JVM failing to start and the Wrapper shutting down. * Add a new wrapper.disable_console_input to disable to feature which allows the Wrapper to pass console input on to the Java process. * Fix a buffer overflow problem in the logging code which would happen once on startup. This was benign on most platforms but was causing intermittent crashes in the 32-bit AIX version. * Modify the way configuration properties are parsed so that their names are no longer case sensitive. * Modify the WrapperServiceException so that a new getErrorCode method can be used to obtain the integer error code which caused the exception. Feature Request #2188280. * (Standard, Professional) Fix a problem where on some Windows machines the Wrapper would return a random hostId that changed each time the system was rebooted. * (Standard, Professional) Make it possible to define License Keys so that their property names are encoded using either the host name or hostId. This makes it possible to define multiple keys within the same configuration file, visible on the same host. This was necessary to support some load balanced network adapters where the visible hostId changes depending on the active physical network adapter. * Rework the Java side state engine so it is now possible for the Java side of the Wrapper to respond to stop events while the WrapperListener.start method is still in progress. * Add a new wrapper.listener.force_stop property which allows control over whether the WrapperListener.stop method is called on shutdown even if the WrapperListener.start method has not yet completed. * Fix a problem on Windows where the ability to start and stop the Wrapper as a service using the Wrapper itself was requiring the Administrator permission when a lower permission should have been possible. The Wrapper should now allow service control to do whatever is possible from the Services control panel. * Fix a memory corruption error if the value of wrapper.java.maxmemory was more than 4 digits in length. wrapper.java.initmemory did not have any problems. * (Professional) Add a new wrapper.event..email.client.host property which makes it possible to configure the host name sent in EHLO and HELO commands to the SMTP server. * Add a new default environment variable, WRAPPER_HOST_NAME, which stores the name of the machine where the Wrapper is running. 3.3.1 * Add debug output showing the current os name and architecture to aid in debugging problems. * (Standard, Professional) Improve the message displayed when a license key is found but is deemed to be invalid. * Modify the template wrapper.conf file to help users debug include file problems. * Disable the console title feature on all UNIX platforms other than LINUX because the console title does not get reset correctly when the Wrapper process terminates. * Add support for HP-UX IA64 CPUs. * Update the license banner in source files so it is clearer that the user is restricted by the license they agreed to. * Modify the Community edition so it will now display a Licensed to banner on startup if shipped with a Development License. This is required to enable the distribution of the Community Edition under the Development License. * (Professional) Fix a problem where the UNIX versions of the Professional Edition would sometimes deadlock on startup when run as a daemon process. Bug #1989355. * (Professional) Added two new events; jvm_failed_invocation and jvm_max_failed_invocations. Feature Request #1994718. * Fix a problem where the Windows service exit code was not being set correctly when the JVM exited with a non-zero exit code. The problem could be seen by running "sc query {service}" from the command line. Bug #1859061. * Added support for the Windows Itanium 64-bit platform. * Added support for the HP-UX Itanium 32 and 64-bit platforms. * Added support for the MAC OSX 64-bit platform. * (Standard, Professional) Fix a problem on Windows versions where servers which reported a large number of possible host ids could cause a buffer overflow on startup. This crash was possible when using either Development or Server licenses. Removed duplicate host ids from the list of possible ids. * Add a new "condrestart" command to the shell script which will restart the Wrapper only if it is already running. Feature Request #1928045. * Fix a problem where the 64-bit Solaris x86 version was unable to load its JNI library. Bug #1992039. * Fix a problem on Windows versions where a frozen JVM process was not always being killed. This could result in the zombie JVM processes being left around that consumed memory and other resources. * Add a new wrapper.ignore_console_logouts property which allows the Wrapper and JVM to survive logouts when launched as a console application from another service. * (Standard, Professional) Add a wrapperw.exe binary in Windows implementations which makes it possible to run the Wrapper without a console. A console still flickers for an instant when the Wrapper starts. This is the same issue that has existed when running as an interactive service and is required to make thread dumping possible. * (Standard, Professional) Add new wrapper.logdialog.enable, wrapper.logdialog.format, wrapper.logdialog.lines, and wrapper.logdialog.loglevel properties used to configure the display of a Log dialog when the wrapperw.exe binary exits in an error state. * Fix a problem where the Wrapper was attempting to reopen its backend port even when the JVM was down. This was only a problem when the defined port range was limited to a single port with the wrapper.port.min and wrapper.port.max properties. In such a case one or more warning messages were being displayed because the port is locked for a few moments after being closed. * (Standard, Professional) The wrapper.initmemory.percent and wrapper.maxmemory.percent properties were not correctly being calculated relative to a maximum of 2048MB for 32-bit versions of the Wrapper. Bug #2053167. 3.3.0 * Add a new wrapper.ping.interval.logged property which makes it possible to reduce the debug output caused by ping transactions between the Wrapper and JVM. * Fix a problem on Windows where the Windows Service Manager was not waiting the full configured time of the wrapper.jvm_exit.timeout and wrapper.shutdown.timeout properties. This was leading to the net stop command timing out and the system shutting down without the java application having fully stopped. Bug #1582568. * If internal firewalls were preventing the backend socket from being created, it was not being made clear what the cause was. It was also possible that the JVM would deadlock on shutdown. This second problem was recovered from when the Wrapper killed the JVM. * Rework the console output from all Wrapper classes to make it much more obvious that that is their source. * Submit a patch to the UNIX sh script by Chris Dance which makes it possible to tell the shell to wait a few seconds for the wrapper to start up. Also includes some modifications to work correctly on older Debian and RedHat systems. * Fix a problem where the local copy of ant was not always being used correctly on UNIX systems which have a default copy of any installed. Thanks to Robey Pointer for the patch. * Add the -lm to the command line when building Linux 32 and 64 bit versions of the wrapper on Linux. This is to support building on recent Debian and Ubuntu versions. Thanks to Robey Pointer for the patch. * Add support for the SIGUSR1 and SIGUSR2 signals so they can now trigger a shutdown, restart or be forwarded to the JVM for custom functionality. See the wrapper.signal.mode.usr1 and wrapper.signal.mode.usr2 properties. Based on a patch by Robey Pointer. Note that the JVM process does not trap SIGUSR1 or SIGUSR2 signals as they are used internally by the JVM as part of the garbage collection process. * Fix a problem where the WRAPPER_OS, WRAPPER_ARCH, and WRAPPER_BITS environment variables were not being initialized correctly for the various unix platforms. * Removed the 4096Mb upper limit set on the wrapper.java.initmemory and wrapper.java.maxmemory properties. 64bit users need to go way beyond that. * Fix a problem where relative include file references in the configuration file were not working correctly if the wrapper.working.dir was used to change the working directory. The working directory is now always reset to its original value as the configuration file is being loaded. * Added a new wrapper.registry.java_home property which makes it possible to specify the location of java within the registry. * Set a new WRAPPER_JAVA_HOME environment variable if the JAVA_HOME is located in the Windows registry. * Modify the way properties are looked up so that any unreplaced environment variable references will be reevaluated in case they were set after the property was originally set. This is possible with some WRAPPER_* environment variables or depending on the placement of set.* properties in the configuration file. * Set any unset properties to their default values internally. This is necessary so the WrapperManager.getProperties() method returns the correct set of active properties. * Fix an occasional crash with 64-bit UNIX caused by a native synchronization problem when system signals are trapped. Bug #1614010. * Fix a problem on Solaris versions, where the Wrapper was not correctly recovering and attempting another back end port when the first was already in use. Bug #1594073. * Fix a problem on Solaris and AIX where the RUN_AS_USER feature of the shell script was not working due to lack of support for the "-m" option of su. The shell script now uses "su -". Bug #1590168. * Add HP-UX Makefiles for building with make. Fix some problems in the shell script to make the script work better on HP-UX. Thanks to David Brown and Nicolas Varney for the patches. * Fix a problem where any signals received by the JVM and triggering a SIGCHLD signal in the Wrapper were being interpretted as the JVM having stopped. This was not always true. Bug #1643666. * The Wrapper is now able to detect when the JVM process is stopped and continued. It will still timeout if stopped, but a descriptive warning is now logged. * Increase the maximum number of log entries which can be queued to avoid losing them. These are only used for log entries outside of the primary thread. * Fix a problem in the shell script which was making it impossible to stop the Wrapper or query its status on OSX. * Add support for 64 bit AIX. Thanks to Nicolas Varney for supplying a Makefile. * Correct the AIX library extension to be ".a". * Rename the UNIX Makefiles so it is more obvious which tool must be used to build with them. * Fix a problem where the HP-UX native library would not be located correctly for some processor types if the file had a platform specific name. * Internally rename the WRAPPER_JSTATE_LAUNCH state to WRAPPER_JSTATE_LAUNCH_DELAY for clarity. * Fix a problem where the UNIX versions of the Wrapper would shutdown rather than restarting a frozen JVM if the arrival of the SIGCHLD signal from the old JVM process was delayed by more than a second or two. * Rework the Windows build so it now uses nmake and a Makefile rather than vcbuild. This is probably not as clean, but it was needed to get the 64-bit build working. * Fix a problem on Windows versions where quoted values specified from the command line were not always being requoted correctly when generating the java command line. * Add validation checks for Windows versions to make sure that all additional parameters, application arguments, the classpath, and library path all contain values which are quoted correctly. Incorrectly quoted values will now result in a warning message that will help resolve the problem. * Fix a memory leak when calling WrapperManager.listServices() on Windows. Bug #1665947. * Fix a buffer overflow problem if the Wrapper was launched without explicitly specifying a configuration file. * Add tests of the return values of all malloc calls to catch out of memory errors and recover as gracefully as possible. Bug #1649880. * Modify the WrapperManager.signalStarting and signalStopping methods so that they will never have the effect of shortening the existing timeout. Updated the javadocs of both methods so they more accurately reflect what the methods do. * Move the Wrapper Copyright banner into the Wrapper process so it will be output more consistently. * Branch the code to support Community, Standard, and Professional Editions of the Java Service Wrapper. * (Standard, Professional) Add support for Server (Fixed) as well as Development (OEM based) licenses. * (Standard, Professional) Add 64-bit versions of the Windows version. The 64-bit Community Edition will not be distributed initially to support ongoing development costs. * (Professional) Add event handling callbacks for Wrapper start/stop, JVM start/stop, JVM started/stopped, JVM restart, JVM killed, and JVM unexpected exit events. * (Professional) Add the ability to send emails in response to any event callback. * (Professional) Add the ability to execute a a user configured command in response to any event callback. * Add WRAPPER_BIN_DIR and WRAPPER_WORKING_DIR environment variables which are now available for use within the wrapper.conf file as well as by any child processes. * Add documentation to the integration pages about existing system properties that be used to control the way the WrapperSimpleApp and WrapperStartStopApp handle application startup. * Remove support for native PPC and x86 distributions of the Wrapper for MAC OSX in favor of the universal X-Code distribution. This appears to be the standard for the market and saves lots of time on testing. * Add a new '-it' command to the Windows version which makes it possible to install and start a service as a single command. * Fix a problem where the PATH environment variable was not being set correctly on Windows versions when run as a service if the wrapper.ntservice.account was set and a PATH was set for both the SYSTEM and user accounts. Bug #1702274. * Modify the shell script to set JAVA_HOME to the default JVM location on OSX systems if it is not already set. OSX always places the JVM in a known location so this is possible. Thanks to Andrew Williams for the patch. * Fix a problem where the UNIX shell script would fail if the APP_NAME was set to a value containing spaces. Thanks to Andrew Williams for the patch. Bug #1695678. * Fix a problem where the DIST_ARCH was not being resolved correctly on HP-UX systems. Thanks to Matej Kraus for the patch. Patch #1697421. * Log output from the timer thread was not being queued correctly, this could have lead to timing problems if there were any delays writing to disk. * Add partial support for OS/400 into the build file. Still needs a Makefile. * Fix a problem where the WrapperActionServer would deadlock in its stop method if the JVM shutdown was initiated by a call to the shutdown or restart actions. * Add support for wrapper.console.title on UNIX platforms. Add a set of wrapper.console.title. properties which make it possible to set the title based on the platform. * Fix a problem where the wrapper.ntservice.account and wrapper.ntservice.password properties were being stored in the system registry if the they were specified on the command line when the Wrapper was installed as a service. This was broken in version 3.2.2. Bug #1538725. * Fix a problem where the DUMP command was not working with the wrapper.commandfile property when run as a service on Windows. Bug #1644421. * Fix a problem where wildcards like "*.*" or "*" in a classpath property were including the "." and ".." files on Windows versions. Bug #1517928. * Modify the debug log output when the Wrapper is attempting to load its native library in an attempt to make the expected failures less threatening. * Commit a patch by Rob Oxspring which adds the start_msg and stop_msg commands to the shell script. These are expected by init scripts on HP-UX. Patch #1750027. * Add a DETAIL_STATUS flag to the UNIX shell script which will cause the status, start_msg, and stop_msg commands to display the current internal status of both the Wrapper and Java processes. * Commit a patch by Rob Oxspring which adds an init block to the UNIX shell script to make it work with install_initd and remove_initd scripts used by SUSE linux. Patch #1750028. * Commit a patch by Travis Carlson, ia64 systems were being grouped as "x86" systems. They now are assigned the name "ia" which makes it possible to create a distribution for them. Patch #1663887. * (Standard, Professional) Add new wrapper.java.initmemory.percent and wrapper.java.maxmemory.percent properties which make it possible to set the initial and maximum memory values relative to the amount of physical memory on the system. Feature Request #1741051. * Add a new #include.debug declaration in the wrapper configuration file which makes it much easier to debug problems with cascading include files. * Add -l and --controlcode commands to the Windows version which make it easy to send user defined control codes to the Wrapper running as a service. * Fix a synchronization problem in the logging code which could result in data corruption or access violations. * Add version numbers to the bat and sh scripts to make them easier to support. * Make the wrapper.ntservice.name, wrapper.ntservice.displayname, and wrapper.ntservice.description properties aliases of new wrapper.name, wrapper.displayname, and wrapper.description properties as they are now used on UNIX platforms as well as Windows. * Fix a problem where the wrapper would sometimes fail to send a packet to the JVM because the sending of the packet would block. Thanks to Peter Gorgon for the patch. * Fix a problem where CPUs identifying themselves as 'ia64n' or 'ia64w' were not correctly being categorized as 'ia'. Thanks to Stirling Chow for the patch. Patch #1859412. * Add -d and --dump commands to the Windows version which make it possible to send thread dump requests to the Wrapper when running as a service. Works in association with the new wrapper.thread_dump_control_code property. Feature Request #1118110. * (Professional) Add wrapper.timer..interval and wrapper.timer..action properties which make it possible to schedule Wrapper shutdowns, JVM restarts and thread dumps at arbitrary times. * Fix a problem where the default configuration file name was being corrupted by a buffer overrun. Thanks to Rob Joyce for the patch. Patch #1879049. * Fix a problem where the WrapperManager would sometimes attempt to unregister its shutdown hook after the shutdown hook had been initiated. Bug #1799489. * Fix a problem where log files were limited to 2GB on Linux systems. Bug #1881038. 3.2.3 * Add support for x86 Mac OS X distributions. * The 3.2.2 Windows version was accidentally released with its MFC libraries dynamically linked. This meant that anyone who did not have VS8 installed were not able to run the Wrapper due to missing DLLs. This version fixes that snafu by correctly using statically linked libraries as was done in previous versions built with VS6. Bug #1578554. 3.2.2 * Correct a typo in the usage output of the WrapperStartStopApp. Thanks to Michael Giroux for pointing it out. * Fix a problem on OSF1 systems where the backend socket was not listening correctly due to a backlog of 0. This was broken in 3.2.0. Thanks to Phillip Gussow for supplying a patch. * Remove the com.silveregg.wrapper package classes that were deprecated in version 3.0.0. * Fix a potential problem in the UNIX script where the lock file permissions were not being set correctly if the LOCKFILE and RUN_AS_USER variables are specified but the group of the specified user could not be resolved. * Fix a problem where the exit code returned by WrapperListener.stop was being ignored in some cases. * Fix a problem where the shell script would not work correctly when the wrapper or its configuration files were located in a directory path containing spaces. * Apply a series of patches by Michael Saya to get the Windows 64 bit build working. * Fix a problem in UNIX versions where the SIGTERM handler was being disabled when a SIGCHLD was received. * Added support in UNIX versions for the SIGHUP signal. * Migrated the source to Subversion from CVS. Did a bunch of cleanup in the source, removing CVS specific tags. * Fix a problem in UNIX versions were the pid file specified by the wrapper.java.pidfile property contained the wrapper pid rather than the jvm pid. Bug #1565011. * Fix a problem in UNIX versions where the file specified by the wrapper.java.pidfile property was not always being deleted when the JVM process went away. * A user encountered a JVM bug where calls to System.exit were resulting in an IllegalThreadStateException being thrown. Added some code to trap this and shut down the JVM using other means to avoid a hang during shutdown. * Fix a NullPointerException caused by users incorrectly implementing an Integration Method #3 class and then calling WrapperManager.start with a null value for the args parameter. * Update the banner displayed by the Wrapper on startup to include a copyright notice. Please see the license page of the documentation for details. * Add a new 'Z' log format which will log the time to millisecond accuracy. * Fix a problem where the JVM exit code was not being set correctly when the JVM was shutdown using WrapperManager.stopImmediate(). The exit code of the Wrapper was being set correctly through other means however. * Fix a potential synchronization problem in the logging code if a JVM exits with debug output enabled. * Updated the WrapperListener.stop method javadocs to better explain the exitCode value under certain exit modes. * On UNIX versions, add a log message which records the signal that caused the JVM process to exit when it terminates unexpectedly. * Fix a problem where the wrapper.on_exit. property was not working correctly in some cases on UNIX. With help from Andreas Schafer. * Add support for building the Wrapper with Visual Studio 8 for Windows. Releases will now be done using this compiler. * Fix a CRITICAL bug in the 3.2.0 and 3.2.1 Windows versions of the Wrapper where the Wrapper would crash under rare circumstances when running as a service. If the service manager interrogated the service at the same instant as the wrapper was pinging the JVM, the wrapper was sometimes crashing due to a synchronization problem. The problem did not exist prior to 3.2.0. Bug #1574537. * Fix a minor logging problem where the 'D' format was not displaying the correct thread name for queued log messages. 3.2.1 * Fix a problem with the solaris-sparc-64 makefile. * Add a solaris-x86-64 makefile. * Merge in a patch by Hugo Weber to make it possible to configure the Wrapper to pull the JRE from the system registry on windows. * Fix a batch file bug added in 3.2.0 where the scripts would not function correctly if the full path to the batch file contained spaces. Bug #1450601. * Modify the message shown when a native library fails to load so the exception message text is now shown in the log without having to enable debug log output. * Modify the UNIX shell script to be more informative if the script is unable to locate a wrapper binary due to a executable bit permission problem. * Fix a minor permission problem with the build for the delta-pack. * Commit a patch by Juergen Hermann to make the error shown when realpath fails clearer. * Add the ability to use a default wrapper.conf file that is in the same directory as the wrapper binary. The file will be named based on the name of the wrapper binary. * Synchronize the command line so that both the Windows and UNIX versions are now the same. The old command line syntaxes are now supported everywhere so there will be no compatibility problems. * It is no longer possible to specify arguments using the '/c' syntax. This was undocumented so hopefully it is not being used. The documented '-c' syntax must now be used. The change was necessary to synchronize the command line between UNIX and windows platforms. * The 32-bit HP-UX 3.2.0 build was generating a libwrapper.so file rather than libwrapper.sl. * Make the WrapperManager.setConsoleTitle, getWrapperPID, and getJavaPID methods available through JMX. * Fix a state engine problem introduced in 3.2.0 which was causing the wrapper.on_exit. properties to be ignored in most cases. * Fix a potential problem that could have caused crashes when debug logging was enabled. * Fix a problem where signals were not being handled correctly on some UNIX platforms, including AIX. This was making it impossible to shutdown the wrapper cleanly with the TERM signal. Bug #1477619. * Add new default environment variables which can be referenced in a configuration file to configure platform specific directories and file names. WRAPPER_OS, WRAPPER_ARCH, and WRAPPER_BITS. * Add a -v argument to make it possible to request the version from a wrapper binary. * Add support for registering the WrapperManager MBean with the PlatformMBeanServer when run on a 1.5+ JVM. See the JMX section in the documentation for details. * Rework the way timeout properties are handled. Values of 0 now actually disable the timeouts rather than setting them to a large value. To avoid overflow problems when converting to internal timer ticks, timeouts are now restricted to a maximum of 20 days, or 1728000 seconds. Change affects the wrapper.cpu.timeout, wrapper.startup.timeout, wrapper.ping.timeout, wrapper.shutdown.timeout, and wrapper.jvm_exit.timeout properties. For values less than 20 days, there should be no change in functionality. * Add support for debuggers. The Wrapper will now show a warning on startup and then again the first time a timeout occurs. But all timeouts will be ignored. This is to avoid problems with the Wrapper restarting a suspended JVM in the middle of a debugging session. The wrapper enters this mode if the wrapper.java.command ends with the string "jdb" or "jdb.exe", or the "-Xdebug" parameter is passed to the JVM. * Add 'athlon' to the list of supported architectures. * Fix a problem where the environment variables loaded when a service was started were always the system environment even if the service was running as a specific account. The environment of a specific account will now be loaded on top of the system environment if the USERNAME environment variable is set by the system. Bug #1491138. * Added new wrapper.ntservice.pausable and wrapper.ntservice.pausable.stop_jvm properties to make it possible to pause and resume the Wrapper when installed as a Windows service. * Added new Pause and Resume batch files as well as modified the command batch file to support pause and resume. * Added PAUSE and RESUME commands for use by the wrapper.commandfile property. * Fix a problem with the wrapper.pidfile, wrapper.java.pidfile, wrapper.anchorfile, wrapper.commandfile, wrapper.statusfile, wrapper.java.statusfile, wrapper.java.idfile, and wrapper.lockfile properties where forward slashes in paths were not being changed to back slashes on Windows versions. * Simplify the code used to load a native library by using the System.mapLibraryName method rather than doing the same thing manually. * Add a new wrapper.syslog.facility property which makes it possible to specify the syslog facility on UNIX systems. Thanks for the patch from Bruce Pennypacker. * Removed the custom thread counting used to keep track of when the wrapped Java application has completed. It is now done in a different way that will work on all Java implementations without requiring any special consideration of the current JVM. Deprecated the wrapper.monitor_thread_count and wrapper.thread_count_delay properties. Bug #1470265. * The WrapperStartStopApp helper class still requires thread counting if the stopWait parameter is set to true. Previous versions all hardcoded the system thread count to 1 which worked for most JVMs. A new system property, org.tanukisoftware.wrapper.WrapperStartStopApp.systemThreadCount, was added to make it possible to customize. It currently defaults to 1. * Make it possible to extend the WrapperSimpleApp and WrapperStartStopApp helper classes. Feature Request #1510274. * Add warning messages if the old org.silveregg.wrapper package classes are still being used. They will be removed in the next release. 3.2.0 * Rework the release cycle so that the wrapper.jar file released for all platforms is now built on the same machine. This resolves a few incompatibility problems caused by jars built on very new JVMs but run on old JVMs. * Add additional output when the JVM can not be launched due to security restrictions on Windows. * Greatly improved the performance of file logging. On a windows test machine 3.1.2 could log 67210 lines of output in 20 seconds with a 80-15% split between the Wrapper and JVM process CPU usage. It now outputs 215214 lines with a 64-34% split, also showing less load on the system process. This is a 220% increase in performance. In both cases, the JVM was completely idle other than the console output which makes the Wrapper appear to be a bit of a CPU hog. In fact it is the only process doing any work in this case. This improvement was accomplished by keeping the log file open unless idle. The idle time can be controlled using the new wrapper.logfile.inactivity.timeout property. The speed increase on UNIX platforms was much smaller at around 10%. * Add a new property, wrapper.disable_restarts, which will completely disable the Wrapper's ability to restart JVMs. * Add a pair of new properties, wrapper.port.min and wrapper.port.max, which make it possible to define the port range used when a specific wrapper.port is not specified. * Fix a problem where certain characters like umlauts were being stripped from property values. Bug #1049528. * Make the PIDs of the Wrapper and Java process easier to access by providing a new pair os system properties; wrapper.pid and wrapper.java.pid, as well as a new pair of methods; WrapperManager.getWrapperPID() and WrapperManager.getJavaPID(). * Add a new WrapperEventListener class which can be implemented to receive a wide variety of events from the Wrapper. * Add a WrapperServiceControlEvent class which will report any service control codes received by the Wrapper as it is running as an NT service. This was added to make it possible for other applications to sent custom codes to the Wrapper using the Window Service Manager. * Add a WrapperManager.listServices() method which can be used to obtain the status of all services on a Windows system. * Add a WrapperManager.sendServiceControlCode() method which makes it possible to start, stop, pause, continue, any service on Windows systems. It is also possible to send custom user codes via the service manager. * Add comments in the sh script to support the chkconfig command. * Implement the ability to read from standard input via System.in. Feature Request #1024693. * Made the tick based timer the default by changing the default value of the wrapper.use_system_time property to false. Most users should see an improvement in reliability under heavy loads with this new setting. If you have extended any timeouts in the past, you may wish to try going back to defaults as they may no longer need to be extended. * Add a new wrapper.restart.reload_configuration property which causes the Wrapper to reload its configuration file immediately before a JVM restart. Properties which can not be reloaded have comments stating that fact in their documentation. Feature Request #981060. * Fix a problem in the UNIX shell script which was preventing the script from locating the PID and anchor files when the wrapper.working.dir property was used. * Modify UNIX versions so that the wrapper binary will now force its working directory to the location of the wrapper binary. This change was made to make the UNIX version work the same way as the Windows version and thus make configuration files that modify their working directory work correctly on a cross platform basis. Users which have been using the scripts supplied with the Wrapper should not encounter any problems. Other users may require slight changes to their configuration file to deal with the new way that the Wrapper deals with its initial working directory. * Add a new method WrapperManager.getProperties() which makes it possible to access any property in the Wrapper configuration file. * Fix a problem where TERM signals were not being correctly ignored by the JVM process on UNIX platforms even if the wrapper.ignore_signals property was set to true. Earlier versions of the Wrapper would generate a WRAPPER_CTRL_SHUTDOWN_EVENT when a TERM signal was received. On Windows that signal should never be ignored. To resolve this a new WRAPPER_CTRL_TERM_EVENT was added making it possible to selectively ignore the TERM signals. This change may affect user implementations of the WrapperListener.controlEvent() method. Bug #1086344. * The Windows version has a feature which allows the user to immediately kill the Wrapper and its Java application without waiting for a clean shutdown by pressing CTRL-C twice. Windows sends the CTRL-C signal to both the Wrapper and Java processes. Due to a rare timing problem, it was possible for the Java process to get the signal first and initialize a shutdown before the Wrapper could respond to the signal. In this case the Wrapper was interpreting this as a second CTRL-C signal even though the user only pressed it once. * If the wrapper.anchorfile or wrapper.pidfile properties are used on Windows they were being unintentionally deleted if the -t, -p, -i, or -r commands were used while another Wrapper instance was running. In the case of the anchor file, this would result in the Wrapper being shutdown unintentionally. This was not an issue on non-Windows versions. Bug #1108517. * Fix a security problem where the value of the wrapper.ntservice.account and wrapper.ntservice.password properties were being stored in plain text within the registry if they were specified on the command line when installing the Wrapper as a Windows service. Bug #1110183. * Add a pair of properties wrapper.ntservice.password.prompt and wrapper.ntservice.password.prompt.mask which which will cause the Wrapper to prompt the user for an account password when it is being installed as an NT service. * Added system properties to make it possible to configure whether or not the WrapperSimpleApp and WrapperStartStopApp helper classes will wait for the configured main methods to complete before reporting that the application has started. See the javadocs for these classes for more details. * Modify the HP-UX build so that it now dynamically links with the pthread library. This was to make the binaries work with HP-UX 11.00. Thanks to Sun Kun Choi for the patch. * Add new wrapper.statusfile and wrapper.java.statusfile properties which can be used by external applications to monitor the internal state of the Wrapper or JVM at any given time. These will not be useful to most users. * Add a new wrapper.commandfile property which can be used by external applications to control the Wrapper and its JVM. * Add a new wrapper.java.idfile property which can be used by external applications to monitor the internal state of the JVM at any given time. * Add a warning on startup if the JVM has a SecurityManager set but the wrapper.jar has not been granted the AllPermissions permission. Failure to do so will almost certainly lead to the Wrapper throwing a number of errors and this helps to point out the cause. * Add a security model which protects most Wrapper method calls when a SecurityManager has been registered. See the Security Model section for more details. * Add a new pair of batch files which can be used to start and stop the Wrapper when installed as a service. * Add new -q and -qs commands to the Windows version of the Wrapper which make it possible to query the currently installed status of the service. * Fix a problem where the wrapper.java.library.path.append_system_path property was not working correctly on Windows when the system PATH contained quotes. Bug #1238726. * Modify the usage output of the Wrapper on all platforms so the Wrapper's version is now included. It was not previously possible to get the version of the Wrapper being used without launching a JVM. * Add a pair of new methods WrapperManager.stopAndReturn() and WrapperManager.restartAndReturn() which make it possible for code to stop or restart the JVM and then continue until the JVM is shutdown. This can be useful for shutdowns initiated within places like servlets, whose operation is expected to complete. * Fix a problem on UNIX where the child JVM was sometimes leaving around zombie processes after a restart. The SIGCHLD signal was not being handled correctly. Thanks to Traun Leyden for the patch. Bug #1291201. * Implement the ability to catch control events using the WrapperEventLisener. Feature Request #836975. * Add new wrapper.jvm.port, wrapper.jvm.port.min, and wrapper.jvm.port.max properties which make it possible to control the port the JVM uses to open a connection back to the JVM. The Wrapper uses to leave this up to the OS, but some users were having problems with the default conflicting with other ports. * Switch from using ftime() to gettimeofday() on UNIX platforms to work around a problem where the Wrapper would not run on new versions of OSX because they deprecated the ftime() function call. Thanks for the patch by Michael Macaluso. Bug #1313162. * Remove the shutdown timeout from the UNIX shell script. It is not needed and can cause a zombie JVM if the wrapper's internal shutdown timeout was longer than that of the shell script. * Add the ability to specify integer property values in base 8 or 16 in addition to base 10. Base 8 values start with a '0' and base 16 values start with a '0x'. * Make it possible to set the umask on all files created by the Wrapper as well as the default umask of files created by the JVM. Added new wrapper.umask, wrapper.java.umask, wrapper.pidfile.umask, wrapper.lockfile.umask, wrapper.java.pidfile.umask, wrapper.java.idfile.umask, wrapper.statusfile.umask, wrapper.java.statusfile.umask, wrapper.anchorfile.umask, and wrapper.logfile.umask properties. * Improve the message when the native library can not be loaded to make mention of the possibility of a 32/64 bit mismatch. * Add a new wrapper.monitor_thread_count property which makes it possible to disable the Wrapper's counting of non-daemon threads and thus the shutting down of the JVM when they have all completed. * Add support for BELOW_NORMAL and ABOVE_NORMAL options to the wrapper.ntservice.process_priority property. Feature Request #1373922. * Ignore '#' characters which are included within double quotes in the value of a property in the configuration file. Unquoted values must be escaped with a second '#' characters or it will be interpreted as a comment. * Display the Wrapper banner in the JVM earlier so that it is displayed even where there are startup errors. * Modify the WrapperSimpleApp and WrapperStartStopApp classes so that the WrapperManager is always initialized immediately. This makes the output clearer in the event of startup errors. * Fix a problem where the Windows ServiceManager was not correctly reporting a startup error if a service failed on startup. The service was being reported as having started even though it failed to start. * Fix a problem on UNIX versions where the Wrapper would go into a recursive state of attempting to launch the JVM from failed child processes if there was any problems executing the configured java process. * Rework the way the RUN_AS_USER setting in the UNIX shell script works so the specified user is now set regardless of the command being executed. To make sure the user never has to enter the password twice when running the script, it now recurses after changing the user. The script then runs entirely as the configured user. * Improve the message that is displayed when attempting to start, stop, or remove a windows service which is not installed. * Add new wrapper.lockfile property which makes it possible to specify a lock file containing a pid. * Modified the sh script so it now creates a lock file on startup in the /var/lock/subsys directory if it exists. This is needed by fedora systems on shutdown. * Store javadocs in tar distibutions in a nested tar file to avoid problems with long filenames in some tar distributions. * Fix a problem with the WrapperSimpleApp and WrapperStartStopApp helper classes where on heavily loaded systems it was possible for the Wrapper to get a running thread count of 0 and shutdown before the main thread had a chance to be started. * Add a new wrapper.thread_count_delay property which will force the WrapperManager to wait the specified number of seconds before it begins to check the number of running threads. * Fix a problem where the wrapper.java.library.path.append_system_path property was appending the PATH rather than the LD_LIBRARY_PATH environment variable on Unix systems. PATH is correct for Windows systems. * Add a new wrapper.logfile.rollmode property which makes it possible to control how and when the logfile is rolled. Feature Requests #864463, #1085097, and #1085850. * Fix a problem on Linux where the test for the status of the Java child process would sometimes fail causing the Wrapper to shutdown with the error "Critical error: wait for JVM process failed (No child processes)" rather than restart the child JVM. Users who encountered this problem found it easy to reproduce, but it only happened on some systems. * Modify the way the UNIX shell script tests for the existence of a process matching the pid in an existing pid file. It now verifies the process command as well as the pid to fix a system reboot problem where a stale pid has been reused by another application, making the script think the wrapper was already running. * Add support for the GNU libjcj JVM. Like JRocket, it requires slightly different thread counting. * Add support for Linux 64-bit PPC and Solaris 32-bit x86 versions. * Add a new set.default.ENV syntax to the configuration file making it possible to environment variable values which do not overwrite existing values, ie. to specify a default value. * Added a new wrapper.console.flush property which forces the wrapper to explicitly flush stdout after each line of log output. * Change the error shown when the JVM shuts down prematurely during a shutdown to a warning message. * Fix a problem where the Wrapper would show the following error message if user code called System.exit from within the WrapperListener.stop callback method. This would happen if the stop class's main method registered with the WrapperStartStopApp called System.exit. "JVM exited unexpectedly while stopping the application." Bug #945976. * Add a new wrapper.syslog.ident property which makes it possible to specify the identity used in syslog entries on UNIX. This was possible in older versions but was set using the wrapper.ntservice.name property. Bug #1432855. * Add support for MacOSX Universal Binary distributions. * Add support for Delta Pack distributions. This is a distribution that contains the binaries of multiple platforms. 3.1.2 * Modify the way boolean system properties are resolved by the WrapperManager so it is now possible to set them to true or false rather than assuming they are true if set. * Fix a problem where some localized error messages were not having their tokens replaced correctly. * Fix a problem when using the WrapperStartStopApp helper class. The usage text was incorrectly being displayed in the console if an exception was thrown while executing the main method of the configured stop class. This did not change the functionality of the application, but it did cause some confusion. * Fix a problem on Windows where a library path or class path which ended in a backslash was preventing the Wrapper from launching the JVM. The Windows OS was using the backslash to escape the quote used to close the path. The fix was to add a second backslash where needed. * Added a new wrapper.java.command.loglevel property which makes it possible to control the log level of the generated java command. * Add support for escaped quotes when stripping quotes on UNIX for the wrapper.java.additional. and wrapper.app.parameter. properties. * Change the default value of wrapper.jvm_exit.timeout from 5 to 15 seconds. The old default was too fast for some applications which take a while to exit. Applications which were exiting promptly will not see any difference. * Fix a problem where the JVM would restart at certain times when using the system time based timer due to an overflow error. This problem was introduced in 3.1.0. Due to a separate bug in 3.1.0, the Wrapper would shutdown rather than simply restarting the JVM as was happening in 3.1.1. The last restart happened on Aug 21, 2004. It will next occur Oct 10, 2004 and repeat at regular intervals. There are no problems when using the new Tick based timer. Bug #1014405. * Correct the wrapper.logfile.maxsize property so that a a kilobyte is now 1024 rather than 1000, and a megabyte is a megabyte. We aren't a hard drive manufacturer after all. * Add try-catch blocks around all thread entry points in the Windows version. This has always been done in the main function, but these blocks will help to narrow down the cause of problems should they ever be encountered in control or service handlers. * Centralize shutdown code on UNIX version in an appExit method as was already being done for Windows versions. * Fix a problem where the build.sh was not correctly using the included ant if an ANT_HOME environment variable was defined. * Add a new wrapper.single_invocation property which will prevent multiple invocations of an application from being started on Windows platforms. The shell script handles this on UNIX platforms. Feature Request #889123. * Fix a crash problem introduced in 3.1.1, caused by a pair of uninitialized pointers. The crash was possible on all platforms but would only happen if the Wrapper was started without any arguments. It would not affect users running the Wrapper normally. Bug #1018481. * Fix a problem with the run as user feature of the shell script on Solaris. Needed to be using /usr/xpg4/bin/id rather than /usr/bin/in if available. Bug #1024008. * Replace calls to usleep with nanosleep on platforms where it is available. This was to fix an occasional hang on a specific Solaris machine. It would occasionally hang on calls to usleep. From research, it appears that usleep has problems when signals are encountered while sleeping. Still testing whether or not this change solved the problem. * Upgrade the version of Ant included with source releases to 1.6.2 to fix some problems generating jni headers when building with Java 1.4.2. * Upgrade the version of Cocoon included with source releases to 2.0.4 to fix some problems generating documentation using Java 1.4.2. * Display a warning if the exit status of a JVM process ever returns the STILL_ACTIVE status on Windows. There was no known problem here, just noticed it while looking over the code. * Display a descriptive error message on Windows if the JVM process crashes due to an uncaught exception in native JVM code. * Add a test for invalid jvm arguments set using the wrapper.java.additional. properties. Invalid arguments could cause the Wrapper startup to fail in non obvious ways if they are mistaken by the JVM as the main class. 3.1.1 * Modified the way libwrapper.so is built on Solaris and Linux so that it no longer statically links its required libraries. * Fix a file handle leak when calling WrapperManager.getUser or WrapperManager.getInteractiveUser on Windows platforms. * Fix a problem introduced in 3.1.0 where the JVM would not be restarted correctly if it quit after a ping timeout to let the Wrapper resynch and restart it. * Fix a problem where CTRL-C was not being handled correctly if the console was configured to be shown when running as an NT service. * Fix a problem where signals fired at UNIX versions of the wrapper were not being handled correctly when the tick timer was being used. * Fix a synchronization problem in the logging code which would occassionally cause the Wrapper to crash with an Access Violation. The problem was only encountered when the tick timer was enabled, and was only seen on multi-CPU systems. Bug #949877. * Fix a problem when using the tick timer where the Wrapper would sometimes exit on startup due to an uncaught SIGALRM. Only reported on multi-CPU Solaris systems. * Fix a problem where the Wrapper would sometimes hang on shutdown if another thread called System.exit while the Wrapper was shutting down. Bug #955248. * Fix a problem introduced in 3.1.0 where a very very large CPU timeout warning message was being displayed if the system time was set back while using the default system timer. * Added a new property, wrapper.anchorfile, which makes it possible to cause the Wrapper to shutdown by deleting an anchor file. The UNIX sh script has been modified to optionally make use of this feature. * Add a debug message at startup which makes it clear which timer is being used. * A Windows user reported that using forward slashes in the path the log file was failing. Avoid this problem by always converting '/' to '\' in the wrapper.logfile property on Windows. * Fix a problem where it was not possible disable the wrapper log file as documented in the wrapper.logfile property. Most likely broken way back in version 2.2.5. * Add some additional error checks after calls to control the pipe between the JVM and Wrapper as well as improving the messages around other socket related error messages. * Fix a problem on some HP-UX systems were not working correctly because the EAGAIN and EWOULDBLOCK constants are not equal with some compilers. * Change some of the defaults in the src/conf/wrapper.conf.in file which ships with the Wrapper to avoid confusion with new users. * Rewrote the routine which reads and logs console output from the JVM for Windows versions. Internal buffers are now scaled dynamically, fixing a problem where long lines were being wrapped at 1024 characters. This rewrite also resulted in a 4 fold increase in speed when the JVM is sending large quantities of output to the console. * Increase debug output on UNIX platforms when a signal is caught. When possible, information about where the signal came from is now logged. * Modified the way log output from within signal handlers is handled so it is now queued and then logged by the main event loop. * Back out a 3.1.0 change where a JVM that had failed to exit cleanly was sent a SIGTERM prior to a SIGKILL. The SIGTERM made no difference and slowed down the forced shutdown. A modification to the event loop made the functionality more difficult to implement. * Add the ability to set the user that the Wrapper and its JVM will run as from within the sh script on UNIX platforms. * Add an icon resource to the Wrapper binary on Windows versions. * Fix a typo in the UNIX sh script which caused an extra slash to be included in the path of the pid file. Was not causing any known problems. * Added support for 64-bit HP-UX. Big thanks to Venkatesh Sellappa for supplying the patch. * Fix a deadlock problem introduced in 3.1.0 with some FreeBSD systems. Not all users were experiencing it, but those who did were able to reliably reproduce the problem. The problem appears to have been caused by FreeBSD bug #kern/64313. * Make the signal handling variables in the wrapper native library volatile. Directly this was to fix a compiler warning on HP-UX64 systems but it should also make the calls more efficient. 3.1.0 * The license was revised for this version to include a copyright omission. This change is to be retroactively applied to all versions of the Java Service Wrapper starting with version 3.0.0. The changes should have no effect on users. * The Online documentation and web site were both reworked. The logo has been updated so that Duke is no longer used. The new online site now has the ability for users to logon and append comments to any page. * Added a new batch file which accepts commands like the UNIX shell script. The new file is offered as an alternative to the default batch files, and can be found at src/bin/AppCommand.bat.in. Thanks to Mike Castle for donating the new script. * The Windows version of the Wrapper was not correctly registering that it would accept SHUTDOWN messages when running as a service. The Wrapper was getting the message anyway so this should not change functionality. Thanks to Jason Tishler for noticing this and sending in a patch. * Add a new property, wrapper.native_library, which can be used to specify the base name of the native library which is loaded by the WrapperManager class. * Modify the WrapperManager class so it now stores references to System.out and System.err on initialization and always writes to those stored streams. This makes sure that all Wrapper console output always goes to the wrapper.log file even if user code overrides those streams with calls to System.setOut and System.setErr. This was necessary to prevent deadlocks in such user code from affecting the functionality of the Wrapper. * Fixed a problem where some environment variables where not being correctly loaded from the system registry when running as an NT service. Big thanks to Eric Smith for tracking this down and submitting a patch. It turns out that the putenv function was not being used correctly. * Modified the way the wrapper.conf file is loaded so it will now read the contents correctly even if the line feeds in the file are incorrect for the current platform. Windows line feeds had been causing problems when used on UNIX platforms. Feature Request #829896. * Added a new property, wrapper.ntservice.console, which allows a console to be displayed when running as an NT service. * Fixed a problem where the request thread dump on failed JVM exit feature had never worked when running as an NT service. Bug #831775. * Add a new property, wrapper.console.title, which makes it possible to set the title of the console in which the Wrapper is currently running. This currently only works on Windows platforms. * Added a new method, setConsoleTitle, to the WrapperManager class which enables the application to dynamically set the console title. Like the wrapper.console.title property, this only works on Windows platforms. * Improved the algorithm of the request thread dump on failed JVM exit feature so that extremely large thread dumps will not be truncated when the JVM is killed. * Fix a problem where CTRL-C was being ignored by the WrapperManager if a WrapperListener is never registered. This is not possible if the Wrapper is being used correctly but never the less a user did come across it. * Add some additional debug output to help identify the cause of problems loading the native library. * The WrapperManager class now checks to make sure that its current version matches the version of the native library and Wrapper. If there are any discrepancies found then appropriate warnings will be displayed, but the Application will still be allowed to start. This was added to make obvious the cause of problems resulting from mismatched versions. * Added a new property wrapper.use_system_time system time. By setting this property to false, the Wrapper will start using a new experimental timer which uses a background thread to manage time rather than the system time. This has a number of advantages over using the system time and should give most users even more reliable behavior when the system is under high load or there are changes being made to the system time. The timer is very critical to the operation of the Wrapper so the old behavior is left as the default for the time being until this feature has had the chance to be "time" tested. If all goes well then this will be enabled as the default in a future version of the Wrapper. A pair of related properties, wrapper.timer_fast_threshold and wrapper.timer_slow_threshold were also added to aid in debugging. * Rework the logging code so it is now thread safe. The addition of the timer thread means that there is now more than a single thread accessing that code. This was causing problems as the two threads tried to use the same buffers. As part of this change, a new format variable 'D' was added to display the thread which is doing the logging. * Fix a problem where a thread dump would be invoked if the request thread dump on failed JVM exit was enabled and the user forced an immediate shutdown by pressing CTRL-C more than once. * Add getUser and getInteractiveUser methods to the WrapperManager class to make it possible for user code to query information about the user running Wrapper or the user who is interacting with the Wrapper and its JVM. Feature Request #812175. * The Wrapper will now always exit with the exit code used to terminate the JVM whether System.exit is used or WrapperManager.stop. When running as an NT service the Wrapper now correctly returns the correct exit code to the service manager so failure recovery tools should now work correctly. Feature Request #852491. * Add a status command to the UNIX shell script which can be used to find out whether or not the wrapper is currently running. Patch submitted by Joseph Benavidez. * Modify the WrapperSimpleApp and WrapperStartStopApp so that the main method of a class is located even if it exists in a parent class rather than the class specified. * To make debugging classpath problems easier, the Wrapper now verifies all classpath entries before launching a JVM and logs debug level warnings for any entries that do not exist. * Fix a problem where it was possible to define a zero length filter that would trigger on any output. * Add some additional debug output to make it easier to debug startup, shutdown and restart problems. * Modify the way the Wrapper forcibly kills a frozen JVM on UNIX platforms so that it now sends a SIGTERM, waits up to 5 seconds, then sends a SIGKILL. * Add a new wrapper.java.library.path.append_system_path property which will cause the Wrapper to append the system path to the generated library path. Feature Request #917902. * Fix a problem where spaces around the '=' character of a property definition were rendering the property invisible to the Wrapper. Bug #916001. * Fix a problem where the first ping timeout after the JVM was started was still hard coded at 30 seconds. This was causing a combination of large values of wrapper.ping.interval and wrapper.ping.timeout to fail. * Fix a problem where the JVM would fail to shutdown cleanly if the Wrapper was asked to stop too soon after launching a JVM. This was leading to the JVM being killed after the shutdown timeout expired. Bug #917281. * Added an adviser which will print out explanatory messages to the console and wrapper log file when the Wrapper encounters a commonly made configuration mistake. This is designed to cut down on support requests by new users. Can be disabled using the wrapper.adviser property. * The bash script and the realpath utility have been deprecated since version 3.0.3. They have been removed in this release. The sh script is recommended on all UNIX platforms, and the realpath utility which was used by pre-3.0.3 bash and sh scripts has not been used since. * Add the wrapper.startup.delay property along with console and service specific variants which make it possible to configure a delay between the Wrapper being launched and the first JVM being launched. * Promote the wrapper.debug property back from being "deprecated". It has continued to be useful and deserved documentation and official status. * Add wrapper.on_exit. properties to control what happens when a exits based on the exit code. * Modify the way calls to System.in.read() are handled so that they now block rather than throwing an exception. Currently, System.in can not be used with the Wrapper because of the way I/O is passed between the Wrapper and JVM. * Modified the Windows batch files to fix a problem where the path to the Wrapper.exe contained more than one "/bin". The new batch files are much simpler and should be easier to customize if needed. Bug #925308. * Modified the wrapper.java.initmemory and wrapper.java.maxmemory properties so that they now default to a value of 0 which causes the -Xms and -Xmx parameters to be omitted from the command used to launch Java. This will cause the JVM to use its own default values and also makes it possible to specify the memory parameters using the wrapper.java.additional. properties. * Added a pair of environment variables, WRAPPER_FILE_SEPARATOR and WRAPPER_PATH_SEPARATOR, whose values are set to either '/' and ':' or '\' and ';' on startup. They can be used in the wrapper.conf file to construct platform independent property values. * Add a new wrapper.working.dir property which makes if possible to change the Wrapper and JVM's working directory to a location other than the location of the Wrapper binary. Feature Request #738160. 3.0.5 * Added support for SGI Irix. Big thanks to Andreas Wendt for supplying the patch. * Due to a bug in the build, the native library was not included in the 3.0.3 or 3.0.4 binary releases for OSX, building from source was working correctly. This has been fixed and the build greatly simplified to avoid such problems in the future. Bug #791755. * Changed the default location of the pid file generated by the sh script to exist in the same directory as the sh script rather than in the /var/run. This can be changed by setting the PIDDIR variable in the sh script used to launch the Wrapper. * Added support for the wrapper.pidfile property on the Windows platform. * Added the wrapper.java.pidfile property which will cause the pid of the java process to be written to a specified file. (WINDOWS USERS) If you are using a wrapper.conf file that was created prior to version 3.0.0 of the Wrapper, then you may have this property defined in your configuration file. You will get an error on startup if the specified path does not exist. * Stop clearing the file creation mask when the Unix version of the Wrapper is run as a daemon process. The file creation mask will not be inherited from the process which launches the Wrapper. Bug #788849. * Modify the sh script so it works on Linux, then deprecate the bash script. This means that all Unix platforms can now use the same script to control the Wrapper. Thanks to Mike Castle for the patch. The bash script can still be found in the release, but it is deprecated and will be removed in a future version. * Modified the sh script so it is now possible to set the nice priority in the script configuration block. * Remove output to System.out in the WrapperManager.requestThreadDump() method. If some JVM threads were hung while accessing the System.out object, attempting to do a thread a dump would cause the calling thread to hang as well. Thanks to Thomas Hart for the patch. * Make it obvious in the log whether or not the Wrapper was started as a daemon process on UNIX systems. * Modify the way restarts requested from the JVM, or caused by a filter are handled. The Wrapper will no longer reset the restart count in either of these cases. If an application runs for longer than the wrapper.successful_invocation_time timeout then the count will still be reset back to 0. * Added a new wrapper.ignore_signals property which makes it possible to configure the Wrapper so it will ignore CTRL-C, HALT and INT signals. * Modify the WrapperManager.isLaunchedAsService() method on UNIX systems so it now returns true if the Wrapper was launched with the wrapper.daemonize flag set. * Added a pair of MBean interfaces which allow the Wrapper to be controlled using JMX. See the new JMX section in the documentation for details. Thanks to Sal Ingrilli for help with testing. * Modify the Windows build so the Wrapper.exe and Wrapper.dll files can now be built from Ant if MSVC is installed. * Added a new wrapper.ping.interval property which lets users control the frequency that the Wrapper pings the JVM. Feature Request #607768. * When a JVM refuses to shutdown, the Wrapper can be configured to request a thread dump using the wrapper.request_thread_dump_on_failed_jvm_exit property. The Wrapper was then waiting 1 second before the process was killed. This was not always long enough, resulting in a truncated thread dump. Increased the pause to 3 seconds. Feature Request #633761. * Fix a bug where wrapper.app.parameter. and wrapper.java.additional. properties declared from the Windows command line were not correctly handling spaces in their values. Support Request #802139. 3.0.4 * Fix a problem on UNIX systems where requesting a second thread dump any time during the life of a single Wrapper process would cause the Wrapper and JVM to shutdown rather than perform the thread dump. * Fix a problem where a, user without permission, attempting to stop an application was able to delete the pid file even though they were unable to stop the application itself. This would make the scripts think that the application was stopped when was actually still running. * Fix a problem where an application was being killed prematurely if it took longer than 6 seconds to exit on its own. The scripts now make sure that an application always has enough time to shutdown cleanly. * Improve the debug output so that packet codes are now shown using a name rather than a raw number. * Reduce the frequency of "Waiting to stop..." messages displayed when removing an NT service that is currently running. Decreased frequency from once per second to once every five seconds. * Fix a minor problem where the hour in the date returned by WrapperInfo.getBuildTime() was not base 24. * Added -t and -p command line options to the Windows version of the Wrapper to sTart and stoP the Wrapper as an NT service. This can be used in place of "net start" and "net stop", which do not always work correctly when a service takes a long time to start up or shutdown. See the Launching Your Application (Win32) section for more details. * Add a new method WrapperManager.stopImmediate which will cause the JVM to exit immediately without calling any stop methods or shutdown hooks. * Add a new class, WrapperActionServer, which makes it easy to remotely control the Wrapper remotely by opening a socket and sending commands. See the javadocs of the class for more details. * Fix bug #744801. A Java GUI was not being displayed when the application was run in either console mode or as a service with wrapper.ntservice.interactive enabled. This problem was introduced in Version 3.0.0 when using 1.2.x or 1.3.x versions of Java. To use interactive services with 1.2.x or 1.3.x versions of java, please review the documentation for the wrapper.ntservice.interactive property. * Fix a problem where the JVM was not receiving CTRL-C and CTRL-CLOSE events when running under the Wrapper on Windows. This was not a problem in most cases as the Wrapper was taking care of the processing of the events. But the WrapperListener.controlEvent() method was not being called as documented. * Changed the way the WrapperSimpleApp and WrapperStartStopApp respond to control events so that the JVM will respond and call WrapperManager.stop() even when being controlled by the Wrapper. * Modified the suggested behavior of the WrapperListener.controlEvent() method. Users who have implemented the WrapperListener interface themselves should review the Javadocs. The changes are not required and applications will continue to function as they did before. * Added support for DEC OSF1 (Alpha). Big thanks to Andreas Wendt for supplying the patch. * Fix a problem where the sh and bash scripts were failing if the path to the script contained spaces. * Fix a problem where the JVM would sometimes hang when trying to shutdown if the wrapper.key parameter was passed to the JVM while not being controlled by the Wrapper. This would happen if a user copied the command from the Wrapper's debug output and attempted to run it as is without first removing the wrapper.key parameter. * Implement the ability to specify an NT service's load order group in response to feature request #764143. See the javadocs for the new wrapper.ntservice.load_order_group property for details. * Improve the error message displayed when the NT EventLog is full in response to feature request #643617. The EventLog output will now be disabled if any errors are encountered while logging events. This prevents the error from repeating. * Improve the error message displayed on Windows when the configured Java command can not be executed or does not exist. * Fix a problem where the Wrapper was leaving a pipe unclosed each time the JVM was restarted on all UNIX platforms. This was causing the Wrapper to run out of file handles. Bug #767267, discovered and patched by David Wong. * Fix a problem where the '#' character, which signifies a comment, could not be included in property values. A double hash, '##' will now resolve into a '#' within the property value. Bug #777303. * Added support for FreeBSD. Big thanks to Alphonse Bendt for supplying the patch. * Make the wrapper.port property optional. * Changed the way environment variables are loaded from the registry on Windows platforms so users will no longer get warning messages about not being able to handle very large environment variables. Prior versions could only handle environment variables whose expanded value was less than 2048 characters in length. * Fix a problem on UNIX platforms where a shell used to start the Wrapper running as a detached process would hang when the user attempted to exit the shell. Thanks to Mike Castle for this patch. 3.0.3 * Added support for Mac OS X. Big thanks to Andy Barnett for supplying the patch. * Fix a segmentation fault on UNIX systems when the first console output from the JVM was an empty line. Thanks to Mike Castle for finding this. * Fix a problem where a 0 length malloc was being called if there were no configured filters. This was fine on most platforms but caused a crash on MAC OS X. * Rework the initialization of the bash and sh scripts so that they will work correctly when referenced as symbolic links. Thanks go out to Richard Emberson for the code to resolve symbolic links. * Deprecated the realpath binary in the *NIX distributions as it is no longer used by the bash or sh scripts. It is being left in for now so as not to break the build scripts of other projects, but it will be removed after a couple more releases. * Added a test to make sure that wrapper.ntservice.interactive is not set to TRUE when an account is specified using wrapper.ntservice.account. 3.0.2 * Modified the sh and bash scripts so that console log output is disabled by default when the scripts are launched with the 'start' action. Running with the 'console' action will still send output to the console. Logging to the file is still enabled. * Modified the wrapper.ping.timeout property so it also controls the ping timeout within the JVM. Before the timeout on responses to the Wrapper could be controlled, but the ping timeout within the JVM was hardcoded to 30 seconds. * In the last release, some work was done to avoid false timeouts caused by large quantities of output. On some heavily loaded systems, timeouts were still being encountered. Rather than reading up to 50 lines of input, the code will now read for a maximum of 250ms before returning to give the main event loop more cycles. * Fix a problem where the values of environment variables set in the configuration file were not correct when those values included references to other environment variables. * Fix a potential buffer overflow problem if configuration properties referenced extremely large environment variables. * Fix a potential problem where the inability to expand very large environment variables would have led to an access violation when run as an NT service. * Add some extra checks in the event where the native library can not be loaded so that the WrapperManager can differentiate between the library missing and not being readable due to permission problems. * Remove the wrapper.ntservice.process_priority from the default wrapper.conf because its use can produce unexpected results if used improperly. Please see the property documentation for details. * Fix a problem where environment variables in the registry which had no value were causing the Wrapper to crash with an access violation. This was introduced in version 3.0.0 with the feature to load environment variables from the registry. The offending registry entry was WV_GATEWAY_CFG which appears to be related to Oracle. 3.0.1 * Fix a problem with the wrapper.disable_shutdown_hook. Due to a typo in the source, the property was being ignored. This was broken in the 3.0.0 release. * Fix a problem with the HP-UX release build reported by Ashish Gawarikar. * Add the ability to set environment variables from within the configuration file or from the command line. * Fix a problem on HP-UX and AIX machines where the stop() function in the shell scripts was causing a syntax error due to a conflict with a like named shell command on those platforms. This appears to be an issue with the Korn shell on all platforms. * Fix a problem where very heavy output from the JVM can cause the Wrapper to give a false timeout. The Wrapper now only reads 50 lines of input at a time to guarantee that the Wrapper's event loop always gets cycles. * Fix a problem on UNIX versions where extra line breaks would sometimes be added to the logged output when there was large amounts of output being sent from the JVM. * Fix a problem where a large number of calls to WrapperManager.log() immediately before the JVM exits could lead to the Wrapper incorrectly reporting that the JVM exited unexpectedly. 3.0.0 * Deprecated the com.silveregg.wrapper package in favor of org.tanukisoftware.wrapper. The classes and interfaces in the silveregg package will continue to function, but migration to the new package should be done when possible. See the project history for details. * On Windows systems change any forward slashes in the wrapper.java.command property to back slashes. Some users had reported having problems on Windows XP. * Implemented feature request #633178. Added WrapperManager.requestThreadDump() to force the current JVM to immediately perform a thread dump. * Fixed bug where wrapper.logfile.maxsize was being set to 0 if the 'k' or 'm' unit was omitted. * Add the ability to specify an account name and password when installing an NT service. * Add a property, wrapper.ntservice.interactive, which makes it possible to control whether or not the Java process can gain access to the desktop while it is running as an NT service. * Add limited support for 1.2.x versions of Java. Shutdown hooks are supported until Java 1.3 so those functions will be disabled. If the application displays a GUI then Java 1.3 should be used as the GUI can not currently be displayed when using Java 1.2.x. * Made it possible to use the wrapper.pidfile property on all *nix platforms. Please notice that the property has been removed from the default wrapper.conf file. The property is not needed when the wrapper is launched with the bash shell script. The sh shell script will set the wrapper.pidfile when the wrapper is launched. If either of the scripts provided with the Wrapper distribution are used then the wrapper.pidfile should always be removed from your wrapper.conf file. * Added a new wrapper.daemonize property which, when set, will form the wrapper process to be a detached non-session group leader. This makes it possible to launch the wrapper in such a way that it will not be terminated when the user launching the process logs out. This had been a problem on Solaris systems when using the sh shell. The default sh and bash scripts both make use of this in the default. Please update your scripts for use with this version. Thanks to Rajiv Subrahmanyam for the patch. * Fix a problem where the Wrapper was incorrectly counting the number of non-daemon threads in BEA's JRockit Virtual Machine. This was causing the application to shutdown when the non-daemon thread count dropped to 1. * Added support for building the wrapper on AIX and HP-UX systems. Thanks for the patches involved go out to Ashish Gawarikar and William Lee. * Implement feature request #653131 to force the JVM to immediately exit when the user presses CTRL-C multiple times. * Added a 'console' action to the bash and sh scripts to make it possible to launch the Wrapper in the current shell process. The 'start' task will launch the Wrapper as a spawned daemon process. * Fixed a problem where missing environment variables specified in classpath or library path properties were not being handled correctly. * Implemented feature request #676599 to enable the filtering of JVM output to trigger JVM restarts or Wrapper shutdowns. See the new wrapper.filter.trigger.n and wrapper.filter.action.n properties. * Modify the Win32 version of the Wrapper so that Environment Variables are always read from the system registry when the Wrapper is run as a service. By doing this, it makes it possible to change or add the system environment variables and have them take effect without having to first reboot the machine. * Implemented cascading configuration files. * Changed the default value for the wrapper.java.initmemory property to be 3Mb. The default on Windows and Linux JVMs is 2Mb, but the Solaris JVM requires a minimum of 3Mb. The minimum value accepted by the Wrapper was changed from 8Mb to 1Mb to make it possible to reduce the footprint of applications to what is possible without using the wrapper. * Improve the parsing of configuration files so that leading and trailing white space is now correctly trimmed. It is also now possible to have comments at the end of a line containing a property. * Modify the way exceptions thrown by an application's main method are presented to the user by the WrapperSimpleApp and WrapperStartStopApp so they no longer look like a problem with Wrapper configuration. 2.2.9 * Added a new property, wrapperper.restart.delay, which allows the user to control the amount of time to pause between a JVM exiting and a new JVM being launched. * Fixed bug #611024. The Wrapper would sometimes fail to start if wrapper.max_failed_invocations is set to 1. * Fix a problem where the number of non-daemon threads was not being calculated in some cases. * Implemented feature request #491443. Environment variables referenced in the wrapper.conf file will now be evaluated as the file is loaded. The windows syntax for environment variables is used on all platforms to make them platform independent. * Fixed a problem where the wrapper.conf was being open with both read and write locks when a read lock is all that is needed. Made the wrapper fail on startup if another application held a read lock on the conf file. * Modified the message displayed when the native library could not be found, so that it is much more descriptive. Hopefully it will cut down on questions caused by configuration problems. * Implemented feature request #613539. Modified the wrapper.java.library.path to function like the wrapper.java.classpath.n properties so that multiple directories can be specified in the library path in a platform independent way. The old property is still supported, but deprecated. * Fix Bug #632215. The WrapperManager.isLaunchedAsService() method was always returning false, even when run as a service under Windows. On linux, the Wrapper is always run as a console app, so this method will always return false. * Improve the message thrown when user code attempts to access System.in from within a JVM being controlled by the Wrapper. System.in will not work because the JVM is a spawned process. 2.2.8 * Fixed a compiler problem on Solaris some systems. * Added a new property, wrapper.cpu.timeout, which allows the user to control how much time without receiving any CPU the Wrapper will tolerate before displaying a warning message. The CPU timeout feature was added in 2.2.7 but the default timeout of 10 seconds was not configurable. * The Wrapper was only allowing 5 seconds between the JVM informing the Wrapper that it was going to exit and the JVM process actually exiting. This would cause the Wrapper to terminate the process prematurely in cases where an application shutdown thread took longer than 5 seconds to complete. The Wrapper now allows wrapper.jvm_exit.timeout seconds for the JVM process to exit on its own before being forcibly terminated. * When there is a configuration problem or a resource is unavailable, a JVM will sometimes exit abnormally very shortly after being launched. This can lead the JVM being infinitely restarted due to a simple class path misconfiguration. To work around this, the Wrapper has always had a hard limit of 5 restarts within a short period of time. If the JVM has been running for more than a few minutes, then the count was reset. In this version, a new property. wrapper.max_failed_invocations was added to allow the max value to be set. The time period which the JVM must now be running for the JVM launch to have been considered a success for restart purposes is set using the new wrapper.successful_invocation_time property. * The number of advanced properties which most users do not need has been increasing as the Wrapper has been made more and more flexible. This has been causing confusion in their usage by people who play with them without first reading the documentation. To solve this, the advanced properties were removed from the default configuration file. They still function. But users must now read to the advanced configuration documentation to learn about their existence. Added quite about to the descriptions of these properties to hopefully clear up any confusion about their usage. * When the JVM exits abnormally, the Wrapper will pause for a few seconds before starting another JVM. If the user pressed CTRL-C during this pause, a new JVM would still be launched. The new JVM was exiting immediately but it was a waste of time. The Wrapper now recognizes the event and aborts launching the new JVM. * Added a page to the documentation which shows inline javadocs. This will hopefully make it easier to navigate them as part of the full documentation set. * Added a new method to the WrapperManager which enables user code to log at any log level. * Added a new Helper class WrapperStartStopApp which allows users to easily integrate applications like Tomcat which use a separate class to stop the application. * Added a samples section to the documentation. Just includes Tomcat 4 for now. 2.2.7 * Fix a problem where the JVM was trying to reconnect the Wrapper as it was being shutdown. This was causing problems if the JVM was being restarted. * Added support for the system being suspended to RAM or disk. Also improved wrapper performance when a system is under 100% load. See the new example output in the example section. * Fix a problem where the log output was not being directed to a file called wrapper.log in the same directory as the Wrapper binary in the event that the configured wrapper log file could not be accessed. * Fix a problem where the Wrapper was not shutting down the JVM correctly when all non daemon threads completed. Normally a JVM will exit when all of its non daemon threads have completed so this was causing some problems. (Thanks to Jung Tamas) * Added the ability to set the priority of the Wrapper and its JVM when run as an NT service or console application. The same thing can be better achieved on Unix systems by using "nice" in the shell script used to launch the Wrapper. See the documentation for for details. * JVM information was not being displayed correctly when the Wrapper native library could not be loaded. * Added a new property to cause the wrapper to attempt to request a thread dump when the JVM does not exit on request. * Improved the documentation of the WrapperSimpleApp and WrapperListener classes. * Adding a new property wrapper.shutdown.timeout to allow the user to extend the length of time that an application is allowed to take shutting down. * Rework the way the shutdown process works so that System.exit will never be called before the stop method in WrapperListener has had a chance to complete. * Add a Restart button to the TestWrapper application. * Fix a problem on Unix versions where '%' characters in Java output would sometimes cause the wrapper to crash. Somehow missed getting this into the last release. * Added a test to make sure that WrapperManager.stop is not called recursively. * Added support for building under Windows XP. Prebuilt installations had already been working. 2.2.6 * Fix a problem where '%' characters in Java output would sometimes cause the wrapper to crash. (Thanks to Frode Moe) * Added support for requesting a Java thread dump without shutting down the Java process. * Fixed a problem on windows where the java command was looking in the windows system and system32 directories for the java executable before checking the path when the full path to the java executable was not specified in the configuration file. This could lead to different JVM being run from the Wrapper than was run if java -version was run from the command line. The Wrapper will now attempt to resolve the full java path using the PATH environment variable. * Added debug output showing Java version information when the JVM first starts. * Modified c source to use /* */ style comments rather than // style comments. Some people were having problems with some compilers. 2.2.5 * Added support for service descriptions for Win2k and XP. * Fixed bug issue when reading configuration files from Windows on Unix. * Deprecated the wrapper.debug property in favor of loglevels. * Added new logger functionality includes the following features: Loglevels like Log4j, NT Eventlog support, UNIX syslog support and rolling log files. * Added wildcard support for classpath entries in wrapper.conf. * Added the ability to specify configuration properties from the command line. * Changed the way NT services are installed so that a patched version of the Wrapper.exe file no longer needs to be created to reference the wrapper.conf file. 2.2.4 * The value of APP_NAME in the bash or sh scripts no longer needs to be the same as the script. * Added the ability to format and/or disable file logging and output to the console. * Set mode of executables in binary release tar files so that they can be run without modification after being extracted. * Fixed line feeds in release so that bat files are always CRLF, unix scripts are always LF. Other source files are always CRLF in ZIP archives and LF in tar.gz archives. * Make the build fail if Wrapper.exe or Wrapper.dll do not exist for Windows builds. * Added an empty wrapper.log to the releases so that the TestWrapper example program runs out of the box. 2.2.3 * Added template scripts and conf files for ease of integration with other applications. * Cleaned up the build. * The WrapperSimpleApp method of launchine applications was not working correctly with applications whose main method did not return. * Add sample scripts and wrapper.conf files in the bin and lib directories. These scripts are used to start a sample application which runs out of the box. See the new example.html page in the documentation for more details. * Get rid of the platform specific directories in the bin and lib directories. * Enable relative paths for Windows version. In previous versions of Wrapper, it was necessary to always use absolute paths because the working directory of the wrapper would be then NT System32 directory when run as a service. * On the windows version, the wrapper always sets the current working directory to the location of the wrapper executable immediately after startup. * Improvements to the documentation / web page. 2.2.2 * Added HTML based documentation. 2.2.1 * Added Linux and Solaris build files. * Added Linux and Solaris documentation. 2.2.0 * Initial Public Release. wrapper_3.5.51_src/doc/wrapper-community-license-1.3.txt100644 0 0 152561 14333053650 20350 0ustar 0 0 ---------------------------------------------------------------------- ----------------- ----------------- Tanuki Software, Ltd. Community Software License Agreement Version 1.3 IMPORTANT-READ CAREFULLY: This license agreement is a legal agreement between you ("Licensee") and Tanuki Software, Ltd. ("TSI"), which includes computer software, associated media, printed materials, and may include online or electronic documentation ( Software ). PLEASE READ THIS AGREEMENT CAREFULLY BEFORE YOU INSTALL, COPY, DOWNLOAD OR USE THE SOFTWARE ACCOMPANYING THIS PACKAGE. Section 1 - Grant of License Community editions of the Software are made available on the GNU General Public License, Version 2 ("GPLv2") or Version 3 ("GPLv3"), included in Sections 4 and 5 of this license document. All sections of the Community Software License Agreement must be complied with in addition to those of either the GPLv2 or GPLv3. This license allows the Software Program to be used with Products that are released under either GPLv2 or GPLv3. Section 2 - Definitions 2.1. "Community Edition" shall mean versions of the Software Program distributed in source form under this license agreement, and all new releases, corrections, enhancements and updates to the Software Program, which TSI makes generally available under this agreement. 2.2. "Documentation" shall mean the contents of the website describing the functionality and use of the Software Program, located at http://wrapper.tanukisoftware.org 2.3. "Product" shall mean the computer programs, that are provided by Licensee to Licensee customers or potential customers, and that contain both the Software Program as a component of the Product, and a component or components (other than the Software Program) that provide the material functionality of the Product. If the Product is released in source form, the Software Program or any of its components may only be included in executable form. 2.4. "Software Program" shall mean the computer software and license file provided by TSI under this Agreement, including all new releases, corrections, enhancements and updates to such computer software, which TSI makes generally available and which Licensee receive pursuant to Licensee subscription to TSIMS. Some specific features or platforms may not be enabled if they do not fall under the feature set(s) covered by the specific license fees paid. 2.5 "End User" shall mean the customers of the Licensee or any recipient of the Product whether or not any payment is made to use the Product. Section 3 - Licensee Obligations A copy of this license must be distributed in full with the Product in a location that is obvious to any End User. In accordance with Section 4 (GPLv2) or Section 5 (GPLv3), the full source code of all components of the Product must be made available to any and all End Users. Licensee may extend and/or modify the Software Program and distribute under the terms of this agreement provided that the copyright notice and license information displayed in the console and log files are not obfuscated or obstructed in any way. Section 4 - GPLv2 License Agreement GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Section 5 - GPLv3 License Agreement GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright c 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine- readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty- free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. Section 6 - 3rd Party Components (1) The Software Program includes software and documentation components developed in part by Silver Egg Technology, Inc.("SET") prior to 2001 and released under the following license. Copyright (c) 2001 Silver Egg Technology Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sub-license, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. wrapper_3.5.51_src/src/bin/App.bat.in100644 0 0 17737 14333053647 14570 0ustar 0 0 @echo off setlocal rem rem Copyright (c) 1999, 2022 Tanuki Software, Ltd. rem http://www.tanukisoftware.com rem All rights reserved. rem rem This software is the proprietary information of Tanuki Software. rem You shall use it only in accordance with the terms of the rem license agreement you entered into with Tanuki Software. rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html rem rem Java Service Wrapper script - Run as a console application. rem rem ----------------------------------------------------------------------------- rem These settings can be modified to fit the needs of your application rem Optimized for use with version 3.5.51 of the Wrapper. rem The base name for the Wrapper binary. set _WRAPPER_BASE=wrapper rem The directory where the Wrapper binary (.exe) file is located. It can be rem either an absolute or a relative path. If the path contains any special rem characters, please make sure to quote the variable. set _WRAPPER_DIR= rem The name and location of the Wrapper configuration file. This will be used rem if the user does not specify a configuration file as the first parameter to rem this script. rem If a relative path is specified, please note that the location is based on the rem location of the Wrapper executable. set _WRAPPER_CONF_DEFAULT="../conf/%_WRAPPER_BASE%.conf" rem Makes it possible to override the Wrapper configuration file by specifying it rem as the first parameter. rem set _WRAPPER_CONF_OVERRIDE=true rem _PASS_THROUGH controls how the script arguments should be passed to the rem Wrapper. Possible values are: rem - commented or 'false': the arguments will be ignored (not passed). rem - 'app_args' or 'true': the arguments will be passed through the Wrapper as rem arguments for the Java Application. rem - 'both': both Wrapper properties and Application arguments can be passed to rem the Wrapper. The Wrapper properties come in first position. The rem user can optionally add a '--' separator followed by application rem arguments. rem NOTE - If _WRAPPER_CONF_OVERRIDE is set to true the above applies to arguments rem starting with the second, otherwise it applies to all arguments. rem set _PASS_THROUGH=app_args rem If there are any errors, the script will pause for a specific number of seconds rem or until the user presses a key. (0 not to wait, negative to wait forever). set _WRAPPER_TIMEOUT=-1 rem Do not modify anything beyond this point rem ----------------------------------------------------------------------------- rem rem Resolve the real path of the wrapper.exe rem For non NT systems, the _REALPATH and _WRAPPER_CONF values rem can be hard-coded below and the following test removed. rem if "%OS%"=="Windows_NT" goto nt echo This script only works with NT-based versions of Windows. goto :eof :nt rem Find the application home. rem if no path path specified do the default action IF not DEFINED _WRAPPER_DIR goto dir_undefined set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR:"=%" if not "%_WRAPPER_DIR:~-2,1%" == "\" set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR_QUOTED:"=%\" rem check if absolute path if "%_WRAPPER_DIR_QUOTED:~2,1%" == ":" goto absolute_path if "%_WRAPPER_DIR_QUOTED:~1,1%" == "\" goto absolute_path rem everythig else means relative path set _REALPATH="%~dp0%_WRAPPER_DIR_QUOTED:"=%" goto pathfound :dir_undefined rem Use a relative path to the wrapper %~dp0 is location of current script under NT set _REALPATH="%~dp0" goto pathfound :absolute_path rem Use an absolute path to the wrapper set _REALPATH="%_WRAPPER_DIR_QUOTED:"=%" :pathfound rem rem Decide on the specific Wrapper binary to use (See delta-pack) rem if "%PROCESSOR_ARCHITEW6432%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 :x86_32 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-32.exe" set _BIN_BITS="32" goto search :amd64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-64.exe" set _BIN_BITS="64" goto search :ia64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-ia-64.exe" set _BIN_BITS="64" goto search :search set _WRAPPER_EXE="%_WRAPPER_L_EXE:"=%" if exist %_WRAPPER_EXE% goto check_lic_bits set _WRAPPER_EXE="%_REALPATH:"=%%_WRAPPER_BASE%.exe" if exist %_WRAPPER_EXE% goto conf if %_BIN_BITS%=="64" goto x86_32 echo Unable to locate a Wrapper executable using any of the following names: echo %_WRAPPER_L_EXE% echo %_WRAPPER_EXE% goto preexitpause :check_lic_bits if %_BIN_BITS%=="64" ( set _CHECK_LIC_BITS=true ) rem rem Find the wrapper.conf rem :conf if [%_WRAPPER_CONF_OVERRIDE%]==[true] ( set _WRAPPER_CONF="%~f1" if not [%_WRAPPER_CONF%]==[""] ( shift goto conf ) ) set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT:"=%" rem The command should not be called inside a IF, else errorlevel would be 0 if not [%_CHECK_LIC_BITS%]==[true] goto conf %_WRAPPER_EXE% --request_delta_binary_bits %_WRAPPER_CONF% > nul 2>&1 if %errorlevel% equ 32 ( set _LIC32_OS64=true set _CHECK_LIC_BITS=false goto x86_32 ) :conf if [%_PASS_THROUGH%]==[true] ( set _PASS_THROUGH=app_args ) if [%_PASS_THROUGH%]==[app_args] ( set _PARAMETERS=-- set ARGS_ARE_APP_PARAMS=true set _PASS_THROUGH_ON=true ) if [%_PASS_THROUGH%]==[both] ( set _PASS_THROUGH_ON=true ) if not [%_PASS_THROUGH_ON%]==[true] ( set _PASS_THROUGH=false ) if not [%1]==[] ( if [%_PASS_THROUGH%]==[false] ( echo Additional arguments are not allowed when _PASS_THROUGH is set to false. goto preexitpause ) ) rem Collect all parameters :parameters if [%1]==[] goto callcommand if [%ARGS_ARE_APP_PARAMS%]==[true] goto append if [%1]==[--] ( set ARGS_ARE_APP_PARAMS=true goto append ) rem So we are appending a wrapper property. rem 1) Check it is wrapped inside double quotes. if not ["%~1"]==[%1] ( if not [%_MISSING_QUOTES_REPORTED%]==[true] ( set _MISSING_QUOTES_REPORTED=true echo WARNING: Any property assignment before '--' should be wrapped inside double quotes on Windows. In a powershell prompt command, double quotes should be escaped with backquote characters ^(^`^). ) rem If not wrapped inside quotes, the following tests are not relevant, so skip them. Should we stop? We always used to continue.. but the Wrapper will probably fail. goto append ) rem 2) Check that the arg matches the pattern of a property (the command should be outside of a IF block for errorlevel to be correct) echo %1 | findstr ^wrapper\..*\=.*$ > nul 2>&1 if %errorlevel% equ 0 goto append echo %1 | findstr ^.*\=.*$ > nul 2>&1 if %errorlevel% equ 0 goto unkown_property rem Not a valid assignment. echo WARNING: Encountered an invalid configuration property assignment '%~1'. When PASS_THROUGH is set to 'both', any argument before '--' should be in the format '^=^'. goto append :unkown_property rem The property name is not starting with 'wrapper.' so invalid. rem Extract the property name (this should be outside of a IF-ELSE block) for /f "tokens=1* delims==" %%a in ("%~1") do set _COMMAND_PROP=%%a echo WARNING: Encountered an unknown configuration property '%_COMMAND_PROP%'. When PASS_THROUGH is set to 'both', any argument before '--' should target a valid Wrapper configuration property. :append set _PARAMETERS=%_PARAMETERS% %1 shift goto parameters rem rem Run the Wrapper rem :callcommand if [%_PASS_THROUGH%]==[false] ( %_WRAPPER_EXE% -c %_WRAPPER_CONF% ) else ( %_WRAPPER_EXE% -c %_WRAPPER_CONF% %_PARAMETERS% ) if not errorlevel 1 goto :eof rem Exit when there is a problem :preexitpause if %errorlevel% equ 0 ( set EXIT_CODE=1 ) else ( set EXIT_CODE=%errorlevel% ) if %_WRAPPER_TIMEOUT% gtr 0 ( timeout /t %_WRAPPER_TIMEOUT% ) else ( if %_WRAPPER_TIMEOUT% lss 0 ( pause ) ) EXIT /B %EXIT_CODE% wrapper_3.5.51_src/src/bin/App.sh.in100644 0 0 340201 14333053647 14435 0ustar 0 0 #! /bin/sh # # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html # # Java Service Wrapper sh script. Suitable for starting and stopping # wrapped Java applications on UNIX platforms. # Optimized for use with version 3.5.51 of the Wrapper. # #----------------------------------------------------------------------------- # These settings can be modified to fit the needs of your application # IMPORTANT - Please always stop and uninstall an application before making # any changes to this file. Failure to do so could remove the # script's ability to control the application. # NOTE - After loading the variables below, the script will attempt to locate a # file with the same basename as this script and having a '.shconf' extension. # If such file exists, it will be executed giving the user a chance to # override the default settings. Having the customized configuration in a # separate '.shconf' file makes it easier to upgrade the Wrapper, as the # present script file can then be replaced with minimal changes (although at # least the 'INIT INFO' below needs to be updated). # Initialization block for the install_initd and remove_initd scripts used by # SUSE linux, CentOS and RHEL distributions. Also used by update-rc.d. # Note: From CentOS 6, make sure the BEGIN INIT INFO section is before any line # of code otherwise the service won't be displayed in the Service # Configuration GUI. ### BEGIN INIT INFO # Provides: @app.name@ # Required-Start: $remote_fs $syslog # Should-Start: $network $time # Should-Stop: $network $time # Required-Stop: $remote_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: @app.long.name@ # Description: @app.description@ ### END INIT INFO # Application name and long name: If these variables are not set (or left to # the default tokens), APP_NAME will default to the name of the script, then # APP_LONG_NAME will default to the value of APP_NAME. APP_NAME="@app.name@" APP_LONG_NAME="@app.long.name@" # If uncommented (and set to false), APP_NAME and APP_LONG_NAME will no longer # be passed to the wrapper. See documentation for details. #APP_NAME_PASS_TO_WRAPPER=false # Wrapper WRAPPER_CMD="./wrapper" WRAPPER_CONF="../conf/wrapper.conf" # Priority at which to run the wrapper. See "man nice" for valid priorities. # nice is only used if a priority is specified. PRIORITY= # Location of the pid file. PIDDIR="." # PIDFILE_CHECK_PID tells the script to double check whether the pid in the pid # file actually exists and belongs to this application. When not set, only # check the pid, but not what it is. This is only needed when multiple # applications need to share the same pid file. PIDFILE_CHECK_PID=true # FIXED_COMMAND tells the script to use a hard coded action rather than # expecting the first parameter of the command line to be the command. # By default the command will be expected to be the first parameter. #FIXED_COMMAND=console # PASS_THROUGH controls how the script arguments should be passed to the # Wrapper. Possible values are: # - commented or 'false': the arguments will be ignored (not passed). # - 'app_args' or 'true': the arguments will be passed through the Wrapper as # arguments for the Java Application. # - 'both': both Wrapper properties and Application arguments can be passed to # the Wrapper. The Wrapper properties come in first position. The # user can optionally add a '--' separator followed by application # arguments. # NOTE - If FIXED_COMMAND is set to true the above applies to all arguments, # otherwise it applies to arguments starting with the second. # NOTE - Passing arguments is only valid with the following commands: # - 'console' # - 'start', 'restart', 'condrestart' (if not installed as a daemon) #PASS_THROUGH=app_args # If uncommented, causes the Wrapper to be shutdown using an anchor file. # When launched with the 'start' command, it will also ignore all INT and # TERM signals. #IGNORE_SIGNALS=true # Wrapper will start the JVM asynchronously. Your application may have some # initialization tasks and it may be desirable to wait a few seconds # before returning. For example, to delay the invocation of following # startup scripts. Setting WAIT_AFTER_STARTUP to a positive number will # cause the start command to delay for the indicated period of time # (in seconds). WAIT_AFTER_STARTUP=0 # If set, wait for the wrapper to report that the daemon has started WAIT_FOR_STARTED_STATUS=true WAIT_FOR_STARTED_TIMEOUT=120 # If set, the status, start_msg and stop_msg commands will print out detailed # state information on the Wrapper and Java processes. #DETAIL_STATUS=true # If set, the 'pause' and 'resume' commands will be enabled. These make it # possible to pause the JVM or Java application without completely stopping # the Wrapper. See the wrapper.pausable and wrapper.pausable.stop_jvm # properties for more information. #PAUSABLE=true # Set the mode used to 'pause' or 'resume' the Wrapper. Possible values are # 'signals' which uses SIGUSR1 and SIGUSR2, and 'file' which uses the command # file to communicate these actions. The default value is 'signals'. # Be aware that depending on the mode, the properties wrapper.signal.mode.usr1, # wrapper.signal.mode.usr2, or wrapper.commandfile of the configuration file may # be overriden. #PAUSABLE_MODE=signals # If set, the Wrapper will be run as the specified user. # IMPORTANT - Make sure that the user has the required privileges to write # the PID file and wrapper.log files. Failure to be able to write the log # file will cause the Wrapper to exit without any way to write out an error # message. # NOTES - This will set the user which is used to run the Wrapper as well as # the JVM and is not useful in situations where a privileged resource or # port needs to be allocated prior to the user being changed. # - Setting this variable will cause stdin to be closed. While this # should not be a problem when running as Daemon, it would disable ability # for console applications to receive inputs. #RUN_AS_USER= # When RUN_AS_USER is set, the 'runuser' command will be used to substitute the # user. If not present on the machine, 'su' will be used as a fallback. # The parameter below lets you specify option(s) for the 'runuser' (or 'su') # command. # NOTES - The '-u' option is not allowed. Please set the user with RUN_AS_USER. # - On GNU/Linux systems, if the user specified by RUN_AS_USER doesn't # have a default shell please specify one with the '-s' option. #SU_OPTS="-s /bin/bash" # By default we show a detailed usage block. Uncomment to show brief usage. #BRIEF_USAGE=true # Set which service management tool to use. # Possible values are: # for linux...: auto, systemd, upstart, initd # for aix.....: auto, src, initd # When set to 'auto', this script file will try to detect in the order of the # list for each platform which service management tool to use to install the Wrapper. SERVICE_MANAGEMENT_TOOL=auto # Specify how the Wrapper daemon and its child processes should be killed # when using systemd. # The default is 'control-group' which tells systemd to kill all child # processes (including detached ones) in the control group of the daemon # when it stops. # Alternatively, 'process' can be specified to prevent systemd from # killing the child processes leaving this responsibility to the Wrapper. # In this case child processes marked as 'detached' will not be killed on shutdown. # NOTE - the daemon must be reinstalled for any changes on this property to take effect. SYSTEMD_KILLMODE=control-group # When installing on Mac OSX platforms, the following domain will be used to # prefix the plist file name. PLIST_DOMAIN=org.tanukisoftware.wrapper # When installing on Mac OSX platforms, this parameter controls whether the daemon # is to be kept continuously running or to let demand and conditions control the # invocation. MACOSX_KEEP_RUNNING="false" # The following two lines are used by the chkconfig command. Change as is # appropriate for your application. They should remain commented. # chkconfig: 2345 20 80 # description: @app.long.name@ # Set run level to use when installing the application to start and stop on # system startup and shutdown. It is important that the application always # be uninstalled before making any changes to the run levels. # It is also possible to specify different run levels based on the individual # platform. When doing so this script will look for defined run levels in # the following order: # 1) "RUN_LEVEL_S_$DIST_OS" or "RUN_LEVEL_K_$DIST_OS", where "$DIST_OS" is # the value of DIST_OS. "RUN_LEVEL_S_solaris=20" for example. # 2) RUN_LEVEL_S or RUN_LEVEL_K, to specify specify start or stop run levels. # 3) RUN_LEVEL, to specify a general run level. RUN_LEVEL=20 # List of files to source prior to executing any commands. Use ';' as delimiter. # For example: # FILES_TO_SOURCE="/home/user/.bashrc;anotherfile;../file3" FILES_TO_SOURCE= # Do not modify anything beyond this point #----------------------------------------------------------------------------- gettext() { "$WRAPPER_CMD" --translate "$1" "$WRAPPER_CONF" 2>/dev/null if [ $? != 0 ] ; then echo "$1" fi } checkIsRoot() { if [ `id | sed 's/^uid=//;s/(.*$//'` != "0" ] ; then IS_ROOT=false else IS_ROOT=true fi } mustBeRootOrExit() { checkIsRoot if [ "$IS_ROOT" != "true" ] ; then eval echo `gettext 'Must be root to perform this action.'` exit 1 fi } ## # Normalize a dir path. # # $1: the name of the variable to set (without the $) # $2: the path to normalize normalizeDirPath() { # NOTE: Variables are prefixed with 'ndp_' to avoid conflict with code # outside this function. Keyword 'local' is not supported on some # systems (z/OS, Solaris 10) # # Use '&&' to ensure pwd is only executed on an existing directory ndp_normalDirPath=`unset CDPATH && cd "$2" 2>/dev/null && pwd` ndp_ret=$? if [ $ndp_ret -eq 0 ] ; then eval "$1=$ndp_normalDirPath" else eval "$1=$2" fi return $ndp_ret } ## # Normalize a file path. # # $1: the name of the variable to set (without the $) # $2: the path to normalize normalizeFilePath() { # NOTE: Variables are prefixed with 'nfp_' to avoid conflict with code # outside this function. Keyword 'local' is not supported on some # systems (z/OS, Solaris 10) nfp_dirPath=`dirname "$2"` normalizeDirPath nfp_normalDirPath $nfp_dirPath nfp_ret=$? if [ $nfp_ret -eq 0 ] ; then nfp_fileName=`basename "$2"` eval "$1=$nfp_normalDirPath/$nfp_fileName" else eval "$1=$2" fi return $nfp_ret } ## # Resolves the location of a system command. # # $1: the name of the variable to set (without the $) # $2: the name of the command # $3: an ordered and semicolon-separated list of paths where the command should # be searched. The list should contain an empty value for the command to be # searched using the PATH environment variable. # $4: 1 to be strict (the script will stop with an error), 0 otherwise. resolveLocation() { eval "CMD_TEMP=\$$1" if [ "X$CMD_TEMP" = "X" ] ; then found=0 OIFS=$IFS IFS=';' for CMD_PATH in $3 do if [ -z "$CMD_PATH" ] ; then # empty path CMD_TEMP="$2" ret=`command -v $CMD_TEMP 2>/dev/null` if [ $? -eq 0 ] ; then found=1 break fi else CMD_TEMP="${CMD_PATH}/$2" if [ -x "$CMD_TEMP" ] ; then found=1 break fi fi done IFS=$OIFS if [ $found -eq 1 ] ; then eval "$1=$CMD_TEMP" elif [ $4 -eq 1 ] ; then eval echo `gettext 'Unable to locate "$2".'` eval echo `gettext 'Please report this message along with the location of the command on your system.'` exit 1 else # return the error return 1 fi fi return 0 } resolveIdLocation() { # On Solaris, the version in /usr/xpg4/bin should be used in priority. resolveLocation ID_BIN id "/usr/xpg4/bin;;/usr/bin" 1 } resolveCurrentUser() { if [ "X$CURRENT_USER" = "X" ] ; then resolveIdLocation CURRENT_USER=`$ID_BIN -u -n 2>/dev/null` fi } validateRunUser() { if [ "X$RUN_AS_USER" != "X" ] then resolveCurrentUser if [ "$CURRENT_USER" = "$RUN_AS_USER" ] then # Already running as the configured user. RUN_AS_USER="" fi fi if [ "X$RUN_AS_USER" != "X" ] then # Make sure the user exists if [ "`$ID_BIN -u -n "$RUN_AS_USER" 2>/dev/null`" != "$RUN_AS_USER" ] then eval echo `gettext 'User $RUN_AS_USER does not exist.'` exit 1 fi # Make sure to be root when using RUN_AS_USER. This is required for runuser. For su, it is technically possible to use # it with a normal user, but we are using the command several times so this would result in multiple password prompts. checkIsRoot if [ "$IS_ROOT" != "true" ] ; then eval echo `gettext 'Must be root to run with RUN_AS_USER=$RUN_AS_USER.'` exit 1 fi # Resolve the location of the 'runuser' command, or fall back on 'su' if it is not present. resolveLocation RUNUSER_BIN runuser ";/sbin;/usr/sbin" 0 ret=$? if [ $ret -eq 1 ] ; then resolveLocation RUNUSER_BIN su ";/bin" 0 ret=$? fi if [ $ret -eq 1 ] ; then eval echo `gettext 'Unable to locate the command to run with a substitute user.'` exit 1 fi fi } # Make sure APP_NAME is less than 14 characters, otherwise in AIX, the commands # "lsitab" or "lssrc" will fail validateAppNameLength() { if [ ${#APP_NAME} -gt 14 ] ; then eval echo `gettext ' APP_NAME must be less than 14 characters long. Length of ${APP_NAME} is ${#APP_NAME}.'` exit 1 fi } # Method to check if systemd is running. # Returns 0 if systemd is found, otherwise returns 1. systemdDetection() { if [ ! -d "/etc/systemd" ] ; then return 1 fi resolveLocation PIDOF_BIN pidof ";/bin;/usr/sbin" 1 result=`$PIDOF_BIN systemd` return $? } # Detect if upstart is installed # Returns 0 if upstart is found, otherwise returns 1. upstartDetection() { if [ ! -d "/etc/init" ] ; then return 1 fi INITCTL_BIN="initctl" result=`command -v $INITCTL_BIN 2>/dev/null` if [ $? -ne 0 ] ; then INITCTL_BIN="/sbin/initctl" if [ ! -x "$INITCTL_BIN" ] ; then return 1 fi fi result=`$INITCTL_BIN version 2>/dev/null` if [ $? -eq 0 ] ; then # if the word "upstart" is in the result, then we assume upstart is installed. result=`echo $result | grep upstart` if [ $? -eq 0 ] ; then return 0 fi fi # The command /sbin/initctl may fail if the user doesn't have enough privilege # but that doesn't mean upstart is not present. # In this case we can assume upstart exists if the service was previously # installed by the root user. if [ -f "/etc/init/${APP_NAME}.conf" ] ; then return 0 fi return 1 } # Method to check if SRC is running. # Returns 0 if SRC is found, otherwise returns 1. srcDetection() { # $PS_BIN has already been resolved at startup result=`$PS_BIN -A | grep srcmstr` return $? } setServiceManagementToolLinux() { case "$SERVICE_MANAGEMENT_TOOL" in 'auto') systemdDetection if [ $? -eq 0 ] ; then USE_SYSTEMD=1 return fi upstartDetection if [ $? -eq 0 ] ; then USE_UPSTART=1 return fi ;; 'systemd') USE_SYSTEMD=1 ;; 'upstart') USE_UPSTART=1 ;; 'initd') ;; *) return 1 esac } setServiceManagementToolAix() { case "$SERVICE_MANAGEMENT_TOOL" in 'auto') srcDetection if [ $? -eq 0 ] ; then USE_SRC=1 fi ;; 'src') USE_SRC=1 ;; 'initd') ;; *) return 1 esac } # Resolve the service management tool for linux and aix. setServiceManagementTool() { if [ ! -n "$SERVICE_MANAGEMENT_TOOL" ] ; then # Set the default value to auto. SERVICE_MANAGEMENT_TOOL=auto fi if [ "$DIST_OS" = "linux" ] ; then setServiceManagementToolLinux elif [ "$DIST_OS" = "aix" ] ; then setServiceManagementToolAix else if [ "$SERVICE_MANAGEMENT_TOOL" != "auto" ] ; then eval echo `gettext 'The script does not support the service management tool \"$SERVICE_MANAGEMENT_TOOL\" on this platform.'` SERVICE_MANAGEMENT_TOOL=auto fi return fi if [ $? = 1 ] ; then eval echo `gettext 'Service management tool value is invalid: $SERVICE_MANAGEMENT_TOOL.'` exit 1 fi } # default location for the service file SYSTEMD_SERVICE_FILE="/etc/systemd/system/$APP_NAME.service" # Installation status SERVICE_NOT_INSTALLED=0 SERVICE_INSTALLED_DEFAULT=1 SERVICE_INSTALLED_SYSTEMD=2 SERVICE_INSTALLED_UPSTART=4 SERVICE_INSTALLED_SRC=8 SERVICE_INSTALLED_SRC_PARTIAL=16 #incomplete installation with SRC (lssrc or lsitab failed to return a record) getServiceControlMethod() { if [ "$DIST_OS" = "macosx" -a -f "/Library/LaunchDaemons/${APP_PLIST}" ] ; then CTRL_WITH_LAUNCHD=true elif [ "$DIST_OS" = "linux" -a -n "$USE_UPSTART" ] && [ -f "/etc/init/${APP_NAME}.conf" ] ; then CTRL_WITH_UPSTART=true elif [ "$DIST_OS" = "linux" -a -n "$USE_SYSTEMD" -a -z "$SYSD" ] && [ -f "${SYSTEMD_SERVICE_FILE}" ] ; then CTRL_WITH_SYSTEMD=true elif [ "$DIST_OS" = "aix" -a -n "$USE_SRC" ] && [ -n "`/usr/bin/lssrc -S -s $APP_NAME`" ] ; then CTRL_WITH_SRC=true else CTRL_WITH_DEFAULT=true fi } checkInstalled() { # Install status installedStatus=$SERVICE_NOT_INSTALLED installedWith="" if [ "$DIST_OS" = "solaris" ] ; then if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" ] ; then installedStatus=$SERVICE_INSTALLED_DEFAULT fi elif [ "$DIST_OS" = "linux" ] ; then if [ "X$1" != "Xstrict" -o \( -z "$USE_SYSTEMD" -a -z "$USE_UPSTART" \) ] ; then if [ -f "/etc/init.d/$APP_NAME" -o -L "/etc/init.d/$APP_NAME" ] ; then installedStatus=$SERVICE_INSTALLED_DEFAULT installedWith="init.d" fi fi if [ "X$1" != "Xstrict" -o -n "$USE_SYSTEMD" ] ; then if [ -f "${SYSTEMD_SERVICE_FILE}" ] ; then installedStatus=`expr $installedStatus + $SERVICE_INSTALLED_SYSTEMD` installedWith="${installedWith}${installedWith:+, }systemd" fi fi if [ "X$1" != "Xstrict" -o -n "$USE_UPSTART" ] ; then if [ -f "/etc/init/${APP_NAME}.conf" ] ; then installedStatus=`expr $installedStatus + $SERVICE_INSTALLED_UPSTART` installedWith="${installedWith}${installedWith:+, }upstart" fi fi elif [ "$DIST_OS" = "hpux" ] ; then if [ -f "/sbin/init.d/$APP_NAME" -o -L "/sbin/init.d/$APP_NAME" ] ; then installedStatus=$SERVICE_INSTALLED_DEFAULT fi elif [ "$DIST_OS" = "aix" ] ; then if [ "X$1" != "Xstrict" -o -z "$USE_SRC" ] ; then if [ -f "/etc/rc.d/init.d/$APP_NAME" -o -L "/etc/rc.d/init.d/$APP_NAME" ] ; then installedStatus=$SERVICE_INSTALLED_DEFAULT installedWith="init.d" fi fi if [ "X$1" != "Xstrict" -o -n "$USE_SRC" ] ; then validateAppNameLength if [ "$IS_ROOT" = "true" ] ; then # Check both lssrc & lsitab to make sure the installation is complete. lsitab requires root privileges. # We will go through this case before installing or removing a service, so we can redo a clean installation # or a clean removal if installedStatus=5 if [ -n "`/usr/sbin/lsitab $APP_NAME`" -a -n "`/usr/bin/lssrc -S -s $APP_NAME`" ] ; then installedStatus=`expr $installedStatus + $SERVICE_INSTALLED_SRC` installedWith="${installedWith}${installedWith:+, }SRC" elif [ -n "`/usr/sbin/lsitab $APP_NAME`" -o -n "`/usr/bin/lssrc -S -s $APP_NAME`" ] ; then installedStatus=`expr $installedStatus + $SERVICE_INSTALLED_SRC_PARTIAL` installedWith="${installedWith}${installedWith:+, }SRC" fi else # Using lssrc is enough to test normal installations and doesn't require root privileges if [ -n "`/usr/bin/lssrc -S -s $APP_NAME`" ] ; then installedStatus=`expr $installedStatus + $SERVICE_INSTALLED_SRC` installedWith="${installedWith}${installedWith:+, }SRC" fi fi fi elif [ "$DIST_OS" = "freebsd" ] ; then if [ -f "/etc/rc.d/$APP_NAME" -o -L "/etc/rc.d/$APP_NAME" ] ; then installedStatus=$SERVICE_INSTALLED_DEFAULT fi elif [ "$DIST_OS" = "macosx" ] ; then if [ -f "/Library/LaunchDaemons/${APP_PLIST}" -o -L "/Library/LaunchDaemons/${APP_PLIST}" ] ; then installedStatus=$SERVICE_INSTALLED_DEFAULT fi elif [ "$DIST_OS" = "zos" ] ; then if [ -f /etc/rc.bak ] ; then installedStatus=$SERVICE_INSTALLED_DEFAULT fi fi } showUsage() { # $1 bad command if [ -n "$1" ] then eval echo `gettext 'Unexpected command: $1'` echo ""; fi if [ "X${PASS_THROUGH}" = "Xapp_args" ] ; then ARGS=" {JavaAppArgs}" elif [ "X${PASS_THROUGH}" = "Xboth" ] ; then ARGS=" {WrapperProperties} [-- {JavaAppArgs}]" else ARGS="" fi eval MSG=`gettext 'Usage: '` if [ -n "$FIXED_COMMAND" ] ; then echo "${MSG} $0${ARGS}" else setServiceManagementTool checkInstalled "strict" if [ $installedStatus -eq $SERVICE_NOT_INSTALLED ] ; then # not installed, allow arguments to be passed through ARGS_START=$ARGS else ARGS_START="" fi if [ -n "$PAUSABLE" ] ; then echo "${MSG} $0 [ console${ARGS} | start${ARGS_START} | stop | restart${ARGS_START} | condrestart${ARGS_START} | pause | resume | status | install | installstart | remove | dump ]" else echo "${MSG} $0 [ console${ARGS} | start${ARGS_START} | stop | restart${ARGS_START} | condrestart${ARGS_START} | status | install | installstart | remove | dump ]" fi fi if [ ! -n "$BRIEF_USAGE" ] then echo ""; if [ ! -n "$FIXED_COMMAND" ] ; then echo "`gettext 'Commands:'`" echo "`gettext ' console Launch in the current console.'`" echo "`gettext ' start Start in the background as a daemon process.'`" echo "`gettext ' stop Stop if running as a daemon or in another console.'`" echo "`gettext ' restart Stop if running and then start.'`" echo "`gettext ' condrestart Restart only if already running.'`" if [ -n "$PAUSABLE" ] ; then echo "`gettext ' pause Pause if running.'`" echo "`gettext ' resume Resume if paused.'`" fi echo "`gettext ' status Query the current status.'`" echo "`gettext ' install Install to start automatically when system boots.'`" echo "`gettext ' installstart Install and start running as a daemon process.'`" echo "`gettext ' remove Uninstall.'`" echo "`gettext ' dump Request a Java thread dump if running.'`" echo ""; fi if [ "X${PASS_THROUGH}" = "Xapp_args" -o "X${PASS_THROUGH}" = "Xboth" ] ; then if [ "X${PASS_THROUGH}" = "Xboth" ] ; then echo "WrapperProperties:" echo "`gettext ' Optional configuration properties which will be passed to the Wrapper.'`" echo ""; fi echo "JavaAppArgs:" echo "`gettext ' Optional arguments which will be passed to the Java application.'`" echo ""; fi fi exit 1 } # Output help to understand why the working directory is not accessible. # The permissions of each folder composing the path will be printed. # If a folder doesn't have enough permissions, the wrapper binaries or the # script located in that directory won't be able to execute. # - When running with the current user, this means we won't be able to detect # whether the Wrapper binaries are executable, nor send requests such as # --translate, --request_delta_binary_bits, --request_log_file. # - When RUN_AS_USER is set, we must be root so the current user should always # have enough permission. However the command to substitute the user would # fail if the specified user doesn't have permission on the full path. # # $1: The name of the user for which we fail to execute a command in the # working directory. reportRealDirNotAccessible() { # Unfortunately the following message will not be translated because currently the Wrapper fails to execute correctly when it can't access to the absolute path. eval echo `gettext 'Failed to access the script using an absolute path. Insufficient permissions may prevent the user \"$1\" from traversing one of the folders. Please check the following permissions:'` OIFS=$IFS IFS='/' for DIR in $REALDIR do # REALDIR should already be normalized, but skip '.' if any remains. if [ "$DIR" != "" -a "$DIR" != "." ] ; then INT_PATH="${INT_PATH}/$DIR" ALL_PATHS="${ALL_PATHS} ${INT_PATH}" result=`cd "${INT_PATH}" 2>/dev/null` if [ $? -ne 0 ] ; then # no access to this folder and so to the rest of the path. break fi fi done IFS=$OIFS ls -dal $ALL_PATHS } # check if we are running under Cygwin terminal. # Note: on some OS's (for example Solaris, MacOS), -o is not a valid parameter # and it shows an error message. We redirect stderr to null so the error message # doesn't show up. CYGWIN=`uname -o 2>/dev/null` if [ "$CYGWIN" = "Cygwin" ] then eval echo `gettext 'This script is not compatible with Cygwin. Please use the Wrapper batch files to control the Wrapper.'` exit 1 fi # Resolve the location of the 'ps' & 'tr' command resolveLocation PS_BIN ps "/usr/ucb;/usr/bin;/bin" 1 resolveLocation TR_BIN tr "/usr/bin;/bin" 1 # Resolve the OS (needs to be done before any call to showUsage, checkInstalled, etc.) DIST_OS=`uname -s | $TR_BIN "[A-Z]" "[a-z]" | $TR_BIN -d ' '` case "$DIST_OS" in 'sunos') DIST_OS="solaris" ;; 'hp-ux' | 'hp-ux64') # HP-UX needs the XPG4 version of ps (for -o args) DIST_OS="hpux" UNIX95="" export UNIX95 PATH=$PATH:/usr/bin ;; 'darwin') DIST_OS="macosx" ;; 'unix_sv') DIST_OS="unixware" ;; 'os/390') DIST_OS="zos" ;; 'linux') DIST_OS="linux" ;; esac # check if there is a parameter "sysd" SYSD= if [ $# -gt 1 ] ; then if [ $2 = "sysd" ] ; then SYSD=1 fi fi # Get the fully qualified path to the script case $0 in /*) SCRIPT="$0" ;; *) PWD=`pwd` if [ $? -ne 0 ] ; then # On some systems pwd may fail if one of the parent folder has insufficient permissions. # Is there a way to access the current location which would allow to print the permissions # for each folder like we do below? exit 1 fi SCRIPT="$PWD/$0" ;; esac # Resolve the true real path without any sym links. CHANGED=true while [ "X$CHANGED" != "X" ] do # Change spaces to ":" so the tokens can be parsed. SAFESCRIPT=`echo "$SCRIPT" | sed -e 's; ;:;g'` # Get the real path to this script, resolving any symbolic links TOKENS=`echo $SAFESCRIPT | sed -e 's;/; ;g'` REALPATH= for C in $TOKENS; do # Change any ":" in the token back to a space. C=`echo $C | sed -e 's;:; ;g'` REALPATH="$REALPATH/$C" # If REALPATH is a sym link, resolve it. Loop for nested links. while [ -h "$REALPATH" ] ; do LS="`ls -ld "$REALPATH"`" LINK="`expr "$LS" : '.*-> \(.*\)$'`" if expr "$LINK" : '/.*' > /dev/null; then # LINK is absolute. REALPATH="$LINK" else # LINK is relative. REALPATH="`dirname "$REALPATH"`""/$LINK" fi done done if [ "$REALPATH" = "$SCRIPT" ] then CHANGED="" else SCRIPT="$REALPATH" fi done normalizeFilePath REALPATH $REALPATH # Try to source a file with the same filename as the script and with the '.shconf' extension. case $REALPATH in *.sh) SHCONF_FILE=`echo $REALPATH | rev | cut -d "." -f2- | rev` ;; *) SHCONF_FILE="$REALPATH" ;; esac SHCONF_FILE="${SHCONF_FILE}.shconf" if [ -f "$SHCONF_FILE" ] ; then if [ ! -x "$SHCONF_FILE" ] ; then # We should stop here because the configuration expected in the shconf file will not be loaded. eval echo `gettext 'Found $SHCONF_FILE but could not execute it. Please make sure that the file has execute permissions.'` exit 1 fi . "$SHCONF_FILE" fi if [ -n "$FIXED_COMMAND" ] ; then COMMAND="$FIXED_COMMAND" FIRST_ARG=$1 else COMMAND="$1" FIRST_ARG=$2 fi if [ "X${PASS_THROUGH}" = "Xtrue" -o "X${PASS_THROUGH}" = "Xapp_args" ] ; then PASS_THROUGH=app_args elif [ "X${PASS_THROUGH}" != "Xboth" ] ; then PASS_THROUGH=false fi # Get the location of the script. REALDIR=`dirname "$REALPATH"` if [ "X$IS_SUBSTITUTE_USER" = "X" ] ; then # First check if the command is valid for the current user. case "$COMMAND" in 'console' | 'dump' | 'start_msg' | 'stop_msg') validateRunUser ;; 'install' | 'installstart' | 'remove') mustBeRootOrExit ;; 'start' | 'stop' | 'restart' | 'condrestart') setServiceManagementTool getServiceControlMethod if [ "$CTRL_WITH_DEFAULT" = "true" -o "$CTRL_WITH_SYSTEMD" = "true" ] ; then validateRunUser else mustBeRootOrExit fi ;; 'launchdinternal' | 'upstartinternal') if [ ! "$DIST_OS" = "macosx" -o ! -f "/Library/LaunchDaemons/${APP_PLIST}" ] ; then validateRunUser fi ;; 'pause' | 'resume') if [ -z "$PAUSABLE" ] ; then showUsage "$COMMAND" fi ;; 'status') ;; *) showUsage "$COMMAND" ;; esac # The user has appropriate rights for the given command, but we still need to verify that it has permission to acccess the real dir. # NOTE: This is not necessary when running as a substitute user because runuser (or su) would have failed if the specified user could not access the real dir. result=`cd "${REALDIR}" 2>/dev/null` if [ $? -ne 0 ] ; then # The command can be run with a normal user, but we don't have sufficient permission to access the real dir. resolveCurrentUser reportRealDirNotAccessible $CURRENT_USER exit 1 fi fi # If the PIDDIR is relative, set its value relative to the full REALPATH to avoid problems if # the working directory is later changed. FIRST_CHAR=`echo $PIDDIR | cut -c1,1` if [ "$FIRST_CHAR" != "/" ] then PIDDIR=$REALDIR/$PIDDIR normalizeDirPath PIDDIR $PIDDIR fi # Same test for WRAPPER_CMD FIRST_CHAR=`echo $WRAPPER_CMD | cut -c1,1` if [ "$FIRST_CHAR" != "/" ] then WRAPPER_CMD=$REALDIR/$WRAPPER_CMD fi # Same test for WRAPPER_CONF FIRST_CHAR=`echo $WRAPPER_CONF | cut -c1,1` if [ "$FIRST_CHAR" != "/" ] then WRAPPER_CONF=$REALDIR/$WRAPPER_CONF fi # Give default values to $APP_NAME and $APP_LONG_NAME DELIMITER="@" if [ "X$APP_NAME" = "X" -o "$APP_NAME" = "${DELIMITER}app.name${DELIMITER}" ] ; then APP_NAME=`basename -- "$0"` fi if [ "X$APP_LONG_NAME" = "X" -o "$APP_LONG_NAME" = "${DELIMITER}app.long.name${DELIMITER}" ] ; then APP_LONG_NAME=$APP_NAME fi # Process ID ANCHORFILE="$PIDDIR/$APP_NAME.anchor" COMMANDFILE="$PIDDIR/$APP_NAME.command" STATUSFILE="$PIDDIR/$APP_NAME.status" JAVASTATUSFILE="$PIDDIR/$APP_NAME.java.status" PIDFILE="$PIDDIR/$APP_NAME.pid" LOCKDIR="/var/lock/subsys" LOCKFILE="$LOCKDIR/$APP_NAME" pid="" # Compare Versions $1<$2=0, $1==$2=1, $1>$2=2 compareVersions () { if [ "$1" = "$2" ] then return 1 else local i=1 while true do local v1=`echo "$1" | cut -d '.' -f $i` local v2=`echo "$2" | cut -d '.' -f $i` if [ "X$v1" = "X" ] then if [ "X$v2" = "X" ] then return 1 fi v1="0" elif [ "X$v2" = "X" ] then v2="0" fi if [ $v1 -lt $v2 ] then return 0 elif [ $v1 -gt $v2 ] then return 2 fi i=`expr $i + 1` done fi } # Resolve the architecture if [ "$DIST_OS" = "macosx" ] then OS_VER=`sw_vers | grep 'ProductVersion:' | grep -o '[0-9]*\.[0-9]*\.[0-9]*\|[0-9]*\.[0-9]*'` DIST_ARCH=`uname -p 2>/dev/null | $TR_BIN "[A-Z]" "[a-z]" | $TR_BIN -d ' '` if [ "X${DIST_ARCH}" = "Xarm" ] then DIST_ARCH="arm" else DIST_ARCH="universal" fi compareVersions "$OS_VER" "10.5.0" if [ $? -lt 1 ] then DIST_BITS="32" KEY_KEEP_ALIVE="OnDemand" else # Note: "OnDemand" has been deprecated and replaced from Mac OS X 10.5 by "KeepAlive" KEY_KEEP_ALIVE="KeepAlive" if [ "X`/usr/sbin/sysctl -n hw.cpu64bit_capable`" = "X1" ] then DIST_BITS="64" else DIST_BITS="32" fi fi APP_PLIST_BASE=${PLIST_DOMAIN}.${APP_NAME} APP_PLIST=${APP_PLIST_BASE}.plist elif [ "$DIST_OS" = "zos" ] ; then # Z/Os is not supported in the Delta Pack, therefore we only provide a binary # file named "wrapper". However this script will still check for a file named # "wrapper-zos-390-32" and "wrapper-zos-390-64" in case the user edited the name. DIST_ARCH="390" DIST_BITS="64" else if [ "$DIST_OS" = "linux" ] then DIST_ARCH= else DIST_ARCH=`uname -p 2>/dev/null | $TR_BIN "[A-Z]" "[a-z]" | $TR_BIN -d ' '` fi if [ "X$DIST_ARCH" = "X" ] then DIST_ARCH="unknown" fi if [ "$DIST_ARCH" = "unknown" ] then DIST_ARCH=`uname -m 2>/dev/null | $TR_BIN "[A-Z]" "[a-z]" | $TR_BIN -d ' '` fi case "$DIST_ARCH" in 'athlon' | 'i386' | 'i486' | 'i586' | 'i686') DIST_ARCH="x86" if [ "${DIST_OS}" = "solaris" ] ; then DIST_BITS=`isainfo -b` else DIST_BITS="32" fi ;; 'amd64' | 'x86_64') DIST_ARCH="x86" DIST_BITS="64" ;; 'ia32') DIST_ARCH="ia" DIST_BITS="32" ;; 'ia64' | 'ia64n' | 'ia64w') DIST_ARCH="ia" DIST_BITS="64" ;; 'ip27') DIST_ARCH="mips" DIST_BITS="32" ;; 'ppc64le') DIST_ARCH="ppcle" DIST_BITS="64" ;; 'power' | 'powerpc' | 'power_pc' | 'ppc64') if [ "${DIST_ARCH}" = "ppc64" ] ; then DIST_BITS="64" else DIST_BITS="32" fi DIST_ARCH="ppcbe" if [ "${DIST_OS}" = "aix" ] ; then DIST_ARCH="ppc" if [ `getconf KERNEL_BITMODE` -eq 64 ]; then DIST_BITS="64" else DIST_BITS="32" fi fi ;; 'pa_risc' | 'pa-risc') DIST_ARCH="parisc" if [ `getconf KERNEL_BITS` -eq 64 ]; then DIST_BITS="64" else DIST_BITS="32" fi ;; 'sun4u' | 'sparcv9' | 'sparc') DIST_ARCH="sparc" DIST_BITS=`isainfo -b` ;; '9000/800' | '9000/785') DIST_ARCH="parisc" if [ `getconf KERNEL_BITS` -eq 64 ]; then DIST_BITS="64" else DIST_BITS="32" fi ;; s390* ) DIST_ARCH="390" if [ `getconf LONG_BIT` -eq 64 ] ; then DIST_BITS="64" else DIST_BITS="32" fi ;; aarch64* | arm64*) # 'aarch64_be', 'aarch64', 'arm64', etc. DIST_ARCH="arm" DIST_BITS="64" ;; armv*) # 'armv8b', 'armv8l', 'armv7l', 'armv5tel', etc. # => armv8 and above should be 64-bit, but it is more reliable to check the bits with getconf. if [ `getconf LONG_BIT` -eq 64 ]; then DIST_ARCH="arm" DIST_BITS="64" else # Note: The following command returns nothing on SUSE for Raspberry Pi 3 (aarch64). # An alternative command would be 'dpkg --print-architecture', but dpkg may not exist. if [ -z "`readelf -A /proc/self/exe | grep Tag_ABI_VFP_args`" ] ; then DIST_ARCH="armel" else DIST_ARCH="armhf" fi DIST_BITS="32" fi ;; esac fi # OSX always places Java in the same location so we can reliably set JAVA_HOME if [ "$DIST_OS" = "macosx" ] then if [ -z "$JAVA_HOME" ]; then if [ -x /usr/libexec/java_home ]; then JAVA_HOME=`/usr/libexec/java_home`; export JAVA_HOME else JAVA_HOME="/Library/Java/Home"; export JAVA_HOME fi fi fi # Test Echo ECHOTEST=`echo -n "x"` if [ "$ECHOTEST" = "x" ] then ECHOOPT="-n " ECHOOPTC="" else ECHOOPT="" ECHOOPTC="\c" fi outputFile() { if [ -f "$1" ] then eval echo `gettext ' $1 Found but not executable.'`; else echo " $1" fi } # Check if the first parameter is an existing executable file. detectWrapperBinary() { if [ -f "$1" ] ; then WRAPPER_TEST_CMD="$1" if [ ! -x "$WRAPPER_TEST_CMD" ] ; then chmod +x "$WRAPPER_TEST_CMD" 2>/dev/null fi if [ -x "$WRAPPER_TEST_CMD" ] ; then WRAPPER_CMD="$WRAPPER_TEST_CMD" else outputFile "$WRAPPER_TEST_CMD" WRAPPER_TEST_CMD="" fi fi } # Decide on the wrapper binary to use. # If the bits of the OS could be detected, we will try to look for the # binary with the correct bits value. If it doesn't exist, fall back # and look for the 32-bit binary. If that doesn't exist either then # look for the default. WRAPPER_TEST_CMD="" BIN_BITS=$DIST_BITS if [ ! "$BIN_BITS" = "32" ] ; then # On a 64-bit platform, both Wrapper 32-Bit and 64-Bit can be used. # Send a request to the Wrapper to know if the license has the 64-bit feature. WRAPPER_CMD_ORIG=$WRAPPER_CMD detectWrapperBinary "$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-$BIN_BITS" if [ ! -z "$WRAPPER_TEST_CMD" ] ; then if [ "$COMMAND" = "console" -o "$COMMAND" = "start" -o "$COMMAND" = "restart" -o "$COMMAND" = "condrestart" -o "$COMMAND" = "installstart" ] ; then "$WRAPPER_CMD" --request_delta_binary_bits "$WRAPPER_CONF" 2>/dev/null if [ $? = 32 ] ; then # License is 32-Bit. Reset and search for 32-Bit Wrapper binaries. WRAPPER_TEST_CMD="" WRAPPER_CMD=$WRAPPER_CMD_ORIG BIN_BITS=32 fi fi fi fi if [ -z "$WRAPPER_TEST_CMD" ] ; then detectWrapperBinary "$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-32" fi if [ -z "$WRAPPER_TEST_CMD" ] ; then detectWrapperBinary "$WRAPPER_CMD" fi if [ -z "$WRAPPER_TEST_CMD" ] ; then eval echo `gettext 'Unable to locate any of the following binaries:'` outputFile "$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-$BIN_BITS" if [ ! "$BIN_BITS" = "32" ] ; then outputFile "$WRAPPER_CMD-$DIST_OS-$DIST_ARCH-32" fi outputFile "$WRAPPER_CMD" exit 1 fi # Build the nice clause if [ "X$PRIORITY" = "X" ] then CMDNICE="" else CMDNICE="nice -$PRIORITY" fi # Build the anchor file clause. if [ "X$IGNORE_SIGNALS" = "X" ] then ANCHORPROP= IGNOREPROP= else ANCHORPROP=wrapper.anchorfile=\"$ANCHORFILE\" IGNOREPROP=wrapper.ignore_signals=TRUE fi # Build the status file clause. if [ "X$DETAIL_STATUS$WAIT_FOR_STARTED_STATUS" = "X" ] then STATUSPROP= else STATUSPROP="wrapper.statusfile=\"$STATUSFILE\" wrapper.java.statusfile=\"$JAVASTATUSFILE\"" fi # Build the command file clause. if [ -n "$PAUSABLE" ] then if [ "$PAUSABLE_MODE" = "file" ] ; then COMMANDPROP="wrapper.commandfile=\"$COMMANDFILE\" wrapper.pausable=TRUE" else COMMANDPROP="wrapper.signal.mode.usr1=PAUSE wrapper.signal.mode.usr2=RESUME wrapper.pausable=TRUE" fi else COMMANDPROP= fi if [ ! -n "$WAIT_FOR_STARTED_STATUS" ] then WAIT_FOR_STARTED_STATUS=true fi if [ $WAIT_FOR_STARTED_STATUS = true ] ; then DETAIL_STATUS=true fi # Build the lock file clause. Only create a lock file if the lock directory exists on this platform. LOCKPROP= if [ -d $LOCKDIR ] then if [ -w $LOCKDIR ] then LOCKPROP=wrapper.lockfile=\"$LOCKFILE\" fi fi # Build app name clause if [ ! -n "$APP_NAME_PASS_TO_WRAPPER" ] then APP_NAME_PASS_TO_WRAPPER=true fi if [ $APP_NAME_PASS_TO_WRAPPER = false ] then APPNAMEPROP= else APPNAMEPROP="wrapper.name=\"$APP_NAME\" wrapper.displayname=\"$APP_LONG_NAME\"" fi # Decide on run levels. RUN_LEVEL_S_DIST_OS_TMP=`eval "echo \$\{RUN_LEVEL_S_${DIST_OS}\}"` RUN_LEVEL_S_DIST_OS=`eval "echo ${RUN_LEVEL_S_DIST_OS_TMP}"` if [ "X${RUN_LEVEL_S_DIST_OS}" != "X" ] ; then APP_RUN_LEVEL_S=${RUN_LEVEL_S_DIST_OS} elif [ "X$RUN_LEVEL_S" != "X" ] ; then APP_RUN_LEVEL_S=$RUN_LEVEL_S else APP_RUN_LEVEL_S=$RUN_LEVEL fi APP_RUN_LEVEL_S_CHECK=`echo "$APP_RUN_LEVEL_S" | sed "s/[(0-9)*]/0/g"` if [ "X${APP_RUN_LEVEL_S_CHECK}" != "X00" ] ; then eval echo `gettext 'Run level \"${APP_RUN_LEVEL_S}\" must be numeric and have a length of two \(00-99\).'` exit 1; fi RUN_LEVEL_K_DIST_OS_TMP=`eval "echo \$\{RUN_LEVEL_K_${DIST_OS}\}"` RUN_LEVEL_K_DIST_OS=`eval "echo ${RUN_LEVEL_K_DIST_OS_TMP}"` if [ "X${RUN_LEVEL_K_DIST_OS}" != "X" ] ; then APP_RUN_LEVEL_K=${RUN_LEVEL_K_DIST_OS} elif [ "X$RUN_LEVEL_K" != "X" ] ; then APP_RUN_LEVEL_K=$RUN_LEVEL_K else APP_RUN_LEVEL_K=$RUN_LEVEL fi APP_RUN_LEVEL_K_CHECK=`echo "$APP_RUN_LEVEL_K" | sed "s/[(0-9)*]/0/g"` if [ "X${APP_RUN_LEVEL_K_CHECK}" != "X00" ] ; then eval echo `gettext 'Run level \"${APP_RUN_LEVEL_K}\" must be numeric and have a length of two \(00-99\).'` exit 1; fi prepAdditionalParams() { ADDITIONAL_PARA="" if [ "X${PASS_THROUGH}" = "Xapp_args" -o "X${PASS_THROUGH}" = "Xboth" ] ; then if [ "X${PASS_THROUGH}" = "Xapp_args" ] ; then ADDITIONAL_PARA="--" ARGS_ARE_APP_PARAMS=true fi while [ -n "$1" ] ; do if [ ! -n "$ARGS_ARE_APP_PARAMS" ] ; then if [ "$1" = "--" ] ; then ARGS_ARE_APP_PARAMS=true else # Check that the arg matches the pattern of a property case "$1" in wrapper.*=*) # Correct, nothing to do ;; *=*) # The property name is not starting with 'wrapper.' so invalid. COMMAND_PROP=${1%%=*} eval echo `gettext 'WARNING: Encountered an unknown configuration property \"${COMMAND_PROP}\". When PASS_THROUGH is set to \"both\", any argument before \"--\" should target a valid Wrapper configuration property.'` ;; *) # Not a valid assignment. eval echo `gettext 'WARNING: Encountered an invalid configuration property assignment \"$1\". When PASS_THROUGH is set to \"both\", any argument before \"--\" should be in the format \"\=\\".'` ;; esac fi fi ADDITIONAL_PARA="$ADDITIONAL_PARA \"$1\"" shift done fi } resolveSysLocale() { # First try to get the system encoding from /etc/default/locale. # Note: For some reason, the system encoding stored in /etc/default/locale and the value returned by localectl may differ. # When using 'localectl set-local', /etc/default/locale is always updated accordingly, but when manually editing /etc/default/locale, # the output of the command sometimes doesn't get updated. When the values differ, SU uses the same locale as /etc/default/locale, # so that's why we start trying to get the locale using this method. if [ -f "/etc/default/locale" ] ; then PASS_SYS_LANG=`grep 'LANG=' /etc/default/locale` case "$PASS_SYS_LANG" in LANG=*) PASS_SYS_LANG="$PASS_SYS_LANG " ;; *) PASS_SYS_LANG="" ;; esac fi if [ "X$PASS_SYS_LANG" = "X" ] ; then # Try to get the system encoding using localectl. LOCALECTL_BIN="localectl" result=`command -v $LOCALECTL_BIN 2>/dev/null` if [ $? -ne 0 ] ; then LOCALECTL_BIN="/usr/bin/localectl" if [ ! -x "$LOCALECTL_BIN" ] ; then # Keep the warning for debugging, but don't actually show it because it may be normal for some OS to not have the command. # echo " WARNING: Could not locate localectl used to get the system locale. The encoding may not be correct when running as $RUN_AS_USER." LOCALECTL_BIN="" fi fi if [ "X$LOCALECTL_BIN" != "X" ] ; then # The first line that localectl outputs should look like this: ' System Locale: n/a' PASS_SYS_LANG=`$LOCALECTL_BIN | grep "System Locale" | awk '{print $3}' 2>/dev/null` case "$PASS_SYS_LANG" in *n/a*) # No system locale set. Skip. PASS_SYS_LANG="" ;; LANG=*) PASS_SYS_LANG="$PASS_SYS_LANG " ;; *) # echo " WARNING: Failed to parse the output of localectl. The encoding may not be correct when running as $RUN_AS_USER.'" PASS_SYS_LANG="" ;; esac fi fi } checkRunUser() { # $1 touchLock flag # $2.. [command] args # Make sure the configuration is valid. validateRunUser # Check the configured user. If necessary rerun this script as the desired user. if [ "X$RUN_AS_USER" != "X" ] then # If LOCKPROP and $RUN_AS_USER are defined then the new user will most likely not be # able to create the lock file. The Wrapper will be able to update this file once it # is created but will not be able to delete it on shutdown. If $1 is set then # the lock file should be created for the current command if [ "X$LOCKPROP" != "X" ] then if [ "X$1" != "X" ] then # Resolve the primary group RUN_AS_GROUP=`groups $RUN_AS_USER | awk '{print $3}' | tail -1` if [ "X$RUN_AS_GROUP" = "X" ] then RUN_AS_GROUP=$RUN_AS_USER fi touch $LOCKFILE chown $RUN_AS_USER:$RUN_AS_GROUP $LOCKFILE fi fi # Still want to change users, recurse. This means that the user will only be # prompted for a password once. Variables shifted by 1 shift # Wrap the parameters so they can be passed. ADDITIONAL_PARA="" while [ -n "$1" ] ; do if [ "$1" = 'installstart' ] ; then # At this point the service is already installed. When we will fork the process we only need to start the service. ADDITIONAL_PARA="$ADDITIONAL_PARA \"start\"" else ADDITIONAL_PARA="$ADDITIONAL_PARA \"$1\"" fi shift done resolveSysLocale RUNUSER_INTERRUPT_TRAPPED=false $RUNUSER_BIN - $RUN_AS_USER -c "IS_SUBSTITUTE_USER=true $PASS_SYS_LANG\"$REALPATH\" $ADDITIONAL_PARA" $SU_OPTS RUN_AS_USER_EXITCODE=$? if [ $RUN_AS_USER_EXITCODE -eq 126 ] ; then reportRealDirNotAccessible $RUN_AS_USER elif [ $RUN_AS_USER_EXITCODE -gt 128 -o "$RUNUSER_INTERRUPT_TRAPPED" = "true" ] ; then # The range 128-255 is reserved for signals, but if the user presses CTRL+C (or sends a signal?) two consecutive times, runuser/su will be interrupted and may return 0. # The RUNUSER_INTERRUPT_TRAPPED is set when trapping INT, TERM, QUIT or HUP, but there might be other signals causing interruption. # We use the 2 above conditions to cover most cases. # Why can this happen: If a signal causing interruption is sent to the process group of the parent script, it will also be caught by the # runuser process which belongs to the same group. The parent script can ignore it but the runuser process will be terminated. # The Wrapper will take some time to exit cleanly or may not even exit depending on the signal, so we should wait for its PID. # NOTES: - it is not possible to use the wait command because the Wrapper is a child of the login shell started by runuser. # - it is possible that Wrapper returned a reserved exit code, but we wouldn't wait below because getpid should return an empty $pid. getpid if [ "X$pid" != "X" ] ; then if [ $RUN_AS_USER_EXITCODE -eq 255 ] ; then # On some system, su returns 255 if it has to kill the command (because it was asked to terminate, and the command did not terminate in time). # Print a different message to make it clear that runuser/su did not force kill the Wrapper process. eval echo `gettext 'Intermediate login shell was killed. Still waiting for the Wrapper process to stop...'` else eval echo `gettext 'Waiting for the Wrapper process to stop...'` fi fi while [ "X$pid" != "X" ] ; do sleep 1 testpid done elif [ $RUN_AS_USER_EXITCODE -eq 1 ] ; then # 1 is a special case as it can either be a generic error before executing the Wrapper, or the Wrapper exit code returned on error. checkForkCommand fi # Now that we are the original user again, we may need to clean up the lock file. if [ "X$LOCKPROP" != "X" ] then getpid if [ "X$pid" = "X" ] then # Wrapper is not running so make sure the lock file is deleted. if [ -f "$LOCKFILE" ] then rm "$LOCKFILE" fi fi fi exit $RUN_AS_USER_EXITCODE fi } # Try to fork by executing a simple command. # With this function, we want to make sure we are able to fork. checkForkCommand() { $RUNUSER_BIN - $RUN_AS_USER -c "ls \"$REALPATH\"" $SU_OPTS > /dev/null 2>&1 & CHECK_EXITCODE=$? if [ $CHECK_EXITCODE -ne 0 ] then # clearly a problem with forking eval echo `gettext 'Error: unable to create fork process.'` eval echo `gettext 'Advice:'` eval echo `gettext 'One possible cause of failure is when the user \(\"$RUN_AS_USER\"\) has no shell.'` eval echo `gettext 'In this case, two solutions are available by editing the script file:'` eval echo `gettext '1. Use \"SU_OPTS\" to set the shell for the user.'` eval echo `gettext '2. Use a OS service management tool \(only available on some platforms\).'` echo "" fi } getpid() { pid="" if [ -f "$PIDFILE" ] then if [ -r "$PIDFILE" ] then pid=`cat "$PIDFILE"` if [ "X$pid" != "X" ] then if [ "X$PIDFILE_CHECK_PID" != "X" ] then # It is possible that 'a' process with the pid exists but that it is not the # correct process. This can happen in a number of cases, but the most # common is during system startup after an unclean shutdown. # The ps statement below looks for the specific wrapper command running as # the pid. If it is not found then the pid file is considered to be stale. case "$DIST_OS" in 'freebsd') pidtest=`$PS_BIN -p $pid -o args | tail -1` if [ "X$pidtest" = "XCOMMAND" ] then pidtest="" fi ;; 'macosx') pidtest=`$PS_BIN -ww -p $pid -o command | grep -F "$WRAPPER_CMD" | tail -1` ;; 'solaris') if [ -f "/usr/bin/pargs" ] then pidtest=`pargs $pid | fgrep "$WRAPPER_CMD" | tail -1` else case "$PS_BIN" in '/usr/ucb/ps') pidtest=`$PS_BIN -auxww $pid | fgrep "$WRAPPER_CMD" | tail -1` ;; '/usr/bin/ps') TRUNCATED_CMD=`$PS_BIN -o comm -p $pid | tail -1` COUNT=`echo $TRUNCATED_CMD | wc -m` COUNT=`echo ${COUNT}` COUNT=`expr $COUNT - 1` TRUNCATED_CMD=`echo $WRAPPER_CMD | cut -c1-$COUNT` pidtest=`$PS_BIN -o comm -p $pid | fgrep "$TRUNCATED_CMD" | tail -1` ;; '/bin/ps') TRUNCATED_CMD=`$PS_BIN -o comm -p $pid | tail -1` COUNT=`echo $TRUNCATED_CMD | wc -m` COUNT=`echo ${COUNT}` COUNT=`expr $COUNT - 1` TRUNCATED_CMD=`echo $WRAPPER_CMD | cut -c1-$COUNT` pidtest=`$PS_BIN -o comm -p $pid | fgrep "$TRUNCATED_CMD" | tail -1` ;; *) echo "Unsupported ps command $PS_BIN" exit 1 ;; esac fi ;; 'hpux') pidtest=`$PS_BIN -p $pid -x -o args | grep -F "$WRAPPER_CMD" | tail -1` ;; 'zos') TRUNCATED_CMD=`$PS_BIN -p $pid -o args | tail -1` COUNT=`echo $TRUNCATED_CMD | wc -m` COUNT=`echo ${COUNT}` COUNT=`expr $COUNT - 1` TRUNCATED_CMD=`echo $WRAPPER_CMD | cut -c1-$COUNT` pidtest=`$PS_BIN -p $pid -o args | grep -F "$TRUNCATED_CMD" | tail -1` ;; 'linux') pidtest=`$PS_BIN -ww -p $pid -o args | grep -F "$WRAPPER_CMD" | tail -1` ;; *) pidtest=`$PS_BIN -p $pid -o args | grep -F "$WRAPPER_CMD" | tail -1` ;; esac else # Check to see whether the pid exists as a running process, but in this mode, don't check what that pid is. case "$DIST_OS" in 'solaris') case "$PS_BIN" in '/usr/ucb/ps') pidtest=`$PS_BIN $pid | grep "$pid" | awk '{print $1}' | tail -1` ;; '/usr/bin/ps') pidtest=`$PS_BIN -p $pid -o pid | grep "$pid" | tail -1` ;; '/bin/ps') pidtest=`$PS_BIN -p $pid -o pid | grep "$pid" | tail -1` ;; *) echo "Unsupported ps command $PS_BIN" exit 1 ;; esac ;; *) pidtest=`$PS_BIN -p $pid -o pid | grep "$pid" | tail -1` ;; esac fi if [ "X$pidtest" = "X" ] then # This is a stale pid file. rm -f "$PIDFILE" eval echo `gettext 'Removed stale pid file: $PIDFILE'` pid="" fi fi else eval echo `gettext 'Cannot read $PIDFILE.'` exit 1 fi fi } getstatus() { STATUS= if [ -f "$STATUSFILE" ] then if [ -r "$STATUSFILE" ] then STATUS=`cat "$STATUSFILE"` fi fi if [ "X$STATUS" = "X" ] then STATUS="Unknown" fi JAVASTATUS= if [ -f "$JAVASTATUSFILE" ] then if [ -r "$JAVASTATUSFILE" ] then JAVASTATUS=`cat "$JAVASTATUSFILE"` fi fi if [ "X$JAVASTATUS" = "X" ] then JAVASTATUS="Unknown" fi } testpid() { case "$DIST_OS" in 'solaris') case "$PS_BIN" in '/usr/ucb/ps') pid=`$PS_BIN $pid | grep $pid | grep -v grep | awk '{print $1}' | tail -1` ;; '/usr/bin/ps') pid=`$PS_BIN -p $pid | grep $pid | grep -v grep | awk '{print $1}' | tail -1` ;; '/bin/ps') pid=`$PS_BIN -p $pid | grep $pid | grep -v grep | awk '{print $1}' | tail -1` ;; *) echo "Unsupported ps command $PS_BIN" exit 1 ;; esac ;; *) pid=`$PS_BIN -p $pid | grep $pid | grep -v grep | awk '{print $1}' | tail -1` 2>/dev/null ;; esac if [ "X$pid" = "X" ] then pid="" fi } launchdtrap() { stopit exit } waitForWrapperStop() { getpid while [ "X$pid" != "X" ] ; do sleep 1 getpid done } launchinternal() { getpid trap launchdtrap TERM if [ "X$pid" = "X" ] then # The string passed to eval must handles spaces in paths correctly. COMMAND_LINE="$CMDNICE \"$WRAPPER_CMD\" \"$WRAPPER_CONF\" wrapper.syslog.ident=\"$APP_NAME\" wrapper.pidfile=\"$PIDFILE\" wrapper.daemonize=TRUE $APPNAMEPROP $ANCHORPROP $IGNOREPROP $STATUSPROP $COMMANDPROP $LOCKPROP wrapper.script.version=3.5.51 $ADDITIONAL_PARA" eval "$COMMAND_LINE" else eval echo `gettext '$APP_LONG_NAME is already running.'` exit 1 fi # launchd expects that this script stay up and running so we need to do our own monitoring of the Wrapper process. if [ $WAIT_FOR_STARTED_STATUS = true ] then waitForWrapperStop fi } consoleTrapInterrupt() { # Set a flag to inform su/runuser that we caught an interrupt signal. RUNUSER_INTERRUPT_TRAPPED=true } consoleTrap() { if [ $2 = 1 ] ; then # Set a flag to inform su/runuser that we caught an interrupt signal (do it as early as possible). RUNUSER_INTERRUPT_TRAPPED=true fi getpid if [ "X$pid" != "X" ] ; then # still alive kill -$1 $pid if [ $? -ne 0 ] ; then # log a message but do not exit eval echo `gettext 'Unable to forward $1 signal to $APP_LONG_NAME.'` fi # Nothing else to do. Let the script complete normally after the Wrapper exits. fi } consoleTrapSignals() { if [ "$IS_SUBSTITUTE_USER" != "true" ] ; then # Trap the following signals in order to forward them to the Wrapper. if [ "X$RUN_AS_USER" != "X" ] ; then trap "consoleTrap INT 1" INT trap "consoleTrap TERM 1" TERM else trap "consoleTrap INT 0" INT trap "consoleTrap TERM 0" TERM fi # SIGQUIT is usually triggered with the keyboard (CTRL+\). Forward it when running as a user. # When running normally, the Wrapper being member of the same process group will also get the signal, so no need to forward it. if [ "X$RUN_AS_USER" != "X" ] ; then trap "consoleTrap QUIT 1" QUIT else trap '' QUIT fi # SIGHUP should be caught otherwise it would interrupt the script, but do not forward it. if [ "X$RUN_AS_USER" != "X" ] ; then # SIGHUP behaves differently than INT/TERM/QUIT and should not interrupt su/runuser when a custom handler is registered. # But this might depend on the shell & platform. Be safe and set the RUNUSER_INTERRUPT_TRAPPED flag anyway. trap 'consoleTrapInterrupt' HUP else trap '' HUP fi fi } console() { eval echo `gettext 'Running $APP_LONG_NAME...'` getpid if [ "X$pid" = "X" ] then prepAdditionalParams "$@" # The string passed to eval must handles spaces in paths correctly. COMMAND_LINE="$CMDNICE \"$WRAPPER_CMD\" \"$WRAPPER_CONF\" wrapper.syslog.ident=\"$APP_NAME\" wrapper.pidfile=\"$PIDFILE\" $APPNAMEPROP $ANCHORPROP $STATUSPROP $COMMANDPROP $LOCKPROP wrapper.script.version=3.5.51 $ADDITIONAL_PARA" eval "$COMMAND_LINE" COMMAND_EXIT_CODE=$? if [ $COMMAND_EXIT_CODE -ne 0 ] ; then exit $COMMAND_EXIT_CODE fi else eval echo `gettext '$APP_LONG_NAME is already running.'` exit 1 fi } waitforjavastartup() { getstatus eval echo $ECHOOPT `gettext 'Waiting for $APP_LONG_NAME...$ECHOOPTC'` # Wait until the timeout or we have something besides Unknown. counter=15 while [ "$JAVASTATUS" = "Unknown" -a $counter -gt 0 -a -n "$JAVASTATUS" ] ; do echo $ECHOOPT".$ECHOOPTC" sleep 1 getstatus counter=`expr $counter - 1` done if [ -n "$WAIT_FOR_STARTED_TIMEOUT" ] ; then counter=$WAIT_FOR_STARTED_TIMEOUT else counter=120 fi while [ "$JAVASTATUS" != "STARTED" -a "$JAVASTATUS" != "Unknown" -a $counter -gt 0 -a -n "$JAVASTATUS" ] ; do echo $ECHOOPT".$ECHOOPTC" sleep 1 getstatus counter=`expr $counter - 1` done echo "" } ## # Request the path to the log file to the Wrapper and print it # # $1 prefix printlogfilepath() { LOG_FILE1=`"$WRAPPER_CMD" --request_log_file "$WRAPPER_CONF" 2>/dev/null` if [ $? = 0 -a "X$LOG_FILE1" != "X" ] ; then # try to see if a default log file exists LOG_FILE2=`"$WRAPPER_CMD" --request_default_log_file "$WRAPPER_CONF" 2>/dev/null` if [ $? != 0 -o ! -f "$LOG_FILE2" ] ; then LOG_FILE2="" fi else # try to see if a default log file exists LOG_FILE1=`"$WRAPPER_CMD" --request_default_log_file "$WRAPPER_CONF" 2>/dev/null` if [ $? != 0 -o ! -f "$LOG_FILE1" ] ; then LOG_FILE1="" fi fi if [ "X$LOG_FILE2" != "X" ] ; then LOG_FILE_MSG=`gettext 'The log files \"${LOG_FILE1}\" and \"${LOG_FILE2}\" may contain further information.'` elif [ "X$LOG_FILE1" != "X" ] ; then LOG_FILE_MSG=`gettext 'The log file \"${LOG_FILE1}\" may contain further information.'` else LOG_FILE_MSG=`gettext 'The syslog may contain further information.'` fi LOG_FILE_MSG=`eval echo "${LOG_FILE_MSG}"` # expand ${LOG_FILE} echo "$1${LOG_FILE_MSG}" # print with indentation } startwait() { if [ $WAIT_FOR_STARTED_STATUS = true ] then waitforjavastartup fi # Sleep for a few seconds to allow for intialization if required # then test to make sure we're still running. # i=0 while [ $i -lt $WAIT_AFTER_STARTUP ] do sleep 1 echo $ECHOOPT".$ECHOOPTC" i=`expr $i + 1` done if [ $WAIT_AFTER_STARTUP -gt 0 -o $WAIT_FOR_STARTED_STATUS = true ] then getpid if [ "X$pid" = "X" ] then eval echo `gettext 'WARNING: $APP_LONG_NAME may have failed to start.'` printlogfilepath " " exit 1 else eval echo `gettext ' running: PID:$pid'` fi else echo "" fi } mustBeStoppedOrExit() { getpid if [ "X$pid" != "X" ] ; then eval echo `gettext '$APP_LONG_NAME is already running.'` exit 1 fi } # Detect if the Wrapper is running # Returns 0 if the process is running, otherwise returns 1. checkRunning() { getpid if [ "X$pid" = "X" ] ; then eval echo `gettext '$APP_LONG_NAME is not running.'` if [ "X$1" = "X1" ] ; then exit 1 fi return 1 fi return 0 } macosxStart() { mustBeRootOrExit mustBeStoppedOrExit eval echo `gettext 'Starting $APP_LONG_NAME with launchd...'` # If the daemon was just installed, it may not be loaded. LOADED_PLIST=`launchctl list | grep ${APP_PLIST_BASE}` if [ "X${LOADED_PLIST}" = "X" ] ; then launchctl load /Library/LaunchDaemons/${APP_PLIST} fi # If launchd is set to run the daemon already at Load, we don't need to call start getpid if [ "X$pid" = "X" ] ; then launchctl start ${APP_PLIST_BASE} fi startwait } macosxStop() { mustBeRootOrExit # The daemon must be running. checkRunning "1" eval echo `gettext 'Stopping $APP_LONG_NAME...'` if [ "$MACOSX_KEEP_RUNNING" = "true" ] ; then echo "" eval echo `gettext 'Daemon is set to be kept continuously running and it will be automatically restarted.'` eval echo `gettext 'To stop the daemon you need to uninstall it.'` eval echo `gettext 'If you want to use the \"stop\" argument, you need to find MACOSX_KEEP_RUNNING'` eval echo `gettext 'at the beginning of the script file and set it to \"false\".'` echo "" fi launchctl stop ${APP_PLIST_BASE} } macosxRestart() { mustBeRootOrExit checkRunning $1 eval echo `gettext 'Restarting $APP_LONG_NAME...'` if [ "$MACOSX_KEEP_RUNNING" = "true" ] ; then # by stopping it, launchd will automatically restart it launchctl stop ${APP_PLIST_BASE} else launchctl unload "/Library/LaunchDaemons/${APP_PLIST}" sleep 1 launchctl load "/Library/LaunchDaemons/${APP_PLIST}" fi startwait } upstartInstall() { # Always verify that upstart exists. upstartDetection if [ $? -ne 0 ] ; then eval echo `gettext 'Unable to install the $APP_LONG_NAME daemon because upstart does not exist.'` return 1 fi eval echo `gettext ' Installing the $APP_LONG_NAME daemon with upstart...'` if [ -f "${REALDIR}/${APP_NAME}.install" ] ; then eval echo `gettext ' Custom upstart conf file ${APP_NAME}.install found.'` cp "${REALDIR}/${APP_NAME}.install" "/etc/init/${APP_NAME}.conf" else eval echo `gettext ' Creating default upstart conf file...'` echo "# ${APP_NAME} - ${APP_LONG_NAME}" > "/etc/init/${APP_NAME}.conf" echo "description \"${APP_LONG_NAME}\"" >> "/etc/init/${APP_NAME}.conf" echo "author \"Tanuki Software Ltd. \"" >> "/etc/init/${APP_NAME}.conf" echo "start on runlevel [2345]" >> "/etc/init/${APP_NAME}.conf" echo "stop on runlevel [!2345]" >> "/etc/init/${APP_NAME}.conf" echo "env LANG=${LANG}" >> "/etc/init/${APP_NAME}.conf" echo "exec \"${REALPATH}\" upstartinternal" >> "/etc/init/${APP_NAME}.conf" fi } upstartStart() { mustBeRootOrExit mustBeStoppedOrExit eval echo `gettext 'Starting $APP_LONG_NAME with upstart...'` /sbin/start ${APP_NAME} startwait } upstartStop() { mustBeRootOrExit # The daemon must be running. checkRunning "1" eval echo `gettext 'Stopping $APP_LONG_NAME...'` /sbin/stop ${APP_NAME} } upstartRestart() { mustBeRootOrExit checkRunning $1 if [ "X$pid" = "X" ] ; then # Don't use /sbin/restart because it requires the daemon to be running. # We want to match with other systems behaviour which is to restart the # daemon even if it was not running. upstartStart else # The daemon was running eval echo `gettext 'Restarting $APP_LONG_NAME...'` /sbin/restart ${APP_NAME} startwait fi } upstartRemove() { stopit "0" eval echo `gettext ' Removing the $APP_LONG_NAME daemon from upstart...'` rm "/etc/init/${APP_NAME}.conf" } systemdInstall() { # Always verify that systemd exists. systemdDetection if [ $? -ne 0 ] ; then eval echo `gettext 'Unable to install the $APP_LONG_NAME daemon because systemd does not exist.'` return 1 fi eval echo `gettext ' Installing the $APP_LONG_NAME daemon with systemd...'` if [ -f "${REALDIR}/${APP_NAME}.service" ] ; then eval echo `gettext ' Custom service file ${APP_NAME}.service found.'` cp "${REALDIR}/${APP_NAME}.service" "${SYSTEMD_SERVICE_FILE}" else eval echo `gettext ' Creating default service file...'` echo "[Unit]" > "${SYSTEMD_SERVICE_FILE}" echo "Description=${APP_LONG_NAME}" >> "${SYSTEMD_SERVICE_FILE}" echo "After=syslog.target" >> "${SYSTEMD_SERVICE_FILE}" echo "" >> "${SYSTEMD_SERVICE_FILE}" echo "[Service]" >> "${SYSTEMD_SERVICE_FILE}" echo "Type=forking" >> "${SYSTEMD_SERVICE_FILE}" case "${REALPATH}" in *\ *) # REALPATH contains spaces LINE_HEAD='ExecStart=/usr/bin/env "' LINE_TAIL='" start sysd' echo "${LINE_HEAD}${REALPATH}${LINE_TAIL}" >> "${SYSTEMD_SERVICE_FILE}" LINE_HEAD='ExecStop=/usr/bin/env "' LINE_TAIL='" stop sysd' echo "${LINE_HEAD}${REALPATH}${LINE_TAIL}" >> "${SYSTEMD_SERVICE_FILE}" ;; *) echo "ExecStart=${REALPATH} start sysd" >> "${SYSTEMD_SERVICE_FILE}" echo "ExecStop=${REALPATH} stop sysd" >> "${SYSTEMD_SERVICE_FILE}" ;; esac if [ "X${RUN_AS_USER}" != "X" ] ; then echo "User=${RUN_AS_USER}" >> "${SYSTEMD_SERVICE_FILE}" fi if [ "X${SYSTEMD_KILLMODE}" != "X" ] ; then echo "KillMode=${SYSTEMD_KILLMODE}" >> "${SYSTEMD_SERVICE_FILE}" fi if [ $SYSTEMD_KILLMODE != "process" -a $SYSTEMD_KILLMODE != "none" ] ; then # Set an environment variable to show a warning if a detached process is launched by the WrapperManager. echo "Environment=SYSTEMD_KILLMODE_WARNING=true" >> "${SYSTEMD_SERVICE_FILE}" fi echo "" >> "${SYSTEMD_SERVICE_FILE}" echo "[Install]" >> "${SYSTEMD_SERVICE_FILE}" echo "WantedBy=multi-user.target" >> "${SYSTEMD_SERVICE_FILE}" fi systemctl daemon-reload systemctl enable "${APP_NAME}" } systemdStart() { # Don't do mustBeRootOrExit because systemd will ask for the password when not root mustBeStoppedOrExit result=`systemctl -p KillMode show $APP_NAME` if [ $result != "KillMode=$SYSTEMD_KILLMODE" ] ; then eval echo `gettext 'SYSTEMD_KILLMODE is set to \"${SYSTEMD_KILLMODE}\" on the top of the script, but the daemon is running with $result.'` eval echo `gettext 'The daemon must be reinstalled for the value of SYSTEMD_KILLMODE to take effect.'` exit 1 fi eval echo `gettext 'Starting $APP_LONG_NAME with systemd...'` systemctl start $APP_NAME if [ $? -ne 0 ] ; then eval echo `gettext 'Failed to start $APP_LONG_NAME.'` printlogfilepath exit 1 fi startwait } systemdStop() { # Don't do mustBeRootOrExit because systemd will ask for the password when not root # The daemon must be running. checkRunning "1" eval echo `gettext 'Stopping $APP_LONG_NAME...'` systemctl stop $APP_NAME if [ $? -ne 0 ] ; then eval echo `gettext 'Failed to stop $APP_LONG_NAME.'` exit 1 fi } systemdRestart() { # Don't do mustBeRootOrExit because systemd will ask for the password when not root checkRunning $1 eval echo `gettext 'Restarting $APP_LONG_NAME...'` systemctl restart $APP_NAME if [ $? -ne 0 ] ; then eval echo `gettext 'Failed to restart service $APP_NAME'` printlogfilepath exit 1 fi startwait } systemdRemove() { stopit "0" eval echo `gettext ' Removing the $APP_LONG_NAME daemon from systemd...'` systemctl disable $APP_NAME rm "/etc/systemd/system/${APP_NAME}.service" systemctl daemon-reload } srcInstall() { # Always verify that SRC exists. srcDetection if [ $? -ne 0 ] ; then eval echo `gettext 'Unable to install the $APP_LONG_NAME daemon because SRC does not exist.'` return 1 fi if [ "X$RUN_AS_USER" = "X" ] ; then USERID="0" else resolveIdLocation USERID=`$ID_BIN -u "$RUN_AS_USER"` if [ $? -ne 0 ] ; then eval echo `gettext 'Failed to get user id for $RUN_AS_USER'` exit 1 fi fi validateAppNameLength /usr/bin/mkssys -s "$APP_NAME" -p "$REALPATH" -a "launchdinternal" -u "$USERID" -f 9 -n 15 -S /usr/sbin/mkitab "$APP_NAME":2:once:"/usr/bin/startsrc -s \"${APP_NAME}\" >/dev/console 2>&1" } srcStart() { mustBeRootOrExit mustBeStoppedOrExit eval echo `gettext 'Starting $APP_LONG_NAME with SRC...'` startsrc -s "${APP_NAME}" if [ $? -ne 0 ] ; then eval echo `gettext 'Failed to start $APP_LONG_NAME.'` printlogfilepath exit 1 fi startwait } srcStop() { mustBeRootOrExit # The daemon must be running. checkRunning "1" eval echo `gettext 'Stopping $APP_LONG_NAME...'` stopsrc -s "${APP_NAME}" if [ $? -ne 0 ] ; then eval echo `gettext 'Failed to stop $APP_LONG_NAME.'` exit 1 fi } srcRestart() { mustBeRootOrExit checkRunning $1 if [ "X$pid" != "X" ] ; then # The daemon was running. Stop it first. eval echo `gettext 'Restarting $APP_LONG_NAME...'` srcStop waitForWrapperStop fi srcStart } start() { mustBeStoppedOrExit eval echo `gettext 'Starting $APP_LONG_NAME...'` prepAdditionalParams "$@" # The string passed to eval must handles spaces in paths correctly. COMMAND_LINE="$CMDNICE \"$WRAPPER_CMD\" \"$WRAPPER_CONF\" wrapper.syslog.ident=\"$APP_NAME\" wrapper.pidfile=\"$PIDFILE\" wrapper.daemonize=TRUE $APPNAMEPROP $ANCHORPROP $IGNOREPROP $STATUSPROP $COMMANDPROP $LOCKPROP wrapper.script.version=3.5.51 $ADDITIONAL_PARA" eval "$COMMAND_LINE" startwait } stopit() { # $1 exit if down flag checkRunning $1 if [ $? -eq 0 ] ; then # The daemon should be running. eval echo `gettext 'Stopping $APP_LONG_NAME...'` if [ "X$IGNORE_SIGNALS" = "X" ] then # Running so try to stop it. kill $pid if [ $? -ne 0 ] then # An explanation for the failure should have been given eval echo `gettext 'Unable to stop $APP_LONG_NAME.'` exit 1 fi else rm -f "$ANCHORFILE" if [ -f "$ANCHORFILE" ] then # An explanation for the failure should have been given eval echo `gettext 'Unable to stop $APP_LONG_NAME.'` exit 1 fi fi # We can not predict how long it will take for the wrapper to # actually stop as it depends on settings in wrapper.conf. # Loop until it does. savepid=$pid CNT=0 TOTCNT=0 while [ "X$pid" != "X" ] do # Show a waiting message every 5 seconds. if [ "$CNT" -lt "5" ] then CNT=`expr $CNT + 1` else eval echo `gettext 'Waiting for $APP_LONG_NAME to exit...'` CNT=0 fi TOTCNT=`expr $TOTCNT + 1` sleep 1 # Check if the process is still running. testpid if [ "X$pid" = "X" ] then # The process is gone, but it is possible that another instance restarted while we waited... SAVE_PIDFILE_CHECK_PID=$PIDFILE_CHECK_PID PIDFILE_CHECK_PID="" getpid PIDFILE_CHECK_PID=$SAVE_PIDFILE_CHECK_PID if [ "X$pid" != "X" ] then # Another process is running. if [ "$pid" = "$savepid" ] then # This should never happen, unless the PID was recycled? eval echo `gettext 'Failed to stop $APP_LONG_NAME.'` exit 1 else eval echo `gettext 'The content of $PIDFILE has changed.'` eval echo `gettext 'Another instance of the Wrapper might have started in the meantime.'` # Exit now. Any further actions might compromise the running instance. exit 1 fi fi fi done eval echo `gettext 'Stopped $APP_LONG_NAME.'` fi } pause() { getpid if [ "X$pid" = "X" ] ; then # The application is not running, print the status status else if [ "$PAUSABLE_MODE" = "file" ] ; then echo "PAUSE" >> $COMMANDFILE if [ $? != 0 ] ; then eval echo `gettext 'Failed to write in the command file to pause $APP_LONG_NAME.'` exit 1 fi else # send a SIGUSR1 (use constants because numbers change depending on the platform) kill -USR1 $pid 2>/dev/null if [ $? != 0 ] ; then kill -SIGUSR1 $pid if [ $? != 0 ] ; then eval echo `gettext 'Failed to send a signal to pause $APP_LONG_NAME.'` exit 1 fi fi fi eval echo `gettext 'Pausing $APP_LONG_NAME.'` fi } resume() { getpid if [ "X$pid" = "X" ] ; then # The application is not running, print the status status else if [ "$PAUSABLE_MODE" = "file" ] ; then echo "RESUME" >> $COMMANDFILE if [ $? != 0 ] ; then eval echo `gettext 'Failed to write in the command file to resume $APP_LONG_NAME.'` exit 1 fi else # send a SIGUSR2 (use constants because numbers change depending on the platform) kill -USR2 $pid 2>/dev/null if [ $? != 0 ] ; then kill -SIGUSR2 $pid if [ $? != 0 ] ; then eval echo `gettext 'Failed to send a signal to resume $APP_LONG_NAME.'` exit 1 fi fi fi eval echo `gettext 'Resuming $APP_LONG_NAME.'` fi } # Get the status of SELinux and sets SELINUX_STATUS # 0: Not present or unkown # 1: Disabled # 2: Permissive (unauthorized access attempts aren't denied but logs are written) # 3: Enforcing getSELinuxStatus() { result=`getenforce 2>/dev/null` if [ $? -ne 0 ] ; then # Not present SELINUX_STATUS=0 else case "$result" in 'Disabled') SELINUX_STATUS=1 ;; 'Permissive') SELINUX_STATUS=2 ;; 'Enforcing') SELINUX_STATUS=3 ;; *) SELINUX_STATUS=0 ;; esac fi } status() { checkInstalled getpid if [ "X$pid" = "X" ] then if [ $installedStatus -eq $SERVICE_NOT_INSTALLED ] ; then eval echo `gettext '$APP_LONG_NAME \(not installed\) is not running.'` elif [ "X$installedWith" = "X" ] ; then eval echo `gettext '$APP_LONG_NAME \(installed\) is not running.'` else eval echo `gettext '$APP_LONG_NAME \(installed with $installedWith\) is not running.'` fi exit 1 else if [ "X$DETAIL_STATUS" = "X" ] then if [ $installedStatus -eq $SERVICE_NOT_INSTALLED ] ; then eval echo `gettext '$APP_LONG_NAME \(not installed\) is running: PID:$pid'` elif [ "X$installedWith" = "X" ] ; then eval echo `gettext '$APP_LONG_NAME \(installed\) is running: PID:$pid'` else eval echo `gettext '$APP_LONG_NAME \(installed with $installedWith\) is running: PID:$pid'` fi else getstatus if [ $installedStatus -eq $SERVICE_NOT_INSTALLED ] ; then eval echo `gettext '$APP_LONG_NAME \(not installed\) is running: PID:$pid, Wrapper:$STATUS, Java:$JAVASTATUS'` elif [ "X$installedWith" = "X" ] ; then eval echo `gettext '$APP_LONG_NAME \(installed\) is running: PID:$pid, Wrapper:$STATUS, Java:$JAVASTATUS'` else eval echo `gettext '$APP_LONG_NAME \(installed with $installedWith\) is running: PID:$pid, Wrapper:$STATUS, Java:$JAVASTATUS'` fi fi exit 0 fi } resolveInitdCommand() { # NOTE: update-rc.d & chkconfig are the recommended interfaces for managing # init scripts. insserv is a low level tool used by these interfaces. # chkconfig was available on old versions Ubuntu, but update-rc.d is # preferred. chkconfig is used on RHEL based Linux. # update-rc.d is used on distros such as Debian/Ubuntu resolveLocation INITD_COMMAND "update-rc.d" ";/usr/sbin" 0 if [ $? -eq 0 ] ; then USE_UPDATE_RC=1 else # chkconfig is used on distros such as RHEL or Amazon Linux resolveLocation INITD_COMMAND chkconfig ";/sbin" 0 if [ $? -eq 0 ] ; then USE_CHKCONFIG=1 else # if neither chkconfig nor update-rc.d are present, try insserv resolveLocation INITD_COMMAND insserv ";/sbin" 0 if [ $? -eq 0 ] ; then USE_INSSERV=1 else INITD_COMMAND="" fi fi fi } installdaemon() { mustBeRootOrExit checkInstalled APP_NAME_LOWER=`echo "$APP_NAME" | $TR_BIN "[A-Z]" "[a-z]"` if [ "$DIST_OS" = "solaris" ] ; then eval echo `gettext 'Detected Solaris:'` if [ $installedStatus -ne $SERVICE_NOT_INSTALLED ] ; then eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` else eval echo `gettext ' Installing the $APP_LONG_NAME daemon...'` ln -s "$REALPATH" "/etc/init.d/$APP_NAME" for i in `ls "/etc/rc3.d/K"??"$APP_NAME_LOWER" "/etc/rc3.d/S"??"$APP_NAME_LOWER" 2>/dev/null` ; do eval echo `gettext ' Removing unexpected file before proceeding: $i'` rm -f $i done ln -s "/etc/init.d/$APP_NAME" "/etc/rc3.d/K${APP_RUN_LEVEL_K}$APP_NAME_LOWER" ln -s "/etc/init.d/$APP_NAME" "/etc/rc3.d/S${APP_RUN_LEVEL_S}$APP_NAME_LOWER" fi elif [ "$DIST_OS" = "linux" ] ; then eval echo `gettext 'Detected Linux:'` if [ $installedStatus -ne $SERVICE_NOT_INSTALLED ] ; then eval echo `gettext ' The $APP_LONG_NAME daemon is already installed with $installedWith.'` elif [ -n "$USE_SYSTEMD" ] ; then systemdInstall # If SELinux is present and enabled, then the context of the script needs to be updated for systemd to be allowed to execute it. getSELinuxStatus if [ $SELINUX_STATUS -ge 2 ] ; then # Use chcon rather than semanage because it is present on more installations and also allows us to restore the default context more easily. result=`chcon -t bin_t $REALPATH 2>/dev/null` if [ $? -ne 0 ] ; then # Print a warning, but the user can still configure SELinux manually. SCRIPT_BASENAME=`basename "$REALDIR"` eval echo `gettext ' WARNING: Could not update the SELinux context of $SCRIPT_BASENAME.'` fi fi elif [ -n "$USE_UPSTART" ] ; then upstartInstall else resolveInitdCommand if [ -n "$USE_CHKCONFIG" ] ; then eval echo `gettext ' Installing the $APP_LONG_NAME daemon with init.d \($INITD_COMMAND\)...'` ln -s "$REALPATH" "/etc/init.d/$APP_NAME" $INITD_COMMAND --add "$APP_NAME" $INITD_COMMAND "$APP_NAME" on elif [ "$USE_INSSERV" ] ; then eval echo `gettext ' Installing the $APP_LONG_NAME daemon with init.d \($INITD_COMMAND\)...'` ln -s "$REALPATH" "/etc/init.d/$APP_NAME" $INITD_COMMAND "/etc/init.d/$APP_NAME" elif [ "$USE_UPDATE_RC" ] ; then eval echo `gettext ' Installing the $APP_LONG_NAME daemon with init.d \($INITD_COMMAND\)...'` ln -s "$REALPATH" "/etc/init.d/$APP_NAME" $INITD_COMMAND "$APP_NAME" defaults else eval echo `gettext ' Installing the $APP_LONG_NAME daemon with init.d...'` ln -s "$REALPATH" /etc/init.d/$APP_NAME for i in `ls "/etc/rc3.d/K"??"$APP_NAME_LOWER" "/etc/rc5.d/K"??"$APP_NAME_LOWER" "/etc/rc3.d/S"??"$APP_NAME_LOWER" "/etc/rc5.d/S"??"$APP_NAME_LOWER" 2>/dev/null` ; do eval echo `gettext ' Removing unexpected file before proceeding: $i'` rm -f $i done ln -s "/etc/init.d/$APP_NAME" "/etc/rc3.d/K${APP_RUN_LEVEL_K}$APP_NAME_LOWER" ln -s "/etc/init.d/$APP_NAME" "/etc/rc3.d/S${APP_RUN_LEVEL_S}$APP_NAME_LOWER" ln -s "/etc/init.d/$APP_NAME" "/etc/rc5.d/S${APP_RUN_LEVEL_S}$APP_NAME_LOWER" ln -s "/etc/init.d/$APP_NAME" "/etc/rc5.d/K${APP_RUN_LEVEL_K}$APP_NAME_LOWER" fi fi elif [ "$DIST_OS" = "hpux" ] ; then eval echo `gettext 'Detected HP-UX:'` if [ $installedStatus -ne $SERVICE_NOT_INSTALLED ] ; then eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` else eval echo `gettext ' Installing the $APP_LONG_NAME daemon...'` ln -s "$REALPATH" "/sbin/init.d/$APP_NAME" for i in `ls "/sbin/rc3.d/K"??"$APP_NAME_LOWER" "/sbin/rc3.d/S"??"$APP_NAME_LOWER" 2>/dev/null` ; do eval echo `gettext ' Removing unexpected file before proceeding: $i'` rm -f $i done ln -s "/sbin/init.d/$APP_NAME" "/sbin/rc3.d/K${APP_RUN_LEVEL_K}$APP_NAME_LOWER" ln -s "/sbin/init.d/$APP_NAME" "/sbin/rc3.d/S${APP_RUN_LEVEL_S}$APP_NAME_LOWER" fi elif [ "$DIST_OS" = "aix" ] ; then eval echo `gettext 'Detected AIX:'` if [ $installedStatus -ne $SERVICE_NOT_INSTALLED -a $installedStatus -ne $SERVICE_INSTALLED_SRC_PARTIAL ] ; then eval echo `gettext ' The $APP_LONG_NAME daemon is already installed with $installedWith.'` else eval echo `gettext ' Installing the $APP_LONG_NAME daemon...'` if [ -n "`/usr/sbin/lsitab install_assist`" ] ; then eval echo `gettext ' The task /usr/sbin/install_assist was found in the inittab, this might cause problems for all subsequent tasks to launch as this process is known to block the init task. Please make sure this task is not needed anymore and remove/deactivate it.'` fi for i in `ls "/etc/rc.d/rc2.d/K"??"$APP_NAME_LOWER" "/etc/rc.d/rc2.d/S"??"$APP_NAME_LOWER" 2>/dev/null` ; do eval echo `gettext ' Removing unexpected file before proceeding: $i'` rm -f $i done if [ -n "$USE_SRC" ] ; then srcInstall else # install using initd ln -s "$REALPATH" "/etc/rc.d/init.d/$APP_NAME" ln -s "/etc/rc.d/init.d/$APP_NAME" "/etc/rc.d/rc2.d/K${APP_RUN_LEVEL_K}$APP_NAME_LOWER" ln -s "/etc/rc.d/init.d/$APP_NAME" "/etc/rc.d/rc2.d/S${APP_RUN_LEVEL_S}$APP_NAME_LOWER" fi fi elif [ "$DIST_OS" = "freebsd" ] ; then eval echo `gettext 'Detected FreeBSD:'` if [ $installedStatus -ne $SERVICE_NOT_INSTALLED ] ; then eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` else eval echo `gettext ' Installing the $APP_LONG_NAME daemon...'` sed -i.bak "/${APP_NAME}_enable=\"YES\"/d" /etc/rc.conf if [ -f "${REALDIR}/${APP_NAME}.install" ] ; then ln -s "${REALDIR}/${APP_NAME}.install" "/etc/rc.d/$APP_NAME" else echo '#!/bin/sh' > "/etc/rc.d/$APP_NAME" echo "#" >> "/etc/rc.d/$APP_NAME" echo "# PROVIDE: $APP_NAME" >> "/etc/rc.d/$APP_NAME" echo "# REQUIRE: NETWORKING" >> "/etc/rc.d/$APP_NAME" echo "# KEYWORD: shutdown" >> "/etc/rc.d/$APP_NAME" echo ". /etc/rc.subr" >> "/etc/rc.d/$APP_NAME" echo "name=\"$APP_NAME\"" >> "/etc/rc.d/$APP_NAME" echo "rcvar=\`set_rcvar\`" >> "/etc/rc.d/$APP_NAME" echo "command=\"${REALPATH}\"" >> "/etc/rc.d/$APP_NAME" echo 'start_cmd="${name}_start"' >> "/etc/rc.d/$APP_NAME" echo 'load_rc_config $name' >> "/etc/rc.d/$APP_NAME" echo 'status_cmd="${name}_status"' >> "/etc/rc.d/$APP_NAME" echo 'stop_cmd="${name}_stop"' >> "/etc/rc.d/$APP_NAME" echo "${APP_NAME}_status() {" >> "/etc/rc.d/$APP_NAME" echo '${command} status' >> "/etc/rc.d/$APP_NAME" echo '}' >> "/etc/rc.d/$APP_NAME" echo "${APP_NAME}_stop() {" >> "/etc/rc.d/$APP_NAME" echo '${command} stop' >> "/etc/rc.d/$APP_NAME" echo '}' >> "/etc/rc.d/$APP_NAME" echo "${APP_NAME}_start() {" >> "/etc/rc.d/$APP_NAME" echo '${command} start' >> "/etc/rc.d/$APP_NAME" echo '}' >> "/etc/rc.d/$APP_NAME" echo 'run_rc_command "$1"' >> "/etc/rc.d/$APP_NAME" fi echo "${APP_NAME}_enable=\"YES\"" >> /etc/rc.conf chmod 555 "/etc/rc.d/$APP_NAME" fi elif [ "$DIST_OS" = "macosx" ] ; then eval echo `gettext 'Detected Mac OSX:'` if [ $installedStatus -ne $SERVICE_NOT_INSTALLED ] ; then eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` else eval echo `gettext ' Installing the $APP_LONG_NAME daemon...'` if [ -f "${REALDIR}/${APP_PLIST}" ] ; then ln -s "${REALDIR}/${APP_PLIST}" "/Library/LaunchDaemons/${APP_PLIST}" else echo "" > "/Library/LaunchDaemons/${APP_PLIST}" echo "> "/Library/LaunchDaemons/${APP_PLIST}" echo "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">" >> "/Library/LaunchDaemons/${APP_PLIST}" echo "" >> "/Library/LaunchDaemons/${APP_PLIST}" echo " " >> "/Library/LaunchDaemons/${APP_PLIST}" echo " Label" >> "/Library/LaunchDaemons/${APP_PLIST}" echo " ${APP_PLIST_BASE}" >> "/Library/LaunchDaemons/${APP_PLIST}" echo " ProgramArguments" >> "/Library/LaunchDaemons/${APP_PLIST}" echo " " >> "/Library/LaunchDaemons/${APP_PLIST}" echo " ${REALPATH}" >> "/Library/LaunchDaemons/${APP_PLIST}" echo " launchdinternal" >> "/Library/LaunchDaemons/${APP_PLIST}" echo " " >> "/Library/LaunchDaemons/${APP_PLIST}" echo " ${KEY_KEEP_ALIVE}" >> "/Library/LaunchDaemons/${APP_PLIST}" echo " <${MACOSX_KEEP_RUNNING}/>" >> "/Library/LaunchDaemons/${APP_PLIST}" echo " RunAtLoad" >> "/Library/LaunchDaemons/${APP_PLIST}" echo " " >> "/Library/LaunchDaemons/${APP_PLIST}" if [ "X$RUN_AS_USER" != "X" ] ; then echo " UserName" >> "/Library/LaunchDaemons/${APP_PLIST}" echo " ${RUN_AS_USER}" >> "/Library/LaunchDaemons/${APP_PLIST}" fi echo " " >> "/Library/LaunchDaemons/${APP_PLIST}" echo "" >> "/Library/LaunchDaemons/${APP_PLIST}" fi chmod 555 "/Library/LaunchDaemons/${APP_PLIST}" fi elif [ "$DIST_OS" = "zos" ] ; then eval echo `gettext 'Detected z/OS:'` if [ $installedStatus -ne $SERVICE_NOT_INSTALLED ] ; then eval echo `gettext ' The $APP_LONG_NAME daemon is already installed.'` else eval echo `gettext ' Installing the $APP_LONG_NAME daemon...'` cp /etc/rc /etc/rc.bak sed "s:echo /etc/rc script executed, \`date\`::g" /etc/rc.bak > /etc/rc echo "_BPX_JOBNAME='${APP_NAME}' \"${REALDIR}/${APP_NAME}\" start" >>/etc/rc echo '/etc/rc script executed, `date`' >>/etc/rc fi else eval echo `gettext 'Install not currently supported for $DIST_OS.'` exit 1 fi # Exit with 1 if the daemon was already installed when calling installdaemon(). # The test below requires $installedStatus to be unchanged since the call to # checkInstalled() at the beginning of the function. If the script was launched # with 'installstart' we want to start normally, so don't exit here. if [ $installedStatus -ne $SERVICE_NOT_INSTALLED -a "$COMMAND" != 'installstart' ] ; then exit 1 fi # Check again the installation status. The script should exit with 1 if the service # could not be installed (even if it was launched with 'installstart'). checkInstalled if [ $installedStatus -eq $SERVICE_NOT_INSTALLED ] ; then eval echo `gettext 'Service not installed.'` exit 1 fi } startdaemon() { getServiceControlMethod if [ "$CTRL_WITH_LAUNCHD" = "true" ] ; then macosxStart elif [ "$CTRL_WITH_UPSTART" = "true" ] ; then upstartStart elif [ "$CTRL_WITH_SYSTEMD" = "true" ] ; then systemdStart elif [ "$CTRL_WITH_SRC" = "true" ] ; then srcStart else if [ -n "$SYSD" ] ; then shift fi checkRunUser touchlock "$@" if [ ! -n "$FIXED_COMMAND" ] ; then shift fi if [ "$SERVICE_MANAGEMENT_TOOL" != "auto" -a "$SERVICE_MANAGEMENT_TOOL" != "initd" ] ; then eval echo `gettext 'Starting without using the configured service management tool \"$SERVICE_MANAGEMENT_TOOL\" because the service $APP_NAME is not installed.'` fi start "$@" fi } restartdaemon() { getServiceControlMethod if [ "$CTRL_WITH_LAUNCHD" = "true" ] ; then macosxRestart $1 elif [ "$CTRL_WITH_UPSTART" = "true" ] ; then upstartRestart $1 elif [ "$CTRL_WITH_SYSTEMD" = "true" ] ; then systemdRestart $1 elif [ "$CTRL_WITH_SRC" = "true" ] ; then srcRestart $1 else checkRunUser touchlock "$COMMAND" checkRunning $1 if [ "X$pid" != "X" ] ; then # The daemon was running. Stop it first. eval echo `gettext 'Restarting $APP_LONG_NAME...'` stopit $1 fi shift if [ ! -n "$FIXED_COMMAND" ] ; then shift fi start "$@" fi } isBitSet() { if [ `expr \( $1 / $2 \) % 2` -eq 1 ]; then return 1 else return 0 fi } removedaemon() { mustBeRootOrExit checkInstalled APP_NAME_LOWER=`echo "$APP_NAME" | $TR_BIN "[A-Z]" "[a-z]"` if [ "$DIST_OS" = "solaris" ] ; then eval echo `gettext 'Detected Solaris:'` isBitSet $installedStatus $SERVICE_INSTALLED_DEFAULT if [ $? -eq 1 ] ; then stopit "0" eval echo `gettext ' Removing the $APP_LONG_NAME daemon...'` for i in `ls "/etc/rc3.d/K"??"$APP_NAME_LOWER" "/etc/rc3.d/S"??"$APP_NAME_LOWER" "/etc/init.d/$APP_NAME" 2>/dev/null` ; do rm -f $i done else eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` exit 1 fi elif [ "$DIST_OS" = "linux" ] ; then eval echo `gettext 'Detected Linux:'` isBitSet $installedStatus $SERVICE_INSTALLED_SYSTEMD if [ $? -eq 1 ] ; then systemdRemove # Always try to restore the SELinux context. result=`restorecon $REALPATH 2>/dev/null` if [ $? -ne 0 ] ; then # Only print a warning if 'restorecon' exists or if SELinux is present. getSELinuxStatus result=`command -v restorecon 2>/dev/null` if [ $? -eq 0 -o $SELINUX_STATUS -gt 0 ] ; then SCRIPT_BASENAME=`basename "$REALDIR"` eval echo `gettext ' WARNING: Could not restore the SELinux context of $SCRIPT_BASENAME.'` fi fi fi isBitSet $installedStatus $SERVICE_INSTALLED_UPSTART if [ $? -eq 1 ] ; then upstartRemove fi isBitSet $installedStatus $SERVICE_INSTALLED_DEFAULT if [ $? -eq 1 ] ; then stopit "0" eval echo `gettext ' Removing the $APP_LONG_NAME daemon from init.d...'` resolveInitdCommand if [ -n "$USE_CHKCONFIG" ] ; then $INITD_COMMAND "$APP_NAME" off $INITD_COMMAND --del "$APP_NAME" rm -f "/etc/init.d/$APP_NAME" elif [ "$USE_INSSERV" ] ; then $INITD_COMMAND -r "/etc/init.d/$APP_NAME" rm -f "/etc/init.d/$APP_NAME" elif [ "$USE_UPDATE_RC" ] ; then $INITD_COMMAND -f "$APP_NAME" remove rm -f "/etc/init.d/$APP_NAME" else for i in `ls "/etc/rc3.d/K"??"$APP_NAME_LOWER" "/etc/rc5.d/K"??"$APP_NAME_LOWER" "/etc/rc3.d/S"??"$APP_NAME_LOWER" "/etc/rc5.d/S"??"$APP_NAME_LOWER" "/etc/init.d/$APP_NAME" 2>/dev/null` ; do rm -f $i done fi fi if [ $installedStatus -eq $SERVICE_NOT_INSTALLED ] ; then eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` exit 1 fi elif [ "$DIST_OS" = "hpux" ] ; then eval echo `gettext 'Detected HP-UX:'` isBitSet $installedStatus $SERVICE_INSTALLED_DEFAULT if [ $? -eq 1 ] ; then stopit "0" eval echo `gettext ' Removing the $APP_LONG_NAME daemon...'` for i in `ls "/sbin/rc3.d/K"??"$APP_NAME_LOWER" "/sbin/rc3.d/S"??"$APP_NAME_LOWER" "/sbin/init.d/$APP_NAME" 2>/dev/null` ; do rm -f $i done else eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` exit 1 fi elif [ "$DIST_OS" = "aix" ] ; then eval echo `gettext 'Detected AIX:'` if [ $installedStatus -ne $SERVICE_NOT_INSTALLED ] ; then stopit "0" eval echo `gettext ' Removing the $APP_LONG_NAME daemon...'` isBitSet $installedStatus $SERVICE_INSTALLED_DEFAULT if [ $? -eq 1 ] ; then for i in `ls "/etc/rc.d/rc2.d/K"??"$APP_NAME_LOWER" "/etc/rc.d/rc2.d/S"??"$APP_NAME_LOWER" "/etc/rc.d/init.d/$APP_NAME" 2>/dev/null` ; do rm -f $i done fi isBitSet $installedStatus $SERVICE_INSTALLED_SRC isSrcSet=$? isBitSet $installedStatus $SERVICE_INSTALLED_SRC_PARTIAL isSrcPartialSet=$? if [ $isSrcSet -eq 1 -o $isSrcPartialSet -eq 1 ] ; then validateAppNameLength /usr/sbin/rmitab $APP_NAME /usr/bin/rmssys -s $APP_NAME fi else eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` exit 1 fi elif [ "$DIST_OS" = "freebsd" ] ; then eval echo `gettext 'Detected FreeBSD:'` if [ -f "/etc/rc.d/$APP_NAME" -o -L "/etc/rc.d/$APP_NAME" ] ; then stopit "0" eval echo `gettext ' Removing the $APP_LONG_NAME daemon...'` for i in "/etc/rc.d/$APP_NAME" do rm -f $i done sed -i.bak "/${APP_NAME}_enable=\"YES\"/d" /etc/rc.conf else eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` exit 1 fi elif [ "$DIST_OS" = "macosx" ] ; then eval echo `gettext 'Detected Mac OSX:'` if [ -f "/Library/LaunchDaemons/${APP_PLIST}" -o -L "/Library/LaunchDaemons/${APP_PLIST}" ] ; then stopit "0" eval echo `gettext ' Removing the $APP_LONG_NAME daemon...'` # Make sure the plist is installed LOADED_PLIST=`launchctl list | grep ${APP_PLIST_BASE}` if [ "X${LOADED_PLIST}" != "X" ] ; then launchctl unload "/Library/LaunchDaemons/${APP_PLIST}" fi rm -f "/Library/LaunchDaemons/${APP_PLIST}" else eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` exit 1 fi elif [ "$DIST_OS" = "zos" ] ; then eval echo `gettext 'Detected z/OS:'` if [ -f /etc/rc.bak ] ; then stopit "0" eval echo `gettext ' Removing the $APP_LONG_NAME daemon...'` cp /etc/rc /etc/rc.bak sed "s/_BPX_JOBNAME=\'APP_NAME\'.*//g" /etc/rc.bak > /etc/rc rm /etc/rc.bak else eval echo `gettext ' The $APP_LONG_NAME daemon is not currently installed.'` exit 1 fi else eval echo `gettext 'Remove not currently supported for $DIST_OS.'` exit 1 fi } dump() { eval echo `gettext 'Dumping $APP_LONG_NAME...'` getpid if [ "X$pid" = "X" ] then eval echo `gettext '$APP_LONG_NAME was not running.'` else kill -3 $pid if [ $? -ne 0 ] then eval echo `gettext 'Failed to dump $APP_LONG_NAME.'` exit 1 else eval echo `gettext 'Dumped $APP_LONG_NAME.'` fi fi } # Used by HP-UX init scripts. startmsg() { getpid if [ "X$pid" = "X" ] then eval echo `gettext 'Starting $APP_LONG_NAME... Wrapper:Stopped'` else if [ "X$DETAIL_STATUS" = "X" ] then eval echo `gettext 'Starting $APP_LONG_NAME... Wrapper:Running'` else getstatus eval echo `gettext 'Starting $APP_LONG_NAME... Wrapper:$STATUS, Java:$JAVASTATUS'` fi fi } # Used by HP-UX init scripts. stopmsg() { getpid if [ "X$pid" = "X" ] then eval echo `gettext 'Stopping $APP_LONG_NAME... Wrapper:Stopped'` else if [ "X$DETAIL_STATUS" = "X" ] then eval echo `gettext 'Stopping $APP_LONG_NAME... Wrapper:Running'` else getstatus eval echo `gettext 'Stopping $APP_LONG_NAME... Wrapper:$STATUS, Java:$JAVASTATUS'` fi fi } # 'source' files sourceFiles() { if [ -n "$FILES_TO_SOURCE" ] ; then OIFS=$IFS IFS=';' files=$FILES_TO_SOURCE for file in $files do . $file done IFS=$OIFS fi } # Check if arguments are correct. This needs to be done after knowing the # command, the application name and OS (to check the installation status). # NOTE: When the validity of the arguments depends on the daemon installation, # make sure to call setServiceManagementTool() before this function. # # $1 0: no parameter allowed # -1: parameter allowed if not installed # 1: parameter allowed checkArguments() { # If this is a systemd call (from the systemd service file), we don't want # to check arguments ('sysd' itself should not be blocking and the user # may also have edited the file and added additional args in it). if [ -n "$SYSD" ] ; then return fi if [ -n "$FIRST_ARG" ] ; then if [ $1 = -1 ] ; then checkInstalled "strict" if [ $installedStatus -eq $SERVICE_NOT_INSTALLED ] ; then PASS_THROUGH_ALLOWED=true fi elif [ $1 = 1 ] ; then PASS_THROUGH_ALLOWED=true fi if [ "X${PASS_THROUGH_ALLOWED}" != "Xtrue" ] ; then eval echo `gettext 'Additional arguments are not allowed with the ${COMMAND} command.'` if [ -n "$FIXED_COMMAND" ] ; then # The command can't be used with PASS_THROUGH, so disable it to show appropriate usage. PASS_THROUGH=false fi showUsage exit 2 # LSB - invalid or excess argument(s) elif [ "X${PASS_THROUGH}" = "Xfalse" ] ; then eval echo `gettext 'Additional arguments are not allowed when PASS_THROUGH is set to false.'` showUsage exit 2 # LSB - invalid or excess argument(s) fi fi } docommand() { case "$COMMAND" in 'console') checkArguments 1 # trap signals (this must be done before changing the user) consoleTrapSignals checkRunUser touchlock "$@" if [ ! -n "$FIXED_COMMAND" ] ; then shift fi console "$@" ;; 'start') setServiceManagementTool checkArguments -1 startdaemon "$@" ;; 'stop') checkArguments 0 setServiceManagementTool getServiceControlMethod if [ "$CTRL_WITH_LAUNCHD" = "true" ] ; then macosxStop elif [ "$CTRL_WITH_UPSTART" = "true" ] ; then upstartStop elif [ "$CTRL_WITH_SYSTEMD" = "true" ] ; then systemdStop elif [ "$CTRL_WITH_SRC" = "true" ] ; then srcStop else checkRunUser "" "$COMMAND" stopit "0" fi ;; 'restart') setServiceManagementTool checkArguments -1 restartdaemon "0" "$@" ;; 'condrestart') setServiceManagementTool checkArguments -1 restartdaemon "1" "$@" ;; 'pause') checkArguments 0 setServiceManagementTool if [ -n "$PAUSABLE" ] then pause else showUsage "$COMMAND" fi ;; 'resume') checkArguments 0 setServiceManagementTool if [ -n "$PAUSABLE" ] then resume else showUsage "$COMMAND" fi ;; 'status') checkArguments 0 setServiceManagementTool status ;; 'install') checkArguments 0 setServiceManagementTool installdaemon "$@" ;; 'installstart') checkArguments 0 setServiceManagementTool installdaemon "$@" startdaemon "$@" ;; 'remove') checkArguments 0 setServiceManagementTool removedaemon ;; 'dump') checkArguments 0 checkRunUser "" "$COMMAND" dump ;; 'start_msg') # Internal command called by launchd on HP-UX. checkRunUser "" "$COMMAND" startmsg ;; 'stop_msg') # Internal command called by launchd on HP-UX. checkRunUser "" "$COMMAND" stopmsg ;; 'launchdinternal' | 'upstartinternal') if [ ! "$DIST_OS" = "macosx" -o ! -f "/Library/LaunchDaemons/${APP_PLIST}" ] ; then checkRunUser touchlock "$@" fi # Internal command called by launchd on Max OSX. # We do not want to call checkRunUser here as it is handled in the launchd plist file. Doing it here would confuse launchd. if [ ! -n "$FIXED_COMMAND" ] ; then shift fi launchinternal "$@" ;; *) showUsage "$COMMAND" ;; esac } sourceFiles docommand "$@" exit 0 wrapper_3.5.51_src/src/bin/App.shconf.in100644 0 0 20376 14333053647 15273 0ustar 0 0 #! /bin/sh # # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html # # Java Service Wrapper shconf file. Suitable for overriding the default # settings of the Wrapper Shell Script. # Optimized for use with version 3.5.51 of the Wrapper. # #----------------------------------------------------------------------------- # Having the customized settings in this file makes it easier to upgrade the # Shell script which can then be replaced with minimal changes (just its # 'INIT INFO' section needs to be updated). # IMPORTANT - Please always stop and uninstall an application before making # any changes to this file. Failure to do so could remove the # script's ability to control the application. # Application name and long name: If these variables are not set (or left to # the default tokens), APP_NAME will default to the name of the script, then # APP_LONG_NAME will default to the value of APP_NAME. APP_NAME="@app.name@" APP_LONG_NAME="@app.long.name@" # If uncommented (and set to false), APP_NAME and APP_LONG_NAME will no longer # be passed to the wrapper. See documentation for details. #APP_NAME_PASS_TO_WRAPPER=false # Wrapper WRAPPER_CMD="./wrapper" WRAPPER_CONF="../conf/wrapper.conf" # Priority at which to run the wrapper. See "man nice" for valid priorities. # nice is only used if a priority is specified. PRIORITY= # Location of the pid file. PIDDIR="." # PIDFILE_CHECK_PID tells the script to double check whether the pid in the pid # file actually exists and belongs to this application. When not set, only # check the pid, but not what it is. This is only needed when multiple # applications need to share the same pid file. PIDFILE_CHECK_PID=true # FIXED_COMMAND tells the script to use a hard coded action rather than # expecting the first parameter of the command line to be the command. # By default the command will be expected to be the first parameter. #FIXED_COMMAND=console # PASS_THROUGH controls how the script arguments should be passed to the # Wrapper. Possible values are: # - commented or 'false': the arguments will be ignored (not passed). # - 'app_args' or 'true': the arguments will be passed through the Wrapper as # arguments for the Java Application. # - 'both': both Wrapper properties and Application arguments can be passed to # the Wrapper. The Wrapper properties come in first position. The # user can optionally add a '--' separator followed by application # arguments. # NOTE - If FIXED_COMMAND is set to true the above applies to all arguments, # otherwise it applies to arguments starting with the second. # NOTE - Passing arguments is only valid with the following commands: # - 'console' # - 'start', 'restart', 'condrestart' (if not installed as a daemon) #PASS_THROUGH=app_args # If uncommented, causes the Wrapper to be shutdown using an anchor file. # When launched with the 'start' command, it will also ignore all INT and # TERM signals. #IGNORE_SIGNALS=true # Wrapper will start the JVM asynchronously. Your application may have some # initialization tasks and it may be desirable to wait a few seconds # before returning. For example, to delay the invocation of following # startup scripts. Setting WAIT_AFTER_STARTUP to a positive number will # cause the start command to delay for the indicated period of time # (in seconds). WAIT_AFTER_STARTUP=0 # If set, wait for the wrapper to report that the daemon has started WAIT_FOR_STARTED_STATUS=true WAIT_FOR_STARTED_TIMEOUT=120 # If set, the status, start_msg and stop_msg commands will print out detailed # state information on the Wrapper and Java processes. #DETAIL_STATUS=true # If set, the 'pause' and 'resume' commands will be enabled. These make it # possible to pause the JVM or Java application without completely stopping # the Wrapper. See the wrapper.pausable and wrapper.pausable.stop_jvm # properties for more information. #PAUSABLE=true # Set the mode used to 'pause' or 'resume' the Wrapper. Possible values are # 'signals' which uses SIGUSR1 and SIGUSR2, and 'file' which uses the command # file to communicate these actions. The default value is 'signals'. # Be aware that depending on the mode, the properties wrapper.signal.mode.usr1, # wrapper.signal.mode.usr2, or wrapper.commandfile of the configuration file may # be overriden. #PAUSABLE_MODE=signals # If set, the Wrapper will be run as the specified user. # IMPORTANT - Make sure that the user has the required privileges to write # the PID file and wrapper.log files. Failure to be able to write the log # file will cause the Wrapper to exit without any way to write out an error # message. # NOTES - This will set the user which is used to run the Wrapper as well as # the JVM and is not useful in situations where a privileged resource or # port needs to be allocated prior to the user being changed. # - Setting this variable will cause stdin to be closed. While this # should not be a problem when running as Daemon, it would disable ability # for console applications to receive inputs. #RUN_AS_USER= # When RUN_AS_USER is set, the 'runuser' command will be used to substitute the # user. If not present on the machine, 'su' will be used as a fallback. # The parameter below lets you specify option(s) for the 'runuser' (or 'su') # command. # NOTES - The '-u' option is not allowed. Please set the user with RUN_AS_USER. # - On GNU/Linux systems, if the user specified by RUN_AS_USER doesn't # have a default shell please specify one with the '-s' option. #SU_OPTS="-s /bin/bash" # By default we show a detailed usage block. Uncomment to show brief usage. #BRIEF_USAGE=true # Set which service management tool to use. # Possible values are: # for linux...: auto, systemd, upstart, initd # for aix.....: auto, src, initd # When set to 'auto', this script file will try to detect in the order of the # list for each platform which service management tool to use to install the Wrapper. SERVICE_MANAGEMENT_TOOL=auto # Specify how the Wrapper daemon and its child processes should be killed # when using systemd. # The default is 'control-group' which tells systemd to kill all child # processes (including detached ones) in the control group of the daemon # when it stops. # Alternatively, 'process' can be specified to prevent systemd from # killing the child processes leaving this responsibility to the Wrapper. # In this case child processes marked as 'detached' will not be killed on shutdown. # NOTE - the daemon must be reinstalled for any changes on this property to take effect. SYSTEMD_KILLMODE=control-group # When installing on Mac OSX platforms, the following domain will be used to # prefix the plist file name. PLIST_DOMAIN=org.tanukisoftware.wrapper # When installing on Mac OSX platforms, this parameter controls whether the daemon # is to be kept continuously running or to let demand and conditions control the # invocation. MACOSX_KEEP_RUNNING="false" # The following two lines are used by the chkconfig command. Change as is # appropriate for your application. They should remain commented. # chkconfig: 2345 20 80 # description: @app.long.name@ # Set run level to use when installing the application to start and stop on # system startup and shutdown. It is important that the application always # be uninstalled before making any changes to the run levels. # It is also possible to specify different run levels based on the individual # platform. When doing so this script will look for defined run levels in # the following order: # 1) "RUN_LEVEL_S_$DIST_OS" or "RUN_LEVEL_K_$DIST_OS", where "$DIST_OS" is # the value of DIST_OS. "RUN_LEVEL_S_solaris=20" for example. # 2) RUN_LEVEL_S or RUN_LEVEL_K, to specify specify start or stop run levels. # 3) RUN_LEVEL, to specify a general run level. RUN_LEVEL=20 # List of files to source prior to executing any commands. Use ';' as delimiter. # For example: # FILES_TO_SOURCE="/home/user/.bashrc;anotherfile;../file3" FILES_TO_SOURCE= wrapper_3.5.51_src/src/bin/AppCommand.bat.in100644 0 0 26430 14333053647 16055 0ustar 0 0 @echo off setlocal rem rem Copyright (c) 1999, 2022 Tanuki Software, Ltd. rem http://www.tanukisoftware.com rem All rights reserved. rem rem This software is the proprietary information of Tanuki Software. rem You shall use it only in accordance with the terms of the rem license agreement you entered into with Tanuki Software. rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html rem rem Java Service Wrapper command based script. rem rem ----------------------------------------------------------------------------- rem These settings can be modified to fit the needs of your application rem Optimized for use with version 3.5.51 of the Wrapper. rem The base name for the Wrapper binary. set _WRAPPER_BASE=wrapper rem The directory where the Wrapper binary (.exe) file is located. It can be rem either an absolute or a relative path. If the path contains any special rem characters, please make sure to quote the variable. set _WRAPPER_DIR= rem The name and location of the Wrapper configuration file. This will be used rem if the user does not specify a configuration file as the first parameter to rem this script. set _WRAPPER_CONF="../conf/%_WRAPPER_BASE%.conf" rem _FIXED_COMMAND tells the script to use a hard coded command rather than rem expecting the first parameter of the command line to be the command. rem By default the command will will be expected to be the first parameter. rem set _FIXED_COMMAND=console rem _PASS_THROUGH controls how the script arguments should be passed to the rem Wrapper. Possible values are: rem - commented or 'false': the arguments will be ignored (not passed). rem - 'app_args' or 'true': the arguments will be passed through the Wrapper as rem arguments for the Java Application. rem - 'both': both Wrapper properties and Application arguments can be passed to rem the Wrapper. The Wrapper properties come in first position. The rem user can optionally add a '--' separator followed by application rem arguments. rem NOTE - If _FIXED_COMMAND is set to true the above applies to all arguments, rem otherwise it applies to arguments starting with the second. rem NOTE - Passing arguments is only valid with the 'console', 'install', rem 'installstart' and 'update' commands. set _PASS_THROUGH=app_args rem If there are any errors, the script will pause for a specific number of seconds rem or until the user presses a key. (0 not to wait, negative to wait forever). set _WRAPPER_TIMEOUT=-1 rem Do not modify anything beyond this point rem ----------------------------------------------------------------------------- if "%OS%"=="Windows_NT" goto nt echo This script only works with NT-based versions of Windows. goto :eof :nt rem Find the application home. rem if no path path specified do the default action IF not DEFINED _WRAPPER_DIR goto dir_undefined set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR:"=%" if not "%_WRAPPER_DIR:~-2,1%" == "\" set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR_QUOTED:"=%\" rem check if absolute path if "%_WRAPPER_DIR_QUOTED:~2,1%" == ":" goto absolute_path if "%_WRAPPER_DIR_QUOTED:~1,1%" == "\" goto absolute_path rem everythig else means relative path set _REALPATH="%~dp0%_WRAPPER_DIR_QUOTED:"=%" goto pathfound :dir_undefined rem Use a relative path to the wrapper %~dp0 is location of current script under NT set _REALPATH="%~dp0" goto pathfound :absolute_path rem Use an absolute path to the wrapper set _REALPATH="%_WRAPPER_DIR_QUOTED:"=%" :pathfound rem rem Decide on the specific Wrapper binary to use (See delta-pack) rem if "%PROCESSOR_ARCHITEW6432%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 :x86_32 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-32.exe" set _BIN_BITS="32" goto search :amd64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-64.exe" set _BIN_BITS="64" goto search :ia64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-ia-64.exe" set _BIN_BITS="64" goto search :search set _WRAPPER_EXE="%_WRAPPER_L_EXE:"=%" if exist %_WRAPPER_EXE% goto check_lic_bits set _WRAPPER_EXE="%_REALPATH:"=%%_WRAPPER_BASE%.exe" if exist %_WRAPPER_EXE% goto conf if %_BIN_BITS%=="64" goto x86_32 echo Unable to locate a Wrapper executable using any of the following names: echo %_WRAPPER_L_EXE% echo %_WRAPPER_EXE% goto preexitpause :check_lic_bits rem The command should not be called inside a IF, else errorlevel would be 0 if not %_BIN_BITS%=="64" goto conf %_WRAPPER_EXE% --request_delta_binary_bits %_WRAPPER_CONF% > nul 2>&1 if %errorlevel% equ 32 ( set _LIC32_OS64=true goto x86_32 ) :conf if [%_PASS_THROUGH%]==[true] ( set _PASS_THROUGH=app_args ) if [%_PASS_THROUGH%]==[app_args] ( set _PARAMETERS=-- set ARGS_ARE_APP_PARAMS=true set _PASS_THROUGH_ON=true ) if [%_PASS_THROUGH%]==[both] ( set _PASS_THROUGH_ON=true ) if not [%_PASS_THROUGH_ON%]==[true] ( set _PASS_THROUGH=false ) set _SCRIPT_NAME=%~n0 if not [%_FIXED_COMMAND%]==[] ( set _COMMAND=%_FIXED_COMMAND% ) else ( set _COMMAND=%1 shift ) rem Check the command if [%_COMMAND%]==[console] goto args_allowed if [%_COMMAND%]==[setup] goto args_not_allowed if [%_COMMAND%]==[teardown] goto args_not_allowed if [%_COMMAND%]==[start] goto args_not_allowed if [%_COMMAND%]==[stop] goto args_not_allowed if [%_COMMAND%]==[install] goto args_allowed if [%_COMMAND%]==[installstart] goto args_allowed if [%_COMMAND%]==[pause] goto args_not_allowed if [%_COMMAND%]==[resume] goto args_not_allowed if [%_COMMAND%]==[status] goto args_not_allowed if [%_COMMAND%]==[remove] goto args_not_allowed if [%_COMMAND%]==[restart] goto args_not_allowed rem The command is invalid if [%_FIXED_COMMAND%]==[] ( echo Unexpected command: %_COMMAND% echo. goto showusage ) else ( echo Invalid value '%_COMMAND%' for _FIXED_COMMAND. goto preexitpause ) :args_not_allowed if not [%1]==[] ( echo Additional arguments are not allowed with the %_COMMAND% command. if not [%_FIXED_COMMAND%]==[] ( rem The command can't be used with PASS_THROUGH, so disable it to show appropriate usage. set _PASS_THROUGH=false ) goto showusage ) :args_allowed if not [%1]==[] ( if [%_PASS_THROUGH%]==[false] ( echo Additional arguments are not allowed when _PASS_THROUGH is set to false. goto preexitpause ) ) rem Collect all parameters :parameters if [%1]==[] goto callcommand if [%ARGS_ARE_APP_PARAMS%]==[true] goto append if [%1]==[--] ( set ARGS_ARE_APP_PARAMS=true goto append ) rem So we are appending a wrapper property. rem 1) Check it is wrapped inside double quotes. if not ["%~1"]==[%1] ( if not [%_MISSING_QUOTES_REPORTED%]==[true] ( set _MISSING_QUOTES_REPORTED=true echo WARNING: Any property assignment before '--' should be wrapped inside double quotes on Windows. In a powershell prompt command, double quotes should be escaped with backquote characters ^(^`^). ) rem If not wrapped inside quotes, the following tests are not relevant, so skip them. Should we stop? We always used to continue.. but the Wrapper will probably fail. goto append ) rem 2) Check that the arg matches the pattern of a property (the command should be outside of a IF block for errorlevel to be correct) echo %1 | findstr ^wrapper\..*\=.*$ > nul 2>&1 if %errorlevel% equ 0 goto append echo %1 | findstr ^.*\=.*$ > nul 2>&1 if %errorlevel% equ 0 goto unkown_property rem Not a valid assignment. echo WARNING: Encountered an invalid configuration property assignment '%~1'. When PASS_THROUGH is set to 'both', any argument before '--' should be in the format '^=^'. goto append :unkown_property rem The property name is not starting with 'wrapper.' so invalid. rem Extract the property name (this should be outside of a IF-ELSE block) for /f "tokens=1* delims==" %%a in ("%~1") do set _COMMAND_PROP=%%a echo WARNING: Encountered an unknown configuration property '%_COMMAND_PROP%'. When PASS_THROUGH is set to 'both', any argument before '--' should target a valid Wrapper configuration property. :append set _PARAMETERS=%_PARAMETERS% %1 shift goto parameters rem rem Run the Wrapper rem :callcommand if [%_COMMAND%]==[console] ( %_WRAPPER_EXE% -c "%_WRAPPER_CONF%" %_PARAMETERS% ) else if [%_COMMAND%]==[setup] ( %_WRAPPER_EXE% -su "%_WRAPPER_CONF%" ) else if [%_COMMAND%]==[teardown] ( %_WRAPPER_EXE% -td "%_WRAPPER_CONF%" ) else if [%_COMMAND%]==[start] ( call :start ) else if [%_COMMAND%]==[stop] ( call :stop ) else if [%_COMMAND%]==[install] ( %_WRAPPER_EXE% -i "%_WRAPPER_CONF%" %_PARAMETERS% ) else if [%_COMMAND%]==[installstart] ( %_WRAPPER_EXE% -it "%_WRAPPER_CONF%" %_PARAMETERS% ) else if [%_COMMAND%]==[pause] ( %_WRAPPER_EXE% -a "%_WRAPPER_CONF%" ) else if [%_COMMAND%]==[resume] ( %_WRAPPER_EXE% -e "%_WRAPPER_CONF%" ) else if [%_COMMAND%]==[status] ( %_WRAPPER_EXE% -q "%_WRAPPER_CONF%" ) else if [%_COMMAND%]==[remove] ( %_WRAPPER_EXE% -r "%_WRAPPER_CONF%" ) else if [%_COMMAND%]==[restart] ( call :stop call :start ) if not errorlevel 1 goto :eof goto preexitpause :start %_WRAPPER_EXE% -t "%_WRAPPER_CONF%" goto :eof :stop %_WRAPPER_EXE% -p "%_WRAPPER_CONF%" goto :eof :showusage if [%_PASS_THROUGH%]==[app_args] ( set ARGS= {JavaAppArgs} ) else if [%_PASS_THROUGH%]==[both] ( set ARGS= {WrapperProperties} [-- {JavaAppArgs}] ) else ( set ARGS= ) if [%_FIXED_COMMAND%]==[] ( echo Usage: %_SCRIPT_NAME% [ console%ARGS% : start : stop : restart : pause : resume : status : install%ARGS% : installstart%ARGS% : remove : setup : teardown ] echo. echo Commands: echo console Launch in the current console. echo start Start the Service. echo stop Stop the Service. echo restart Stop the Service if running and then start. echo pause Pause the Service if running. echo resume Resume the Service if paused. echo status Query the current status of the Service. echo install Install the Service. echo installstart Install the Service and then start running it. echo remove Uninstall the Service. echo setup Setup the Wrapper ^(for registration to the Windows Event Log^). echo teardown Teardown the Wrapper ^(unregister from the Windows Event Log^). echo. ) else ( echo Usage: %_SCRIPT_NAME%%ARGS% ) if not [%_PASS_THROUGH%]==[false] ( if [%_PASS_THROUGH%]==[both] ( echo WrapperProperties: echo Optional configuration properties which will be passed to the Wrapper. echo. ) echo JavaAppArgs: echo Optional arguments which will be passed to the Java application. echo. ) rem Exit when there is a problem :preexitpause if %errorlevel% equ 0 ( set EXIT_CODE=1 ) else ( set EXIT_CODE=%errorlevel% ) if %_WRAPPER_TIMEOUT% gtr 0 ( timeout /t %_WRAPPER_TIMEOUT% ) else ( if %_WRAPPER_TIMEOUT% lss 0 ( pause ) ) EXIT /B %EXIT_CODE% wrapper_3.5.51_src/src/bin/AppNoWrapper.bat.in100644 0 0 2032 14333053647 16364 0ustar 0 0 @echo off setlocal rem rem Copyright (c) 1999, 2022 Tanuki Software, Ltd. rem http://www.tanukisoftware.com rem All rights reserved. rem rem This software is the proprietary information of Tanuki Software. rem You shall use it only in accordance with the terms of the rem license agreement you entered into with Tanuki Software. rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html rem rem This script is an example of how to run your application without the Wrapper, but with the rem Wrapper helper classes. You can obtain the actual command generated by the wrapper for rem your application by running the Wrapper with the wrapper.java.command.loglevel=INFO rem property set. rem rem The wrapper.key property MUST be removed from the resulting command or it will fail to rem run correctly. java -Xms16m -Xmx64m -Djava.library.path="../lib" -Djava.class.path="../lib/wrapper.jar;../lib/wrappertest.jar" -Dwrapper.native_library="wrapper" -Dwrapper.debug="TRUE" org.tanukisoftware.wrapper.test.Main wrapper_3.5.51_src/src/bin/AppTemplate.bat.in100644 0 0 11771 14333053647 16254 0ustar 0 0 @echo off setlocal rem rem Copyright (c) 1999, 2022 Tanuki Software, Ltd. rem http://www.tanukisoftware.com rem All rights reserved. rem rem This software is the proprietary information of Tanuki Software. rem You shall use it only in accordance with the terms of the rem license agreement you entered into with Tanuki Software. rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html rem rem @script.description@ rem rem ----------------------------------------------------------------------------- rem These settings can be modified to fit the needs of your application rem Optimized for use with version 3.5.51 of the Wrapper. rem The base name for the Wrapper binary. set _WRAPPER_BASE=wrapper rem The directory where the Wrapper binary (.exe) file is located. It can be rem either an absolute or a relative path. If the path contains any special rem characters, please make sure to quote the variable. set _WRAPPER_DIR= rem The name and location of the Wrapper configuration file. This will be used rem if the user does not specify a configuration file as the first parameter to rem this script. rem If a relative path is specified, please note that the location is based on the rem location of the Wrapper executable. set _WRAPPER_CONF_DEFAULT="../conf/%_WRAPPER_BASE%.conf" rem Makes it possible to override the Wrapper configuration file by specifying it rem as the first parameter. rem set _WRAPPER_CONF_OVERRIDE=true rem If there are any errors, the script will pause for a specific number of seconds rem or until the user presses a key. (0 not to wait, negative to wait forever). set _WRAPPER_TIMEOUT=-1 rem Note that it is only possible to pass parameters through to the JVM when rem installing the service, or when running in a console. rem Do not modify anything beyond this point rem ----------------------------------------------------------------------------- rem rem Resolve the real path of the wrapper.exe rem For non NT systems, the _REALPATH and _WRAPPER_CONF values rem can be hard-coded below and the following test removed. rem if "%OS%"=="Windows_NT" goto nt echo This script only works with NT-based versions of Windows. goto :eof :nt rem Find the application home. rem if no path path specified do the default action IF not DEFINED _WRAPPER_DIR goto dir_undefined set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR:"=%" if not "%_WRAPPER_DIR:~-2,1%" == "\" set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR_QUOTED:"=%\" rem check if absolute path if "%_WRAPPER_DIR_QUOTED:~2,1%" == ":" goto absolute_path if "%_WRAPPER_DIR_QUOTED:~1,1%" == "\" goto absolute_path rem everythig else means relative path set _REALPATH="%~dp0%_WRAPPER_DIR_QUOTED:"=%" goto pathfound :dir_undefined rem Use a relative path to the wrapper %~dp0 is location of current script under NT set _REALPATH="%~dp0" goto pathfound :absolute_path rem Use an absolute path to the wrapper set _REALPATH="%_WRAPPER_DIR_QUOTED:"=%" :pathfound rem rem Decide on the specific Wrapper binary to use (See delta-pack) rem if "%PROCESSOR_ARCHITEW6432%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 :x86_32 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-32.exe" set _BIN_BITS="32" goto search :amd64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-64.exe" set _BIN_BITS="64" goto search :ia64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-ia-64.exe" set _BIN_BITS="64" goto search :search set _WRAPPER_EXE="%_WRAPPER_L_EXE:"=%" if exist %_WRAPPER_EXE% goto check_lic_bits set _WRAPPER_EXE="%_REALPATH:"=%%_WRAPPER_BASE%.exe" if exist %_WRAPPER_EXE% goto conf if %_BIN_BITS%=="64" goto x86_32 echo Unable to locate a Wrapper executable using any of the following names: echo %_WRAPPER_L_EXE% echo %_WRAPPER_EXE% goto preexitpause :check_lic_bits if %_BIN_BITS%=="64" ( set _CHECK_LIC_BITS=true ) rem rem Find the wrapper.conf rem :conf if [%_WRAPPER_CONF_OVERRIDE%]==[true] ( set _WRAPPER_CONF="%~f1" if not [%_WRAPPER_CONF%]==[""] ( shift goto callcommand ) ) set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT:"=%" rem The command should not be called inside a IF, else errorlevel would be 0 if not [%_CHECK_LIC_BITS%]==[true] goto callcommand %_WRAPPER_EXE% --request_delta_binary_bits %_WRAPPER_CONF% > nul 2>&1 if %errorlevel% equ 32 ( set _LIC32_OS64=true set _CHECK_LIC_BITS=false goto x86_32 ) rem rem Run the Wrapper rem :callcommand if not [%1]==[] ( echo Additional arguments are not allowed. goto preexitpause ) %_WRAPPER_EXE% @wrapper.command@ %_WRAPPER_CONF% if not errorlevel 1 goto :eof rem Exit when there is a problem :preexitpause if %errorlevel% equ 0 ( set EXIT_CODE=1 ) else ( set EXIT_CODE=%errorlevel% ) if %_WRAPPER_TIMEOUT% gtr 0 ( timeout /t %_WRAPPER_TIMEOUT% ) else ( if %_WRAPPER_TIMEOUT% lss 0 ( pause ) ) EXIT /B %EXIT_CODE% wrapper_3.5.51_src/src/bin/AppTemplatePassThrough.bat.in100644 0 0 17726 14333053647 20452 0ustar 0 0 @echo off setlocal rem rem Copyright (c) 1999, 2022 Tanuki Software, Ltd. rem http://www.tanukisoftware.com rem All rights reserved. rem rem This software is the proprietary information of Tanuki Software. rem You shall use it only in accordance with the terms of the rem license agreement you entered into with Tanuki Software. rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html rem rem @script.description@ rem rem ----------------------------------------------------------------------------- rem These settings can be modified to fit the needs of your application rem Optimized for use with version 3.5.51 of the Wrapper. rem The base name for the Wrapper binary. set _WRAPPER_BASE=wrapper rem The directory where the Wrapper binary (.exe) file is located. It can be rem either an absolute or a relative path. If the path contains any special rem characters, please make sure to quote the variable. set _WRAPPER_DIR= rem The name and location of the Wrapper configuration file. This will be used rem if the user does not specify a configuration file as the first parameter to rem this script. rem If a relative path is specified, please note that the location is based on the rem location of the Wrapper executable. set _WRAPPER_CONF_DEFAULT="../conf/%_WRAPPER_BASE%.conf" rem Makes it possible to override the Wrapper configuration file by specifying it rem as the first parameter. rem set _WRAPPER_CONF_OVERRIDE=true rem _PASS_THROUGH controls how the script arguments should be passed to the rem Wrapper. Possible values are: rem - commented or 'false': the arguments will be ignored (not passed). rem - 'app_args' or 'true': the arguments will be passed through the Wrapper as rem arguments for the Java Application. rem - 'both': both Wrapper properties and Application arguments can be passed to rem the Wrapper. The Wrapper properties come in first position. The rem user can optionally add a '--' separator followed by application rem arguments. rem NOTE - If _WRAPPER_CONF_OVERRIDE is set to true the above applies to arguments rem starting with the second, otherwise it applies to all arguments. rem set _PASS_THROUGH=app_args rem If there are any errors, the script will pause for a specific number of seconds rem or until the user presses a key. (0 not to wait, negative to wait forever). set _WRAPPER_TIMEOUT=-1 rem Do not modify anything beyond this point rem ----------------------------------------------------------------------------- rem rem Resolve the real path of the wrapper.exe rem For non NT systems, the _REALPATH and _WRAPPER_CONF values rem can be hard-coded below and the following test removed. rem if "%OS%"=="Windows_NT" goto nt echo This script only works with NT-based versions of Windows. goto :eof :nt rem Find the application home. rem if no path path specified do the default action IF not DEFINED _WRAPPER_DIR goto dir_undefined set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR:"=%" if not "%_WRAPPER_DIR:~-2,1%" == "\" set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR_QUOTED:"=%\" rem check if absolute path if "%_WRAPPER_DIR_QUOTED:~2,1%" == ":" goto absolute_path if "%_WRAPPER_DIR_QUOTED:~1,1%" == "\" goto absolute_path rem everythig else means relative path set _REALPATH="%~dp0%_WRAPPER_DIR_QUOTED:"=%" goto pathfound :dir_undefined rem Use a relative path to the wrapper %~dp0 is location of current script under NT set _REALPATH="%~dp0" goto pathfound :absolute_path rem Use an absolute path to the wrapper set _REALPATH="%_WRAPPER_DIR_QUOTED:"=%" :pathfound rem rem Decide on the specific Wrapper binary to use (See delta-pack) rem if "%PROCESSOR_ARCHITEW6432%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 :x86_32 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-32.exe" set _BIN_BITS="32" goto search :amd64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-64.exe" set _BIN_BITS="64" goto search :ia64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-ia-64.exe" set _BIN_BITS="64" goto search :search set _WRAPPER_EXE="%_WRAPPER_L_EXE:"=%" if exist %_WRAPPER_EXE% goto check_lic_bits set _WRAPPER_EXE="%_REALPATH:"=%%_WRAPPER_BASE%.exe" if exist %_WRAPPER_EXE% goto conf if %_BIN_BITS%=="64" goto x86_32 echo Unable to locate a Wrapper executable using any of the following names: echo %_WRAPPER_L_EXE% echo %_WRAPPER_EXE% goto preexitpause :check_lic_bits if %_BIN_BITS%=="64" ( set _CHECK_LIC_BITS=true ) rem rem Find the wrapper.conf rem :conf if [%_WRAPPER_CONF_OVERRIDE%]==[true] ( set _WRAPPER_CONF="%~f1" if not [%_WRAPPER_CONF%]==[""] ( shift goto conf ) ) set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT:"=%" rem The command should not be called inside a IF, else errorlevel would be 0 if not [%_CHECK_LIC_BITS%]==[true] goto conf %_WRAPPER_EXE% --request_delta_binary_bits %_WRAPPER_CONF% > nul 2>&1 if %errorlevel% equ 32 ( set _LIC32_OS64=true set _CHECK_LIC_BITS=false goto x86_32 ) :conf if [%_PASS_THROUGH%]==[true] ( set _PASS_THROUGH=app_args ) if [%_PASS_THROUGH%]==[app_args] ( set _PARAMETERS=-- set ARGS_ARE_APP_PARAMS=true set _PASS_THROUGH_ON=true ) if [%_PASS_THROUGH%]==[both] ( set _PASS_THROUGH_ON=true ) if not [%_PASS_THROUGH_ON%]==[true] ( set _PASS_THROUGH=false ) if not [%1]==[] ( if [%_PASS_THROUGH%]==[false] ( echo Additional arguments are not allowed when _PASS_THROUGH is set to false. goto preexitpause ) ) rem Collect all parameters :parameters if [%1]==[] goto callcommand if [%ARGS_ARE_APP_PARAMS%]==[true] goto append if [%1]==[--] ( set ARGS_ARE_APP_PARAMS=true goto append ) rem So we are appending a wrapper property. rem 1) Check it is wrapped inside double quotes. if not ["%~1"]==[%1] ( if not [%_MISSING_QUOTES_REPORTED%]==[true] ( set _MISSING_QUOTES_REPORTED=true echo WARNING: Any property assignment before '--' should be wrapped inside double quotes on Windows. In a powershell prompt command, double quotes should be escaped with backquote characters ^(^`^). ) rem If not wrapped inside quotes, the following tests are not relevant, so skip them. Should we stop? We always used to continue.. but the Wrapper will probably fail. goto append ) rem 2) Check that the arg matches the pattern of a property (the command should be outside of a IF block for errorlevel to be correct) echo %1 | findstr ^wrapper\..*\=.*$ > nul 2>&1 if %errorlevel% equ 0 goto append echo %1 | findstr ^.*\=.*$ > nul 2>&1 if %errorlevel% equ 0 goto unkown_property rem Not a valid assignment. echo WARNING: Encountered an invalid configuration property assignment '%~1'. When PASS_THROUGH is set to 'both', any argument before '--' should be in the format '^=^'. goto append :unkown_property rem The property name is not starting with 'wrapper.' so invalid. rem Extract the property name (this should be outside of a IF-ELSE block) for /f "tokens=1* delims==" %%a in ("%~1") do set _COMMAND_PROP=%%a echo WARNING: Encountered an unknown configuration property '%_COMMAND_PROP%'. When PASS_THROUGH is set to 'both', any argument before '--' should target a valid Wrapper configuration property. :append set _PARAMETERS=%_PARAMETERS% %1 shift goto parameters rem rem Run the Wrapper rem :callcommand if [%_PASS_THROUGH%]==[false] ( %_WRAPPER_EXE% @wrapper.command@ %_WRAPPER_CONF% ) else ( %_WRAPPER_EXE% @wrapper.command@ %_WRAPPER_CONF% %_PARAMETERS% ) if not errorlevel 1 goto :eof rem Exit when there is a problem :preexitpause if %errorlevel% equ 0 ( set EXIT_CODE=1 ) else ( set EXIT_CODE=%errorlevel% ) if %_WRAPPER_TIMEOUT% gtr 0 ( timeout /t %_WRAPPER_TIMEOUT% ) else ( if %_WRAPPER_TIMEOUT% lss 0 ( pause ) ) EXIT /B %EXIT_CODE% wrapper_3.5.51_src/src/bin/InstallApp-NT.bat.in100644 0 0 17733 14333053647 16432 0ustar 0 0 @echo off setlocal rem rem Copyright (c) 1999, 2022 Tanuki Software, Ltd. rem http://www.tanukisoftware.com rem All rights reserved. rem rem This software is the proprietary information of Tanuki Software. rem You shall use it only in accordance with the terms of the rem license agreement you entered into with Tanuki Software. rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html rem rem Java Service Wrapper script - Install as an NT service. rem rem ----------------------------------------------------------------------------- rem These settings can be modified to fit the needs of your application rem Optimized for use with version 3.5.51 of the Wrapper. rem The base name for the Wrapper binary. set _WRAPPER_BASE=wrapper rem The directory where the Wrapper binary (.exe) file is located. It can be rem either an absolute or a relative path. If the path contains any special rem characters, please make sure to quote the variable. set _WRAPPER_DIR= rem The name and location of the Wrapper configuration file. This will be used rem if the user does not specify a configuration file as the first parameter to rem this script. rem If a relative path is specified, please note that the location is based on the rem location of the Wrapper executable. set _WRAPPER_CONF_DEFAULT="../conf/%_WRAPPER_BASE%.conf" rem Makes it possible to override the Wrapper configuration file by specifying it rem as the first parameter. rem set _WRAPPER_CONF_OVERRIDE=true rem _PASS_THROUGH controls how the script arguments should be passed to the rem Wrapper. Possible values are: rem - commented or 'false': the arguments will be ignored (not passed). rem - 'app_args' or 'true': the arguments will be passed through the Wrapper as rem arguments for the Java Application. rem - 'both': both Wrapper properties and Application arguments can be passed to rem the Wrapper. The Wrapper properties come in first position. The rem user can optionally add a '--' separator followed by application rem arguments. rem NOTE - If _WRAPPER_CONF_OVERRIDE is set to true the above applies to arguments rem starting with the second, otherwise it applies to all arguments. rem set _PASS_THROUGH=app_args rem If there are any errors, the script will pause for a specific number of seconds rem or until the user presses a key. (0 not to wait, negative to wait forever). set _WRAPPER_TIMEOUT=-1 rem Do not modify anything beyond this point rem ----------------------------------------------------------------------------- rem rem Resolve the real path of the wrapper.exe rem For non NT systems, the _REALPATH and _WRAPPER_CONF values rem can be hard-coded below and the following test removed. rem if "%OS%"=="Windows_NT" goto nt echo This script only works with NT-based versions of Windows. goto :eof :nt rem Find the application home. rem if no path path specified do the default action IF not DEFINED _WRAPPER_DIR goto dir_undefined set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR:"=%" if not "%_WRAPPER_DIR:~-2,1%" == "\" set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR_QUOTED:"=%\" rem check if absolute path if "%_WRAPPER_DIR_QUOTED:~2,1%" == ":" goto absolute_path if "%_WRAPPER_DIR_QUOTED:~1,1%" == "\" goto absolute_path rem everythig else means relative path set _REALPATH="%~dp0%_WRAPPER_DIR_QUOTED:"=%" goto pathfound :dir_undefined rem Use a relative path to the wrapper %~dp0 is location of current script under NT set _REALPATH="%~dp0" goto pathfound :absolute_path rem Use an absolute path to the wrapper set _REALPATH="%_WRAPPER_DIR_QUOTED:"=%" :pathfound rem rem Decide on the specific Wrapper binary to use (See delta-pack) rem if "%PROCESSOR_ARCHITEW6432%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 :x86_32 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-32.exe" set _BIN_BITS="32" goto search :amd64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-64.exe" set _BIN_BITS="64" goto search :ia64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-ia-64.exe" set _BIN_BITS="64" goto search :search set _WRAPPER_EXE="%_WRAPPER_L_EXE:"=%" if exist %_WRAPPER_EXE% goto check_lic_bits set _WRAPPER_EXE="%_REALPATH:"=%%_WRAPPER_BASE%.exe" if exist %_WRAPPER_EXE% goto conf if %_BIN_BITS%=="64" goto x86_32 echo Unable to locate a Wrapper executable using any of the following names: echo %_WRAPPER_L_EXE% echo %_WRAPPER_EXE% goto preexitpause :check_lic_bits if %_BIN_BITS%=="64" ( set _CHECK_LIC_BITS=true ) rem rem Find the wrapper.conf rem :conf if [%_WRAPPER_CONF_OVERRIDE%]==[true] ( set _WRAPPER_CONF="%~f1" if not [%_WRAPPER_CONF%]==[""] ( shift goto conf ) ) set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT:"=%" rem The command should not be called inside a IF, else errorlevel would be 0 if not [%_CHECK_LIC_BITS%]==[true] goto conf %_WRAPPER_EXE% --request_delta_binary_bits %_WRAPPER_CONF% > nul 2>&1 if %errorlevel% equ 32 ( set _LIC32_OS64=true set _CHECK_LIC_BITS=false goto x86_32 ) :conf if [%_PASS_THROUGH%]==[true] ( set _PASS_THROUGH=app_args ) if [%_PASS_THROUGH%]==[app_args] ( set _PARAMETERS=-- set ARGS_ARE_APP_PARAMS=true set _PASS_THROUGH_ON=true ) if [%_PASS_THROUGH%]==[both] ( set _PASS_THROUGH_ON=true ) if not [%_PASS_THROUGH_ON%]==[true] ( set _PASS_THROUGH=false ) if not [%1]==[] ( if [%_PASS_THROUGH%]==[false] ( echo Additional arguments are not allowed when _PASS_THROUGH is set to false. goto preexitpause ) ) rem Collect all parameters :parameters if [%1]==[] goto callcommand if [%ARGS_ARE_APP_PARAMS%]==[true] goto append if [%1]==[--] ( set ARGS_ARE_APP_PARAMS=true goto append ) rem So we are appending a wrapper property. rem 1) Check it is wrapped inside double quotes. if not ["%~1"]==[%1] ( if not [%_MISSING_QUOTES_REPORTED%]==[true] ( set _MISSING_QUOTES_REPORTED=true echo WARNING: Any property assignment before '--' should be wrapped inside double quotes on Windows. In a powershell prompt command, double quotes should be escaped with backquote characters ^(^`^). ) rem If not wrapped inside quotes, the following tests are not relevant, so skip them. Should we stop? We always used to continue.. but the Wrapper will probably fail. goto append ) rem 2) Check that the arg matches the pattern of a property (the command should be outside of a IF block for errorlevel to be correct) echo %1 | findstr ^wrapper\..*\=.*$ > nul 2>&1 if %errorlevel% equ 0 goto append echo %1 | findstr ^.*\=.*$ > nul 2>&1 if %errorlevel% equ 0 goto unkown_property rem Not a valid assignment. echo WARNING: Encountered an invalid configuration property assignment '%~1'. When PASS_THROUGH is set to 'both', any argument before '--' should be in the format '^=^'. goto append :unkown_property rem The property name is not starting with 'wrapper.' so invalid. rem Extract the property name (this should be outside of a IF-ELSE block) for /f "tokens=1* delims==" %%a in ("%~1") do set _COMMAND_PROP=%%a echo WARNING: Encountered an unknown configuration property '%_COMMAND_PROP%'. When PASS_THROUGH is set to 'both', any argument before '--' should target a valid Wrapper configuration property. :append set _PARAMETERS=%_PARAMETERS% %1 shift goto parameters rem rem Run the Wrapper rem :callcommand if [%_PASS_THROUGH%]==[false] ( %_WRAPPER_EXE% -i %_WRAPPER_CONF% ) else ( %_WRAPPER_EXE% -i %_WRAPPER_CONF% %_PARAMETERS% ) if not errorlevel 1 goto :eof rem Exit when there is a problem :preexitpause if %errorlevel% equ 0 ( set EXIT_CODE=1 ) else ( set EXIT_CODE=%errorlevel% ) if %_WRAPPER_TIMEOUT% gtr 0 ( timeout /t %_WRAPPER_TIMEOUT% ) else ( if %_WRAPPER_TIMEOUT% lss 0 ( pause ) ) EXIT /B %EXIT_CODE% wrapper_3.5.51_src/src/bin/PauseApp-NT.bat.in100644 0 0 12017 14333053647 16067 0ustar 0 0 @echo off setlocal rem rem Copyright (c) 1999, 2022 Tanuki Software, Ltd. rem http://www.tanukisoftware.com rem All rights reserved. rem rem This software is the proprietary information of Tanuki Software. rem You shall use it only in accordance with the terms of the rem license agreement you entered into with Tanuki Software. rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html rem rem Java Service Wrapper script - Pause a running NT service. rem rem ----------------------------------------------------------------------------- rem These settings can be modified to fit the needs of your application rem Optimized for use with version 3.5.51 of the Wrapper. rem The base name for the Wrapper binary. set _WRAPPER_BASE=wrapper rem The directory where the Wrapper binary (.exe) file is located. It can be rem either an absolute or a relative path. If the path contains any special rem characters, please make sure to quote the variable. set _WRAPPER_DIR= rem The name and location of the Wrapper configuration file. This will be used rem if the user does not specify a configuration file as the first parameter to rem this script. rem If a relative path is specified, please note that the location is based on the rem location of the Wrapper executable. set _WRAPPER_CONF_DEFAULT="../conf/%_WRAPPER_BASE%.conf" rem Makes it possible to override the Wrapper configuration file by specifying it rem as the first parameter. rem set _WRAPPER_CONF_OVERRIDE=true rem If there are any errors, the script will pause for a specific number of seconds rem or until the user presses a key. (0 not to wait, negative to wait forever). set _WRAPPER_TIMEOUT=-1 rem Note that it is only possible to pass parameters through to the JVM when rem installing the service, or when running in a console. rem Do not modify anything beyond this point rem ----------------------------------------------------------------------------- rem rem Resolve the real path of the wrapper.exe rem For non NT systems, the _REALPATH and _WRAPPER_CONF values rem can be hard-coded below and the following test removed. rem if "%OS%"=="Windows_NT" goto nt echo This script only works with NT-based versions of Windows. goto :eof :nt rem Find the application home. rem if no path path specified do the default action IF not DEFINED _WRAPPER_DIR goto dir_undefined set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR:"=%" if not "%_WRAPPER_DIR:~-2,1%" == "\" set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR_QUOTED:"=%\" rem check if absolute path if "%_WRAPPER_DIR_QUOTED:~2,1%" == ":" goto absolute_path if "%_WRAPPER_DIR_QUOTED:~1,1%" == "\" goto absolute_path rem everythig else means relative path set _REALPATH="%~dp0%_WRAPPER_DIR_QUOTED:"=%" goto pathfound :dir_undefined rem Use a relative path to the wrapper %~dp0 is location of current script under NT set _REALPATH="%~dp0" goto pathfound :absolute_path rem Use an absolute path to the wrapper set _REALPATH="%_WRAPPER_DIR_QUOTED:"=%" :pathfound rem rem Decide on the specific Wrapper binary to use (See delta-pack) rem if "%PROCESSOR_ARCHITEW6432%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 :x86_32 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-32.exe" set _BIN_BITS="32" goto search :amd64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-64.exe" set _BIN_BITS="64" goto search :ia64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-ia-64.exe" set _BIN_BITS="64" goto search :search set _WRAPPER_EXE="%_WRAPPER_L_EXE:"=%" if exist %_WRAPPER_EXE% goto check_lic_bits set _WRAPPER_EXE="%_REALPATH:"=%%_WRAPPER_BASE%.exe" if exist %_WRAPPER_EXE% goto conf if %_BIN_BITS%=="64" goto x86_32 echo Unable to locate a Wrapper executable using any of the following names: echo %_WRAPPER_L_EXE% echo %_WRAPPER_EXE% goto preexitpause :check_lic_bits if %_BIN_BITS%=="64" ( set _CHECK_LIC_BITS=true ) rem rem Find the wrapper.conf rem :conf if [%_WRAPPER_CONF_OVERRIDE%]==[true] ( set _WRAPPER_CONF="%~f1" if not [%_WRAPPER_CONF%]==[""] ( shift goto callcommand ) ) set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT:"=%" rem The command should not be called inside a IF, else errorlevel would be 0 if not [%_CHECK_LIC_BITS%]==[true] goto callcommand %_WRAPPER_EXE% --request_delta_binary_bits %_WRAPPER_CONF% > nul 2>&1 if %errorlevel% equ 32 ( set _LIC32_OS64=true set _CHECK_LIC_BITS=false goto x86_32 ) rem rem Run the Wrapper rem :callcommand if not [%1]==[] ( echo Additional arguments are not allowed. goto preexitpause ) %_WRAPPER_EXE% -a %_WRAPPER_CONF% if not errorlevel 1 goto :eof rem Exit when there is a problem :preexitpause if %errorlevel% equ 0 ( set EXIT_CODE=1 ) else ( set EXIT_CODE=%errorlevel% ) if %_WRAPPER_TIMEOUT% gtr 0 ( timeout /t %_WRAPPER_TIMEOUT% ) else ( if %_WRAPPER_TIMEOUT% lss 0 ( pause ) ) EXIT /B %EXIT_CODE% wrapper_3.5.51_src/src/bin/QueryApp-NT.bat.in100644 0 0 12032 14333053647 16114 0ustar 0 0 @echo off setlocal rem rem Copyright (c) 1999, 2022 Tanuki Software, Ltd. rem http://www.tanukisoftware.com rem All rights reserved. rem rem This software is the proprietary information of Tanuki Software. rem You shall use it only in accordance with the terms of the rem license agreement you entered into with Tanuki Software. rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html rem rem Java Service Wrapper script - Query the current status of a service. rem rem ----------------------------------------------------------------------------- rem These settings can be modified to fit the needs of your application rem Optimized for use with version 3.5.51 of the Wrapper. rem The base name for the Wrapper binary. set _WRAPPER_BASE=wrapper rem The directory where the Wrapper binary (.exe) file is located. It can be rem either an absolute or a relative path. If the path contains any special rem characters, please make sure to quote the variable. set _WRAPPER_DIR= rem The name and location of the Wrapper configuration file. This will be used rem if the user does not specify a configuration file as the first parameter to rem this script. rem If a relative path is specified, please note that the location is based on the rem location of the Wrapper executable. set _WRAPPER_CONF_DEFAULT="../conf/%_WRAPPER_BASE%.conf" rem Makes it possible to override the Wrapper configuration file by specifying it rem as the first parameter. rem set _WRAPPER_CONF_OVERRIDE=true rem If there are any errors, the script will pause for a specific number of seconds rem or until the user presses a key. (0 not to wait, negative to wait forever). set _WRAPPER_TIMEOUT=-1 rem Note that it is only possible to pass parameters through to the JVM when rem installing the service, or when running in a console. rem Do not modify anything beyond this point rem ----------------------------------------------------------------------------- rem rem Resolve the real path of the wrapper.exe rem For non NT systems, the _REALPATH and _WRAPPER_CONF values rem can be hard-coded below and the following test removed. rem if "%OS%"=="Windows_NT" goto nt echo This script only works with NT-based versions of Windows. goto :eof :nt rem Find the application home. rem if no path path specified do the default action IF not DEFINED _WRAPPER_DIR goto dir_undefined set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR:"=%" if not "%_WRAPPER_DIR:~-2,1%" == "\" set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR_QUOTED:"=%\" rem check if absolute path if "%_WRAPPER_DIR_QUOTED:~2,1%" == ":" goto absolute_path if "%_WRAPPER_DIR_QUOTED:~1,1%" == "\" goto absolute_path rem everythig else means relative path set _REALPATH="%~dp0%_WRAPPER_DIR_QUOTED:"=%" goto pathfound :dir_undefined rem Use a relative path to the wrapper %~dp0 is location of current script under NT set _REALPATH="%~dp0" goto pathfound :absolute_path rem Use an absolute path to the wrapper set _REALPATH="%_WRAPPER_DIR_QUOTED:"=%" :pathfound rem rem Decide on the specific Wrapper binary to use (See delta-pack) rem if "%PROCESSOR_ARCHITEW6432%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 :x86_32 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-32.exe" set _BIN_BITS="32" goto search :amd64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-64.exe" set _BIN_BITS="64" goto search :ia64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-ia-64.exe" set _BIN_BITS="64" goto search :search set _WRAPPER_EXE="%_WRAPPER_L_EXE:"=%" if exist %_WRAPPER_EXE% goto check_lic_bits set _WRAPPER_EXE="%_REALPATH:"=%%_WRAPPER_BASE%.exe" if exist %_WRAPPER_EXE% goto conf if %_BIN_BITS%=="64" goto x86_32 echo Unable to locate a Wrapper executable using any of the following names: echo %_WRAPPER_L_EXE% echo %_WRAPPER_EXE% goto preexitpause :check_lic_bits if %_BIN_BITS%=="64" ( set _CHECK_LIC_BITS=true ) rem rem Find the wrapper.conf rem :conf if [%_WRAPPER_CONF_OVERRIDE%]==[true] ( set _WRAPPER_CONF="%~f1" if not [%_WRAPPER_CONF%]==[""] ( shift goto callcommand ) ) set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT:"=%" rem The command should not be called inside a IF, else errorlevel would be 0 if not [%_CHECK_LIC_BITS%]==[true] goto callcommand %_WRAPPER_EXE% --request_delta_binary_bits %_WRAPPER_CONF% > nul 2>&1 if %errorlevel% equ 32 ( set _LIC32_OS64=true set _CHECK_LIC_BITS=false goto x86_32 ) rem rem Run the Wrapper rem :callcommand if not [%1]==[] ( echo Additional arguments are not allowed. goto preexitpause ) %_WRAPPER_EXE% -q %_WRAPPER_CONF% if not errorlevel 1 goto :eof rem Exit when there is a problem :preexitpause if %errorlevel% equ 0 ( set EXIT_CODE=1 ) else ( set EXIT_CODE=%errorlevel% ) if %_WRAPPER_TIMEOUT% gtr 0 ( timeout /t %_WRAPPER_TIMEOUT% ) else ( if %_WRAPPER_TIMEOUT% lss 0 ( pause ) ) EXIT /B %EXIT_CODE% wrapper_3.5.51_src/src/bin/README.txt100644 0 0 3047 14333053650 14410 0ustar 0 0 ----------------------------------------------------------------------------- Java Service Wrapper Community Edition 3.5.51 Copyright (C) 1999-2022 Tanuki Software, Ltd. All Rights Reserved. https://wrapper.tanukisoftware.com ----------------------------------------------------------------------------- Depending on the security policy of your Windows installation, a popup window titled "Windows protected your PC" may appear when trying to execute the batch files located in the bin folder. The Wrapper binaries have been signed to verify that they are unmodified as published by Tanuki Software. But batch files by nature can't be signed in the same way. The batch files are provided to ease launching the Wrapper binaries, and it is perfectly safe to execute them if they were acquired from the official links on the Tanuki Software Wrapper website or SourceForge. Here is a simple workaround: 1. Right click on the bat file you want to execute and open the Properties window from the contextual menu. 2. At the bottom of the "General" tab, you should see a "Security" section with a "Unblock" checkbox or button. Click on the button or check the box. 3. Click OK. 4. You should now be able to execute the BAT file without warning. It is also possible to do the same on the downloaded zip file before extracting it. All of the extracted files will maintain the unblocked status. For further explanation regarding this issue, please refer to the following page of our website: https://wrapper.tanukisoftware.com/doc/english/howto-windows-install.html#zip wrapper_3.5.51_src/src/bin/ResumeApp-NT.bat.in100644 0 0 12017 14333053647 16252 0ustar 0 0 @echo off setlocal rem rem Copyright (c) 1999, 2022 Tanuki Software, Ltd. rem http://www.tanukisoftware.com rem All rights reserved. rem rem This software is the proprietary information of Tanuki Software. rem You shall use it only in accordance with the terms of the rem license agreement you entered into with Tanuki Software. rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html rem rem Java Service Wrapper script - Resume a paused NT service. rem rem ----------------------------------------------------------------------------- rem These settings can be modified to fit the needs of your application rem Optimized for use with version 3.5.51 of the Wrapper. rem The base name for the Wrapper binary. set _WRAPPER_BASE=wrapper rem The directory where the Wrapper binary (.exe) file is located. It can be rem either an absolute or a relative path. If the path contains any special rem characters, please make sure to quote the variable. set _WRAPPER_DIR= rem The name and location of the Wrapper configuration file. This will be used rem if the user does not specify a configuration file as the first parameter to rem this script. rem If a relative path is specified, please note that the location is based on the rem location of the Wrapper executable. set _WRAPPER_CONF_DEFAULT="../conf/%_WRAPPER_BASE%.conf" rem Makes it possible to override the Wrapper configuration file by specifying it rem as the first parameter. rem set _WRAPPER_CONF_OVERRIDE=true rem If there are any errors, the script will pause for a specific number of seconds rem or until the user presses a key. (0 not to wait, negative to wait forever). set _WRAPPER_TIMEOUT=-1 rem Note that it is only possible to pass parameters through to the JVM when rem installing the service, or when running in a console. rem Do not modify anything beyond this point rem ----------------------------------------------------------------------------- rem rem Resolve the real path of the wrapper.exe rem For non NT systems, the _REALPATH and _WRAPPER_CONF values rem can be hard-coded below and the following test removed. rem if "%OS%"=="Windows_NT" goto nt echo This script only works with NT-based versions of Windows. goto :eof :nt rem Find the application home. rem if no path path specified do the default action IF not DEFINED _WRAPPER_DIR goto dir_undefined set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR:"=%" if not "%_WRAPPER_DIR:~-2,1%" == "\" set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR_QUOTED:"=%\" rem check if absolute path if "%_WRAPPER_DIR_QUOTED:~2,1%" == ":" goto absolute_path if "%_WRAPPER_DIR_QUOTED:~1,1%" == "\" goto absolute_path rem everythig else means relative path set _REALPATH="%~dp0%_WRAPPER_DIR_QUOTED:"=%" goto pathfound :dir_undefined rem Use a relative path to the wrapper %~dp0 is location of current script under NT set _REALPATH="%~dp0" goto pathfound :absolute_path rem Use an absolute path to the wrapper set _REALPATH="%_WRAPPER_DIR_QUOTED:"=%" :pathfound rem rem Decide on the specific Wrapper binary to use (See delta-pack) rem if "%PROCESSOR_ARCHITEW6432%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 :x86_32 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-32.exe" set _BIN_BITS="32" goto search :amd64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-64.exe" set _BIN_BITS="64" goto search :ia64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-ia-64.exe" set _BIN_BITS="64" goto search :search set _WRAPPER_EXE="%_WRAPPER_L_EXE:"=%" if exist %_WRAPPER_EXE% goto check_lic_bits set _WRAPPER_EXE="%_REALPATH:"=%%_WRAPPER_BASE%.exe" if exist %_WRAPPER_EXE% goto conf if %_BIN_BITS%=="64" goto x86_32 echo Unable to locate a Wrapper executable using any of the following names: echo %_WRAPPER_L_EXE% echo %_WRAPPER_EXE% goto preexitpause :check_lic_bits if %_BIN_BITS%=="64" ( set _CHECK_LIC_BITS=true ) rem rem Find the wrapper.conf rem :conf if [%_WRAPPER_CONF_OVERRIDE%]==[true] ( set _WRAPPER_CONF="%~f1" if not [%_WRAPPER_CONF%]==[""] ( shift goto callcommand ) ) set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT:"=%" rem The command should not be called inside a IF, else errorlevel would be 0 if not [%_CHECK_LIC_BITS%]==[true] goto callcommand %_WRAPPER_EXE% --request_delta_binary_bits %_WRAPPER_CONF% > nul 2>&1 if %errorlevel% equ 32 ( set _LIC32_OS64=true set _CHECK_LIC_BITS=false goto x86_32 ) rem rem Run the Wrapper rem :callcommand if not [%1]==[] ( echo Additional arguments are not allowed. goto preexitpause ) %_WRAPPER_EXE% -e %_WRAPPER_CONF% if not errorlevel 1 goto :eof rem Exit when there is a problem :preexitpause if %errorlevel% equ 0 ( set EXIT_CODE=1 ) else ( set EXIT_CODE=%errorlevel% ) if %_WRAPPER_TIMEOUT% gtr 0 ( timeout /t %_WRAPPER_TIMEOUT% ) else ( if %_WRAPPER_TIMEOUT% lss 0 ( pause ) ) EXIT /B %EXIT_CODE% wrapper_3.5.51_src/src/bin/SetupApp.bat.in100644 0 0 11771 14333053647 15601 0ustar 0 0 @echo off setlocal rem rem Copyright (c) 1999, 2022 Tanuki Software, Ltd. rem http://www.tanukisoftware.com rem All rights reserved. rem rem This software is the proprietary information of Tanuki Software. rem You shall use it only in accordance with the terms of the rem license agreement you entered into with Tanuki Software. rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html rem rem Java Service Wrapper setup script. rem rem ----------------------------------------------------------------------------- rem These settings can be modified to fit the needs of your application rem Optimized for use with version 3.5.51 of the Wrapper. rem The base name for the Wrapper binary. set _WRAPPER_BASE=wrapper rem The directory where the Wrapper binary (.exe) file is located. It can be rem either an absolute or a relative path. If the path contains any special rem characters, please make sure to quote the variable. set _WRAPPER_DIR= rem The name and location of the Wrapper configuration file. This will be used rem if the user does not specify a configuration file as the first parameter to rem this script. rem If a relative path is specified, please note that the location is based on the rem location of the Wrapper executable. set _WRAPPER_CONF_DEFAULT="../conf/%_WRAPPER_BASE%.conf" rem Makes it possible to override the Wrapper configuration file by specifying it rem as the first parameter. rem set _WRAPPER_CONF_OVERRIDE=true rem If there are any errors, the script will pause for a specific number of seconds rem or until the user presses a key. (0 not to wait, negative to wait forever). set _WRAPPER_TIMEOUT=-1 rem Note that it is only possible to pass parameters through to the JVM when rem installing the service, or when running in a console. rem Do not modify anything beyond this point rem ----------------------------------------------------------------------------- rem rem Resolve the real path of the wrapper.exe rem For non NT systems, the _REALPATH and _WRAPPER_CONF values rem can be hard-coded below and the following test removed. rem if "%OS%"=="Windows_NT" goto nt echo This script only works with NT-based versions of Windows. goto :eof :nt rem Find the application home. rem if no path path specified do the default action IF not DEFINED _WRAPPER_DIR goto dir_undefined set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR:"=%" if not "%_WRAPPER_DIR:~-2,1%" == "\" set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR_QUOTED:"=%\" rem check if absolute path if "%_WRAPPER_DIR_QUOTED:~2,1%" == ":" goto absolute_path if "%_WRAPPER_DIR_QUOTED:~1,1%" == "\" goto absolute_path rem everythig else means relative path set _REALPATH="%~dp0%_WRAPPER_DIR_QUOTED:"=%" goto pathfound :dir_undefined rem Use a relative path to the wrapper %~dp0 is location of current script under NT set _REALPATH="%~dp0" goto pathfound :absolute_path rem Use an absolute path to the wrapper set _REALPATH="%_WRAPPER_DIR_QUOTED:"=%" :pathfound rem rem Decide on the specific Wrapper binary to use (See delta-pack) rem if "%PROCESSOR_ARCHITEW6432%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 :x86_32 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-32.exe" set _BIN_BITS="32" goto search :amd64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-64.exe" set _BIN_BITS="64" goto search :ia64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-ia-64.exe" set _BIN_BITS="64" goto search :search set _WRAPPER_EXE="%_WRAPPER_L_EXE:"=%" if exist %_WRAPPER_EXE% goto check_lic_bits set _WRAPPER_EXE="%_REALPATH:"=%%_WRAPPER_BASE%.exe" if exist %_WRAPPER_EXE% goto conf if %_BIN_BITS%=="64" goto x86_32 echo Unable to locate a Wrapper executable using any of the following names: echo %_WRAPPER_L_EXE% echo %_WRAPPER_EXE% goto preexitpause :check_lic_bits if %_BIN_BITS%=="64" ( set _CHECK_LIC_BITS=true ) rem rem Find the wrapper.conf rem :conf if [%_WRAPPER_CONF_OVERRIDE%]==[true] ( set _WRAPPER_CONF="%~f1" if not [%_WRAPPER_CONF%]==[""] ( shift goto callcommand ) ) set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT:"=%" rem The command should not be called inside a IF, else errorlevel would be 0 if not [%_CHECK_LIC_BITS%]==[true] goto callcommand %_WRAPPER_EXE% --request_delta_binary_bits %_WRAPPER_CONF% > nul 2>&1 if %errorlevel% equ 32 ( set _LIC32_OS64=true set _CHECK_LIC_BITS=false goto x86_32 ) rem rem Run the Wrapper rem :callcommand if not [%1]==[] ( echo Additional arguments are not allowed. goto preexitpause ) %_WRAPPER_EXE% -su %_WRAPPER_CONF% if not errorlevel 1 goto :eof rem Exit when there is a problem :preexitpause if %errorlevel% equ 0 ( set EXIT_CODE=1 ) else ( set EXIT_CODE=%errorlevel% ) if %_WRAPPER_TIMEOUT% gtr 0 ( timeout /t %_WRAPPER_TIMEOUT% ) else ( if %_WRAPPER_TIMEOUT% lss 0 ( pause ) ) EXIT /B %EXIT_CODE% wrapper_3.5.51_src/src/bin/StartApp-NT.bat.in100644 0 0 12013 14333053647 16103 0ustar 0 0 @echo off setlocal rem rem Copyright (c) 1999, 2022 Tanuki Software, Ltd. rem http://www.tanukisoftware.com rem All rights reserved. rem rem This software is the proprietary information of Tanuki Software. rem You shall use it only in accordance with the terms of the rem license agreement you entered into with Tanuki Software. rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html rem rem Java Service Wrapper script - Start as an NT service. rem rem ----------------------------------------------------------------------------- rem These settings can be modified to fit the needs of your application rem Optimized for use with version 3.5.51 of the Wrapper. rem The base name for the Wrapper binary. set _WRAPPER_BASE=wrapper rem The directory where the Wrapper binary (.exe) file is located. It can be rem either an absolute or a relative path. If the path contains any special rem characters, please make sure to quote the variable. set _WRAPPER_DIR= rem The name and location of the Wrapper configuration file. This will be used rem if the user does not specify a configuration file as the first parameter to rem this script. rem If a relative path is specified, please note that the location is based on the rem location of the Wrapper executable. set _WRAPPER_CONF_DEFAULT="../conf/%_WRAPPER_BASE%.conf" rem Makes it possible to override the Wrapper configuration file by specifying it rem as the first parameter. rem set _WRAPPER_CONF_OVERRIDE=true rem If there are any errors, the script will pause for a specific number of seconds rem or until the user presses a key. (0 not to wait, negative to wait forever). set _WRAPPER_TIMEOUT=-1 rem Note that it is only possible to pass parameters through to the JVM when rem installing the service, or when running in a console. rem Do not modify anything beyond this point rem ----------------------------------------------------------------------------- rem rem Resolve the real path of the wrapper.exe rem For non NT systems, the _REALPATH and _WRAPPER_CONF values rem can be hard-coded below and the following test removed. rem if "%OS%"=="Windows_NT" goto nt echo This script only works with NT-based versions of Windows. goto :eof :nt rem Find the application home. rem if no path path specified do the default action IF not DEFINED _WRAPPER_DIR goto dir_undefined set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR:"=%" if not "%_WRAPPER_DIR:~-2,1%" == "\" set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR_QUOTED:"=%\" rem check if absolute path if "%_WRAPPER_DIR_QUOTED:~2,1%" == ":" goto absolute_path if "%_WRAPPER_DIR_QUOTED:~1,1%" == "\" goto absolute_path rem everythig else means relative path set _REALPATH="%~dp0%_WRAPPER_DIR_QUOTED:"=%" goto pathfound :dir_undefined rem Use a relative path to the wrapper %~dp0 is location of current script under NT set _REALPATH="%~dp0" goto pathfound :absolute_path rem Use an absolute path to the wrapper set _REALPATH="%_WRAPPER_DIR_QUOTED:"=%" :pathfound rem rem Decide on the specific Wrapper binary to use (See delta-pack) rem if "%PROCESSOR_ARCHITEW6432%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 :x86_32 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-32.exe" set _BIN_BITS="32" goto search :amd64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-64.exe" set _BIN_BITS="64" goto search :ia64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-ia-64.exe" set _BIN_BITS="64" goto search :search set _WRAPPER_EXE="%_WRAPPER_L_EXE:"=%" if exist %_WRAPPER_EXE% goto check_lic_bits set _WRAPPER_EXE="%_REALPATH:"=%%_WRAPPER_BASE%.exe" if exist %_WRAPPER_EXE% goto conf if %_BIN_BITS%=="64" goto x86_32 echo Unable to locate a Wrapper executable using any of the following names: echo %_WRAPPER_L_EXE% echo %_WRAPPER_EXE% goto preexitpause :check_lic_bits if %_BIN_BITS%=="64" ( set _CHECK_LIC_BITS=true ) rem rem Find the wrapper.conf rem :conf if [%_WRAPPER_CONF_OVERRIDE%]==[true] ( set _WRAPPER_CONF="%~f1" if not [%_WRAPPER_CONF%]==[""] ( shift goto callcommand ) ) set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT:"=%" rem The command should not be called inside a IF, else errorlevel would be 0 if not [%_CHECK_LIC_BITS%]==[true] goto callcommand %_WRAPPER_EXE% --request_delta_binary_bits %_WRAPPER_CONF% > nul 2>&1 if %errorlevel% equ 32 ( set _LIC32_OS64=true set _CHECK_LIC_BITS=false goto x86_32 ) rem rem Run the Wrapper rem :callcommand if not [%1]==[] ( echo Additional arguments are not allowed. goto preexitpause ) %_WRAPPER_EXE% -t %_WRAPPER_CONF% if not errorlevel 1 goto :eof rem Exit when there is a problem :preexitpause if %errorlevel% equ 0 ( set EXIT_CODE=1 ) else ( set EXIT_CODE=%errorlevel% ) if %_WRAPPER_TIMEOUT% gtr 0 ( timeout /t %_WRAPPER_TIMEOUT% ) else ( if %_WRAPPER_TIMEOUT% lss 0 ( pause ) ) EXIT /B %EXIT_CODE% wrapper_3.5.51_src/src/bin/StopApp-NT.bat.in100644 0 0 12016 14333053647 15736 0ustar 0 0 @echo off setlocal rem rem Copyright (c) 1999, 2022 Tanuki Software, Ltd. rem http://www.tanukisoftware.com rem All rights reserved. rem rem This software is the proprietary information of Tanuki Software. rem You shall use it only in accordance with the terms of the rem license agreement you entered into with Tanuki Software. rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html rem rem Java Service Wrapper script - Stop a started NT service. rem rem ----------------------------------------------------------------------------- rem These settings can be modified to fit the needs of your application rem Optimized for use with version 3.5.51 of the Wrapper. rem The base name for the Wrapper binary. set _WRAPPER_BASE=wrapper rem The directory where the Wrapper binary (.exe) file is located. It can be rem either an absolute or a relative path. If the path contains any special rem characters, please make sure to quote the variable. set _WRAPPER_DIR= rem The name and location of the Wrapper configuration file. This will be used rem if the user does not specify a configuration file as the first parameter to rem this script. rem If a relative path is specified, please note that the location is based on the rem location of the Wrapper executable. set _WRAPPER_CONF_DEFAULT="../conf/%_WRAPPER_BASE%.conf" rem Makes it possible to override the Wrapper configuration file by specifying it rem as the first parameter. rem set _WRAPPER_CONF_OVERRIDE=true rem If there are any errors, the script will pause for a specific number of seconds rem or until the user presses a key. (0 not to wait, negative to wait forever). set _WRAPPER_TIMEOUT=-1 rem Note that it is only possible to pass parameters through to the JVM when rem installing the service, or when running in a console. rem Do not modify anything beyond this point rem ----------------------------------------------------------------------------- rem rem Resolve the real path of the wrapper.exe rem For non NT systems, the _REALPATH and _WRAPPER_CONF values rem can be hard-coded below and the following test removed. rem if "%OS%"=="Windows_NT" goto nt echo This script only works with NT-based versions of Windows. goto :eof :nt rem Find the application home. rem if no path path specified do the default action IF not DEFINED _WRAPPER_DIR goto dir_undefined set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR:"=%" if not "%_WRAPPER_DIR:~-2,1%" == "\" set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR_QUOTED:"=%\" rem check if absolute path if "%_WRAPPER_DIR_QUOTED:~2,1%" == ":" goto absolute_path if "%_WRAPPER_DIR_QUOTED:~1,1%" == "\" goto absolute_path rem everythig else means relative path set _REALPATH="%~dp0%_WRAPPER_DIR_QUOTED:"=%" goto pathfound :dir_undefined rem Use a relative path to the wrapper %~dp0 is location of current script under NT set _REALPATH="%~dp0" goto pathfound :absolute_path rem Use an absolute path to the wrapper set _REALPATH="%_WRAPPER_DIR_QUOTED:"=%" :pathfound rem rem Decide on the specific Wrapper binary to use (See delta-pack) rem if "%PROCESSOR_ARCHITEW6432%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 :x86_32 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-32.exe" set _BIN_BITS="32" goto search :amd64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-64.exe" set _BIN_BITS="64" goto search :ia64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-ia-64.exe" set _BIN_BITS="64" goto search :search set _WRAPPER_EXE="%_WRAPPER_L_EXE:"=%" if exist %_WRAPPER_EXE% goto check_lic_bits set _WRAPPER_EXE="%_REALPATH:"=%%_WRAPPER_BASE%.exe" if exist %_WRAPPER_EXE% goto conf if %_BIN_BITS%=="64" goto x86_32 echo Unable to locate a Wrapper executable using any of the following names: echo %_WRAPPER_L_EXE% echo %_WRAPPER_EXE% goto preexitpause :check_lic_bits if %_BIN_BITS%=="64" ( set _CHECK_LIC_BITS=true ) rem rem Find the wrapper.conf rem :conf if [%_WRAPPER_CONF_OVERRIDE%]==[true] ( set _WRAPPER_CONF="%~f1" if not [%_WRAPPER_CONF%]==[""] ( shift goto callcommand ) ) set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT:"=%" rem The command should not be called inside a IF, else errorlevel would be 0 if not [%_CHECK_LIC_BITS%]==[true] goto callcommand %_WRAPPER_EXE% --request_delta_binary_bits %_WRAPPER_CONF% > nul 2>&1 if %errorlevel% equ 32 ( set _LIC32_OS64=true set _CHECK_LIC_BITS=false goto x86_32 ) rem rem Run the Wrapper rem :callcommand if not [%1]==[] ( echo Additional arguments are not allowed. goto preexitpause ) %_WRAPPER_EXE% -p %_WRAPPER_CONF% if not errorlevel 1 goto :eof rem Exit when there is a problem :preexitpause if %errorlevel% equ 0 ( set EXIT_CODE=1 ) else ( set EXIT_CODE=%errorlevel% ) if %_WRAPPER_TIMEOUT% gtr 0 ( timeout /t %_WRAPPER_TIMEOUT% ) else ( if %_WRAPPER_TIMEOUT% lss 0 ( pause ) ) EXIT /B %EXIT_CODE% wrapper_3.5.51_src/src/bin/TeardownApp.bat.in100644 0 0 11774 14333053647 16267 0ustar 0 0 @echo off setlocal rem rem Copyright (c) 1999, 2022 Tanuki Software, Ltd. rem http://www.tanukisoftware.com rem All rights reserved. rem rem This software is the proprietary information of Tanuki Software. rem You shall use it only in accordance with the terms of the rem license agreement you entered into with Tanuki Software. rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html rem rem Java Service Wrapper teardown script. rem rem ----------------------------------------------------------------------------- rem These settings can be modified to fit the needs of your application rem Optimized for use with version 3.5.51 of the Wrapper. rem The base name for the Wrapper binary. set _WRAPPER_BASE=wrapper rem The directory where the Wrapper binary (.exe) file is located. It can be rem either an absolute or a relative path. If the path contains any special rem characters, please make sure to quote the variable. set _WRAPPER_DIR= rem The name and location of the Wrapper configuration file. This will be used rem if the user does not specify a configuration file as the first parameter to rem this script. rem If a relative path is specified, please note that the location is based on the rem location of the Wrapper executable. set _WRAPPER_CONF_DEFAULT="../conf/%_WRAPPER_BASE%.conf" rem Makes it possible to override the Wrapper configuration file by specifying it rem as the first parameter. rem set _WRAPPER_CONF_OVERRIDE=true rem If there are any errors, the script will pause for a specific number of seconds rem or until the user presses a key. (0 not to wait, negative to wait forever). set _WRAPPER_TIMEOUT=-1 rem Note that it is only possible to pass parameters through to the JVM when rem installing the service, or when running in a console. rem Do not modify anything beyond this point rem ----------------------------------------------------------------------------- rem rem Resolve the real path of the wrapper.exe rem For non NT systems, the _REALPATH and _WRAPPER_CONF values rem can be hard-coded below and the following test removed. rem if "%OS%"=="Windows_NT" goto nt echo This script only works with NT-based versions of Windows. goto :eof :nt rem Find the application home. rem if no path path specified do the default action IF not DEFINED _WRAPPER_DIR goto dir_undefined set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR:"=%" if not "%_WRAPPER_DIR:~-2,1%" == "\" set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR_QUOTED:"=%\" rem check if absolute path if "%_WRAPPER_DIR_QUOTED:~2,1%" == ":" goto absolute_path if "%_WRAPPER_DIR_QUOTED:~1,1%" == "\" goto absolute_path rem everythig else means relative path set _REALPATH="%~dp0%_WRAPPER_DIR_QUOTED:"=%" goto pathfound :dir_undefined rem Use a relative path to the wrapper %~dp0 is location of current script under NT set _REALPATH="%~dp0" goto pathfound :absolute_path rem Use an absolute path to the wrapper set _REALPATH="%_WRAPPER_DIR_QUOTED:"=%" :pathfound rem rem Decide on the specific Wrapper binary to use (See delta-pack) rem if "%PROCESSOR_ARCHITEW6432%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 :x86_32 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-32.exe" set _BIN_BITS="32" goto search :amd64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-64.exe" set _BIN_BITS="64" goto search :ia64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-ia-64.exe" set _BIN_BITS="64" goto search :search set _WRAPPER_EXE="%_WRAPPER_L_EXE:"=%" if exist %_WRAPPER_EXE% goto check_lic_bits set _WRAPPER_EXE="%_REALPATH:"=%%_WRAPPER_BASE%.exe" if exist %_WRAPPER_EXE% goto conf if %_BIN_BITS%=="64" goto x86_32 echo Unable to locate a Wrapper executable using any of the following names: echo %_WRAPPER_L_EXE% echo %_WRAPPER_EXE% goto preexitpause :check_lic_bits if %_BIN_BITS%=="64" ( set _CHECK_LIC_BITS=true ) rem rem Find the wrapper.conf rem :conf if [%_WRAPPER_CONF_OVERRIDE%]==[true] ( set _WRAPPER_CONF="%~f1" if not [%_WRAPPER_CONF%]==[""] ( shift goto callcommand ) ) set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT:"=%" rem The command should not be called inside a IF, else errorlevel would be 0 if not [%_CHECK_LIC_BITS%]==[true] goto callcommand %_WRAPPER_EXE% --request_delta_binary_bits %_WRAPPER_CONF% > nul 2>&1 if %errorlevel% equ 32 ( set _LIC32_OS64=true set _CHECK_LIC_BITS=false goto x86_32 ) rem rem Run the Wrapper rem :callcommand if not [%1]==[] ( echo Additional arguments are not allowed. goto preexitpause ) %_WRAPPER_EXE% -td %_WRAPPER_CONF% if not errorlevel 1 goto :eof rem Exit when there is a problem :preexitpause if %errorlevel% equ 0 ( set EXIT_CODE=1 ) else ( set EXIT_CODE=%errorlevel% ) if %_WRAPPER_TIMEOUT% gtr 0 ( timeout /t %_WRAPPER_TIMEOUT% ) else ( if %_WRAPPER_TIMEOUT% lss 0 ( pause ) ) EXIT /B %EXIT_CODE% wrapper_3.5.51_src/src/bin/UninstallApp-NT.bat.in100644 0 0 12023 14333053647 16760 0ustar 0 0 @echo off setlocal rem rem Copyright (c) 1999, 2022 Tanuki Software, Ltd. rem http://www.tanukisoftware.com rem All rights reserved. rem rem This software is the proprietary information of Tanuki Software. rem You shall use it only in accordance with the terms of the rem license agreement you entered into with Tanuki Software. rem http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html rem rem Java Service Wrapper script - Uninstall/remove an NT service. rem rem ----------------------------------------------------------------------------- rem These settings can be modified to fit the needs of your application rem Optimized for use with version 3.5.51 of the Wrapper. rem The base name for the Wrapper binary. set _WRAPPER_BASE=wrapper rem The directory where the Wrapper binary (.exe) file is located. It can be rem either an absolute or a relative path. If the path contains any special rem characters, please make sure to quote the variable. set _WRAPPER_DIR= rem The name and location of the Wrapper configuration file. This will be used rem if the user does not specify a configuration file as the first parameter to rem this script. rem If a relative path is specified, please note that the location is based on the rem location of the Wrapper executable. set _WRAPPER_CONF_DEFAULT="../conf/%_WRAPPER_BASE%.conf" rem Makes it possible to override the Wrapper configuration file by specifying it rem as the first parameter. rem set _WRAPPER_CONF_OVERRIDE=true rem If there are any errors, the script will pause for a specific number of seconds rem or until the user presses a key. (0 not to wait, negative to wait forever). set _WRAPPER_TIMEOUT=-1 rem Note that it is only possible to pass parameters through to the JVM when rem installing the service, or when running in a console. rem Do not modify anything beyond this point rem ----------------------------------------------------------------------------- rem rem Resolve the real path of the wrapper.exe rem For non NT systems, the _REALPATH and _WRAPPER_CONF values rem can be hard-coded below and the following test removed. rem if "%OS%"=="Windows_NT" goto nt echo This script only works with NT-based versions of Windows. goto :eof :nt rem Find the application home. rem if no path path specified do the default action IF not DEFINED _WRAPPER_DIR goto dir_undefined set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR:"=%" if not "%_WRAPPER_DIR:~-2,1%" == "\" set _WRAPPER_DIR_QUOTED="%_WRAPPER_DIR_QUOTED:"=%\" rem check if absolute path if "%_WRAPPER_DIR_QUOTED:~2,1%" == ":" goto absolute_path if "%_WRAPPER_DIR_QUOTED:~1,1%" == "\" goto absolute_path rem everythig else means relative path set _REALPATH="%~dp0%_WRAPPER_DIR_QUOTED:"=%" goto pathfound :dir_undefined rem Use a relative path to the wrapper %~dp0 is location of current script under NT set _REALPATH="%~dp0" goto pathfound :absolute_path rem Use an absolute path to the wrapper set _REALPATH="%_WRAPPER_DIR_QUOTED:"=%" :pathfound rem rem Decide on the specific Wrapper binary to use (See delta-pack) rem if "%PROCESSOR_ARCHITEW6432%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="AMD64" goto amd64 if "%PROCESSOR_ARCHITECTURE%"=="IA64" goto ia64 :x86_32 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-32.exe" set _BIN_BITS="32" goto search :amd64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-x86-64.exe" set _BIN_BITS="64" goto search :ia64 set _WRAPPER_L_EXE="%_REALPATH:"=%%_WRAPPER_BASE%-windows-ia-64.exe" set _BIN_BITS="64" goto search :search set _WRAPPER_EXE="%_WRAPPER_L_EXE:"=%" if exist %_WRAPPER_EXE% goto check_lic_bits set _WRAPPER_EXE="%_REALPATH:"=%%_WRAPPER_BASE%.exe" if exist %_WRAPPER_EXE% goto conf if %_BIN_BITS%=="64" goto x86_32 echo Unable to locate a Wrapper executable using any of the following names: echo %_WRAPPER_L_EXE% echo %_WRAPPER_EXE% goto preexitpause :check_lic_bits if %_BIN_BITS%=="64" ( set _CHECK_LIC_BITS=true ) rem rem Find the wrapper.conf rem :conf if [%_WRAPPER_CONF_OVERRIDE%]==[true] ( set _WRAPPER_CONF="%~f1" if not [%_WRAPPER_CONF%]==[""] ( shift goto callcommand ) ) set _WRAPPER_CONF="%_WRAPPER_CONF_DEFAULT:"=%" rem The command should not be called inside a IF, else errorlevel would be 0 if not [%_CHECK_LIC_BITS%]==[true] goto callcommand %_WRAPPER_EXE% --request_delta_binary_bits %_WRAPPER_CONF% > nul 2>&1 if %errorlevel% equ 32 ( set _LIC32_OS64=true set _CHECK_LIC_BITS=false goto x86_32 ) rem rem Run the Wrapper rem :callcommand if not [%1]==[] ( echo Additional arguments are not allowed. goto preexitpause ) %_WRAPPER_EXE% -r %_WRAPPER_CONF% if not errorlevel 1 goto :eof rem Exit when there is a problem :preexitpause if %errorlevel% equ 0 ( set EXIT_CODE=1 ) else ( set EXIT_CODE=%errorlevel% ) if %_WRAPPER_TIMEOUT% gtr 0 ( timeout /t %_WRAPPER_TIMEOUT% ) else ( if %_WRAPPER_TIMEOUT% lss 0 ( pause ) ) EXIT /B %EXIT_CODE% wrapper_3.5.51_src/src/c/MSG00001.bin100644 0 0 300 14333053647 14040 0ustar 0 0  dd jvm1 jvm2 jvm3 jvm4 jvm5 jvm6 jvm7 jvm8 jvm9 jvmxx wrapper wrapperp %2 wrapper_3.5.51_src/src/c/Makefile-aix-ppc-32.make100644 0 0 2766 14333053647 16536 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html COMPILE = gcc -O3 -fPIC --pedantic -DAIX -D_FILE_OFFSET_BITS=64 -liconv -DUNICODE -D_UNICODE INCLUDE=$(JAVA_HOME)/include DEFS = -I$(INCLUDE) -I$(INCLUDE)/aix wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_SOURCE = wrapper_i18n.c wrapperjni_unix.c wrapperinfo.c wrapperjni.c loggerjni.c BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.so clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.so init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) -pthread $(wrapper_SOURCE) -lnsl -lm -o $(BIN)/wrapper libwrapper.so: $(libwrapper_so_SOURCE) ${COMPILE} $(DEFS) -shared -pthread $(libwrapper_so_SOURCE) -o $(LIB)/libwrapper.so %.o: %.c @echo '$(COMPILE) -c $<'; \ $(COMPILE) $(DEFS) -Wp,-MD,.deps/$(*F).pp -c $< @-cp .deps/$(*F).pp .deps/$(*F).P; \ tr ' ' '\012' < .deps/$(*F).pp \ | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \ >> .deps/$(*F).P; \ rm .deps/$(*F).pp wrapper_3.5.51_src/src/c/Makefile-aix-ppc-64.make100644 0 0 3006 14333053647 16527 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html COMPILE = gcc -O3 -fPIC --pedantic -DAIX -DJSW64 -maix64 -D_FILE_OFFSET_BITS=64 -liconv -DUNICODE -D_UNICODE INCLUDE=$(JAVA_HOME)/include DEFS = -I$(INCLUDE) -I$(INCLUDE)/aix wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_SOURCE = wrapper_i18n.c wrapperjni_unix.c wrapperinfo.c wrapperjni.c loggerjni.c BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.so clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.so init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) -pthread $(wrapper_SOURCE) -lnsl -lm -o $(BIN)/wrapper libwrapper.so: $(libwrapper_so_SOURCE) ${COMPILE} $(DEFS) -shared -pthread $(libwrapper_so_SOURCE) -o $(LIB)/libwrapper.so %.o: %.c @echo '$(COMPILE) -c $<'; \ $(COMPILE) $(DEFS) -Wp,-MD,.deps/$(*F).pp -c $< @-cp .deps/$(*F).pp .deps/$(*F).P; \ tr ' ' '\012' < .deps/$(*F).pp \ | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \ >> .deps/$(*F).P; \ rm .deps/$(*F).pp wrapper_3.5.51_src/src/c/Makefile-freebsd-arm-64.gmake100644 0 0 2471 14333053647 17531 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html CC = gcc -Wall -pedantic -DFREEBSD -DJSW64 -fPIC -I/usr/local/include -L/usr/local/lib -DUNICODE -D_UNICODE INCLUDE=$(JAVA_HOME)/include CFLAGS = -I$(INCLUDE) -I$(INCLUDE)/freebsd wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_OBJECTS = wrapper_i18n.o wrapperjni_unix.o wrapperinfo.o wrapperjni.o loggerjni.o BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.so clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.so init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(CC) $(wrapper_SOURCE) -lm -lcompat -rdynamic -lc -pthread -o $(BIN)/wrapper libwrapper.so: $(libwrapper_so_OBJECTS) $(CC) -shared -rdynamic -lc -pthread $(libwrapper_so_OBJECTS) -o $(LIB)/libwrapper.so #%.o: %.c # $(COMPILE) -c $(DEFS) $< wrapper_3.5.51_src/src/c/Makefile-freebsd-x86-32.gmake100644 0 0 2461 14333053647 17371 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html CC = gcc -Wall -pedantic -DFREEBSD -fPIC -I/usr/local/include -L/usr/local/lib -DUNICODE -D_UNICODE INCLUDE=$(JAVA_HOME)/include CFLAGS = -I$(INCLUDE) -I$(INCLUDE)/freebsd wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_OBJECTS = wrapper_i18n.o wrapperjni_unix.o wrapperinfo.o wrapperjni.o loggerjni.o BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.so clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.so init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(CC) $(wrapper_SOURCE) -lm -lcompat -rdynamic -lc -pthread -o $(BIN)/wrapper libwrapper.so: $(libwrapper_so_OBJECTS) $(CC) -shared -rdynamic -lc -pthread $(libwrapper_so_OBJECTS) -o $(LIB)/libwrapper.so #%.o: %.c # $(COMPILE) -c $(DEFS) $< wrapper_3.5.51_src/src/c/Makefile-freebsd-x86-64.gmake100644 0 0 2471 14333053647 17377 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html CC = gcc -Wall -pedantic -DFREEBSD -DJSW64 -fPIC -I/usr/local/include -L/usr/local/lib -DUNICODE -D_UNICODE INCLUDE=$(JAVA_HOME)/include CFLAGS = -I$(INCLUDE) -I$(INCLUDE)/freebsd wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_OBJECTS = wrapper_i18n.o wrapperjni_unix.o wrapperinfo.o wrapperjni.o loggerjni.o BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.so clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.so init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(CC) $(wrapper_SOURCE) -lm -lcompat -rdynamic -lc -pthread -o $(BIN)/wrapper libwrapper.so: $(libwrapper_so_OBJECTS) $(CC) -shared -rdynamic -lc -pthread $(libwrapper_so_OBJECTS) -o $(LIB)/libwrapper.so #%.o: %.c # $(COMPILE) -c $(DEFS) $< wrapper_3.5.51_src/src/c/Makefile-hpux-ia-32.make100644 0 0 2451 14333053647 16537 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html COMPILE = gcc -O3 -fpic -Wall --pedantic -milp32 -DHPUX_IA -DHPUX -D_INCLUDE__STDC_A1_SOURCE -DUNICODE -D_UNICODE INCLUDE=$(JAVA_HOME)/include DEFS = -I$(INCLUDE) -I$(INCLUDE)/hp-ux wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_SOURCE = wrapper_i18n.c wrapperjni_unix.c wrapperinfo.c wrapperjni.c loggerjni.c BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.so clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.so init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) $(wrapper_SOURCE) -lm -pthread -o $(BIN)/wrapper libwrapper.so: $(libwrapper_so_SOURCE) ${COMPILE} ${DEFS} $(libwrapper_so_SOURCE) -shared -lm -pthread -o $(LIB)/libwrapper.so %.o: %.c ${COMPILE} -c ${DEFS} $< wrapper_3.5.51_src/src/c/Makefile-hpux-ia-64.make100644 0 0 2457 14333053647 16552 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html COMPILE = gcc -O3 -fpic -Wall --pedantic -mlp64 -DHPUX_IA -DHPUX -DJSW64 -D_INCLUDE__STDC_A1_SOURCE -DUNICODE -D_UNICODE INCLUDE=$(JAVA_HOME)/include DEFS = -I$(INCLUDE) -I$(INCLUDE)/hp-ux wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_SOURCE = wrapper_i18n.c wrapperjni_unix.c wrapperinfo.c wrapperjni.c loggerjni.c BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.so clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.so init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) $(wrapper_SOURCE) -lm -pthread -o $(BIN)/wrapper libwrapper.so: $(libwrapper_so_SOURCE) ${COMPILE} ${DEFS} $(libwrapper_so_SOURCE) -shared -lm -pthread -o $(LIB)/libwrapper.so %.o: %.c ${COMPILE} -c ${DEFS} $< wrapper_3.5.51_src/src/c/Makefile-hpux-parisc-32.make100644 0 0 2435 14333053647 17431 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html COMPILE = cc -DHPUX -Ae -D_INCLUDE__STDC_A1_SOURCE +Z +DAportable +DS1.1 -D_REENTRANT -DUNICODE -D_UNICODE INCLUDE=$(JAVA_HOME)/include DEFS = -I$(INCLUDE) -I$(INCLUDE)/hp-ux wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_sl_SOURCE = wrapper_i18n.c wrapperjni_unix.c wrapperinfo.c wrapperjni.c loggerjni.c BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.sl clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.sl init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) $(wrapper_SOURCE) -lm -lpthread -o $(BIN)/wrapper libwrapper.sl: $(libwrapper_sl_SOURCE) ${COMPILE} ${DEFS} $(libwrapper_sl_SOURCE) -b -lm -lpthread -o $(LIB)/libwrapper.sl %.o: %.c ${COMPILE} -c ${DEFS} $< wrapper_3.5.51_src/src/c/Makefile-hpux-parisc-64.make100644 0 0 2436 14333053647 17437 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html COMPILE = cc +z +DD64 -D_INCLUDE__STDC_A1_SOURCE -O3 -Wall -DHPUX -DJSW64 -D_REENTRANT -DUNICODE -D_UNICODE INCLUDE=$(JAVA_HOME)/include DEFS = -I$(INCLUDE) -I$(INCLUDE)/hp-ux wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_sl_SOURCE = wrapper_i18n.c wrapperjni_unix.c wrapperinfo.c wrapperjni.c loggerjni.c BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.sl clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.sl init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) $(wrapper_SOURCE) -lm -lpthread -o $(BIN)/wrapper libwrapper.sl: $(libwrapper_sl_SOURCE) ${COMPILE} ${DEFS} $(libwrapper_sl_SOURCE) -b -lm -lpthread -o $(LIB)/libwrapper.sl %.o: %.c ${COMPILE} -c ${DEFS} $< wrapper_3.5.51_src/src/c/Makefile-linux-arm-64.make100644 0 0 3134 14333053647 17104 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html COMPILE = gcc -O3 -fPIC -Wall --pedantic -DLINUX -D_FORTIFY_SOURCE=2 -DJSW64 -D_FILE_OFFSET_BITS=64 -fpic -D_GNU_SOURCE -DUNICODE -D_UNICODE WRAPPER_LINK_OPTS = -Wl,-z,relro,-z,now INCLUDE=$(JAVA_HOME)/include DEFS = -I$(INCLUDE) -I$(INCLUDE)/linux wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_OBJECTS = wrapper_i18n.o wrapperjni_unix.o wrapperinfo.o wrapperjni.o loggerjni.o BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.so clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.so init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) $(WRAPPER_LINK_OPTS) -pthread $(wrapper_SOURCE) -lm -o $(BIN)/wrapper libwrapper.so: $(libwrapper_so_OBJECTS) ${COMPILE} -shared $(libwrapper_so_OBJECTS) -o $(LIB)/libwrapper.so %.o: %.c @echo '$(COMPILE) -c $<'; \ $(COMPILE) -pthread $(DEFS) -Wp,-MD,.deps/$(*F).pp -c $< @-cp .deps/$(*F).pp .deps/$(*F).P; \ tr ' ' '\012' < .deps/$(*F).pp \ | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \ >> .deps/$(*F).P; \ rm .deps/$(*F).pp wrapper_3.5.51_src/src/c/Makefile-linux-armel-32.make100644 0 0 3124 14333053647 17417 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html COMPILE = gcc -O3 -fPIC -Wall --pedantic -DLINUX -D_FORTIFY_SOURCE=2 -D_FILE_OFFSET_BITS=64 -fpic -D_GNU_SOURCE -DUNICODE -D_UNICODE WRAPPER_LINK_OPTS = -Wl,-z,relro,-z,now INCLUDE=$(JAVA_HOME)/include DEFS = -I$(INCLUDE) -I$(INCLUDE)/linux wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_OBJECTS = wrapper_i18n.o wrapperjni_unix.o wrapperinfo.o wrapperjni.o loggerjni.o BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.so clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.so init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) $(WRAPPER_LINK_OPTS) -pthread $(wrapper_SOURCE) -lm -o $(BIN)/wrapper libwrapper.so: $(libwrapper_so_OBJECTS) ${COMPILE} -shared $(libwrapper_so_OBJECTS) -o $(LIB)/libwrapper.so %.o: %.c @echo '$(COMPILE) -c $<'; \ $(COMPILE) -pthread $(DEFS) -Wp,-MD,.deps/$(*F).pp -c $< @-cp .deps/$(*F).pp .deps/$(*F).P; \ tr ' ' '\012' < .deps/$(*F).pp \ | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \ >> .deps/$(*F).P; \ rm .deps/$(*F).pp wrapper_3.5.51_src/src/c/Makefile-linux-armhf-32.make100644 0 0 3124 14333053647 17414 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html COMPILE = gcc -O3 -fPIC -Wall --pedantic -DLINUX -D_FORTIFY_SOURCE=2 -D_FILE_OFFSET_BITS=64 -fpic -D_GNU_SOURCE -DUNICODE -D_UNICODE WRAPPER_LINK_OPTS = -Wl,-z,relro,-z,now INCLUDE=$(JAVA_HOME)/include DEFS = -I$(INCLUDE) -I$(INCLUDE)/linux wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_OBJECTS = wrapper_i18n.o wrapperjni_unix.o wrapperinfo.o wrapperjni.o loggerjni.o BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.so clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.so init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) $(WRAPPER_LINK_OPTS) -pthread $(wrapper_SOURCE) -lm -o $(BIN)/wrapper libwrapper.so: $(libwrapper_so_OBJECTS) ${COMPILE} -shared $(libwrapper_so_OBJECTS) -o $(LIB)/libwrapper.so %.o: %.c @echo '$(COMPILE) -c $<'; \ $(COMPILE) -pthread $(DEFS) -Wp,-MD,.deps/$(*F).pp -c $< @-cp .deps/$(*F).pp .deps/$(*F).P; \ tr ' ' '\012' < .deps/$(*F).pp \ | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \ >> .deps/$(*F).P; \ rm .deps/$(*F).pp wrapper_3.5.51_src/src/c/Makefile-linux-ia-64.make100644 0 0 3113 14333053647 16713 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html COMPILE = gcc -O3 -fPIC -Wall --pedantic -DLINUX -DJSW64 -D_FILE_OFFSET_BITS=64 -fpic -D_GNU_SOURCE -DUNICODE -D_UNICODE WRAPPER_LINK_OPTS = -Wl,-z,relro,-z,now INCLUDE=$(JAVA_HOME)/include DEFS = -I$(INCLUDE) -I$(INCLUDE)/linux wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_OBJECTS = wrapper_i18n.o wrapperjni_unix.o wrapperinfo.o wrapperjni.o loggerjni.o BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.so clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.so init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) $(WRAPPER_LINK_OPTS) -lm -pthread $(wrapper_SOURCE) -o $(BIN)/wrapper libwrapper.so: $(libwrapper_so_OBJECTS) ${COMPILE} -shared $(libwrapper_so_OBJECTS) -lm -o $(LIB)/libwrapper.so %.o: %.c @echo '$(COMPILE) -c $<'; \ $(COMPILE) -pthread $(DEFS) -Wp,-MD,.deps/$(*F).pp -c $< @-cp .deps/$(*F).pp .deps/$(*F).P; \ tr ' ' '\012' < .deps/$(*F).pp \ | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \ >> .deps/$(*F).P; \ rm .deps/$(*F).pp wrapper_3.5.51_src/src/c/Makefile-linux-ppcle-64.make100644 0 0 3140 14333053647 17425 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html COMPILE = gcc -O3 -m64 -fPIC -Wall --pedantic -DLINUX -D_FORTIFY_SOURCE=2 -DJSW64 -D_FILE_OFFSET_BITS=64 -fpic -D_GNU_SOURCE -DUNICODE -D_UNICODE WRAPPER_LINK_OPTS = -Wl,-z,relro,-z,now INCLUDE=$(JAVA_HOME)/include DEFS = -I$(INCLUDE) -I$(INCLUDE)/linux wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_OBJECTS = wrapper_i18n.o wrapperjni_unix.o wrapperinfo.o wrapperjni.o loggerjni.o BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.so clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.so init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) $(WRAPPER_LINK_OPTS) -lm -pthread $(wrapper_SOURCE) -o $(BIN)/wrapper libwrapper.so: $(libwrapper_so_OBJECTS) ${COMPILE} -shared $(libwrapper_so_OBJECTS) -o $(LIB)/libwrapper.so %.o: %.c @echo '$(COMPILE) -c $<'; \ $(COMPILE) -pthread $(DEFS) -Wp,-MD,.deps/$(*F).pp -c $< @-cp .deps/$(*F).pp .deps/$(*F).P; \ tr ' ' '\012' < .deps/$(*F).pp \ | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \ >> .deps/$(*F).P; \ rm .deps/$(*F).pp wrapper_3.5.51_src/src/c/Makefile-linux-x86-32.make100644 0 0 4034 14333053647 16745 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html COMPILE = gcc -O3 -fPIC -Wall --pedantic -DLINUX -D_FORTIFY_SOURCE=2 -D_FILE_OFFSET_BITS=64 -fpic -D_GNU_SOURCE -DUNICODE -D_UNICODE WRAPPER_LINK_OPTS = -Wl,-z,relro,-z,now INCLUDE=$(JAVA_HOME)/include DEFS = -I$(INCLUDE) -I$(INCLUDE)/linux wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_OBJECTS = wrapper_i18n.o wrapperjni_unix.o wrapperinfo.o wrapperjni.o loggerjni.o testsuite_SOURCE = testsuite.c test_example.c test_javaadditionalparam.c test_hashmap.c test_filter.c wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c BIN = ../../bin LIB = ../../lib TEST = ../../test all: init wrapper libwrapper.so clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.so init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) $(WRAPPER_LINK_OPTS) -pthread $(wrapper_SOURCE) -lm -o $(BIN)/wrapper testsuite: $(testsuite_SOURCE) $(COMPILE) -DCUNIT $(testsuite_SOURCE) -lm -pthread -L/usr/local/lib -lncurses -lcunit -o $(TEST)/testsuite libwrapper.so: $(libwrapper_so_OBJECTS) ${COMPILE} -shared $(libwrapper_so_OBJECTS) -o $(LIB)/libwrapper.so %.o: %.c @echo '$(COMPILE) -c $<'; \ $(COMPILE) -pthread $(DEFS) -Wp,-MD,.deps/$(*F).pp -c $< @-cp .deps/$(*F).pp .deps/$(*F).P; \ tr ' ' '\012' < .deps/$(*F).pp \ | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \ >> .deps/$(*F).P; \ rm .deps/$(*F).pp wrapper_3.5.51_src/src/c/Makefile-linux-x86-64.make100644 0 0 4056 14333053647 16756 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html COMPILE = gcc -O3 -fPIC -Wall --pedantic -DLINUX -D_FORTIFY_SOURCE=2 -DJSW64 -D_FILE_OFFSET_BITS=64 -fpic -D_GNU_SOURCE -DUNICODE -D_UNICODE WRAPPER_LINK_OPTS = -Wl,-z,relro,-z,now INCLUDE=$(JAVA_HOME)/include DEFS = -I$(INCLUDE) -I$(INCLUDE)/linux wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_OBJECTS = wrapper_i18n.o wrapperjni_unix.o wrapperinfo.o wrapperjni.o loggerjni.o testsuite_SOURCE = testsuite.c test_example.c test_javaadditionalparam.c test_hashmap.c test_filter.c wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c BIN = ../../bin LIB = ../../lib TEST = ../../test all: init wrapper libwrapper.so testsuite clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.so init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) $(WRAPPER_LINK_OPTS) -pthread $(wrapper_SOURCE) -lm -o $(BIN)/wrapper testsuite: $(testsuite_SOURCE) $(COMPILE) -DCUNIT $(testsuite_SOURCE) -lm -pthread -L/usr/local/lib -lncurses -lcunit -o $(TEST)/testsuite libwrapper.so: $(libwrapper_so_OBJECTS) ${COMPILE} -shared $(libwrapper_so_OBJECTS) -o $(LIB)/libwrapper.so %.o: %.c @echo '$(COMPILE) -c $<'; \ $(COMPILE) -pthread $(DEFS) -Wp,-MD,.deps/$(*F).pp -c $< @-cp .deps/$(*F).pp .deps/$(*F).P; \ tr ' ' '\012' < .deps/$(*F).pp \ | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \ >> .deps/$(*F).P; \ rm .deps/$(*F).pp wrapper_3.5.51_src/src/c/Makefile-macosx-arm-64.make100644 0 0 3037 14333053647 17241 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html UNIVERSAL_SDK_HOME=/Library/Developer/CommandLineTools/SDKs/MacOSX11.sdk ISYSROOT=-isysroot $(UNIVERSAL_SDK_HOME) #DEFS=-I$(UNIVERSAL_SDK_HOME)/System/Library/Frameworks/JavaVM.framework/Headers DEFS=-I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/darwin COMPILE = gcc -O3 -m64 -Wall -DUSE_NANOSLEEP -DMACOSX -D_FORTIFY_SOURCE=2 -DJSW64 -arch arm64 $(ISYSROOT) -mmacosx-version-min=11.1 -DUNICODE -D_UNICODE wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_OBJECTS = wrapper_i18n.o wrapperjni_unix.o wrapperinfo.o wrapperjni.o loggerjni.o BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.dylib clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.dylib init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) -DMACOSX $(wrapper_SOURCE) -liconv -pthread -o $(BIN)/wrapper libwrapper.dylib: $(libwrapper_so_OBJECTS) $(COMPILE) -bundle -liconv -pthread -o $(LIB)/libwrapper.dylib $(libwrapper_so_OBJECTS) %.o: %.c $(COMPILE) -c $(DEFS) $< wrapper_3.5.51_src/src/c/Makefile-macosx-universal-32.make100644 0 0 4524 14333053647 20467 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html UNIVERSAL_SDK_HOME=/Developer/SDKs/MacOSX10.5.sdk INCLUDE = -I/opt/local/include COMPILE = gcc -O3 -Wall -DUSE_NANOSLEEP -DMACOSX -D_FORTIFY_SOURCE=2 -arch ppc -arch i386 -isysroot $(UNIVERSAL_SDK_HOME) $(INCLUDE) -mmacosx-version-min=10.4 -DUNICODE -D_UNICODE COMPILET = gcc -O3 -Wall -DUSE_NANOSLEEP -DMACOSX -isysroot $(UNIVERSAL_SDK_HOME) $(INCLUDE) -mmacosx-version-min=10.4 -DUNICODE -D_UNICODE #COMPILE = gcc -ggdb -O1 -Wall -DUSE_NANOSLEEP -DMACOSX -DVALGRIND -isysroot $(UNIVERSAL_SDK_HOME) $(INCLUDE) -mmacosx-version-min=10.4 -DUNICODE -D_UNICODE # To debug: # 1) Add "-ggdb" # 2) Remove "-arch ppc -arch i386" # 3) Change "-O3" to "-O1" DEFS = -I$(UNIVERSAL_SDK_HOME)/System/Library/Frameworks/JavaVM.framework/Headers wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_OBJECTS = wrapper_i18n.o wrapperjni_unix.o wrapperinfo.o wrapperjni.o loggerjni.o testsuite_SOURCE = testsuite.c test_example.c test_javaadditionalparam.c test_hashmap.c test_filter.c wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c BIN = ../../bin LIB = ../../lib TEST = ../../test #all: init testsuite wrapper libwrapper.jnilib all: init wrapper libwrapper.jnilib clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.jnilib init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) -DMACOSX $(wrapper_SOURCE) -liconv -pthread -o $(BIN)/wrapper libwrapper.jnilib: $(libwrapper_so_OBJECTS) $(COMPILE) -bundle -liconv -pthread -o $(LIB)/libwrapper.jnilib $(libwrapper_so_OBJECTS) testsuite: $(testsuite_SOURCE) $(COMPILET) -DCUNIT $(testsuite_SOURCE) -liconv -lncurses -lcunit -pthread -o $(TEST)/testsuite %.o: %.c $(COMPILE) -c $(DEFS) $< wrapper_3.5.51_src/src/c/Makefile-macosx-universal-64.make100644 0 0 3764 14333053647 20501 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html # Newer SDKs work differently so we don't want to specify where it is. ifdef DEV_AUTOSDK ISYSROOT= DEV_NOPPC=true DEFS=-I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/darwin else ifdef DEV_NOPPC UNIVERSAL_SDK_HOME=/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk else UNIVERSAL_SDK_HOME=/Developer/SDKs/MacOSX10.5.sdk endif ISYSROOT=-isysroot $(UNIVERSAL_SDK_HOME) DEFS=-I$(UNIVERSAL_SDK_HOME)/System/Library/Frameworks/JavaVM.framework/Headers endif # If you define DEV_NOPPC on your development machine then ppc64 will not be built. This is needed for newer XCode versions. This should never be done on release build machines. ifdef DEV_NOPPC ARCHPPC= else ARCHPPC=-arch ppc64 endif COMPILE = gcc -O3 -m64 -Wall -DUSE_NANOSLEEP -DMACOSX -D_FORTIFY_SOURCE=2 -DJSW64 $(ARCHPPC) -arch x86_64 $(ISYSROOT) -mmacosx-version-min=10.4 -DUNICODE -D_UNICODE wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_OBJECTS = wrapper_i18n.o wrapperjni_unix.o wrapperinfo.o wrapperjni.o loggerjni.o BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.jnilib clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.jnilib init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) -DMACOSX $(wrapper_SOURCE) -liconv -pthread -o $(BIN)/wrapper libwrapper.jnilib: $(libwrapper_so_OBJECTS) $(COMPILE) -bundle -liconv -pthread -o $(LIB)/libwrapper.jnilib $(libwrapper_so_OBJECTS) %.o: %.c $(COMPILE) -c $(DEFS) $< wrapper_3.5.51_src/src/c/Makefile-solaris-sparc-32.gmake100644 0 0 2473 14333053647 20121 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html COMPILE = /opt/studio12/SUNWspro/bin/cc -mt -Kpic -O -DSOLARIS -DJSW64 -D_REENTRANT -DUNICODE -D_UNICODE INCLUDE=$(JAVA_HOME)/include DEFS = -I$(INCLUDE) -I$(INCLUDE)/solaris wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_OBJECTS = wrapper_i18n.o wrapperjni_unix.o wrapperinfo.o wrapperjni.o loggerjni.o BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.so clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.so init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) $(wrapper_SOURCE) -lsocket -lpthread -lnsl -lm -lposix4 -o $(BIN)/wrapper libwrapper.so: $(libwrapper_so_OBJECTS) ${COMPILE} -G -fPIC -lposix4 -lpthread $(libwrapper_so_OBJECTS) -o $(LIB)/libwrapper.so %.o: %.c ${COMPILE} -c ${DEFS} $< wrapper_3.5.51_src/src/c/Makefile-solaris-sparc-64.gmake100644 0 0 2514 14333053647 20122 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html COMPILE = /opt/studio12/SUNWspro/bin/cc -m64 -mt -O -DSOLARIS -DJSW64 -D_REENTRANT -DUNICODE -D_UNICODE INCLUDE=$(JAVA_HOME)/include DEFS = -I$(INCLUDE) -I$(INCLUDE)/solaris wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_OBJECTS = wrapper_i18n.o wrapperjni_unix.o wrapperinfo.o wrapperjni.o loggerjni.o BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.so clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.so init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) -KPIC $(wrapper_SOURCE) -lsocket -lpthread -lnsl -lm -lposix4 -o $(BIN)/wrapper libwrapper.so: $(libwrapper_so_OBJECTS) ${COMPILE} -Kpic -G -fPIC -lposix4 -lpthread $(libwrapper_so_OBJECTS) -o $(LIB)/libwrapper.so %.o: %.c ${COMPILE} -Kpic -c ${DEFS} $< wrapper_3.5.51_src/src/c/Makefile-solaris-x86-32.gmake100644 0 0 2553 14333053647 17435 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html # gcc -O3 -Wall --pedantic -DSOLARIS -liconv -DUNICODE -D_UNICODE COMPILE = /opt/SUNWspro/bin/cc -mt -Kpic -O -DSOLARIS -DUNICODE -D_UNICODE -D_REENTRANT INCLUDE=$(JAVA_HOME)/include DEFS = -I$(INCLUDE) -I$(INCLUDE)/solaris wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_OBJECTS = wrapper_i18n.o wrapperjni_unix.o wrapperinfo.o wrapperjni.o loggerjni.o BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.so clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.so init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) $(wrapper_SOURCE) -lsocket -lpthread -lnsl -lm -lposix4 -o $(BIN)/wrapper libwrapper.so: $(libwrapper_so_OBJECTS) ${COMPILE} -G -fPIC -lposix4 -lpthread $(libwrapper_so_OBJECTS) -o $(LIB)/libwrapper.so %.o: %.c ${COMPILE} -c ${DEFS} $< wrapper_3.5.51_src/src/c/Makefile-solaris-x86-64.gmake100644 0 0 3407 14333053647 17441 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html # gcc - The JNI library generated with gcc does not work on 64-bit x86 systems # due to a bug or simply a configuration problem. We need to use cc here. # see Bug #1992039 for details. # https://sourceforge.net/tracker/index.php?func=detail&aid=1992039&group_id=39428&atid=425187 #COMPILE = gcc -m64 -O3 -Wall --pedantic -DSOLARIS -DJSW64 COMPILE = /opt/SUNWspro/bin/cc -m64 -mt -Kpic -O -DSOLARIS -DJSW64 -DUNICODE -D_UNICODE -D_REENTRANT INCLUDE=$(JAVA_HOME)/include DEFS = -I$(INCLUDE) -I$(INCLUDE)/solaris wrapper_SOURCE = wrapper.c wrapperinfo.c wrappereventloop.c wrapper_unix.c property.c logger.c logger_file.c wrapper_file.c wrapper_i18n.c wrapper_hashmap.c wrapper_ulimit.c wrapper_encoding.c wrapper_jvminfo.c libwrapper_so_OBJECTS = wrapper_i18n.o wrapperjni_unix.o wrapperinfo.o wrapperjni.o loggerjni.o BIN = ../../bin LIB = ../../lib all: init wrapper libwrapper.so clean: rm -f *.o cleanall: clean rm -rf *~ .deps rm -f $(BIN)/wrapper $(LIB)/libwrapper.so init: if test ! -d .deps; then mkdir .deps; fi wrapper: $(wrapper_SOURCE) $(COMPILE) $(wrapper_SOURCE) -lsocket -lpthread -lnsl -lm -lposix4 -o $(BIN)/wrapper #libwrapper.so: $(libwrapper_so_OBJECTS) # ${COMPILE} -G -fPIC -lposix4 $(libwrapper_so_OBJECTS) -o $(LIB)/libwrapper.so libwrapper.so: $(libwrapper_so_OBJECTS) ${COMPILE} -G -fPIC -lposix4 -lpthread $(libwrapper_so_OBJECTS) -o $(LIB)/libwrapper.so %.o: %.c ${COMPILE} -c ${DEFS} $< wrapper_3.5.51_src/src/c/Makefile-windows-x86-32.nmake100644 0 0 7156 14333053647 17466 0ustar 0 0 # Copyright (c) 1999, 2022 Tanuki Software, Ltd. # http://www.tanukisoftware.com # All rights reserved. # # This software is the proprietary information of Tanuki Software. # You shall use it only in accordance with the terms of the # license agreement you entered into with Tanuki Software. # http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html !include makefile.inc PROJ = wrapper COMPILE = cl /D "WIN32" /D "NDEBUG" /FD /EHsc /MT /W3 /nologo /c /Zi /errorReport:prompt /D "_CRT_SECURE_NO_DEPRECATE" /D "UNICODE" /D "_UNICODE" /D "_WIN32_WINNT=0x0501" LINK = link /NOLOGO /MANIFEST /DEBUG /MACHINE:X86 /ERRORREPORT:PROMPT DelayImp.lib RC = rc # EXE Definitions EXE_OUTDIR = $(PROJ)32_VC8__Win32_Release EXE_OBJS = $(EXE_OUTDIR)\wrapper.obj $(EXE_OUTDIR)\wrapperinfo.obj $(EXE_OUTDIR)\wrappereventloop.obj $(EXE_OUTDIR)\wrapper_win.obj $(EXE_OUTDIR)\property.obj $(EXE_OUTDIR)\logger.obj $(EXE_OUTDIR)\logger_file.obj $(EXE_OUTDIR)\wrapper_file.obj $(EXE_OUTDIR)\wrapper_i18n.obj $(EXE_OUTDIR)\wrapper_hashmap.obj $(EXE_OUTDIR)\wrapper_ulimit.obj $(EXE_OUTDIR)\wrapper_encoding.obj $(EXE_OUTDIR)\wrapper_jvminfo.obj EXE_LIBS = mpr.lib shell32.lib netapi32.lib wsock32.lib Ws2_32.lib shlwapi.lib advapi32.lib user32.lib Crypt32.lib Wintrust.lib pdh.lib psapi.lib ole32.lib OleAut32.lib activeds.lib adsiid.lib EXE_COMPILE_OPTS = /O2 /GL /D "_CONSOLE" EXE_LINK_OPTS = /INCREMENTAL:NO /SUBSYSTEM:CONSOLE /MANIFESTFILE:"$(EXE_OUTDIR)\$(PROJ).exe.intermediate.manifest" /PDB:"$(EXE_OUTDIR)\$(PROJ).pdb" /OPT:REF /OPT:ICF /LTCG /DYNAMICBASE # DLL Definitions DLL_OUTDIR = $(PROJ)JNI32_VC8__Win32_Release DLL_OBJS = $(DLL_OUTDIR)\wrapper_i18n.obj $(DLL_OUTDIR)\wrapperjni_win.obj $(DLL_OUTDIR)\wrapperinfo.obj $(DLL_OUTDIR)\wrapperjni.obj $(DLL_OUTDIR)\loggerjni.obj DLL_LIBS = shell32.lib wsock32.lib advapi32.lib user32.lib Iphlpapi.lib DLL_COMPILE_OPTS = /Od /I "..\" /I ".\" /I "$(JAVA_HOME)\include" /I "$(JAVA_HOME)\include\win32" /D "_WINDOWS" /D "_USRDLL" /D "DECODERJNI_VC8_EXPORTS" /D "_WINDLL" DLL_LINK_OPTS = /INCREMENTAL /DLL /SUBSYSTEM:WINDOWS /MANIFESTFILE:"$(DLL_OUTDIR)\$(PROJ).dll.intermediate.manifest" /PDB:"$(DLL_OUTDIR)\$(PROJ).pdb" /DYNAMICBASE /NXCOMPAT all: $(EXE_OUTDIR) $(DLL_OUTDIR) $(PROJ).exe $(PROJ).dll # Define project specific macros #----- If OUTDIR does not exist, then create directory $(EXE_OUTDIR) : if not exist "$(EXE_OUTDIR)/$(NULL)" mkdir $(EXE_OUTDIR) $(DLL_OUTDIR) : if not exist "$(DLL_OUTDIR)/$(NULL)" mkdir $(DLL_OUTDIR) # Inference rule for updating the object files .c{$(EXE_OUTDIR)}.obj: $(COMPILE) $(EXE_COMPILE_OPTS) /Fo"$(EXE_OUTDIR)\\" /Fd"$(EXE_OUTDIR)\\" $** .c{$(DLL_OUTDIR)}.obj: $(COMPILE) $(DLL_COMPILE_OPTS) /Fo"$(DLL_OUTDIR)\\" /Fd"$(DLL_OUTDIR)\\" $** # Build rule for resource file $(EXE_OUTDIR)\$(PROJ).res: $(PROJ).rc $(RC_DEP) $(RC) /fo"$(EXE_OUTDIR)/$(PROJ).res" $(PROJ).rc $(DLL_OUTDIR)\$(PROJ).res: $(PROJ).rc $(RC_DEP) $(RC) /fo"$(DLL_OUTDIR)/$(PROJ).res" $(PROJ).rc $(PROJ).exe: $(BASE_OBJS) $(EXE_OBJS) $(EXE_OUTDIR)\$(PROJ).res $(LINK) $(EXE_LINK_OPTS) $(EXE_OBJS) $(EXE_LIBS) $(EXE_OUTDIR)\$(PROJ).res /OUT:"..\..\bin\$(PROJ).exe" $(_VC_MANIFEST_EMBED_EXE) $(PROJ).dll: $(BASE_OBJS) $(DLL_OBJS) $(DLL_OUTDIR)\$(PROJ).res $(LINK) $(DLL_LINK_OPTS) $(DLL_OBJS) $(DLL_LIBS) $(DLL_OUTDIR)\$(PROJ).res /OUT:"..\..\lib\$(PROJ).dll" $(_VC_MANIFEST_EMBED_DLL) clean: if exist $(EXE_OUTDIR)/$(NULL) rd /s /q $(EXE_OUTDIR) if exist $(DLL_OUTDIR)/$(NULL) rd /s /q $(DLL_OUTDIR) if exist ..\..\bin\$(PROJ).exe del /q ..\..\bin\$(PROJ).exe if exist ..\..\lib\$(PROJ).dll del /q ..\..\lib\$(PROJ).dll $(_VC_MANIFEST_CLEAN) !include makefile.targ.inc wrapper_3.5.51_src/src/c/Wrapper.rc100644 0 0 7557 14333053647 14352 0ustar 0 0 //Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // { resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_JPN) #ifdef _WIN32 LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT #pragma code_page(932) #endif //_WIN32 #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include \r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Version // // http://msdn.microsoft.com/en-us/library/aa381058%28VS.85%29.aspx VS_VERSION_INFO VERSIONINFO FILEVERSION 3,5,51,0 PRODUCTVERSION 3,5,51,0 FILEFLAGSMASK 0x17L #ifdef _DEBUG FILEFLAGS 0x1L #else FILEFLAGS 0x0L #endif FILEOS 0x4L FILETYPE 0x1L FILESUBTYPE 0x0L BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "000904b0" BEGIN VALUE "CompanyName", "Tanuki Software, Ltd." VALUE "FileDescription", "Java Service Wrapper Community Edition 3.5.51" VALUE "FileVersion", "3, 5, 51, 0" VALUE "LegalCopyright", "Copyright (C) 1999, 2022 Tanuki Software, Ltd. All rights reserved." VALUE "InternalName", "wrapper" VALUE "OriginalFilename", "wrapper.exe" VALUE "ProductName", "Java Service Wrapper Community" VALUE "ProductVersion", "3, 5, 51, 0" END END BLOCK "VarFileInfo" BEGIN /* The following line should only be modified for localized versions. */ /* It consists of any number of WORD,WORD pairs, with each pair */ /* describing a language,codepage combination supported by the file. */ /* */ /* For example, a file might have values "0x409,1252" indicating that it */ /* supports English language (0x409) in the Windows ANSI codepage (1252). */ VALUE "Translation", 0x9, 1200 //VALUE "Translation", 0x409, 1252 //VALUE "Translation", 0x411, 932 END END #endif // { resources ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // p (ض) resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) #ifdef _WIN32 LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US #pragma code_page(1252) #endif //_WIN32 ///////////////////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure application icon // remains consistent on all systems. IDI_WRAPPER ICON DISCARDABLE "wrapper.ico" ///////////////////////////////////////////////////////////////////////////// // // Messages // 1 11 DISCARDABLE "MSG00001.bin" #endif // p (ض) resources ///////////////////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED wrapper_3.5.51_src/src/c/Wrapper32.dep.in100644 0 0 1415 14333053647 15253 0ustar 0 0 # Microsoft Developer Studio Generated Dependency File, included by Wrapper32.mak .\logger.c : \ ".\logger.h"\ ".\messages.h"\ "@msvc.home@\vc98\include\basetsd.h"\ .\property.c : \ ".\logger.h"\ ".\property.h"\ "@msvc.home@\vc98\include\basetsd.h"\ .\wrapper.c : \ ".\logger.h"\ ".\property.h"\ ".\wrapper.h"\ ".\wrapperinfo.h"\ "@msvc.home@\vc98\include\basetsd.h"\ .\wrapper_win.c : \ ".\logger.h"\ ".\property.h"\ ".\psapi.h"\ ".\wrapper.h"\ ".\wrapperinfo.h"\ "@msvc.home@\vc98\include\basetsd.h"\ .\wrappereventloop.c : \ ".\logger.h"\ ".\property.h"\ ".\wrapper.h"\ "@msvc.home@\vc98\include\basetsd.h"\ .\wrapperinfo.c : \ ".\wrapperinfo.h"\ .\messages.rc : \ ".\MSG00001.bin"\ wrapper_3.5.51_src/src/c/Wrapper32.dsp100644 0 0 12262 14333053647 14706 0ustar 0 0 # Microsoft Developer Studio Project File - Name="Wrapper" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** ҏWȂł ** # TARGTYPE "Win32 (x86) Console Application" 0x0103 CFG=Wrapper - Win32 Debug !MESSAGE ͗LҲ̧قł͂܂B ۼުĂނ邽߂ɂ NMAKE gpĂB !MESSAGE [Ҳ̧ق̴߰] ނgpĎsĂ !MESSAGE !MESSAGE NMAKE /f "Wrapper32.mak". !MESSAGE !MESSAGE NMAKE ̎sɍ\wł܂ !MESSAGE ײݏϸۂ̐ݒ`܂B: !MESSAGE !MESSAGE NMAKE /f "Wrapper32.mak" CFG="Wrapper - Win32 Debug" !MESSAGE !MESSAGE I”\ Ӱ: !MESSAGE !MESSAGE "Wrapper - Win32 Release" ("Win32 (x86) Console Application" p) !MESSAGE "Wrapper - Win32 Debug" ("Win32 (x86) Console Application" p) !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "Wrapper - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "Release" # PROP BASE Intermediate_Dir "Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "Release32" # PROP Intermediate_Dir "Release32" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c # ADD BASE RSC /l 0x411 /d "NDEBUG" # ADD RSC /l 0x411 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib shlwapi.lib /nologo /subsystem:console /machine:I386 /out:"../../bin/wrapper.exe" !ELSEIF "$(CFG)" == "Wrapper - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "Debug" # PROP BASE Intermediate_Dir "Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "Debug32" # PROP Intermediate_Dir "Debug32" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c # ADD CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "DEBUG" /FR /YX /FD /GZ /c # ADD BASE RSC /l 0x411 /d "_DEBUG" # ADD RSC /l 0x411 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib shlwapi.lib /nologo /subsystem:console /debug /machine:I386 /out:"../../bin/wrapper.exe" /pdbtype:sept !ENDIF # Begin Target # Name "Wrapper - Win32 Release" # Name "Wrapper - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\logger.c # End Source File # Begin Source File SOURCE=.\property.c # End Source File # Begin Source File SOURCE=.\wrapper.c # End Source File # Begin Source File SOURCE=.\wrapper_unix.c # End Source File # Begin Source File SOURCE=.\wrapper_win.c # End Source File # Begin Source File SOURCE=.\wrappereventloop.c # End Source File # Begin Source File SOURCE=.\wrapperinfo.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\logger.h # End Source File # Begin Source File SOURCE=.\messages.h # End Source File # Begin Source File SOURCE=.\property.h # End Source File # Begin Source File SOURCE=.\psapi.h # End Source File # Begin Source File SOURCE=.\resource.h # End Source File # Begin Source File SOURCE=.\wrapper.h # End Source File # Begin Source File SOURCE=.\wrapperinfo.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # Begin Source File SOURCE=.\MSG00001.bin # End Source File # Begin Source File SOURCE=.\wrapper.ico # End Source File # Begin Source File SOURCE=.\Wrapper32.rc # PROP Exclude_From_Build 1 # End Source File # End Group # Begin Source File SOURCE=.\messages.mc # End Source File # End Target # End Project wrapper_3.5.51_src/src/c/Wrapper32.dsw100644 0 0 1342 14333053647 14672 0ustar 0 0 Microsoft Developer Studio Workspace File, Format Version 6.00 # x: ܰ߰ ̧ ҏW܂͍폜Ȃł! ############################################################################### Project: "Wrapper"=.\Wrapper.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Project: "WrapperJNI"=.\WrapperJNI.dsp - Package Owner=<4> Package=<5> {{{ }}} Package=<4> {{{ }}} ############################################################################### Global: Package=<5> {{{ }}} Package=<3> {{{ }}} ############################################################################### wrapper_3.5.51_src/src/c/Wrapper32.mak100644 0 0 17036 14333053647 14674 0ustar 0 0 # Microsoft Developer Studio Generated NMAKE File, Based on Wrapper32.dsp !IF "$(CFG)" == "" CFG=Wrapper - Win32 Debug !MESSAGE Build mode not specified. Defaulting to "Wrapper - Win32 Debug". !ENDIF !IF "$(CFG)" != "Wrapper - Win32 Release" && "$(CFG)" != "Wrapper - Win32 Debug" !MESSAGE The build target "$(CFG)" is invalid. !MESSAGE Usage: !MESSAGE !MESSAGE NMAKE /f "Wrapper32.mak" CFG="Wrapper - Win32 Debug" !MESSAGE !MESSAGE Valid build modes: !MESSAGE !MESSAGE "Wrapper - Win32 Release" ("Win32 (x86) Console Application") !MESSAGE "Wrapper - Win32 Debug" ("Win32 (x86) Console Application") !MESSAGE !ERROR Ivalid build mode. !ENDIF !IF "$(OS)" == "Windows_NT" NULL= !ELSE NULL=nul !ENDIF CPP=cl.exe RSC=rc.exe !IF "$(CFG)" == "Wrapper - Win32 Release" OUTDIR=.\Release32 INTDIR=.\Release32 ALL : "..\..\bin\wrapper.exe" CLEAN : -@erase "$(INTDIR)\logger.obj" -@erase "$(INTDIR)\property.obj" -@erase "$(INTDIR)\vc60.idb" -@erase "$(INTDIR)\wrapper.obj" -@erase "$(INTDIR)\Wrapper.res" -@erase "$(INTDIR)\wrapper_unix.obj" -@erase "$(INTDIR)\wrapper_win.obj" -@erase "$(INTDIR)\wrappereventloop.obj" -@erase "$(INTDIR)\wrapperinfo.obj" -@erase "..\..\bin\wrapper.exe" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP_PROJ=/nologo /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /Fp"$(INTDIR)\Wrapper32.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c RSC_PROJ=/l 0x409 /fo"$(INTDIR)\Wrapper.res" /d "NDEBUG" BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\Wrapper32.bsc" BSC32_SBRS= \ LINK32=link.exe LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib shlwapi.lib /nologo /subsystem:console /incremental:no /pdb:"$(OUTDIR)\wrapper.pdb" /machine:I386 /out:"../../bin/wrapper.exe" LINK32_OBJS= \ "$(INTDIR)\logger.obj" \ "$(INTDIR)\property.obj" \ "$(INTDIR)\wrapper.obj" \ "$(INTDIR)\wrapper_unix.obj" \ "$(INTDIR)\wrapper_win.obj" \ "$(INTDIR)\wrappereventloop.obj" \ "$(INTDIR)\wrapperinfo.obj" \ "$(INTDIR)\Wrapper.res" "..\..\bin\wrapper.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ELSEIF "$(CFG)" == "Wrapper - Win32 Debug" OUTDIR=.\Debug32 INTDIR=.\Debug32 # Begin Custom Macros OutDir=.\Debug32 # End Custom Macros ALL : "..\..\bin\wrapper.exe" "$(OUTDIR)\Wrapper32.bsc" CLEAN : -@erase "$(INTDIR)\logger.obj" -@erase "$(INTDIR)\logger.sbr" -@erase "$(INTDIR)\property.obj" -@erase "$(INTDIR)\property.sbr" -@erase "$(INTDIR)\vc60.idb" -@erase "$(INTDIR)\vc60.pdb" -@erase "$(INTDIR)\wrapper.obj" -@erase "$(INTDIR)\Wrapper.res" -@erase "$(INTDIR)\wrapper.sbr" -@erase "$(INTDIR)\wrapper_unix.obj" -@erase "$(INTDIR)\wrapper_unix.sbr" -@erase "$(INTDIR)\wrapper_win.obj" -@erase "$(INTDIR)\wrapper_win.sbr" -@erase "$(INTDIR)\wrappereventloop.obj" -@erase "$(INTDIR)\wrappereventloop.sbr" -@erase "$(INTDIR)\wrapperinfo.obj" -@erase "$(INTDIR)\wrapperinfo.sbr" -@erase "$(OUTDIR)\wrapper.pdb" -@erase "$(OUTDIR)\Wrapper32.bsc" -@erase "..\..\bin\wrapper.exe" -@erase "..\..\bin\wrapper.ilk" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP_PROJ=/nologo /MLd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "DEBUG" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\Wrapper32.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c RSC_PROJ=/l 0x409 /fo"$(INTDIR)\Wrapper.res" /d "_DEBUG" BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\Wrapper32.bsc" BSC32_SBRS= \ "$(INTDIR)\logger.sbr" \ "$(INTDIR)\property.sbr" \ "$(INTDIR)\wrapper.sbr" \ "$(INTDIR)\wrapper_unix.sbr" \ "$(INTDIR)\wrapper_win.sbr" \ "$(INTDIR)\wrappereventloop.sbr" \ "$(INTDIR)\wrapperinfo.sbr" "$(OUTDIR)\Wrapper32.bsc" : "$(OUTDIR)" $(BSC32_SBRS) $(BSC32) @<< $(BSC32_FLAGS) $(BSC32_SBRS) << LINK32=link.exe LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib shlwapi.lib /nologo /subsystem:console /incremental:yes /pdb:"$(OUTDIR)\wrapper.pdb" /debug /machine:I386 /out:"../../bin/wrapper.exe" /pdbtype:sept LINK32_OBJS= \ "$(INTDIR)\logger.obj" \ "$(INTDIR)\property.obj" \ "$(INTDIR)\wrapper.obj" \ "$(INTDIR)\wrapper_unix.obj" \ "$(INTDIR)\wrapper_win.obj" \ "$(INTDIR)\wrappereventloop.obj" \ "$(INTDIR)\wrapperinfo.obj" \ "$(INTDIR)\Wrapper.res" "..\..\bin\wrapper.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ENDIF .c{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .c{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << !IF "$(NO_EXTERNAL_DEPS)" != "1" !IF EXISTS("Wrapper32.dep") !INCLUDE "Wrapper32.dep" !ELSE !MESSAGE Warning: cannot find "Wrapper32.dep" !ENDIF !ENDIF !IF "$(CFG)" == "Wrapper - Win32 Release" || "$(CFG)" == "Wrapper - Win32 Debug" SOURCE=.\logger.c !IF "$(CFG)" == "Wrapper - Win32 Release" "$(INTDIR)\logger.obj" : $(SOURCE) "$(INTDIR)" !ELSEIF "$(CFG)" == "Wrapper - Win32 Debug" "$(INTDIR)\logger.obj" "$(INTDIR)\logger.sbr" : $(SOURCE) "$(INTDIR)" !ENDIF SOURCE=.\property.c !IF "$(CFG)" == "Wrapper - Win32 Release" "$(INTDIR)\property.obj" : $(SOURCE) "$(INTDIR)" !ELSEIF "$(CFG)" == "Wrapper - Win32 Debug" "$(INTDIR)\property.obj" "$(INTDIR)\property.sbr" : $(SOURCE) "$(INTDIR)" !ENDIF SOURCE=.\wrapper.c !IF "$(CFG)" == "Wrapper - Win32 Release" "$(INTDIR)\wrapper.obj" : $(SOURCE) "$(INTDIR)" !ELSEIF "$(CFG)" == "Wrapper - Win32 Debug" "$(INTDIR)\wrapper.obj" "$(INTDIR)\wrapper.sbr" : $(SOURCE) "$(INTDIR)" !ENDIF SOURCE=.\wrapper_unix.c !IF "$(CFG)" == "Wrapper - Win32 Release" "$(INTDIR)\wrapper_unix.obj" : $(SOURCE) "$(INTDIR)" !ELSEIF "$(CFG)" == "Wrapper - Win32 Debug" "$(INTDIR)\wrapper_unix.obj" "$(INTDIR)\wrapper_unix.sbr" : $(SOURCE) "$(INTDIR)" !ENDIF SOURCE=.\wrapper_win.c !IF "$(CFG)" == "Wrapper - Win32 Release" "$(INTDIR)\wrapper_win.obj" : $(SOURCE) "$(INTDIR)" !ELSEIF "$(CFG)" == "Wrapper - Win32 Debug" "$(INTDIR)\wrapper_win.obj" "$(INTDIR)\wrapper_win.sbr" : $(SOURCE) "$(INTDIR)" !ENDIF SOURCE=.\wrappereventloop.c !IF "$(CFG)" == "Wrapper - Win32 Release" "$(INTDIR)\wrappereventloop.obj" : $(SOURCE) "$(INTDIR)" !ELSEIF "$(CFG)" == "Wrapper - Win32 Debug" "$(INTDIR)\wrappereventloop.obj" "$(INTDIR)\wrappereventloop.sbr" : $(SOURCE) "$(INTDIR)" !ENDIF SOURCE=.\wrapperinfo.c !IF "$(CFG)" == "Wrapper - Win32 Release" "$(INTDIR)\wrapperinfo.obj" : $(SOURCE) "$(INTDIR)" !ELSEIF "$(CFG)" == "Wrapper - Win32 Debug" "$(INTDIR)\wrapperinfo.obj" "$(INTDIR)\wrapperinfo.sbr" : $(SOURCE) "$(INTDIR)" !ENDIF SOURCE=.\Wrapper.rc !IF "$(CFG)" == "Wrapper - Win32 Release" "$(INTDIR)\Wrapper.res" : $(SOURCE) "$(INTDIR)" $(RSC) /l 0x409 /fo"$(INTDIR)\Wrapper.res" /d "NDEBUG" $(SOURCE) !ELSEIF "$(CFG)" == "Wrapper - Win32 Debug" "$(INTDIR)\Wrapper.res" : $(SOURCE) "$(INTDIR)" $(RSC) /l 0x411 /fo"$(INTDIR)\Wrapper.res" /d "_DEBUG" $(SOURCE) !ENDIF !ENDIF wrapper_3.5.51_src/src/c/Wrapper32.plg100644 0 0 2745 14333053647 14667 0ustar 0 0

ނ۸

--------------------\: Wrapper - Win32 Debug--------------------

ײ

ꎞ̧ "C:\DOCUME~1\leif\LOCALS~1\Temp\RSP3E.tmp" 쐬A̓eL^܂ [ /nologo /MLd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /D "DEBUG" /FR"Debug32/" /Fp"Debug32/Wrapper32.pch" /YX /Fo"Debug32/" /Fd"Debug32/" /FD /GZ /c "C:\SourceForge\wrapper\src\c\wrapper.c" "C:\SourceForge\wrapper\src\c\wrapper_unix.c" "C:\SourceForge\wrapper\src\c\wrapper_win.c" "C:\SourceForge\wrapper\src\c\wrappereventloop.c" ] Creating command line "cl.exe @C:\DOCUME~1\leif\LOCALS~1\Temp\RSP3E.tmp" ꎞ̧ "C:\DOCUME~1\leif\LOCALS~1\Temp\RSP3F.tmp" 쐬A̓eL^܂ [ kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib shlwapi.lib /nologo /subsystem:console /incremental:yes /pdb:"Debug32/wrapper.pdb" /debug /machine:I386 /out:"../../bin/wrapper.exe" /pdbtype:sept .\Debug32\logger.obj .\Debug32\property.obj .\Debug32\wrapper.obj .\Debug32\wrapper_unix.obj .\Debug32\wrapper_win.obj .\Debug32\wrappereventloop.obj .\Debug32\wrapperinfo.obj ] ײ "link.exe @C:\DOCUME~1\leif\LOCALS~1\Temp\RSP3F.tmp" ̍쐬

߯ ޳

ْ߲... wrapper.c wrapper_unix.c wrapper_win.c wrappereventloop.c ݸ...

wrapper.exe - װ 0Ax 0
wrapper_3.5.51_src/src/c/WrapperJNI32.dep.in100644 0 0 764 14333053647 15602 0ustar 0 0 # Microsoft Developer Studio Generated Dependency File, included by WrapperJNI32.mak .\wrapperinfo.c : \ ".\wrapperinfo.h"\ .\wrapperjni.c : \ ".\org_tanukisoftware_wrapper_WrapperManager.h"\ ".\wrapperinfo.h"\ ".\wrapperjni.h"\ "@msvc.home@\vc98\include\basetsd.h"\ "@jni.h@"\ "@jni_md.h@"\ .\wrapperjni_win.c : \ ".\org_tanukisoftware_wrapper_WrapperManager.h"\ ".\wrapperjni.h"\ "@msvc.home@\vc98\include\basetsd.h"\ "@jni.h@"\ "@jni_md.h@"\ wrapper_3.5.51_src/src/c/WrapperJNI32.dsp100644 0 0 11527 14333053647 15252 0ustar 0 0 # Microsoft Developer Studio Project File - Name="WrapperJNI" - Package Owner=<4> # Microsoft Developer Studio Generated Build File, Format Version 6.00 # ** ҏWȂł ** # TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 CFG=WrapperJNI - Win32 Debug !MESSAGE ͗LҲ̧قł͂܂B ۼުĂނ邽߂ɂ NMAKE gpĂB !MESSAGE [Ҳ̧ق̴߰] ނgpĎsĂ !MESSAGE !MESSAGE NMAKE /f "WrapperJNI.mak". !MESSAGE !MESSAGE NMAKE ̎sɍ\wł܂ !MESSAGE ײݏϸۂ̐ݒ`܂B: !MESSAGE !MESSAGE NMAKE /f "WrapperJNI.mak" CFG="WrapperJNI - Win32 Debug" !MESSAGE !MESSAGE I”\ Ӱ: !MESSAGE !MESSAGE "WrapperJNI - Win32 Release" ("Win32 (x86) Dynamic-Link Library" p) !MESSAGE "WrapperJNI - Win32 Debug" ("Win32 (x86) Dynamic-Link Library" p) !MESSAGE # Begin Project # PROP AllowPerConfigDependencies 0 # PROP Scc_ProjName "" # PROP Scc_LocalPath "" CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "WrapperJNI - Win32 Release" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 0 # PROP BASE Output_Dir "WrapperJNI___Win32_Release" # PROP BASE Intermediate_Dir "WrapperJNI___Win32_Release" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 0 # PROP Output_Dir "WrapperJNI___Win32_Release32" # PROP Intermediate_Dir "WrapperJNI___Win32_Release32" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WRAPPERJNI_EXPORTS" /YX /FD /c # ADD CPP /nologo /MT /W3 /GX /O2 /I "D:\Sun\j2sdk1.4.0_03\include\\" /I "D:\Sun\j2sdk1.4.0_03\include\win32\\" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WRAPPERJNI_EXPORTS" /YX /FD /c # ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x411 /d "NDEBUG" # ADD RSC /l 0x411 /d "NDEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 /out:"../../lib/wrapper.dll" !ELSEIF "$(CFG)" == "WrapperJNI - Win32 Debug" # PROP BASE Use_MFC 0 # PROP BASE Use_Debug_Libraries 1 # PROP BASE Output_Dir "WrapperJNI___Win32_Debug" # PROP BASE Intermediate_Dir "WrapperJNI___Win32_Debug" # PROP BASE Target_Dir "" # PROP Use_MFC 0 # PROP Use_Debug_Libraries 1 # PROP Output_Dir "WrapperJNI___Win32_Debug32" # PROP Intermediate_Dir "WrapperJNI___Win32_Debug32" # PROP Ignore_Export_Lib 0 # PROP Target_Dir "" # ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WRAPPERJNI_EXPORTS" /YX /FD /GZ /c # ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "D:\Sun\j2sdk1.4.0_03\include\\" /I "D:\Sun\j2sdk1.4.0_03\include\win32\\" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WRAPPERJNI_EXPORTS" /FR /YX /FD /GZ /c # ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 # ADD BASE RSC /l 0x411 /d "_DEBUG" # ADD RSC /l 0x411 /d "_DEBUG" BSC32=bscmake.exe # ADD BASE BSC32 /nologo # ADD BSC32 /nologo LINK32=link.exe # ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept # ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"../../lib/wrapper.dll" /pdbtype:sept !ENDIF # Begin Target # Name "WrapperJNI - Win32 Release" # Name "WrapperJNI - Win32 Debug" # Begin Group "Source Files" # PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" # Begin Source File SOURCE=.\wrapperinfo.c # End Source File # Begin Source File SOURCE=.\wrapperjni.c # End Source File # Begin Source File SOURCE=.\wrapperjni_unix.c # End Source File # Begin Source File SOURCE=.\wrapperjni_win.c # End Source File # End Group # Begin Group "Header Files" # PROP Default_Filter "h;hpp;hxx;hm;inl" # Begin Source File SOURCE=.\org_tanukisoftware_wrapper_WrapperManager.h # End Source File # Begin Source File SOURCE=.\wrapperinfo.h # End Source File # Begin Source File SOURCE=.\wrapperjni.h # End Source File # End Group # Begin Group "Resource Files" # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # End Group # End Target # End Project wrapper_3.5.51_src/src/c/WrapperJNI32.mak.in100644 0 0 14340 14333053647 15635 0ustar 0 0 # Microsoft Developer Studio Generated NMAKE File, Based on WrapperJNI32.dsp !IF "$(CFG)" == "" CFG=WrapperJNI - Win32 Debug !MESSAGE Build mode not specified. Defaulting to "WrapperJNI - Win32 Debug". !ENDIF !IF "$(CFG)" != "WrapperJNI - Win32 Release" && "$(CFG)" != "WrapperJNI - Win32 Debug" !MESSAGE The build target "$(CFG)" is invalid. !MESSAGE Usage: !MESSAGE !MESSAGE NMAKE /f "WrapperJNI32.mak" CFG="WrapperJNI - Win32 Debug" !MESSAGE !MESSAGE Valid build modes: !MESSAGE !MESSAGE "WrapperJNI - Win32 Release" ("Win32 (x86) Dynamic-Link Library") !MESSAGE "WrapperJNI - Win32 Debug" ("Win32 (x86) Dynamic-Link Library") !MESSAGE !ERROR Ivalid build mode. !ENDIF !IF "$(OS)" == "Windows_NT" NULL= !ELSE NULL=nul !ENDIF CPP=cl.exe MTL=midl.exe RSC=rc.exe !IF "$(CFG)" == "WrapperJNI - Win32 Release" OUTDIR=.\WrapperJNI___Win32_Release32 INTDIR=.\WrapperJNI___Win32_Release32 ALL : "..\..\lib\wrapper.dll" CLEAN : -@erase "$(INTDIR)\vc60.idb" -@erase "$(INTDIR)\wrapperinfo.obj" -@erase "$(INTDIR)\wrapperjni.obj" -@erase "$(INTDIR)\wrapperjni_unix.obj" -@erase "$(INTDIR)\wrapperjni_win.obj" -@erase "$(OUTDIR)\wrapper.exp" -@erase "$(OUTDIR)\wrapper.lib" -@erase "..\..\lib\wrapper.dll" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP_PROJ=/nologo /MT /W3 /GX /O2 /I "@java.home@\include\\" /I "@java.home@\include\win32\\" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WRAPPERJNI_EXPORTS" /Fp"$(INTDIR)\WrapperJNI32.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\WrapperJNI32.bsc" BSC32_SBRS= \ LINK32=link.exe LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:no /pdb:"$(OUTDIR)\wrapper.pdb" /machine:I386 /out:"../../lib/wrapper.dll" /implib:"$(OUTDIR)\wrapper.lib" LINK32_OBJS= \ "$(INTDIR)\wrapperinfo.obj" \ "$(INTDIR)\wrapperjni.obj" \ "$(INTDIR)\wrapperjni_unix.obj" \ "$(INTDIR)\wrapperjni_win.obj" "..\..\lib\wrapper.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ELSEIF "$(CFG)" == "WrapperJNI - Win32 Debug" OUTDIR=.\WrapperJNI___Win32_Debug32 INTDIR=.\WrapperJNI___Win32_Debug32 # Begin Custom Macros OutDir=.\WrapperJNI___Win32_Debug32 # End Custom Macros ALL : "..\..\lib\wrapper.dll" "$(OUTDIR)\WrapperJNI32.bsc" CLEAN : -@erase "$(INTDIR)\vc60.idb" -@erase "$(INTDIR)\vc60.pdb" -@erase "$(INTDIR)\wrapperinfo.obj" -@erase "$(INTDIR)\wrapperinfo.sbr" -@erase "$(INTDIR)\wrapperjni.obj" -@erase "$(INTDIR)\wrapperjni.sbr" -@erase "$(INTDIR)\wrapperjni_unix.obj" -@erase "$(INTDIR)\wrapperjni_unix.sbr" -@erase "$(INTDIR)\wrapperjni_win.obj" -@erase "$(INTDIR)\wrapperjni_win.sbr" -@erase "$(OUTDIR)\wrapper.exp" -@erase "$(OUTDIR)\wrapper.lib" -@erase "$(OUTDIR)\wrapper.pdb" -@erase "$(OUTDIR)\WrapperJNI32.bsc" -@erase "..\..\lib\wrapper.dll" -@erase "..\..\lib\wrapper.ilk" "$(OUTDIR)" : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP_PROJ=/nologo /MTd /W3 /Gm /GX /ZI /Od /I "@java.home@\include\\" /I "@java.home@\include\win32\\" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WRAPPERJNI_EXPORTS" /FR"$(INTDIR)\\" /Fp"$(INTDIR)\WrapperJNI32.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 BSC32=bscmake.exe BSC32_FLAGS=/nologo /o"$(OUTDIR)\WrapperJNI32.bsc" BSC32_SBRS= \ "$(INTDIR)\wrapperinfo.sbr" \ "$(INTDIR)\wrapperjni.sbr" \ "$(INTDIR)\wrapperjni_unix.sbr" \ "$(INTDIR)\wrapperjni_win.sbr" "$(OUTDIR)\WrapperJNI32.bsc" : "$(OUTDIR)" $(BSC32_SBRS) $(BSC32) @<< $(BSC32_FLAGS) $(BSC32_SBRS) << LINK32=link.exe LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:yes /pdb:"$(OUTDIR)\wrapper.pdb" /debug /machine:I386 /out:"../../lib/wrapper.dll" /implib:"$(OUTDIR)\wrapper.lib" /pdbtype:sept LINK32_OBJS= \ "$(INTDIR)\wrapperinfo.obj" \ "$(INTDIR)\wrapperjni.obj" \ "$(INTDIR)\wrapperjni_unix.obj" \ "$(INTDIR)\wrapperjni_win.obj" "..\..\lib\wrapper.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) $(LINK32) @<< $(LINK32_FLAGS) $(LINK32_OBJS) << !ENDIF .c{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.obj:: $(CPP) @<< $(CPP_PROJ) $< << .c{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cpp{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << .cxx{$(INTDIR)}.sbr:: $(CPP) @<< $(CPP_PROJ) $< << !IF "$(NO_EXTERNAL_DEPS)" != "1" !IF EXISTS("WrapperJNI32.dep") !INCLUDE "WrapperJNI32.dep" !ELSE !MESSAGE Warning: cannot find "WrapperJNI32.dep" !ENDIF !ENDIF !IF "$(CFG)" == "WrapperJNI - Win32 Release" || "$(CFG)" == "WrapperJNI - Win32 Debug" SOURCE=.\wrapperinfo.c !IF "$(CFG)" == "WrapperJNI - Win32 Release" "$(INTDIR)\wrapperinfo.obj" : $(SOURCE) "$(INTDIR)" !ELSEIF "$(CFG)" == "WrapperJNI - Win32 Debug" "$(INTDIR)\wrapperinfo.obj" "$(INTDIR)\wrapperinfo.sbr" : $(SOURCE) "$(INTDIR)" !ENDIF SOURCE=.\wrapperjni.c !IF "$(CFG)" == "WrapperJNI - Win32 Release" "$(INTDIR)\wrapperjni.obj" : $(SOURCE) "$(INTDIR)" !ELSEIF "$(CFG)" == "WrapperJNI - Win32 Debug" "$(INTDIR)\wrapperjni.obj" "$(INTDIR)\wrapperjni.sbr" : $(SOURCE) "$(INTDIR)" !ENDIF SOURCE=.\wrapperjni_unix.c !IF "$(CFG)" == "WrapperJNI - Win32 Release" "$(INTDIR)\wrapperjni_unix.obj" : $(SOURCE) "$(INTDIR)" !ELSEIF "$(CFG)" == "WrapperJNI - Win32 Debug" "$(INTDIR)\wrapperjni_unix.obj" "$(INTDIR)\wrapperjni_unix.sbr" : $(SOURCE) "$(INTDIR)" !ENDIF SOURCE=.\wrapperjni_win.c !IF "$(CFG)" == "WrapperJNI - Win32 Release" "$(INTDIR)\wrapperjni_win.obj" : $(SOURCE) "$(INTDIR)" !ELSEIF "$(CFG)" == "WrapperJNI - Win32 Debug" "$(INTDIR)\wrapperjni_win.obj" "$(INTDIR)\wrapperjni_win.sbr" : $(SOURCE) "$(INTDIR)" !ENDIF !ENDIF wrapper_3.5.51_src/src/c/WrapperJNI32.plg100644 0 0 3475 14333053647 15231 0ustar 0 0

ނ۸

--------------------\: WrapperJNI - Win32 Debug--------------------

ײ

ꎞ̧ "C:\DOCUME~1\leif\LOCALS~1\Temp\RSP321.tmp" 쐬A̓eL^܂ [ /nologo /MTd /W3 /Gm /GX /ZI /Od /I "C:\Sun\j2sdk1.4.2_08\include\\" /I "C:\Sun\j2sdk1.4.2_08\include\win32\\" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "WRAPPERJNI_EXPORTS" /FR"WrapperJNI___Win32_Debug32/" /Fp"WrapperJNI___Win32_Debug32/WrapperJNI32.pch" /YX /Fo"WrapperJNI___Win32_Debug32/" /Fd"WrapperJNI___Win32_Debug32/" /FD /GZ /c "D:\SourceForge\wrapper\src\c\wrapperinfo.c" "D:\SourceForge\wrapper\src\c\wrapperjni.c" "D:\SourceForge\wrapper\src\c\wrapperjni_unix.c" "D:\SourceForge\wrapper\src\c\wrapperjni_win.c" ] Creating command line "cl.exe @C:\DOCUME~1\leif\LOCALS~1\Temp\RSP321.tmp" ꎞ̧ "C:\DOCUME~1\leif\LOCALS~1\Temp\RSP322.tmp" 쐬A̓eL^܂ [ kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /incremental:yes /pdb:"WrapperJNI___Win32_Debug32/wrapper.pdb" /debug /machine:I386 /out:"../../lib/wrapper.dll" /implib:"WrapperJNI___Win32_Debug32/wrapper.lib" /pdbtype:sept .\WrapperJNI___Win32_Debug32\wrapperinfo.obj .\WrapperJNI___Win32_Debug32\wrapperjni.obj .\WrapperJNI___Win32_Debug32\wrapperjni_unix.obj .\WrapperJNI___Win32_Debug32\wrapperjni_win.obj ] ײ "link.exe @C:\DOCUME~1\leif\LOCALS~1\Temp\RSP322.tmp" ̍쐬

߯ ޳

ْ߲... wrapperinfo.c wrapperjni.c wrapperjni_unix.c wrapperjni_win.c ݸ... ײ WrapperJNI___Win32_Debug32/wrapper.lib Ƶ޼ު WrapperJNI___Win32_Debug32/wrapper.exp 쐬

wrapper.dll - װ 0Ax 0
wrapper_3.5.51_src/src/c/logger.c100644 0 0 506706 14333053650 14061 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ /** * Author: * Johan Sorlin * Leif Mortenson */ #include #include #include #include #include #include #include #include #include "logger_file.h" #ifdef WIN32 #include #include #include #include #include #include #include "messages.h" /* MS Visual Studio 8 went and deprecated the POXIX names for functions. * Fixing them all would be a big headache for UNIX versions. */ #pragma warning(disable : 4996) /* Defines for MS Visual Studio 6 */ #ifndef _INTPTR_T_DEFINED typedef long intptr_t; #define _INTPTR_T_DEFINED #endif #else #include #include #include #include #include #if defined(SOLARIS) #include #include #elif defined(AIX) || defined(HPUX) || defined(MACOSX) #elif defined(FREEBSD) #include #include #else /* LINUX */ #include #include #endif #endif #include "wrapper_i18n.h" #include "logger.h" #ifndef TRUE #define TRUE -1 #endif #ifndef FALSE #define FALSE 0 #endif TCHAR* defaultLogFile; #ifdef WIN32 const TCHAR* syslogName = TEXT("Event Log"); #else const TCHAR* syslogName = TEXT("syslog"); #endif /* Global data for logger */ /* Maximum number of milliseconds that a log write can take before we show a warning. */ int logPrintfWarnThreshold = 0; /* Number of millisecoonds which the previous log message took to process. */ time_t previousLogLag; /* Keep track of when the last log entry was made so we can show the information in the log. */ time_t previousNow; int previousNowMillis; /* Initialize all log levels to unknown until they are set */ int currentConsoleLevel = LEVEL_UNKNOWN; int currentLogfileLevel = LEVEL_UNKNOWN; int currentLoginfoLevel = LEVEL_UNKNOWN; int currentLogSplitMessages = FALSE; int currentLogRegister = TRUE; /* Default syslog facility is LOG_USER */ int currentLogfacilityLevel = LOG_USER; /* Callback notified whenever the active logfile changes. */ void (*logFileChangedCallback)(const TCHAR *logFile); /* Callback to support additional logging format. */ int (*logFormatCountCallback)(const TCHAR format, size_t *reqSize); int (*logFormatPrintCallback)(const TCHAR format, size_t printSize, TCHAR** pBuffer); /* Stores a carefully malloced filename of the most recent log file change. This value is only set in log_printf(), and only cleared in maintainLogger(). */ TCHAR *pendingLogFileChange = NULL; int logPauseTime = -1; int logBufferGrowth = FALSE; TCHAR *logFilePath; int logFilePathHasDateToken = FALSE; /* Keep track if the log file path has changed since we last opened the log file. */ int logFilePathChanged; /* Keep track if the configured log file path has changed since we last opened the log file. */ int confLogFilePathChanged; /* Keep track if the log file was set from the configuration file or generated by the logging. */ int logFilePathSetFromConf = FALSE; /* Size of the currentLogFileName and workLogFileName buffers. */ size_t currentLogFileNameSize; TCHAR *currentLogFileName; TCHAR *workLogFileName; size_t confLogFileNameSize; TCHAR *confLogFileName; TCHAR *workConfLogFileName; int confLogFileLevelInt = LEVEL_UNKNOWN; int whichLogFile; #define LOG_FILE_UNSET 0 #define LOG_FILE_CONFIGURED 1 #define LOG_FILE_DEFAULT 2 #define LOG_FILE_DISABLED 3 int logFileRollMode = ROLL_MODE_SIZE; int confLogFileRollMode = ROLL_MODE_SIZE; int logFileUmask = 0022; #ifndef WIN32 int logFileGroup = -1; /* -1 means the group is unchanged */ #endif TCHAR *logLevelNames[] = { TEXT("NONE "), TEXT("DEBUG "), TEXT("INFO "), TEXT("STATUS"), TEXT("WARN "), TEXT("ERROR "), TEXT("FATAL "), TEXT("ADVICE"), TEXT("NOTICE") }; #ifdef WIN32 TCHAR *defaultLoginfoSourceName = TEXT("wrapper"); TCHAR *loginfoSourceName = NULL; #else char *defaultLoginfoSourceName = "wrapper"; char *loginfoSourceName = NULL; #endif int logFileMaxSize = -1; int confLogFileMaxSize = -1; int logFileMaxLogFiles = -1; int confLogFileMaxLogFiles = -1; TCHAR *logFilePurgePattern = NULL; int logFilePurgeSortMode = LOGGER_FILE_SORT_MODE_TIMES; TCHAR logFileLastNowDate[9]; int enabledDestinationsMask = LOG_DESTINATION_ALL; static int savedLogfileLevel; static int savedConsoleLevel; static int savedLoginfoLevel; /* Defualt formats (Must be 4 chars) */ TCHAR consoleFormat[32]; TCHAR logfileFormat[32]; /* Flag to keep track of whether the console output should be flushed or not. */ int consoleFlush = FALSE; #ifdef WIN32 /* Flag to keep track of whether we should write directly to the console or not. */ int consoleDirect = TRUE; #endif /* Flags to contol where error log level output goes to the console. */ int consoleFatalToStdErr = TRUE; int consoleErrorToStdErr = TRUE; int consoleWarnToStdErr = FALSE; /* Number of seconds since the Wrapper was launched. */ int uptimeSeconds = 0; /* TRUE once the uptime is so large that it is meaningless. */ int uptimeFlipped = FALSE; int isPreload = FALSE; /* Internal function declaration */ #ifdef WIN32 void sendEventlogMessage( int source_id, int level, const TCHAR *szBuff ); #else void sendLoginfoMessage( int source_id, int level, const TCHAR *szBuff ); #endif #ifdef WIN32 int writeToConsole( HANDLE hdl, const TCHAR *lpszFmt, ...); #endif int doesFtellCauseMemoryLeak(); void checkAndRollLogs(const TCHAR *nowDate, size_t printBufferSize); int lockLoggingMutex(); int releaseLoggingMutex(); #if defined(UNICODE) && !defined(WIN32) TCHAR formatMessages[WRAPPER_THREAD_COUNT][QUEUED_BUFFER_SIZE]; #endif int queueWrapped[WRAPPER_THREAD_COUNT]; int queueWriteIndex[WRAPPER_THREAD_COUNT]; int queueReadIndex[WRAPPER_THREAD_COUNT]; TCHAR queueMessages[WRAPPER_THREAD_COUNT][QUEUE_SIZE][QUEUED_BUFFER_SIZE]; int queueSourceIds[WRAPPER_THREAD_COUNT][QUEUE_SIZE]; int queueLevels[WRAPPER_THREAD_COUNT][QUEUE_SIZE]; /* Thread specific work buffers. */ int threadSets[WRAPPER_THREAD_COUNT]; #ifdef WIN32 DWORD threadIds[WRAPPER_THREAD_COUNT]; #else pthread_t threadIds[WRAPPER_THREAD_COUNT]; #endif TCHAR *threadMessageBuffer = NULL; size_t threadMessageBufferSize = 0; size_t threadMessageBufferInitialSize = 100; TCHAR *threadPrintBuffer = NULL; size_t threadPrintBufferSize = 0; #ifdef WIN32 int launcherSource = FALSE; #endif /* Flag which gets set when a log entry is written to the log file. */ int logFileAccessed = FALSE; /* Logger file pointer. It is kept open under high log loads but closed whenever it has been idle. */ FILE *logfileFP = NULL; /** Flag which controls whether or not the logfile is auto flushed after each line. */ int autoFlushLogfile = TRUE; /** Flag which controls whether or not the logfile is auto closed after each line. */ int autoCloseLogfile = 0; /* The number of lines sent to the log file since the getLogfileActivity method was last called. */ DWORD logfileActivityCount = 0; /* Mutex for synchronization of the log_printf function. */ #ifdef WIN32 HANDLE log_printfMutexHandle = NULL; #else pthread_mutex_t log_printfMutex = PTHREAD_MUTEX_INITIALIZER; #endif void outOfMemory(const TCHAR *context, int id) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Out of memory (%s%02d). %s"), context, id, getLastErrorText()); } /* This can be called from within logging code that would otherwise get stuck in recursion. * Log to the console exactly when it happens and then also try to get it into the log * file at the next oportunity. */ void outOfMemoryQueued(const TCHAR *context, int id) { _tprintf(TEXT("Out of memory (%s%02d). %s\n"), context, id, getLastErrorText()); log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Out of memory (%s%02d). %s"), context, id, getLastErrorText()); } #ifdef _DEBUG /** * Used to dump memory directly to the log file in both HEX and readable format. * Useful in debugging applications to track down memory overflows etc. * * @param label A label that will be prepended on all lines of output. * @param memory The memory to be dumped. * @param len The length of the memory to be dumped. */ void log_dumpHex(TCHAR *label, TCHAR *memory, size_t len) { TCHAR *buffer; TCHAR *pos; size_t i; int c; buffer = malloc(sizeof(TCHAR) * (len * 3 + 1)); if (!buffer) { outOfMemory(TEXT("DH"), 1); } pos = buffer; for (i = 0; i < len; i++) { c = memory[i] & 0xff; _sntprintf(pos, 4, TEXT("%02x "), c); pos += 3; } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("%s (HEX) = %s"), label, buffer); pos = buffer; for (i = 0; i < len; i++) { c = memory[i] & 0xff; if (c == 0) { _sntprintf(pos, 4, TEXT("\\0 ")); } else if (c <= 26) { _sntprintf(pos, 4, TEXT("\\%c "), TEXT('a') + c - 1); } else if (c < 127) { _sntprintf(pos, 4, TEXT("%c "), c); } else { _sntprintf(pos, 4, TEXT(". ")); } pos += 3; } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("%s (CHAR) = %s"), label, buffer); free(buffer); } #endif void invalidMultiByteSequence(const TCHAR *context, int id) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Invalid multibyte Sequence found in (%s%02d). %s"), context, id, getLastErrorText()); } /** * Replaces one token with another. The length of the new token must be equal * to or less than that of the old token. * * newToken may be null, implying "". */ TCHAR *replaceStringLongWithShort(TCHAR *string, const TCHAR *oldToken, const TCHAR *newToken) { size_t oldLen = _tcslen(oldToken); size_t newLen; TCHAR *in = string; TCHAR *out = string; if (newToken) { newLen = _tcslen(newToken); } else { newLen = 0; } /* Assertion check. */ if (newLen > oldLen) { return string; } while (in[0] != L'\0') { if (_tcsncmp(in, oldToken, oldLen) == 0) { /* Found the oldToken. Replace it with the new. */ if (newLen > 0) { _tcsncpy(out, newToken, newLen); } in += oldLen; out += newLen; } else { out[0] = in[0]; in++; out++; } } out[0] = L'\0'; return string; } static int isInitialized = FALSE; /** * Return TRUE if the logging is initialized, FALSE otherwise. */ int isLogInitialized() { return isInitialized; } /** * Initializes the logger. Returns 0 if the operation was successful. */ int initLogging(void (*logFileChanged)(const TCHAR *logFile)) { int threadId, i; logFileChangedCallback = logFileChanged; #ifdef WIN32 if (!(log_printfMutexHandle = CreateMutex(NULL, FALSE, NULL))) { _tprintf(TEXT("Failed to create logging mutex. %s\n"), getLastErrorText()); return 1; } #endif defaultLogFile = malloc(sizeof(TCHAR) * 12); if (!defaultLogFile) { _tprintf(TEXT("Out of memory in logging code (%s)\n"), TEXT("IL1")); return 1; } _tcsncpy(defaultLogFile, TEXT("wrapper.log"), 12); defaultLogFile[11] = TEXT('\0'); logPauseTime = -1; loginfoSourceName = defaultLoginfoSourceName; logFileAccessed = FALSE; logFileLastNowDate[0] = L'\0'; for ( threadId = 0; threadId < WRAPPER_THREAD_COUNT; threadId++ ) { threadSets[threadId] = FALSE; /* threadIds[threadId] = 0; */ #if defined(UNICODE) && !defined(WIN32) formatMessages[threadId][0] = TEXT('\0'); #endif for ( i = 0; i < QUEUE_SIZE; i++ ) { queueWrapped[threadId] = 0; queueWriteIndex[threadId] = 0; queueReadIndex[threadId] = 0; queueMessages[threadId][i][0] = TEXT('\0'); queueSourceIds[threadId][i] = 0; queueLevels[threadId][i] = 0; } } isInitialized = TRUE; return 0; } /** * Disposes of any logging resouces prior to shutdown. */ int disposeLogging() { isInitialized = FALSE; #ifdef WIN32 if (log_printfMutexHandle) { if (!CloseHandle(log_printfMutexHandle)) { _tprintf(TEXT("Unable to close Logging Mutex handle. %s\n"), getLastErrorText()); return 1; } } #endif if (threadPrintBuffer && threadPrintBufferSize > 0) { free(threadPrintBuffer); threadPrintBuffer = NULL; threadPrintBufferSize = 0; } if (threadMessageBuffer && threadMessageBufferSize > 0) { free(threadMessageBuffer); threadMessageBuffer = NULL; threadMessageBufferSize = 0; } if (defaultLogFile) { free(defaultLogFile); defaultLogFile = NULL; } if (logFilePath) { free(logFilePath); logFilePath = NULL; } if (currentLogFileName) { free(currentLogFileName); currentLogFileName = NULL; } if (confLogFileName) { free(confLogFileName); confLogFileName = NULL; } if (workLogFileName) { free(workLogFileName); workLogFileName = NULL; } if (workConfLogFileName) { free(workConfLogFileName); workConfLogFileName = NULL; } if (pendingLogFileChange) { free(pendingLogFileChange); pendingLogFileChange = NULL; } if ((loginfoSourceName != defaultLoginfoSourceName) && (loginfoSourceName != NULL)) { free(loginfoSourceName); loginfoSourceName = NULL; } if (logFilePurgePattern) { free(logFilePurgePattern); logFilePurgePattern = NULL; } if (logfileFP) { fclose(logfileFP); logfileFP = NULL; } return 0; } void logRegisterFormatCallbacks(int (*countCallback)(const TCHAR format, size_t *reqSize), int (*printCallback)(const TCHAR format, size_t printSize, TCHAR** pBuffer)) { logFormatCountCallback = countCallback; logFormatPrintCallback = printCallback; } /** Registers the calling thread so it can be recognized when it calls * again later. */ void logRegisterThread( int thread_id ) { #ifdef WIN32 DWORD threadId; threadId = GetCurrentThreadId(); #else pthread_t threadId; threadId = pthread_self(); #endif #ifdef _DEBUG _tprintf(TEXT("logRegisterThread(%d)\n"), thread_id); #endif if ( thread_id >= 0 && thread_id < WRAPPER_THREAD_COUNT ) { threadSets[thread_id] = TRUE; threadIds[thread_id] = threadId; #ifdef _DEBUG _tprintf(TEXT("logRegisterThread(%d) found\n"), thread_id); #endif } } int getThreadId() { int i; #ifdef WIN32 DWORD threadId; threadId = GetCurrentThreadId(); #else pthread_t threadId; threadId = pthread_self(); #endif /*_tprintf(TEXT("threadId=%lu\n"), threadId );*/ for ( i = 0; i < WRAPPER_THREAD_COUNT; i++ ) { #ifdef WIN32 if (threadSets[i] && (threadIds[i] == threadId)) { #else if (threadSets[i] && pthread_equal(threadIds[i], threadId)) { #endif return i; } } _tprintf( TEXT("WARNING - Encountered an unknown thread %ld in getThreadId().\n"), (long int)threadId ); return 0; /* WRAPPER_THREAD_SIGNAL */ } int getLogfileRollModeForName( const TCHAR *logfileRollName ) { if (strcmpIgnoreCase(logfileRollName, TEXT("NONE")) == 0) { return ROLL_MODE_NONE; } else if (strcmpIgnoreCase(logfileRollName, TEXT("SIZE")) == 0) { return ROLL_MODE_SIZE; } else if (strcmpIgnoreCase(logfileRollName, TEXT("WRAPPER")) == 0) { return ROLL_MODE_WRAPPER; } else if (strcmpIgnoreCase(logfileRollName, TEXT("JVM")) == 0) { return ROLL_MODE_JVM; } else if (strcmpIgnoreCase(logfileRollName, TEXT("SIZE_OR_WRAPPER")) == 0) { return ROLL_MODE_SIZE_OR_WRAPPER; } else if (strcmpIgnoreCase(logfileRollName, TEXT("SIZE_OR_JVM")) == 0) { return ROLL_MODE_SIZE_OR_JVM; } else if (strcmpIgnoreCase(logfileRollName, TEXT("DATE")) == 0) { return ROLL_MODE_DATE; } else { return ROLL_MODE_UNKNOWN; } } int getLogLevelForName( const TCHAR *logLevelName ) { if (strcmpIgnoreCase(logLevelName, TEXT("NONE")) == 0) { return LEVEL_NONE; } else if (strcmpIgnoreCase(logLevelName, TEXT("NOTICE")) == 0) { return LEVEL_NOTICE; } else if (strcmpIgnoreCase(logLevelName, TEXT("ADVICE")) == 0) { return LEVEL_ADVICE; } else if (strcmpIgnoreCase(logLevelName, TEXT("FATAL")) == 0) { return LEVEL_FATAL; } else if (strcmpIgnoreCase(logLevelName, TEXT("ERROR")) == 0) { return LEVEL_ERROR; } else if (strcmpIgnoreCase(logLevelName, TEXT("WARN")) == 0) { return LEVEL_WARN; } else if (strcmpIgnoreCase(logLevelName, TEXT("STATUS")) == 0) { return LEVEL_STATUS; } else if (strcmpIgnoreCase(logLevelName, TEXT("INFO")) == 0) { return LEVEL_INFO; } else if (strcmpIgnoreCase(logLevelName, TEXT("DEBUG")) == 0) { return LEVEL_DEBUG; } else { return LEVEL_UNKNOWN; } } #ifndef WIN32 int getLogFacilityForName( const TCHAR *logFacilityName ) { if (strcmpIgnoreCase(logFacilityName, TEXT("USER")) == 0) { return LOG_USER; } else if (strcmpIgnoreCase(logFacilityName, TEXT("LOCAL0")) == 0) { return LOG_LOCAL0; } else if (strcmpIgnoreCase(logFacilityName, TEXT("LOCAL1")) == 0) { return LOG_LOCAL1; } else if (strcmpIgnoreCase(logFacilityName, TEXT("LOCAL2")) == 0) { return LOG_LOCAL2; } else if (strcmpIgnoreCase(logFacilityName, TEXT("LOCAL3")) == 0) { return LOG_LOCAL3; } else if (strcmpIgnoreCase(logFacilityName, TEXT("LOCAL4")) == 0) { return LOG_LOCAL4; } else if (strcmpIgnoreCase(logFacilityName, TEXT("LOCAL5")) == 0) { return LOG_LOCAL5; } else if (strcmpIgnoreCase(logFacilityName, TEXT("LOCAL6")) == 0) { return LOG_LOCAL6; } else if (strcmpIgnoreCase(logFacilityName, TEXT("LOCAL7")) == 0) { return LOG_LOCAL7; } else { return LOG_USER; } } #endif /** * Sets the number of milliseconds to allow logging to take before a warning is logged. * Defaults to 0 for no limit. Possible values 0 to 3600000. * * @param threshold Warning threashold. */ void setLogWarningThreshold(int threshold) { logPrintfWarnThreshold = __max(__min(threshold, 3600000), 0); } /** * Enable or disable log destinations. * At any time, the logging can be turned off for all or a set of destinations. * Even when a destination is disabled, the code can continue to set its log level but * the value will be stored without re-enabling it. This allows the configuration to * be loaded normally and to be used later when the destination will be reactivated. * The same function can be used to re-enable a set of destinations. * * @param currentDestinationsMask Mask of destinations to toggle. * @param enable Wether the destinations should be enabled or disabled. */ void toggleLogDestinations(int currentDestinationsMask, int enable) { int prevEnabledDestinationsMask; int prevEnabled; /* First keep a copy of enabledDestinationsMask, which is a global variable used to toggle the set*LevelInt() functions. * This will allow us to only toggle when the state is changed. */ prevEnabledDestinationsMask = enabledDestinationsMask; if (enable) { /* Add the bits to 'enabledDestinationsMask' for each destination that need to be enabled. * This needs to be done in the beginning so that the set*LevelInt() functions used below work correctly. */ enabledDestinationsMask |= currentDestinationsMask; } /* Now actually toggle each destination. */ if (currentDestinationsMask & LOG_DESTINATION_FILE) { prevEnabled = (prevEnabledDestinationsMask & LOG_DESTINATION_FILE); if ((enable && !prevEnabled) || (!enable && prevEnabled)) { if (enable) { setLogfileLevelInt(savedLogfileLevel); } else { savedLogfileLevel = getLogfileLevelInt(); setLogfileLevelInt(LEVEL_NONE); } } } if (currentDestinationsMask & LOG_DESTINATION_CONSOLE) { prevEnabled = (prevEnabledDestinationsMask & LOG_DESTINATION_CONSOLE); if ((enable && !prevEnabled) || (!enable && prevEnabled)) { if (enable) { setConsoleLogLevelInt(savedConsoleLevel); } else { savedConsoleLevel = getConsoleLogLevelInt(); setConsoleLogLevelInt(LEVEL_NONE); } } } if (currentDestinationsMask & LOG_DESTINATION_SYSLOG) { prevEnabled = (prevEnabledDestinationsMask & LOG_DESTINATION_SYSLOG); if ((enable && !prevEnabled) || (!enable && prevEnabled)) { if (enable) { setSyslogLevelInt(savedLoginfoLevel); } else { savedLoginfoLevel = getSyslogLevelInt(); setSyslogLevelInt(LEVEL_NONE); } } } if (!enable) { /* Remove the bits from 'enabledDestinationsMask' for each destination that need to be disabled. * This needs to be done in the end so that the set*LevelInt() functions used above work correctly. */ enabledDestinationsMask &= ~currentDestinationsMask; } } /** * Sets the console log levels to a simple format for help and usage messages. */ void setSimpleLogLevels() { /* Force the log levels to control output. */ setConsoleLogFormat(TEXT("M")); setConsoleLogLevelInt(LEVEL_INFO); setLogfileLevelInt(LEVEL_NONE); setSyslogLevelInt(LEVEL_NONE); } #ifdef WIN32 /** * This sets a flag which tells the logger that alternate source labels should be used to indicate that the current process is a launcher. */ void setLauncherSource() { launcherSource = TRUE; } #endif int getLoggingIsPreload() { return isPreload; } void setLoggingIsPreload(int value) { isPreload = value; } /* Logfile functions */ int isLogfileAccessed() { return logFileAccessed; } /** * Set the default log file name to an absolute path using the working directory. * This function must be called after setting the working directory. */ int resolveDefaultLogFilePath() { TCHAR* resolved_log_file_path; if (defaultLogFile) { resolved_log_file_path = getAbsolutePathOfFile(defaultLogFile, TEXT("default log file path"), LEVEL_WARN, TRUE); #ifdef _DEBUG log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Absolute path to the default log file resolved to %s."), resolved_log_file_path); #endif if (resolved_log_file_path) { free(defaultLogFile); /* defaultLogFile now points to resolved_log_file_path which was malloced. */ defaultLogFile = resolved_log_file_path; return FALSE; } } return TRUE; } int hasDuplicateToken(const TCHAR *log_file_path, const TCHAR* token) { TCHAR* ptr; ptr = _tcsstr(log_file_path, token); if (ptr && _tcsstr(ptr + 1, token)) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Only one '%s' token is allowed in the name of configured log file."), token); return TRUE; } return FALSE; } int validateLogfilePath(const TCHAR *log_file_path) { if (hasDuplicateToken(log_file_path, TEXT("ROLLNUM"))) { return TRUE; } if (hasDuplicateToken(log_file_path, TEXT("YYYYMMDD"))) { return TRUE; } return FALSE; } /** * Sets the log file to be used. If the specified file is not absolute then * it will be resolved into an absolute path. If there are any problems with * the path, like a directory not existing then the call will fail and the * cause will be written to the existing log. * * @param log_file_path Path to the logfile. * @param isConfigured The value comes from the configuration file. * @param preload TRUE if this is a preload pass. * * @return TRUE if there were any problems. */ int setLogfilePath(const TCHAR *log_file_path, int isConfigured, int preload) { size_t len; TCHAR* prevLogFilePath = NULL; TCHAR* fixed_log_file_path; #ifndef WIN32 int backslashesChanged = FALSE; #endif if (!log_file_path) { return TRUE; } if (!preload && isConfigured && validateLogfilePath(logFilePath)) { return TRUE; } /* Save a copy of logFilePath and free it up. */ if (logFilePath) { len = _tcslen(logFilePath); prevLogFilePath = malloc(sizeof(TCHAR) * (len + 1)); if (!prevLogFilePath) { outOfMemoryQueued(TEXT("SLP"), 1); return TRUE; } _tcsncpy(prevLogFilePath, logFilePath, len + 1); free(logFilePath); logFilePath = NULL; } /* Create a copy of log_file_path and fix '/' and '\' depending on whether it is UNIX or Windows. */ len = _tcslen(log_file_path); fixed_log_file_path = malloc(sizeof(TCHAR) * (len + 1)); if (!fixed_log_file_path) { outOfMemoryQueued(TEXT("SLP"), 2); return TRUE; } _tcsncpy(fixed_log_file_path, log_file_path, len + 1); if (len > 0) { /* An empty value means that the file logging should be disabled. Skip any conversion and keep the value as is. */ #ifdef WIN32 wrapperCorrectWindowsPath(fixed_log_file_path); #else backslashesChanged = wrapperCorrectNixPath(fixed_log_file_path); #endif /* Convert the path to an absolute path. * Log in DEBUG here. We will later show a warning with checkLogfileDir() if the directory does not exist. */ logFilePath = getAbsolutePathOfFile(fixed_log_file_path, TEXT("log file path"), getLoggingIsPreload() ? LEVEL_NONE : LEVEL_DEBUG, FALSE); #ifdef _DEBUG log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Absolute path to the configured log file resolved to %s."), logFilePath); #endif } if (!logFilePath) { /* Continue with the relative path. */ logFilePath = fixed_log_file_path; } else { len = _tcslen(logFilePath); free(fixed_log_file_path); } if (prevLogFilePath) { if (_tcscmp(prevLogFilePath, logFilePath) == 0) { /* The path is unchanged. We don't want to reset currentLogFileName and other variables, so return here. */ free(prevLogFilePath); return FALSE; } free(prevLogFilePath); } /* The currentLogFileNameSize is the size of logFilePath + 10 ("." + a roll number) + 1 (NULL). */ currentLogFileNameSize = len + 10 + 1; if (currentLogFileName) { free(currentLogFileName); } currentLogFileName = malloc(sizeof(TCHAR) * currentLogFileNameSize); if (!currentLogFileName) { outOfMemoryQueued(TEXT("SLP"), 3); free(logFilePath); logFilePath = NULL; return TRUE; } currentLogFileName[0] = TEXT('\0'); if (workLogFileName) { free(workLogFileName); } workLogFileName = malloc(sizeof(TCHAR) * currentLogFileNameSize); if (!workLogFileName) { outOfMemoryQueued(TEXT("SLP"), 4); free(logFilePath); logFilePath = NULL; free(currentLogFileName); currentLogFileNameSize = 0; currentLogFileName = NULL; return TRUE; } workLogFileName[0] = TEXT('\0'); if (_tcsstr(logFilePath, TEXT("YYYYMMDD"))) { logFilePathHasDateToken = TRUE; } else { logFilePathHasDateToken = FALSE; } logFilePathSetFromConf = isConfigured; if (isConfigured) { if ((confLogFileName == NULL) || (strcmpIgnoreCase(logFilePath, confLogFileName) != 0)) { #ifndef WIN32 if (backslashesChanged) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Configured wrapper.logfile contained backslashes converted to forward slashes.")); } #endif if (confLogFileName) { /* This message will be printed in the new configured log file because it is queued. */ log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Configured log file changed from '%s' to '%s'."), confLogFileName, logFilePath); free(confLogFileName); } else { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Configured log file set to '%s'."), logFilePath); } confLogFileNameSize = currentLogFileNameSize; confLogFileName = malloc(sizeof(TCHAR) * confLogFileNameSize); if (!confLogFileName) { outOfMemoryQueued(TEXT("SLP"), 5); free(logFilePath); logFilePath = NULL; free(currentLogFileName); confLogFileNameSize = 0; currentLogFileName = NULL; free(workLogFileName); workLogFileName = NULL; return TRUE; } _tcsncpy(confLogFileName, logFilePath, confLogFileNameSize); if (workConfLogFileName) { free(workConfLogFileName); } workConfLogFileName = malloc(sizeof(TCHAR) * confLogFileNameSize); if (!confLogFileName) { outOfMemoryQueued(TEXT("SLP"), 6); free(logFilePath); logFilePath = NULL; free(currentLogFileName); confLogFileNameSize = 0; currentLogFileName = NULL; free(workLogFileName); workLogFileName = NULL; free(confLogFileName); confLogFileName = NULL; return TRUE; } workConfLogFileName[0] = TEXT('\0'); confLogFilePathChanged = TRUE; } } logFilePathChanged = TRUE; return FALSE; } /** * Returns the default logfile. */ const TCHAR *getDefaultLogfilePath() { return defaultLogFile; } /** * Returns a reference to the currect log file path. * This return value may be changed at any time if the log file is rolled. */ const TCHAR *getLogfilePath() { return logFilePath; } /** * Returns a snapshot of the current log file path. This call safely gets the current path * and returns a copy. It is the responsibility of the caller to free up the memory on * return. Could return null if there was an error. */ TCHAR *getCurrentLogfilePath() { TCHAR *logFileCopy; /* Lock the logging mutex. */ if (lockLoggingMutex()) { return NULL; } /* We should always have a current log file name here because there will be at least one line of log output before this is called. * If that is false then we will return an empty length, but valid, string. */ logFileCopy = malloc(sizeof(TCHAR) * (_tcslen(currentLogFileName) + 1)); if (!logFileCopy) { _tprintf(TEXT("Out of memory in logging code (%s)\n"), TEXT("CLFP1")); } else { _tcsncpy(logFileCopy, currentLogFileName, _tcslen(currentLogFileName) + 1); } /* Release the lock we have on the logging mutex so that other threads can get in. */ if (releaseLoggingMutex()) { if (logFileCopy) { free(logFileCopy); } return NULL; } return logFileCopy; } #ifndef WIN32 /** * Check the directory of the current logfile path to make sure it is writable. * If there are any problems, log a warning. * * @param checkDefaultDir TRUE to check the directory of the default log file (always silent), * FALSE to check the directory of the configured file. * * @return FALSE if the directory is confirmed writable, TRUE otherwise. */ int checkLogfileDir(int checkDefaultDir) { size_t len; TCHAR *c; TCHAR *logFileDir; TCHAR *testfile; int fd; int result = TRUE; len = _tcslen(checkDefaultDir ? defaultLogFile : logFilePath) + 1; if (len > 1) { logFileDir = malloc(len * sizeof(TCHAR)); if (!logFileDir) { outOfMemory(TEXT("CLD"), 1); return TRUE; } _tcsncpy(logFileDir, checkDefaultDir ? defaultLogFile : logFilePath, len); c = _tcsrchr(logFileDir, FILE_SEPARATOR_C); if (c) { c[0] = TEXT('\0'); } else { /* (len > 1) */ logFileDir[0] = TEXT('.'); logFileDir[1] = TEXT('\0'); } /* We want to try writing a test file to the configured log directory to make sure it is writable. */ len = _tcslen(logFileDir) + 23 + 1 + 1000; testfile = malloc(len * sizeof(TCHAR)); if (!testfile) { outOfMemory(TEXT("CLD"), 2); free(logFileDir); return TRUE; } _sntprintf(testfile, len, TEXT("%s%c.wrapper_test-%.4d%.4d"), logFileDir, FILE_SEPARATOR_C, rand() % 9999, rand() % 9999); /* Keep #ifdef WIN32 even though the function is currently not used on this platform. */ if ((fd = _topen(testfile, O_WRONLY | O_CREAT | O_EXCL #ifdef WIN32 , _S_IWRITE #else , S_IRUSR | S_IWUSR #endif )) != -1) { /* Successfully wrote the temp file. */ #ifdef WIN32 _close(fd); #else close(fd); #endif if (_tremove(testfile) == 0) { /* writable! */ result = FALSE; } } free(testfile); free(logFileDir); } return result; } #endif /** * This method will wiat for the specified number of seconds. It is only meant for special * uses within the logging code as it does not itself log any output in a correct way. */ void logSleep(int ms) { #ifdef WIN32 Sleep(ms); #else /* We want to use nanosleep if it is available, but make it possible for the user to build a version that uses usleep if they want. usleep does not behave nicely with signals thrown while sleeping. This was the believed cause of a hang experienced on one Solaris system. */ #ifdef USE_USLEEP wrapperUsleep(ms * 1000); /* microseconds */ #else struct timespec ts; struct timespec tsRemaining; int result; if (ms >= 1000) { ts.tv_sec = ms / 1000; ts.tv_nsec = (ms % 1000) * 1000000; /* nanoseconds */ } else { ts.tv_sec = 0; ts.tv_nsec = ms * 1000000; /* nanoseconds */ } while (((result = nanosleep(&ts, &tsRemaining)) == -1) && (errno == EINTR)) { ts.tv_sec = tsRemaining.tv_sec; ts.tv_nsec = tsRemaining.tv_nsec; } if (result) { if (errno == EAGAIN) { /* On 64-bit AIX this happens once on shutdown. */ return; } else { _tprintf(TEXT("nanosleep(%dms) failed. \n"), ms, getLastErrorText()); } } #endif #endif } /** * Used for testing to set a pause into the next log entry made. * * @param pauseTime Number of seconds to pause, 0 pauses indefinitely. */ void setPauseTime(int pauseTime) { logPauseTime = pauseTime; } /** * Set to true to cause changes in internal buffer sizes to be logged. Useful for debugging. * * @param log TRUE if changes should be logged. */ void setLogBufferGrowth(int log) { logBufferGrowth = log; } void setLogfileRollMode( int log_file_roll_mode ) { logFileRollMode = log_file_roll_mode; confLogFileRollMode = log_file_roll_mode; } int getLogfileRollMode() { return logFileRollMode; } void setLogfileUmask( int log_file_umask ) { logFileUmask = log_file_umask; } #ifndef WIN32 void setLogfileGroup(gid_t log_file_group) { logFileGroup = log_file_group; } #endif void setLogfileFormat( const TCHAR *log_file_format ) { if ( log_file_format != NULL ) { _tcsncpy( logfileFormat, log_file_format, 32 ); /* We only want to time logging if it is needed. */ if ((logPrintfWarnThreshold <= 0) && (_tcschr(log_file_format, TEXT('G')))) { logPrintfWarnThreshold = 99999999; } } } void setLogfileLevelInt( int log_file_level ) { /* Update the configured log level in case the log file was disabled while reloading the configuration. */ if (enabledDestinationsMask & LOG_DESTINATION_FILE) { confLogFileLevelInt = log_file_level; currentLogfileLevel = log_file_level; } else { savedLogfileLevel = log_file_level; } } int getLogfileLevelInt() { return currentLogfileLevel; } void setLogfileLevel( const TCHAR *log_file_level ) { setLogfileLevelInt(getLogLevelForName(log_file_level)); } void setLogfileMaxFileSize( const TCHAR *max_file_size ) { int multiple, i, newLength; TCHAR *tmpFileSizeBuff; TCHAR chr; if ( max_file_size != NULL ) { /* Allocate buffer */ tmpFileSizeBuff = malloc(sizeof(TCHAR) * (_tcslen( max_file_size ) + 1)); if (!tmpFileSizeBuff) { outOfMemoryQueued(TEXT("SLMFS"), 1); return; } /* Generate multiple and remove unwanted chars */ multiple = 1; newLength = 0; for( i = 0; i < (int)_tcslen(max_file_size); i++ ) { chr = max_file_size[i]; switch( chr ) { case TEXT('k'): /* Kilobytes */ case TEXT('K'): multiple = 1024; break; case TEXT('M'): /* Megabytes */ case TEXT('m'): multiple = 1048576; break; } if( (chr >= TEXT('0') && chr <= TEXT('9')) || (chr == TEXT('-')) ) tmpFileSizeBuff[newLength++] = max_file_size[i]; } tmpFileSizeBuff[newLength] = TEXT('\0');/* Crop string */ logFileMaxSize = _ttoi( tmpFileSizeBuff ); if( logFileMaxSize > 0 ) logFileMaxSize *= multiple; /* Free memory */ free( tmpFileSizeBuff ); tmpFileSizeBuff = NULL; if ((logFileMaxSize > 0) && (logFileMaxSize < 1024)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT( "wrapper.logfile.maxsize must be 0 or at least 1024. Changing to %d."), logFileMaxSize); logFileMaxSize = 1024; } confLogFileMaxSize = logFileMaxSize; } } void setLogfileMaxLogFiles( int max_log_files ) { logFileMaxLogFiles = max_log_files; confLogFileMaxLogFiles = max_log_files; } void generateLogFilePattern(TCHAR *buffer, size_t bufferSize); /** * Set the file pattern used to purge old log files. * Every time the log file is rolled, all files matching the specified pattern * will be selected and sorted using the sort mode. The files that exceed the * maximum number of log files will be deleted. * NOTE: This function should always be called after setLogfilePurgeSortMode() * * @param pattern file pattern with wildcard characters. * @param outIsGenerated Boolean out parameter which indicates if the pattern is generated. * When the pattern is generated the first parameter is ignored. */ void setLogfilePurgePattern(const TCHAR *pattern, int* outIsGenerated) { size_t len; TCHAR* newPattern; TCHAR* generatedPattern = NULL; if (logFilePurgePattern) { free(logFilePurgePattern); logFilePurgePattern = NULL; } if (logFilePurgeSortMode == LOGGER_FILE_SORT_MODE_NAMES_SMART) { /* In this mode the pattern needs to be generated. */ generatedPattern = malloc(sizeof(TCHAR) * currentLogFileNameSize); if (!generatedPattern) { outOfMemoryQueued(TEXT("SLPP"), 1); return; } generateLogFilePattern(generatedPattern, currentLogFileNameSize); newPattern = generatedPattern; *outIsGenerated = TRUE; } else { newPattern = (TCHAR*)pattern; } len = _tcslen(newPattern); if (len > 0) { logFilePurgePattern = malloc(sizeof(TCHAR) * (len + 1)); if (!logFilePurgePattern) { free(generatedPattern); outOfMemoryQueued(TEXT("SLPP"), 2); return; } _tcsncpy(logFilePurgePattern, newPattern, len + 1); } free(generatedPattern); } void setLogfilePurgeSortMode(int sortMode) { logFilePurgeSortMode = sortMode; } /** * Disable the logfile. */ void disableLogFile() { /* Don't use setLogfileLevelInt() as it would overwrite confLogFileLevelInt. */ currentLogfileLevel = LEVEL_NONE; whichLogFile = LOG_FILE_DISABLED; } /** * Restore the log level of the logfile. It is the responsibility * of the caller to specify which log file will be used after that. */ void enableLogFile() { /* Restore the log level. */ currentLogfileLevel = confLogFileLevelInt; } /** Returns the number of lines of log file activity since the last call. */ DWORD getLogfileActivity() { DWORD logfileLines; /* Don't worry about synchronization here. Any errors are not critical the way this is used. */ logfileLines = logfileActivityCount; logfileActivityCount = 0; return logfileLines; } /** Obtains a lock on the logging mutex. */ int lockLoggingMutex() { #ifdef WIN32 switch (WaitForSingleObject(log_printfMutexHandle, INFINITE)) { case WAIT_ABANDONED: _tprintf(TEXT("Logging mutex was abandoned.\n")); return -1; case WAIT_FAILED: _tprintf(TEXT("Logging mutex wait failed.\n")); return -1; case WAIT_TIMEOUT: _tprintf(TEXT("Logging mutex wait timed out.\n")); return -1; default: /* Ok */ break; } #else if (pthread_mutex_lock(&log_printfMutex)) { _tprintf(TEXT("Failed to lock the Logging mutex. %s\n"), getLastErrorText()); return -1; } #endif return 0; } /** Releases a lock on the logging mutex. */ int releaseLoggingMutex() { #ifdef WIN32 if (!ReleaseMutex(log_printfMutexHandle)) { _tprintf( TEXT("Failed to release logging mutex. %s\n"), getLastErrorText()); return -1; } #else if (pthread_mutex_unlock(&log_printfMutex)) { _tprintf(TEXT("Failed to unlock the Logging mutex. %s\n"), getLastErrorText()); return -1; } #endif return 0; } /** Sets the auto flush log file flag. */ void setLogfileAutoFlush(int autoFlush) { autoFlushLogfile = autoFlush; } /** Sets the auto close log file flag. */ void setLogfileAutoClose(int autoClose) { autoCloseLogfile = autoClose; } /** Closes the logfile if it is open. */ void closeLogfile() { /* We need to be very careful that only one thread is allowed in here * at a time. On Windows this is done using a Mutex object that is * initialized in the initLogging function. */ if (lockLoggingMutex()) { return; } if (logfileFP != NULL) { #ifdef _DEBUG _tprintf(TEXT("Closing logfile by request...\n")); #endif fclose(logfileFP); logfileFP = NULL; /* Do not clean the currentLogFileName here as the name is not actually changing. */ } /* Release the lock we have on this function so that other threads can get in. */ if (releaseLoggingMutex()) { return; } } /** Flushes any buffered logfile output to the disk. */ void flushLogfile() { /* We need to be very careful that only one thread is allowed in here * at a time. On Windows this is done using a Mutex object that is * initialized in the initLogging function. */ if (lockLoggingMutex()) { return; } if (logfileFP != NULL) { #ifdef _DEBUG _tprintf(TEXT("Flushing logfile by request...\n")); #endif fflush(logfileFP); } /* Release the lock we have on this function so that other threads can get in. */ if (releaseLoggingMutex()) { return; } } /* Console functions */ void setConsoleLogFormat( const TCHAR *console_log_format ) { if ( console_log_format != NULL ) { _tcsncpy( consoleFormat, console_log_format, 32 ); /* We only want to time logging if it is needed. */ if ((logPrintfWarnThreshold <= 0) && (_tcschr(console_log_format, TEXT('G')))) { logPrintfWarnThreshold = 99999999; } } } void setConsoleLogLevelInt( int console_log_level ) { if (enabledDestinationsMask & LOG_DESTINATION_CONSOLE) { currentConsoleLevel = console_log_level; } else { savedConsoleLevel = console_log_level; } } int getConsoleLogLevelInt() { return currentConsoleLevel; } void setConsoleLogLevel( const TCHAR *console_log_level ) { setConsoleLogLevelInt(getLogLevelForName(console_log_level)); } void setConsoleFlush( int flush ) { consoleFlush = flush; } #ifdef WIN32 void setConsoleDirect( int direct ) { consoleDirect = direct; } #endif void setConsoleFatalToStdErr(int toStdErr) { consoleFatalToStdErr = toStdErr; } void setConsoleErrorToStdErr(int toStdErr) { consoleErrorToStdErr = toStdErr; } void setConsoleWarnToStdErr(int toStdErr) { consoleWarnToStdErr = toStdErr; } /* Syslog/eventlog functions */ void setSyslogLevelInt( int loginfo_level ) { if (enabledDestinationsMask & LOG_DESTINATION_SYSLOG) { currentLoginfoLevel = loginfo_level; } else { savedLoginfoLevel = loginfo_level; } } int getSyslogLevelInt() { return currentLoginfoLevel; } void setSyslogLevel( const TCHAR *loginfo_level ) { setSyslogLevelInt(getLogLevelForName(loginfo_level)); } void setSyslogSplitMessages(int splitMessages) { currentLogSplitMessages = splitMessages; } #ifdef WIN32 void setSyslogRegister(int sysRegister) { currentLogRegister = sysRegister; } int getSyslogRegister() { return currentLogRegister; } TCHAR* getSyslogEventSourceName() { return loginfoSourceName; } #endif #ifndef WIN32 void setSyslogFacilityInt( int logfacility_level ) { currentLogfacilityLevel = logfacility_level; } void setSyslogFacility( const TCHAR *loginfo_level ) { setSyslogFacilityInt(getLogFacilityForName(loginfo_level)); } #endif void setSyslogEventSourceName( const TCHAR *event_source_name ) { size_t size; if (event_source_name != NULL) { if (loginfoSourceName != defaultLoginfoSourceName) { if (loginfoSourceName != NULL) { free(loginfoSourceName); } } #ifdef WIN32 size = sizeof(TCHAR) * (_tcslen(event_source_name) + 1); #else size = wcstombs(NULL, event_source_name, 0); if (size == (size_t)-1) { return; } size++; #endif loginfoSourceName = malloc(size); if (!loginfoSourceName) { _tprintf(TEXT("Out of memory in logging code (%s)\n"), TEXT("SSESN")); loginfoSourceName = defaultLoginfoSourceName; return; } #ifdef WIN32 _tcsncpy(loginfoSourceName, event_source_name, _tcslen(event_source_name) + 1); if (_tcslen(loginfoSourceName) > 32) { loginfoSourceName[32] = TEXT('\0'); } #else wcstombs(loginfoSourceName, event_source_name, size); if (strlen(loginfoSourceName) > 32) { loginfoSourceName[32] = '\0'; } #endif } } void resetDuration() { #ifdef WIN32 struct _timeb timebNow; #else struct timeval timevalNow; #endif time_t now; int nowMillis; #ifdef WIN32 _ftime(&timebNow); now = (time_t)timebNow.time; nowMillis = timebNow.millitm; #else gettimeofday(&timevalNow, NULL); now = (time_t)timevalNow.tv_sec; nowMillis = timevalNow.tv_usec / 1000; #endif previousNow = now; previousNowMillis = nowMillis; } int getLowLogLevel() { int lowLogLevel = (currentLogfileLevel < currentConsoleLevel ? currentLogfileLevel : currentConsoleLevel); lowLogLevel = (currentLoginfoLevel < lowLogLevel ? currentLoginfoLevel : lowLogLevel); return lowLogLevel; } void setThreadMessageBufferInitialSize(int initialValue) { /* do not allow too big and too small values */ threadMessageBufferInitialSize = __min(__max(initialValue, 100), 32768); } TCHAR* preparePrintBuffer(size_t reqSize) { if (threadPrintBuffer == NULL) { threadPrintBuffer = malloc(sizeof(TCHAR) * reqSize); if (!threadPrintBuffer) { _tprintf(TEXT("Out of memory in logging code (%s)\n"), TEXT("PPB1")); threadPrintBufferSize = 0; return NULL; } threadPrintBufferSize = reqSize; } else if (threadPrintBufferSize < reqSize) { free(threadPrintBuffer); threadPrintBuffer = malloc(sizeof(TCHAR) * reqSize); if (!threadPrintBuffer) { _tprintf(TEXT("Out of memory in logging code (%s)\n"), TEXT("PPB2")); threadPrintBufferSize = 0; return NULL; } threadPrintBufferSize = reqSize; } return threadPrintBuffer; } /* Returns the number of columns and come up with a required length for the printBuffer. */ int GetColumnsAndReqSizeForPrintBuffer(const TCHAR *format, const TCHAR *message, size_t *reqSize) { int i; int numColumns; *reqSize = 0; for( i = 0, numColumns = 0; i < (int)_tcslen( format ); i++ ) { switch( format[i] ) { #ifdef LOGGER_TEST_NULL_FORMAT case TEXT('0'): *reqSize += 1 + 3; numColumns++; break; #endif case TEXT('P'): case TEXT('p'): *reqSize += 8 + 3; numColumns++; break; case TEXT('L'): case TEXT('l'): *reqSize += 6 + 3; numColumns++; break; case TEXT('D'): case TEXT('d'): *reqSize += 7 + 3; numColumns++; break; case TEXT('Q'): case TEXT('q'): *reqSize += 1 + 3; numColumns++; break; case TEXT('T'): case TEXT('t'): *reqSize += 19 + 3; numColumns++; break; case TEXT('Z'): case TEXT('z'): *reqSize += 23 + 3; numColumns++; break; case TEXT('U'): case TEXT('u'): *reqSize += 8 + 3; numColumns++; break; case TEXT('R'): case TEXT('r'): *reqSize += 8 + 3; numColumns++; break; case TEXT('G'): case TEXT('g'): *reqSize += 10 + 3; numColumns++; break; case TEXT('M'): case TEXT('m'): *reqSize += _tcslen( message ) + 3; numColumns++; break; default: if (logFormatCountCallback && logFormatCountCallback(format[i], reqSize)) { numColumns++; } } } return numColumns; } /* Writes to and then returns a buffer that is reused by the current thread. * It should not be released. */ TCHAR* buildPrintBuffer( int source_id, int level, int threadId, int queued, struct tm *nowTM, int nowMillis, time_t durationMillis, const TCHAR *format, const TCHAR *defaultFormat, const TCHAR *message) { int i; size_t reqSize; int numColumns; TCHAR *pos; int currentColumn; int handledFormat; int temp; int len; numColumns = GetColumnsAndReqSizeForPrintBuffer(format, message, &reqSize); if ((reqSize == 0) && (defaultFormat != NULL)) { /* This means that the specified format was completely invalid. * Recurse using the defaultFormat instead. * The alternative would be to log an empty line, which is useless to everyone. */ return buildPrintBuffer( source_id, level, threadId, queued, nowTM, nowMillis, durationMillis, defaultFormat, NULL /* No default. Prevent further recursion. */, message ); } /* Always add room for the null. */ reqSize += 1; if ( !preparePrintBuffer(reqSize)) { return NULL; } /* Always start with a null terminated string in case there are no formats specified. */ threadPrintBuffer[0] = TEXT('\0'); /* Create a pointer to the beginning of the print buffer, it will be advanced * as the formatted message is build up. */ pos = threadPrintBuffer; /* We now have a buffer large enough to store the entire formatted message. */ for( i = 0, currentColumn = 0, len = 0, temp = 0; i < (int)_tcslen( format ); i++ ) { handledFormat = TRUE; switch( format[i] ) { #ifdef LOGGER_TEST_NULL_FORMAT case TEXT('0'): pos[0] = TEXT('\0'); temp = 1; currentColumn++; break; #endif case TEXT('P'): case TEXT('p'): switch ( source_id ) { case WRAPPER_SOURCE_WRAPPER: #ifdef WIN32 if (launcherSource) { temp = _sntprintf( pos, reqSize - len, TEXT("wrapperm") ); } else { temp = _sntprintf( pos, reqSize - len, TEXT("wrapper ") ); } #else temp = _sntprintf( pos, reqSize - len, TEXT("wrapper ") ); #endif break; case WRAPPER_SOURCE_PROTOCOL: temp = _sntprintf( pos, reqSize - len, TEXT("wrapperp") ); break; case WRAPPER_SOURCE_JVM_VERSION: temp = _sntprintf( pos, reqSize - len, TEXT("jvm ver.") ); break; default: temp = _sntprintf( pos, reqSize - len, TEXT("jvm %-4d"), source_id ); break; } currentColumn++; break; case TEXT('L'): case TEXT('l'): temp = _sntprintf( pos, reqSize - len, TEXT("%s"), logLevelNames[ level ] ); currentColumn++; break; case TEXT('D'): case TEXT('d'): switch ( threadId ) { case WRAPPER_THREAD_SIGNAL: temp = _sntprintf( pos, reqSize - len, TEXT("signal ") ); break; case WRAPPER_THREAD_MAIN: temp = _sntprintf( pos, reqSize - len, TEXT("main ") ); break; case WRAPPER_THREAD_SRVMAIN: temp = _sntprintf( pos, reqSize - len, TEXT("srvmain") ); break; case WRAPPER_THREAD_TIMER: temp = _sntprintf( pos, reqSize - len, TEXT("timer ") ); break; #ifdef WIN32 case WRAPPER_THREAD_MESSAGE: temp = _sntprintf( pos, reqSize - len, TEXT("message") ); break; #else case WRAPPER_THREAD_JAVAIN: temp = _sntprintf( pos, reqSize - len, TEXT("javain ") ); break; #endif case WRAPPER_THREAD_JAVAIO: temp = _sntprintf( pos, reqSize - len, TEXT("javaio ") ); break; case WRAPPER_THREAD_STARTUP: temp = _sntprintf( pos, reqSize - len, TEXT("startup") ); break; default: temp = _sntprintf( pos, reqSize - len, TEXT("unknown") ); break; } currentColumn++; break; case TEXT('Q'): case TEXT('q'): temp = _sntprintf( pos, reqSize - len, TEXT("%c"), ( queued ? TEXT('Q') : TEXT(' '))); currentColumn++; break; case TEXT('T'): case TEXT('t'): temp = _sntprintf( pos, reqSize - len, TEXT("%04d/%02d/%02d %02d:%02d:%02d"), nowTM->tm_year + 1900, nowTM->tm_mon + 1, nowTM->tm_mday, nowTM->tm_hour, nowTM->tm_min, nowTM->tm_sec ); currentColumn++; break; case TEXT('Z'): case TEXT('z'): temp = _sntprintf( pos, reqSize - len, TEXT("%04d/%02d/%02d %02d:%02d:%02d.%03d"), nowTM->tm_year + 1900, nowTM->tm_mon + 1, nowTM->tm_mday, nowTM->tm_hour, nowTM->tm_min, nowTM->tm_sec, nowMillis ); currentColumn++; break; case TEXT('U'): case TEXT('u'): if (uptimeFlipped) { temp = _sntprintf( pos, reqSize - len, TEXT("--------") ); } else { temp = _sntprintf( pos, reqSize - len, TEXT("%8d"), uptimeSeconds); } currentColumn++; break; case TEXT('R'): case TEXT('r'): if (durationMillis == (time_t)-1) { temp = _sntprintf( pos, reqSize - len, TEXT(" ") ); } else if (durationMillis > 99999999) { temp = _sntprintf( pos, reqSize - len, TEXT("99999999") ); } else { temp = _sntprintf( pos, reqSize - len, TEXT("%8d"), durationMillis); } currentColumn++; break; case TEXT('G'): case TEXT('g'): temp = _sntprintf( pos, reqSize - len, TEXT("%8d"), __min(previousLogLag, 99999999)); currentColumn++; break; case TEXT('M'): case TEXT('m'): temp = _sntprintf( pos, reqSize - len, TEXT("%s"), message ); currentColumn++; break; default: if (logFormatPrintCallback && (temp = logFormatPrintCallback(format[i], reqSize - len, &pos))) { currentColumn++; } else { handledFormat = FALSE; } } if (handledFormat) { pos += temp; len += temp; /* Add separator chars */ if (currentColumn != numColumns) { temp = _sntprintf(pos, reqSize - len, TEXT(" | ")); pos += temp; len += temp; } } } /* Return the print buffer to the caller. */ return threadPrintBuffer; } /** * Generates a log file name given. * * buffer - Buffer into which to _sntprintf the generated name. * bufferSize - Size of the buffer. * template - Template from which the name is generated. * nowDate - Optional date used to replace any YYYYMMDD tokens. * rollNum - Optional roll number used to replace any ROLLNUM tokens. */ void generateLogFileName(TCHAR *buffer, size_t bufferSize, const TCHAR *template, const TCHAR *nowDate, const TCHAR *rollNum ) { size_t bufferLen; /* Copy the template to the buffer to get started. */ _tcsncpy(buffer, template, _tcslen(template) + 1); /* Handle the date token. */ if (_tcsstr(buffer, TEXT("YYYYMMDD"))) { /* The token needs to be replaced. */ replaceStringLongWithShort(buffer, TEXT("YYYYMMDD"), nowDate); } /* Handle the roll number token. */ if (_tcsstr(buffer, TEXT("ROLLNUM"))) { if (rollNum == NULL ) { /* The token needs to be removed. */ replaceStringLongWithShort(buffer, TEXT("-ROLLNUM"), NULL); replaceStringLongWithShort(buffer, TEXT("_ROLLNUM"), NULL); replaceStringLongWithShort(buffer, TEXT(".ROLLNUM"), NULL); replaceStringLongWithShort(buffer, TEXT("ROLLNUM"), NULL); } else { /* The token needs to be replaced. */ replaceStringLongWithShort(buffer, TEXT("ROLLNUM"), rollNum); } } else { /* The name did not contain a ROLLNUM token. */ if (rollNum != NULL ) { bufferLen = _tcslen(buffer); if (_tcscmp(rollNum, TEXT("*")) == 0) { /* Append the '*' at the end. */ _sntprintf(buffer + bufferLen, bufferSize - bufferLen, TEXT("%s"), rollNum); } else { /* Generate the name as if ".ROLLNUM" was appended to the template. */ _sntprintf(buffer + bufferLen, bufferSize - bufferLen, TEXT(".%s"), rollNum); } buffer[bufferSize - 1] = TEXT('\0'); } } } void generateLogFilePattern(TCHAR *buffer, size_t bufferSize) { generateLogFileName(buffer, bufferSize, logFilePath, TEXT("????????"), TEXT("*")); } #ifndef WIN32 void generateCurrentLogFileName(TCHAR** pFileName) { struct stat fileStat; struct timeval timevalNow; struct tm *nowTM; time_t now; TCHAR nowDate[9]; TCHAR *buffer; size_t size; size = _tcslen(*pFileName) + 1; buffer = malloc(sizeof(TCHAR) * size); if (buffer) { gettimeofday(&timevalNow, NULL); now = (time_t)timevalNow.tv_sec; nowTM = localtime(&now); _sntprintf(nowDate, 9, TEXT("%04d%02d%02d"), nowTM->tm_year + 1900, nowTM->tm_mon + 1, nowTM->tm_mday); generateLogFileName(buffer, size, *pFileName, nowDate, NULL); if (_tcsstr(*pFileName, TEXT("YYYYMMDD")) && (nowTM->tm_hour == 0) && (nowTM->tm_min == 0)) { /* We are close to midnight... check if the file exists, if not try to get the file from the previous day. */ if (_tstat(buffer, &fileStat) != 0) { now -= 3600; /* -1h (though -1min would be enough). */ nowTM = localtime(&now); _sntprintf(nowDate, 9, TEXT("%04d%02d%02d"), nowTM->tm_year + 1900, nowTM->tm_mon + 1, nowTM->tm_mday); generateLogFileName(buffer, size, *pFileName, nowDate, NULL); } } free(*pFileName); *pFileName = buffer; } } #endif /** * Prints the contents of a buffer to the sysLog target. The log level is * tested prior to this function being called. * * Must be called while locked. */ void log_printf_message_sysLogInner(int source_id, int level, TCHAR *message, struct tm *nowTM) { #ifdef WIN32 sendEventlogMessage(source_id, level, message); #else sendLoginfoMessage(source_id, level, message); #endif } /** * @param invertLogLevelCheck There is a special case where we want to log to * the syslog IF and only if the normal log output * would not go to the syslog. This flag makes it * possible to log it if we normally would not. */ void log_printf_message_sysLog(int source_id, int level, TCHAR *message, struct tm *nowTM, int invertLogLevelCheck) { switch (level) { case LEVEL_NOTICE: case LEVEL_ADVICE: /* Advice and Notice level messages are special in that they never get logged to the * EventLog / SysLog. */ break; default: if (invertLogLevelCheck) { /* Log if we normally should not log. */ if (level < currentLoginfoLevel) { log_printf_message_sysLogInner(source_id, level, message, nowTM); } } else { if (level >= currentLoginfoLevel) { log_printf_message_sysLogInner(source_id, level, message, nowTM); } } } } static void setDefaultRolling() { logFileRollMode = ROLL_MODE_SIZE; logFileMaxSize = 5 * 1048576; logFileMaxLogFiles = 1; } static void restoreConfiguredRolling() { logFileRollMode = confLogFileRollMode; logFileMaxSize = confLogFileMaxSize; logFileMaxLogFiles = confLogFileMaxLogFiles; } static void printFailoverFileHeader(TCHAR* confFileName) { _ftprintf(logfileFP, TEXT("********************************************************************************\n")); _ftprintf(logfileFP, TEXT("* This is a Java Service Wrapper failover log file.\n* Was unable to write to %s.\n"), confFileName); _ftprintf(logfileFP, TEXT("********************************************************************************\n\n")); } #ifndef WIN32 void changeLogFileGroup(const TCHAR* path) { if (_tchown(path, -1, logFileGroup) == -1) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to change the group of %s. %s"), TEXT("the log file"), getLastErrorText()); } } #endif /** * Open log file in the following order of priority: configured log file, default log file, no log file. * This function will also try to resume logging in a file with a higher priority as soon as it becomes * available. * * Must be called while locked. * * @return True if the logfile name changed. */ int openLogFile(struct tm *nowTM, TCHAR *message) { static TCHAR confLogFileStopDateStr[20]; int logFileChanged = FALSE; TCHAR nowDate[9]; int old_umask; const TCHAR *tempBufferFormat; TCHAR tempBufferLastErrorText1[1024]; TCHAR tempBufferLastErrorText2[1024]; size_t tempBufferLen; TCHAR *tempBuffer; TCHAR tempConfLogFileResumeDateStr[20]; size_t reqSize = 0; char *messageMB; size_t messageMBMaxLen = 0; FILE *tempLogfileFP = NULL; int dummyReset = FALSE; int reset = FALSE; #if defined(WIN32) && !defined(WIN64) struct _stat64i32 fileStat; #else struct stat fileStat; #endif /* If the log file was set to a blank value then it will not be used. */ if (logFilePath && (_tcslen(logFilePath) > 0)) { /* If this the roll mode is date then we need a nowDate for this log entry. */ _sntprintf(nowDate, 9, TEXT("%04d%02d%02d"), nowTM->tm_year + 1900, nowTM->tm_mon + 1, nowTM->tm_mday ); /* If ftell() can't be used, we need the size of the logging message in order to calculate the size of the buffered data that is not flushed. */ if (doesFtellCauseMemoryLeak()) { /* We will not use ftell(), so we have to take into account the size of the buffered data that have not been flushed yet. */ GetColumnsAndReqSizeForPrintBuffer(logfileFormat, message, &reqSize); /* The previous function will process the length of message in number of characters. We want it in bytes. */ messageMBMaxLen = _tcslen(message) * sizeof(TCHAR); if (messageMBMaxLen > 0) { messageMB = malloc(messageMBMaxLen); if (!messageMB) { outOfMemoryQueued(TEXT("OLF"), 1); } else { wcstombs(messageMB, message, messageMBMaxLen); reqSize -= _tcslen(message); reqSize += strlen(messageMB); /* Actually GetColumnsAndReqSizeForPrintBuffer() return 3 characters more than needed, but we need to add 2 more caracters for carriage return. */ reqSize -= 1; } free(messageMB); } } /* Make sure that the log file does not need to be rolled. */ checkAndRollLogs(nowDate, reqSize); if (confLogFileName) { /* The logging configuration is now loaded and the configured log file is known. */ workConfLogFileName[0] = TEXT('\0'); /* Check if the log file path has changed since the last call. */ if (logFilePathChanged && (whichLogFile != LOG_FILE_UNSET)) { if (confLogFilePathChanged) { if (whichLogFile == LOG_FILE_CONFIGURED) { /* We are moving from one configured log file to another. We need to reinit. */ reset = TRUE; } else { old_umask = umask( logFileUmask ); generateLogFileName(workConfLogFileName, confLogFileNameSize, confLogFileName, nowDate, NULL); if ((tempLogfileFP = _tfopen(workConfLogFileName, TEXT("a"))) == NULL) { /* The configured log file has changed but is not accessible. Reset the file opening * system in its original state to clearly show that the new file is not accessible. */ dummyReset = TRUE; #ifndef WIN32 } else if (logFileGroup != -1) { changeLogFileGroup(workConfLogFileName); #endif } umask(old_umask); } if (reset || dummyReset) { /* Now actually reset the file opening system */ if (logfileFP != NULL) { /* Close the previous log file. We can do this safely because we are already locked. */ fclose(logfileFP); logfileFP = NULL; } if (whichLogFile == LOG_FILE_DISABLED) { /* We previously disabled file logging. Reactivate it. */ enableLogFile(); } whichLogFile = LOG_FILE_UNSET; } } if (whichLogFile == LOG_FILE_DEFAULT) { /* Continue with the default file. If the configured log file changed, we will resume logging in it just below. */ setLogfilePath(defaultLogFile, FALSE, FALSE); _sntprintf(currentLogFileName, currentLogFileNameSize, TEXT("%s"), defaultLogFile); setDefaultRolling(); } } /* If we previously fell back to the default log file or disabled log file, try to return to the configured log file */ if ((whichLogFile != LOG_FILE_UNSET) && (whichLogFile != LOG_FILE_CONFIGURED) && (confLogFileName != NULL)) { /* Check if the configured log file is available */ old_umask = umask( logFileUmask ); /* Generate the log file name if it is not already set. */ if (workConfLogFileName[0] == TEXT('\0')) { generateLogFileName(workConfLogFileName, confLogFileNameSize, confLogFileName, nowDate, NULL); } tempLogfileFP = _tfopen(workConfLogFileName, TEXT("a")); if (!tempLogfileFP) { _tcsncpy(tempBufferLastErrorText1, getLastErrorText(), 1023); tempBufferLastErrorText1[1023] = 0; } else { #ifndef WIN32 if (logFileGroup != -1) { changeLogFileGroup(workConfLogFileName); } #endif if (logfileFP != NULL) { /* Make sure to close the default log file (we have not set logfileFP yet). */ /* We are already locked. */ fclose(logfileFP); logfileFP = NULL; } if (whichLogFile == LOG_FILE_DISABLED) { /* We previously disabled file logging. Reactivate it. */ enableLogFile(); } /* We need to write our message into a buffer manually so we can use it * both for the log_printf_queue and log_printf_message_sysLog calls below. */ tempBufferFormat = TEXT("The messages could not be logged in the configured log file (%s) between %s and %s.\n The missing log entries may be found in the default log file (%s) of the current working directory or in the %s."); _sntprintf(tempConfLogFileResumeDateStr, 20, TEXT("%04d/%02d/%02d %02d:%02d:%02d"), nowTM->tm_year + 1900, nowTM->tm_mon + 1, nowTM->tm_mday, nowTM->tm_hour, nowTM->tm_min, nowTM->tm_sec ); tempConfLogFileResumeDateStr[19] = 0; tempBufferLen = _tcslen(tempBufferFormat) - 2 - 2 - 2 - 2 - 2 + _tcslen(workConfLogFileName) + _tcslen(confLogFileStopDateStr) + _tcslen(tempConfLogFileResumeDateStr) + _tcslen(defaultLogFile) + _tcslen(syslogName) + 1; tempBuffer = malloc(sizeof(TCHAR) * tempBufferLen); if (!tempBuffer) { outOfMemoryQueued(TEXT("OLF"), 2); } else { _sntprintf(tempBuffer, tempBufferLen, tempBufferFormat, workConfLogFileName, confLogFileStopDateStr, tempConfLogFileResumeDateStr, defaultLogFile, syslogName); log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("%s"), tempBuffer); /* This is critical for debugging problems. If the above message would not have shown * up in the syslog then send it there manually. We are already locked here so this * can be done safely. */ log_printf_message_sysLog(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, tempBuffer, nowTM, TRUE); free(tempBuffer); } /* Set the log file path */ setLogfilePath(confLogFileName, FALSE, FALSE); _sntprintf(currentLogFileName, currentLogFileNameSize, TEXT("%s"), workConfLogFileName); restoreConfiguredRolling(); logFileChanged = TRUE; whichLogFile = LOG_FILE_CONFIGURED; logfileFP = tempLogfileFP; } umask(old_umask); } } /* If the file needs to be opened then do so. */ if (logfileFP == NULL) { old_umask = umask( logFileUmask ); /* Generate the log file name if it is not already set. */ if (whichLogFile != LOG_FILE_DISABLED) { if (currentLogFileName[0] == TEXT('\0')) { generateLogFileName(currentLogFileName, currentLogFileNameSize, logFilePath, nowDate, NULL); logFileChanged = TRUE; } logfileFP = _tfopen(currentLogFileName, TEXT("a")); if (!logfileFP) { if (whichLogFile == LOG_FILE_DEFAULT) { _tcsncpy(tempBufferLastErrorText2, getLastErrorText(), 1023); tempBufferLastErrorText2[1023] = 0; } else { _tcsncpy(tempBufferLastErrorText1, getLastErrorText(), 1023); tempBufferLastErrorText1[1023] = 0; } #ifndef WIN32 } else if (logFileGroup != -1) { changeLogFileGroup(currentLogFileName); #endif } } if (logfileFP != NULL) { if (whichLogFile == LOG_FILE_UNSET) { whichLogFile = LOG_FILE_CONFIGURED; } else if ((whichLogFile == LOG_FILE_DEFAULT) && (_tstat(logFilePath, &fileStat) == 0) && (fileStat.st_size == 0)) { /* Generate the log file name if it is not already set. */ if (workConfLogFileName[0] == TEXT('\0')) { generateLogFileName(workConfLogFileName, confLogFileNameSize, confLogFileName, nowDate, NULL); } /* This is a new default log file, so add a header. */ printFailoverFileHeader(workConfLogFileName); } } else { if (whichLogFile != LOG_FILE_DEFAULT) { logfileFP = _tfopen(defaultLogFile, TEXT("a")); if (!logfileFP) { _tcsncpy(tempBufferLastErrorText2, getLastErrorText(), 1023); tempBufferLastErrorText2[1023] = 0; #ifndef WIN32 } else if (logFileGroup != -1) { changeLogFileGroup(defaultLogFile); #endif } if (whichLogFile == LOG_FILE_DISABLED) { if (logfileFP != NULL) { enableLogFile(); } else { /* The logging was disabled and both the default file and the configured file could not be opened. */ confLogFilePathChanged = FALSE; logFilePathChanged = FALSE; return FALSE; } } else if (!dummyReset) { /* Save the date in string format so that we don't need to worry about time changes. */ _sntprintf(confLogFileStopDateStr, 20, TEXT("%04d/%02d/%02d %02d:%02d:%02d"), nowTM->tm_year + 1900, nowTM->tm_mon + 1, nowTM->tm_mday, nowTM->tm_hour, nowTM->tm_min, nowTM->tm_sec ); confLogFileStopDateStr[19] = 0; } /* We need to write our error message into a buffer manually so we can use it * both for the log_printf_queue and log_printf_message_sysLog calls below. */ tempBufferFormat = TEXT("Unable to write to the configured log file: %s (%s)\n Falling back to the default file in the current working directory: %s"); tempBufferLen = _tcslen(tempBufferFormat) - 2 - 2 - 2 + _tcslen(currentLogFileName) + _tcslen(tempBufferLastErrorText1) + _tcslen(defaultLogFile) + 1; tempBuffer = malloc(sizeof(TCHAR) * tempBufferLen); if (!tempBuffer) { outOfMemoryQueued(TEXT("OLF"), 3); } else { _sntprintf(tempBuffer, tempBufferLen, tempBufferFormat, currentLogFileName, tempBufferLastErrorText1, defaultLogFile); log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("%s"), tempBuffer); /* This is critical for debugging problems. If the above message would not have shown * up in the syslog then send it there manually. We are already locked here so this * can be done safely. */ log_printf_message_sysLog(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, tempBuffer, nowTM, TRUE); free(tempBuffer); } setLogfilePath(defaultLogFile, FALSE, FALSE); _sntprintf(currentLogFileName, currentLogFileNameSize, TEXT("%s"), defaultLogFile); setDefaultRolling(); logFileChanged = TRUE; } if (logfileFP != NULL) { whichLogFile = LOG_FILE_DEFAULT; if ((_tstat(logFilePath, &fileStat) == 0) && (fileStat.st_size == 0)) { /* Generate the log file name if it is not already set. */ if (workConfLogFileName[0] == TEXT('\0')) { generateLogFileName(workConfLogFileName, confLogFileNameSize, confLogFileName, nowDate, NULL); } /* This is a new default log file, so add a header. */ printFailoverFileHeader(workConfLogFileName); } } else { /* Still unable to write. */ /* We need to write our error message into a buffer manually so we can use it * both for the log_printf_queue and log_printf_message_sysLog calls below. */ tempBufferFormat = TEXT("Unable to write to the default log file: %s (%s)\n Disabling log file."); tempBufferLen = _tcslen(tempBufferFormat) - 2 - 2 + _tcslen(currentLogFileName) + _tcslen(tempBufferLastErrorText2) + 1; tempBuffer = malloc(sizeof(TCHAR) * tempBufferLen); if (!tempBuffer) { outOfMemoryQueued(TEXT("OLF"), 4); } else { _sntprintf(tempBuffer, tempBufferLen, tempBufferFormat, currentLogFileName, tempBufferLastErrorText2); log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("%s"), tempBuffer); /* This is critical for debugging problems. If the above message would not have shown * up in the syslog then send it there manually. We are already locked here so this * can be done safely. */ log_printf_message_sysLog(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, tempBuffer, nowTM, TRUE); free(tempBuffer); } disableLogFile(); logFileChanged = FALSE; } } umask(old_umask); #ifdef _DEBUG if (logfileFP != NULL) { _tprintf(TEXT("Opened logfile\n")); } #endif } if (logfileFP == NULL) { currentLogFileName[0] = TEXT('\0'); /* Failure to write to logfile already reported. */ } else if (whichLogFile == LOG_FILE_CONFIGURED) { /* Store the time at which the configured file was opened. * The default log file is not rolled by date and doesn't need this info. */ _tcsncpy(logFileLastNowDate, nowDate, 9); } } confLogFilePathChanged = FALSE; logFilePathChanged = FALSE; return logFileChanged; } /** * Prints the contents of a buffer to the logfile target. The log level is * tested prior to this function being called. * * Must be called while locked. */ void log_printf_message_logFileInner(int source_id, int level, int threadId, int queued, TCHAR *message, struct tm *nowTM, int nowMillis, time_t durationMillis) { TCHAR *printBuffer; if (logfileFP != NULL) { /* Build up the printBuffer. */ printBuffer = buildPrintBuffer(source_id, level, threadId, queued, nowTM, nowMillis, durationMillis, logfileFormat, LOG_FORMAT_LOGFILE_DEFAULT, message); if (printBuffer) { _ftprintf(logfileFP, TEXT("%s\n"), printBuffer); logFileAccessed = TRUE; /* Increment the activity counter. */ logfileActivityCount++; /* Decide whether we want to close or flush the log file immediately after each line. * If not then flushing and closing will be handled externally by calling flushLogfile() or closeLogfile(). */ if (autoCloseLogfile) { /* Close the log file immediately. */ #ifdef _DEBUG _tprintf(TEXT("Closing logfile immediately...\n")); #endif fclose(logfileFP); logfileFP = NULL; /* Do not clear the currentLogFileName here as we are not changing its name. */ } else if (autoFlushLogfile) { /* Flush the log file immediately. */ #ifdef _DEBUG _tprintf(TEXT("Flushing logfile immediately...\n")); #endif fflush(logfileFP); } /* Leave the file open. It will be closed later after a period of inactivity. */ } } } int log_printf_message_logFile(int source_id, int level, int threadId, int queued, TCHAR *message, struct tm *nowTM, int nowMillis, time_t durationMillis) { int logFileChanged = FALSE; if ((level >= currentLogfileLevel) || (whichLogFile == LOG_FILE_DISABLED)) { logFileChanged = openLogFile(nowTM, message); if (level >= currentLogfileLevel) { log_printf_message_logFileInner(source_id, level, threadId, queued, message, nowTM, nowMillis, durationMillis); } } return logFileChanged; } /* Write the print buffer to the console. */ void printToConsoleInner(const TCHAR *printBuffer, FILE *target, int newLine) { const TCHAR* fmt = newLine ? TEXT("%s\n") : TEXT("%s"); #ifdef WIN32 HANDLE targetH; int complete = FALSE; /* Using the WinAPI function WriteConsole would make it impossible to pipe the console output. * We never want to allow direct console writing if this is a launcher instance.*/ if (consoleDirect) { if (target == stderr) { targetH = GetStdHandle(STD_ERROR_HANDLE); } else { targetH = GetStdHandle(STD_OUTPUT_HANDLE); } if (targetH != NULL) { complete = writeToConsole(targetH, fmt, printBuffer); } else { /* Should not happen. But just in case. */ _tprintf(TEXT("Failed to find standard handle. Disabled direct console output.\n")); consoleDirect = FALSE; } } if (!complete) { #endif _ftprintf(target, fmt, printBuffer); if (consoleFlush) { fflush(target); } #ifdef WIN32 } #endif } void printToConsole(const TCHAR *printBuffer, FILE *target) { if (lockLoggingMutex()) { return; } printToConsoleInner(printBuffer, target, FALSE); if (releaseLoggingMutex()) { return; } } void printToConsoleLn(const TCHAR *printBuffer, FILE *target) { if (lockLoggingMutex()) { return; } printToConsoleInner(printBuffer, target, TRUE); if (releaseLoggingMutex()) { return; } } /** * Prints the contents of a buffer to the console target. The log level is * tested prior to this function being called. * * Must be called while locked. */ void log_printf_message_consoleInner(int source_id, int level, int threadId, int queued, TCHAR *message, struct tm *nowTM, int nowMillis, time_t durationMillis) { TCHAR *printBuffer; FILE *target; /* Build up the printBuffer. */ printBuffer = buildPrintBuffer(source_id, level, threadId, queued, nowTM, nowMillis, durationMillis, consoleFormat, LOG_FORMAT_CONSOLE_DEFAULT, message); if (printBuffer) { /* Decide where to send the output. */ switch (level) { case LEVEL_FATAL: if (consoleFatalToStdErr) { target = stderr; } else { target = stdout; } break; case LEVEL_ERROR: if (consoleErrorToStdErr) { target = stderr; } else { target = stdout; } break; case LEVEL_WARN: if (consoleWarnToStdErr) { target = stderr; } else { target = stdout; } break; default: target = stdout; break; } printToConsoleInner(printBuffer, target, TRUE); } } void log_printf_message_console(int source_id, int level, int threadId, int queued, TCHAR *message, struct tm *nowTM, int nowMillis, time_t durationMillis) { if (level >= currentConsoleLevel) { log_printf_message_consoleInner(source_id, level, threadId, queued, message, nowTM, nowMillis, durationMillis); } } /** * Prints the contents of a buffer to all configured targets. * * Must be called while locked. * * @param message The message to log. This is not a message format, but may contain format characters. * The content of the message buffer may be modified by this call if it contains line feeds. * @param sysLogEnabled A flag that is used to help with recursion to control * whether or not the syslog should be considered as a log * target for this call. It is always disabled when we * recurse. * * @return True if the logfile name changed. */ int log_printf_message(int source_id, int level, int threadId, int queued, TCHAR *message, int sysLogEnabled) { #ifndef WIN32 TCHAR *printBuffer; FILE *target; #endif int logFileChanged = FALSE; TCHAR *subMessage; TCHAR *nextLF; #ifdef WIN32 struct _timeb timebNow; #else size_t reqSize; struct timeval timevalNow; TCHAR intBuffer[3]; TCHAR* pos; #endif time_t now; int nowMillis; struct tm *nowTM; time_t durationMillis; #ifndef WIN32 if ((_tcsstr(message, LOG_SPECIAL_MARKER) == message) && (_tcslen(message) >= _tcslen(LOG_SPECIAL_MARKER) + 10)) { /* Got a special encoded log message from the child Wrapper process. * Parse it and continue as if the log message came from this process. * These should never be multi-line messages as the forked child * process will have already broken them up. */ pos = (TCHAR *)(message + _tcslen(LOG_SPECIAL_MARKER) + 1); /* source_id */ _tcsncpy(intBuffer, pos, 2); intBuffer[2] = TEXT('\0'); source_id = _ttoi(intBuffer); pos += 3; /* level */ _tcsncpy(intBuffer, pos, 2); intBuffer[2] = TEXT('\0'); level = _ttoi(intBuffer); pos += 3; /* threadId */ _tcsncpy(intBuffer, pos, 2); intBuffer[2] = TEXT('\0'); threadId = _ttoi(intBuffer); pos += 3; /* message */ message = pos; } #endif /* Build a timestamp */ #ifdef WIN32 _ftime( &timebNow ); now = (time_t)timebNow.time; nowMillis = timebNow.millitm; #else gettimeofday( &timevalNow, NULL ); now = (time_t)timevalNow.tv_sec; nowMillis = timevalNow.tv_usec / 1000; #endif nowTM = localtime( &now ); /* Calculate the number of milliseconds which have passed since the previous log entry. * We only need to display up to 8 digits, so if the result is going to be larger than * that, set it to 100000000. * We only want to do this for output coming from the JVM. Any other log output should * be set to (time_t)-1. */ switch(source_id) { case WRAPPER_SOURCE_WRAPPER: case WRAPPER_SOURCE_PROTOCOL: durationMillis = (time_t)-1; break; default: if (now - previousNow > 100000) { /* Without looking at the millis, we know it is already too long. */ durationMillis = 100000000; } else { durationMillis = (now - previousNow) * 1000 + nowMillis - previousNowMillis; } previousNow = now; previousNowMillis = nowMillis; break; } if (!currentLogSplitMessages) { /* Syslog messages are printed first so we can print them including line feeds as is. * This must be done before we break up multi-line messages into individual lines. */ #ifdef WIN32 if (sysLogEnabled) { #else /* On UNIX we never want to log to the syslog here if this is in a forked thread. * In this case, any lines will be broken up into individual lines and then logged * as usual by the main process. But this can't be helped and is very rare anyway. */ if (sysLogEnabled && (_tcsstr(message, LOG_FORK_MARKER) != message)) { #endif /* syslog/Eventlog. */ log_printf_message_sysLog(source_id, level, message, nowTM, FALSE); } } /* If the message contains line feeds then break up the line into substrings and recurse. */ subMessage = message; nextLF = _tcschr(subMessage, TEXT('\n')); if (nextLF) { /* This string contains more than one line. Loop over the strings. It is Ok to corrupt this string because it is only used once. */ while (nextLF) { nextLF[0] = TEXT('\0'); logFileChanged |= log_printf_message(source_id, level, threadId, queued, subMessage, FALSE); /* Locate the next one. */ subMessage = &(nextLF[1]); nextLF = _tcschr(subMessage, TEXT('\n')); } /* The rest of the buffer will be the final line. */ logFileChanged |= log_printf_message(source_id, level, threadId, queued, subMessage, FALSE); return logFileChanged; } #ifndef WIN32 /* See if this is a special case log entry from the forked child. */ if (_tcsstr(message, LOG_FORK_MARKER) == message) { /* Found the marker. We only want to log the message as is to the console with a special prefix. * This is used to pass the log output through the pipe to the parent Wrapper process where it * will be decoded below and displayed appropriately. */ reqSize = _tcslen(LOG_SPECIAL_MARKER) + 1 + 2 + 1 + 2 + 1 + 2 + 1 + _tcslen(message) - _tcslen(LOG_FORK_MARKER) + 1; if (!(printBuffer = preparePrintBuffer(reqSize))) { return FALSE; } _sntprintf(printBuffer, reqSize, TEXT("%s|%02d|%02d|%02d|%s"), LOG_SPECIAL_MARKER, source_id, level, threadId, message + _tcslen(LOG_FORK_MARKER)); /* Decide where to send the output. */ switch (level) { case LEVEL_FATAL: if (consoleFatalToStdErr) { target = stderr; } else { target = stdout; } break; case LEVEL_ERROR: if (consoleErrorToStdErr) { target = stderr; } else { target = stdout; } break; case LEVEL_WARN: if (consoleWarnToStdErr) { target = stderr; } else { target = stdout; } break; default: target = stdout; break; } _ftprintf(target, TEXT("%s\n"), printBuffer); if (consoleFlush) { fflush(target); } return FALSE; } #endif /* Get the current threadId. */ if ( threadId < 0 ) { /* The current thread was specified. Resolve what thread this actually is. */ threadId = getThreadId(); } /* Syslog outbut by format (If messages splitting is enabled. Otherwise done above.) */ if (currentLogSplitMessages) { log_printf_message_sysLog(source_id, level, message, nowTM, FALSE); } /* Console output by format */ log_printf_message_console(source_id, level, threadId, queued, message, nowTM, nowMillis, durationMillis); /* Logfile output by format */ logFileChanged = log_printf_message_logFile(source_id, level, threadId, queued, message, nowTM, nowMillis, durationMillis); return logFileChanged; } /** * Used for testing to pause the current thread for the specified number of seconds. * This can only be called when logging is locked. * * @param pauseTime Number of seconds to pause, 0 pauses indefinitely. */ void pauseThread(int pauseTime) { int i; if (pauseTime > 0) { for (i = 0; i < pauseTime; i++) { logSleep(1000); } } else { while (TRUE) { logSleep(1000); } } } /** * General log function * * @param source_id The source constant or JVM number, negative values are one of the WRAPPER_SOURCE_* constants. * If the constant is positive then it is a JVM launch number, and the output is from the JVM. * If the constant is 0, the output is from 'java -version'. * @param level Level at which to log the output. * @param lpszFmt Format control. */ void log_printf( int source_id, int level, const TCHAR *lpszFmt, ... ) { va_list vargs; int count; int threadId; int logFileChanged; TCHAR *logFileCopy; #if defined(UNICODE) && !defined(WIN32) TCHAR *msg = NULL; int i; int msgMalloced; #endif #ifdef WIN32 struct _timeb timebNow; #else struct timeval timevalNow; #endif time_t startNow; int startNowMillis; time_t endNow; int endNowMillis; if (level == LEVEL_NONE) { /* Some APIs allow the user to potentially configure the NONE log level. Skip it as it means no logging in this case. */ return; } /* If we are checking on the log time then store the start time. */ if (logPrintfWarnThreshold > 0) { #ifdef WIN32 _ftime(&timebNow); startNow = (time_t)timebNow.time; startNowMillis = timebNow.millitm; #else gettimeofday(&timevalNow, NULL); startNow = (time_t)timevalNow.tv_sec; startNowMillis = timevalNow.tv_usec / 1000; #endif } else { startNow = 0; startNowMillis = 0; } /* We need to be very careful that only one thread is allowed in here * at a time. On Windows this is done using a Mutex object that is * initialized in the initLogging function. */ if (lockLoggingMutex()) { return; } /* If there is a queued pause then do so. */ if ((logPauseTime >= 0) && (level > LEVEL_DEBUG) && (source_id < 0)) { pauseThread(logPauseTime); logPauseTime = -1; } #if defined(UNICODE) && !defined(WIN32) if ((source_id < 0) && (wcsstr(lpszFmt, TEXT("%s")) != NULL)) { msg = malloc(sizeof(wchar_t) * (wcslen(lpszFmt) + 1)); if (msg) { /* Loop over the format and convert all '%s' patterns to %S' so the UNICODE displays correctly. */ if (wcslen(lpszFmt) > 0) { for (i = 0; i < _tcslen(lpszFmt); i++){ msg[i] = lpszFmt[i]; if ((lpszFmt[i] == TEXT('%')) && (i < _tcslen(lpszFmt)) && (lpszFmt[i + 1] == TEXT('s')) && ((i == 0) || (lpszFmt[i - 1] != TEXT('%')))){ msg[i+1] = TEXT('S'); i++; } } } msg[wcslen(lpszFmt)] = TEXT('\0'); } else { _tprintf(TEXT("Out of memory in logging code (%s)\n"), TEXT("P1")); return; } msgMalloced = TRUE; } else { msg = (TCHAR*) lpszFmt; msgMalloced = FALSE; } #endif threadId = getThreadId(); if (source_id < 0) { /* Loop until the buffer is large enough that we are able to successfully * print into it. Once the buffer has grown to the largest message size, * smaller messages will pass through this code without looping. */ do { if ( threadMessageBufferSize == 0 ) { /* No buffer yet. Allocate one to get started. */ threadMessageBufferSize = threadMessageBufferInitialSize; #if defined(HPUX) /* Due to a bug in the HPUX libc (version < 1403), the buffer passed to _vsntprintf must have a length of 1 + N, where N is a multiple of 8. Adjust it as necessary. */ threadMessageBufferSize = threadMessageBufferSize + (((threadMessageBufferSize - 1) % 8) == 0 ? 0 : 8 - ((threadMessageBufferSize - 1) % 8)); #endif threadMessageBuffer = malloc(sizeof(TCHAR) * threadMessageBufferSize); if (!threadMessageBuffer) { _tprintf(TEXT("Out of memory in logging code (%s)\n"), TEXT("P2")); threadMessageBufferSize = 0; #if defined(UNICODE) && !defined(WIN32) if (msgMalloced) { free(msg); } #endif return; } } /* Try writing to the buffer. */ va_start( vargs, lpszFmt ); #if defined(UNICODE) && !defined(WIN32) count = _vsntprintf( threadMessageBuffer, threadMessageBufferSize, msg, vargs ); #else count = _vsntprintf( threadMessageBuffer, threadMessageBufferSize, lpszFmt, vargs ); #endif va_end( vargs ); /* _tprintf(TEXT(" vsnprintf->%d, size=%d\n"), count, threadMessageBufferSize ); */ if ( ( count < 0 ) || ( count >= (int)threadMessageBufferSize ) ) { /* If the count is exactly equal to the buffer size then a null TCHAR was not written. * It must be larger. * Windows will return -1 if the buffer is too small. If the number is * exact however, we still need to expand it to have room for the null. * UNIX will return the required size. */ /* Free the old buffer for starters. */ free( threadMessageBuffer ); /* Decide on a new buffer size. * We can't tell how long the resulting string will be without expanding because the * results are stored in the vargs. * Most messages will be short, but there is a possibility that some will be very * long. To minimize the number of times that we need to loop, while at the same * time trying to avoid using too much memory, increase the size by the maximum of * 1024 or 10% of the current length. * Some platforms will return the required size as count. Use that if available. */ threadMessageBufferSize = __max(threadMessageBufferSize + 1024, __max(threadMessageBufferSize + threadMessageBufferSize / 10, (size_t)count + 1)); #if defined(HPUX) /* Due to a bug in the HPUX libc (version < 1403), the buffer passed to _vsntprintf must have a length of 1 + N, where N is a multiple of 8. Adjust it as necessary. */ threadMessageBufferSize = threadMessageBufferSize + (((threadMessageBufferSize - 1) % 8) == 0 ? 0 : 8 - ((threadMessageBufferSize - 1) % 8)); #endif threadMessageBuffer = malloc(sizeof(TCHAR) * threadMessageBufferSize); if (!threadMessageBuffer) { _tprintf(TEXT("Out of memory in logging code (%s)\n"), TEXT("P3")); threadMessageBufferSize = 0; #if defined(UNICODE) && !defined(WIN32) if (msgMalloced) { free(msg); } #endif return; } /* Always set the count to -1 so we will loop again. */ count = -1; } } while ( count < 0 ); } #if defined(UNICODE) && !defined(WIN32) if (msgMalloced) { free(msg); } #endif logFileCopy = NULL; if (source_id >= 0) { /* As this is content from the JVM, the msg or lpszFmt is direct message, not a message format. */ #if defined(UNICODE) && !defined(WIN32) logFileChanged = log_printf_message(source_id, level, threadId, FALSE, msg, TRUE); #else logFileChanged = log_printf_message(source_id, level, threadId, FALSE, (TCHAR*)lpszFmt, TRUE); #endif } else { /* The lpszFmt parameter was treated as a message format, and was expanded into the threadMessageBuffer buffer. */ logFileChanged = log_printf_message(source_id, level, threadId, FALSE, threadMessageBuffer, TRUE); } if (logFileChanged) { /* We need to enqueue a notification that the log file name was changed. * We can NOT directly send the notification here as that could cause a deadlock, * depending on where exactly this function was called from. (See Wrapper protocol mutex.) */ logFileCopy = malloc(sizeof(TCHAR) * (_tcslen(currentLogFileName) + 1)); if (!logFileCopy) { _tprintf(TEXT("Out of memory in logging code (%s)\n"), TEXT("P4")); } else { _tcsncpy(logFileCopy, currentLogFileName, _tcslen(currentLogFileName) + 1); /* Now after we have 100% prepared the log file name. Put into the queue variable * so the maintainLogging() function can safely grab it at any time. * The reading code is also in a semaphore so we can do a quick test here safely as well. */ if (pendingLogFileChange) { /* The previous file was still in the queue. Free it up to avoid a memory leak. * This can happen if the log file size is 1k or something like that. We will always * keep the most recent file however, so this should not be that big a problem. */ #ifdef _DEBUG _tprintf(TEXT("Log file name change was overwritten in queue: %s\n"), pendingLogFileChange); #endif free(pendingLogFileChange); } pendingLogFileChange = logFileCopy; } } /* Release the lock we have on this function so that other threads can get in. */ if (releaseLoggingMutex()) { return; } /* If we are checking on the log time then store the stop time. * It is Ok that some of the error paths don't make it this far. */ if (logPrintfWarnThreshold > 0) { #ifdef WIN32 _ftime(&timebNow); endNow = (time_t)timebNow.time; endNowMillis = timebNow.millitm; #else gettimeofday(&timevalNow, NULL); endNow = (time_t)timevalNow.tv_sec; endNowMillis = timevalNow.tv_usec / 1000; #endif previousLogLag = __min(endNow - startNow, 3600) * 1000 + endNowMillis - startNowMillis; if (previousLogLag >= logPrintfWarnThreshold) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Write to log took %d milliseconds."), previousLogLag); } } } /* Internal functions */ #ifdef WIN32 static int sysLangId = LANG_NEUTRAL; /** * Sets the language id to use to format system messages. * This function should be called when resolving the locale. * * @param id Language Identifier (16-bit value that consists of a * primary language identifier and a sublanguage identifier) */ void setLogSysLangId(int id) { sysLangId = id; } #endif #define LAST_ERROR_TEXT_BUFFER_SIZE 1024 /** Buffer holding the last error message. * TODO: This needs to be made thread safe, meaning that we need a buffer for each thread. */ TCHAR lastErrorTextBufferW[LAST_ERROR_TEXT_BUFFER_SIZE]; /** * Returns a textual error message of a given error number. * * @param errorNum Error code. * @param handle (for Windows only) A module handle containing the message-table resource(s) to search. If NULL, the current process's application image file will be searched. * * @return The error message. */ const TCHAR* getErrorText(int errorNum, void* handle) { #ifdef WIN32 DWORD dwRet; TCHAR* lpszTemp = NULL; DWORD dwFlags; int formatError; #else char* lastErrorTextMB; size_t req; #endif #ifdef WIN32 if (handle) { /* Some system messages in ntdll.dll (ntstatus.h) contain arguments, but we actually never pass arguments to FormatMessage(). * FORMAT_MESSAGE_IGNORE_INSERTS specifies that any arguments should be left in the message for later formatting. * We use this flag when a handle is specified to be able to check whether there are arguments. However, we don't use * it when just formatting from system messages as those should not contain arguments (if they do, it is better to not * specify the flag as this would cause the percentages to be skipped instead of producing a corrupted string). */ dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE; } else { dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY; } dwRet = FormatMessage(dwFlags, handle, errorNum, sysLangId, (TCHAR*)&lpszTemp, 0, NULL); if (!dwRet) { /* There was an error calling FormatMessage. */ formatError = getLastError(); if ((formatError == ERROR_MUI_FILE_NOT_FOUND) || (formatError == ERROR_MUI_INVALID_FILE) || (formatError == ERROR_MUI_INVALID_RC_CONFIG) || (formatError == ERROR_MUI_INVALID_LOCALE_NAME) || (formatError == ERROR_MUI_INVALID_ULTIMATEFALLBACK_NAME) || (formatError == ERROR_MUI_FILE_NOT_LOADED) || (formatError == ERROR_RESOURCE_LANG_NOT_FOUND)) { /* The following condition is important to not fall into an infinite loop. */ if (sysLangId != LANG_NEUTRAL) { /* Fall back to the system language. */ setLogSysLangId(LANG_NEUTRAL); return getErrorText(errorNum, handle); } } _sntprintf(lastErrorTextBufferW, LAST_ERROR_TEXT_BUFFER_SIZE, TEXT("Failed to format system error message (Error: %d) (Original Error: 0x%x)"), formatError, errorNum); } else if ((long)LAST_ERROR_TEXT_BUFFER_SIZE - 1 < (long)dwRet + 14) { /* supplied buffer is not long enough (14 is for the length of the error code in hexadecimal notation (12)+ space + null termination character) */ _sntprintf(lastErrorTextBufferW, LAST_ERROR_TEXT_BUFFER_SIZE, TEXT("System error message is too large to convert (Required size: %d) (Original Error: 0x%x)"), dwRet, errorNum); formatError = ERROR_BUFFER_OVERFLOW; } else { lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); /* remove cr and newline character */ _sntprintf(lastErrorTextBufferW, LAST_ERROR_TEXT_BUFFER_SIZE, TEXT("%s (0x%x)"), lpszTemp, errorNum); formatError = ERROR_SUCCESS; } /* following the documentation of FormatMessage, LocalFree should be called to free the output buffer. */ if (lpszTemp) { LocalFree(lpszTemp); } #else lastErrorTextMB = strerror(errorNum); req = mbstowcs(NULL, lastErrorTextMB, MBSTOWCS_QUERY_LENGTH); if (req == (size_t)-1) { invalidMultiByteSequence(TEXT("GLET"), 1); _sntprintf(lastErrorTextBufferW, LAST_ERROR_TEXT_BUFFER_SIZE, TEXT("System error message could not be decoded (Error 0x%x)"), errorNum); } else if (req >= LAST_ERROR_TEXT_BUFFER_SIZE) { _sntprintf(lastErrorTextBufferW, LAST_ERROR_TEXT_BUFFER_SIZE, TEXT("System error message too large to convert (Require size: %d) (Original Error: 0x%x)"), req, errorNum); } else { mbstowcs(lastErrorTextBufferW, lastErrorTextMB, LAST_ERROR_TEXT_BUFFER_SIZE); } #endif /* Always reterminate the buffer just to be sure it is safe because badly encoded characters can cause issues. */ lastErrorTextBufferW[LAST_ERROR_TEXT_BUFFER_SIZE - 1] = TEXT('\0'); #ifdef WIN32 SetLastError(formatError); #endif return lastErrorTextBufferW; } /** * Returns a textual error message of the last error encountered. * * @return The last error message. */ const TCHAR* getLastErrorText() { return getErrorText(getLastError(), NULL); } /** * Returns the last error number. * * @return The last error number. */ int getLastError() { #ifdef WIN32 return GetLastError(); #else return errno; #endif } #ifdef WIN32 static int eventLogSourceInstalled = FALSE; /** * Disable Event Log. * This function has to be called if the registration could not be completed. */ void disableSysLog(int silent) { if (getSyslogLevelInt() != LEVEL_NONE) { setSyslogLevelInt(LEVEL_NONE); if (!silent) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Disabling Event Log because the application is not registered.\n Run the wrapper with the '--setup' option to register.")); } } } /** * Check if the Event Log source was registered successfully. * There are 2 ways to register: * CASE 1: If we need elevated privileges, it should be done through the setup process (see --setup argument). * CASE 2: On older versions of windows where running elevated was not needed, registerSyslogMessageFile() can be called when the wrapper runs. * * Returns TRUE if registered, FALSE if not registered. */ int syslogMessageFileRegistered(int silent) { static int checked = FALSE; TCHAR bufferPath[_MAX_PATH]; TCHAR bufferKVal[_MAX_PATH]; TCHAR regPath[1024]; DWORD cbData = _MAX_PATH; DWORD usedLen; DWORD error; HKEY hKey; /* first check if the function was called before */ if (checked || eventLogSourceInstalled) { /* || eventLogSourceInstalled: just in case we would have run registerSyslogMessageFile(TRUE) before this function (should not happen) */ return eventLogSourceInstalled; } /* if the registry key exist, and the path to the wrapper is ok, lets assume the registration was done successfully. No need to check the other values of the key */ /* Get absolute path to service manager */ /* Important : For win XP getLastError() is unchanged if the buffer is too small, so if we don't reset the last error first, we may actually test an old pending error. */ SetLastError(ERROR_SUCCESS); usedLen = GetModuleFileName(NULL, bufferPath, _MAX_PATH); if (usedLen == 0) { if (!silent) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to obtain the full path to the Wrapper. %s"), getLastErrorText()); } } else if ((usedLen == _MAX_PATH) || (getLastError() == ERROR_INSUFFICIENT_BUFFER)) { if (!silent) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to obtain the full path to the Wrapper. Path to Wrapper binary too long.")); } } else { _sntprintf( regPath, 1024, TEXT("SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\%s"), loginfoSourceName ); /* check that the registry key exists. */ if ((error = RegOpenKeyEx( HKEY_LOCAL_MACHINE, regPath, 0, KEY_READ, (PHKEY) &hKey )) == ERROR_SUCCESS ) { /* check that the path to the wrapper is correct. */ if ((error = RegQueryValueEx( hKey, TEXT("EventMessageFile"), NULL, NULL, (LPBYTE) bufferKVal, &cbData)) == ERROR_SUCCESS ) { if (strcmpIgnoreCase(bufferPath, bufferKVal) == 0) { eventLogSourceInstalled = TRUE; } else if (!silent) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The path registered for the Event Log (%s) did not match the location of the Wrapper binary (%s)."), bufferKVal, bufferPath); } } else if (!silent) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The path registered for the Event Log could not be read (0x%x)."), error); } RegCloseKey( hKey ); } else if ((error != ERROR_FILE_NOT_FOUND) && !silent) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The Event Log source could not be found (0x%x)."), error); } } checked = TRUE; return eventLogSourceInstalled; } #endif /** * Register to the Log Event System * There are 2 ways to register: * CASE 1: If we need elevated privileges, it should be done through the setup process (see --setup argument). * CASE 2: On older versions of windows where running elevated was not needed, registerSyslogMessageFile() can called when the wrapper runs. */ int registerSyslogMessageFile(int forceInstall, int silent) { #ifdef WIN32 TCHAR buffer[_MAX_PATH]; DWORD usedLen; TCHAR regPath[1024]; TCHAR regValueName[32]; HKEY hKey; DWORD categoryCount, typesSupported; DWORD error; if (!forceInstall && syslogMessageFileRegistered(silent)) { return 0; } /* Get absolute path to service manager */ /* Important : For win XP getLastError() is unchanged if the buffer is too small, so if we don't reset the last error first, we may actually test an old pending error. */ SetLastError(ERROR_SUCCESS); usedLen = GetModuleFileName(NULL, buffer, _MAX_PATH); if (usedLen == 0) { if (!silent) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to obtain the full path to the Wrapper. %s"), getLastErrorText()); } } else if ((usedLen == _MAX_PATH) || (getLastError() == ERROR_INSUFFICIENT_BUFFER)) { if (!silent) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to obtain the full path to the Wrapper. Path to Wrapper binary too long.")); } } else { _sntprintf( regPath, 1024, TEXT("SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\%s"), loginfoSourceName ); if( RegCreateKey( HKEY_LOCAL_MACHINE, regPath, (PHKEY) &hKey ) == ERROR_SUCCESS ) { RegCloseKey( hKey ); if( RegOpenKeyEx( HKEY_LOCAL_MACHINE, regPath, 0, KEY_WRITE, (PHKEY) &hKey ) == ERROR_SUCCESS ) { /* Set EventMessageFile */ _tcsncpy(regValueName, TEXT("EventMessageFile"), 32); if ((error = RegSetValueEx(hKey, regValueName, 0, REG_SZ, (LPBYTE) buffer, (DWORD)(sizeof(TCHAR) * (_tcslen(buffer) + 1)))) == ERROR_SUCCESS) { /* Set CategoryMessageFile */ _tcsncpy(regValueName, TEXT("CategoryMessageFile"), 32); if ((error = RegSetValueEx(hKey, regValueName, 0, REG_SZ, (LPBYTE) buffer, (DWORD)(sizeof(TCHAR) * (_tcslen(buffer) + 1)))) == ERROR_SUCCESS) { /* Set CategoryCount */ _tcsncpy(regValueName, TEXT("CategoryCount"), 32); categoryCount = 12; if ((error = RegSetValueEx(hKey, regValueName, 0, REG_DWORD, (LPBYTE) &categoryCount, sizeof(DWORD))) == ERROR_SUCCESS) { /* Set TypesSupported */ _tcsncpy(regValueName, TEXT("TypesSupported"), 32); typesSupported = 7; if ((error = RegSetValueEx(hKey, regValueName, 0, REG_DWORD, (LPBYTE) &typesSupported, sizeof(DWORD))) == ERROR_SUCCESS) { eventLogSourceInstalled = TRUE; } } } } RegCloseKey( hKey ); if (error != ERROR_SUCCESS) { if (!silent) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Failed to set '%s' when registering to the Event Log (0x%x)."), regValueName, error); } } } } } if (!eventLogSourceInstalled) { /* not registered or failed to register correctly */ disableSysLog(silent); return 1; } return 0; #else return 0; #endif } /** * Unregister from the Log Event System */ int unregisterSyslogMessageFile(int silent) { #ifdef WIN32 DWORD error; /* If we deregister this application, then the event viewer will not work when the program is not running. */ /* Don't want to clutter up the Registry, but is there another way? */ /* From 3.5.28 we want to allow the user to never register. */ TCHAR regPath[ 1024 ]; /* Get absolute path to service manager */ _sntprintf( regPath, 1024, TEXT("SYSTEM\\CurrentControlSet\\Services\\Eventlog\\Application\\%s"), loginfoSourceName ); error = RegDeleteKey(HKEY_LOCAL_MACHINE, regPath); if((error == ERROR_SUCCESS) || (error == ERROR_FILE_NOT_FOUND)) { eventLogSourceInstalled = FALSE; disableSysLog(silent); return 0; } return 1; /* Failure */ #else return 0; #endif } #ifdef WIN32 void sendEventlogMessage( int source_id, int level, const TCHAR *szBuff ) { TCHAR header[16]; const TCHAR **strings; WORD eventType; HANDLE handle; WORD eventID, categoryID; int result; strings = malloc(sizeof(TCHAR *) * 3); if (!strings) { _tprintf(TEXT("Out of memory in logging code (%s)\n"), TEXT("SEM1")); return; } /* Build the source header */ switch(source_id) { case WRAPPER_SOURCE_WRAPPER: #ifdef WIN32 if (launcherSource) { _sntprintf( header, 16, TEXT("wrapperm") ); } else { _sntprintf( header, 16, TEXT("wrapper") ); } #else _sntprintf( header, 16, TEXT("wrapper") ); #endif break; case WRAPPER_SOURCE_PROTOCOL: _sntprintf( header, 16, TEXT("wrapperp") ); break; case WRAPPER_SOURCE_JVM_VERSION: _sntprintf( header, 16, TEXT("jvm ver.") ); break; default: _sntprintf( header, 16, TEXT("jvm %d"), source_id ); header[15] = TEXT('\0'); /* Just in case we get lots of restarts. */ break; } /* Build event type by level */ switch(level) { case LEVEL_NOTICE: /* Will not get in here. */ case LEVEL_ADVICE: /* Will not get in here. */ case LEVEL_FATAL: eventType = EVENTLOG_ERROR_TYPE; break; case LEVEL_ERROR: case LEVEL_WARN: eventType = EVENTLOG_WARNING_TYPE; break; case LEVEL_STATUS: case LEVEL_INFO: case LEVEL_DEBUG: eventType = EVENTLOG_INFORMATION_TYPE; break; } /* Set the category id to the appropriate resource id. */ if ( source_id == WRAPPER_SOURCE_WRAPPER ) { categoryID = MSG_EVENT_LOG_CATEGORY_WRAPPER; } else if ( source_id == WRAPPER_SOURCE_PROTOCOL ) { categoryID = MSG_EVENT_LOG_CATEGORY_PROTOCOL; } else { /* Source is a JVM. */ switch ( source_id ) { case 1: categoryID = MSG_EVENT_LOG_CATEGORY_JVM1; break; case 2: categoryID = MSG_EVENT_LOG_CATEGORY_JVM2; break; case 3: categoryID = MSG_EVENT_LOG_CATEGORY_JVM3; break; case 4: categoryID = MSG_EVENT_LOG_CATEGORY_JVM4; break; case 5: categoryID = MSG_EVENT_LOG_CATEGORY_JVM5; break; case 6: categoryID = MSG_EVENT_LOG_CATEGORY_JVM6; break; case 7: categoryID = MSG_EVENT_LOG_CATEGORY_JVM7; break; case 8: categoryID = MSG_EVENT_LOG_CATEGORY_JVM8; break; case 9: categoryID = MSG_EVENT_LOG_CATEGORY_JVM9; break; default: categoryID = MSG_EVENT_LOG_CATEGORY_JVMXX; break; } } /* Place event in eventlog */ strings[0] = header; strings[1] = szBuff; strings[2] = 0; eventID = level; handle = RegisterEventSource( NULL, loginfoSourceName ); if( !handle ) return; result = ReportEvent( handle, /* handle to event log */ eventType, /* event type */ categoryID, /* event category */ MSG_EVENT_LOG_MESSAGE, /* event identifier */ NULL, /* user security identifier */ 2, /* number of strings to merge */ 0, /* size of binary data */ (const TCHAR**) strings, /* array of strings to merge */ NULL /* binary data buffer */ ); if (result == 0) { /* If there are any errors accessing the event log, like it is full, then disable its output. */ setSyslogLevelInt(LEVEL_NONE); /* Recurse so this error gets set in the log file and console. The syslog * output has been disabled so we will not get back here. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to write to the EventLog due to: %s"), getLastErrorText()); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Internally setting wrapper.syslog.loglevel=NONE to prevent further messages.")); } DeregisterEventSource( handle ); free( (void *) strings ); strings = NULL; } #else void sendLoginfoMessage( int source_id, int level, const TCHAR *szBuff ) { int eventType; /* Build event type by level */ switch( level ) { case LEVEL_FATAL: eventType = LOG_CRIT; break; case LEVEL_ERROR: eventType = LOG_ERR; break; case LEVEL_WARN: case LEVEL_STATUS: eventType = LOG_NOTICE; break; case LEVEL_INFO: eventType = LOG_INFO; break; case LEVEL_DEBUG: eventType = LOG_DEBUG; break; default: eventType = LOG_DEBUG; } /* openlog, closelog, and syslog all return void. */ openlog( loginfoSourceName, LOG_PID | LOG_NDELAY, currentLogfacilityLevel ); _tsyslog( eventType, szBuff ); closelog( ); } #endif #ifdef WIN32 #define CONSOLE_BLOCK_SIZE 1024 /* The following is an initial (max) size for the number of characters to try writing to WriteConsole at once. * See notes on the WriteConsole function below for details. */ size_t vWriteToConsoleMaxHeapBufferSize = 30000; size_t vWriteToConsoleBufferSize = 0; TCHAR *vWriteToConsoleBuffer = NULL; /** * Write a line of output to the console. * * @param hdl The handle to write to. Must be a valid handle. * * @return TRUE if successful, FALSE if the line was not written. */ int writeToConsole(HANDLE hdl, const TCHAR *lpszFmt, ...) { va_list vargs; int cnt; size_t fullLen; size_t remainLen; size_t offset; size_t thisLen; DWORD wrote; #ifdef DEBUG_CONSOLE_OUTPUT _tprintf(TEXT("writeToConsole BEGIN\n")); #endif if (vWriteToConsoleBuffer == NULL) { vWriteToConsoleBufferSize = CONSOLE_BLOCK_SIZE * 2; vWriteToConsoleBuffer = malloc(sizeof(TCHAR) * vWriteToConsoleBufferSize); if (!vWriteToConsoleBuffer) { _tprintf(TEXT("Out of memory in logging code (%s)\n"), TEXT("WTC1")); return FALSE; } #ifdef DEBUG_CONSOLE_OUTPUT _tprintf(TEXT("Console Buffer Size = %d (Initial Size)\n"), vWriteToConsoleBufferSize); #endif if (logBufferGrowth) { /* This is queued as we can't use direct logging here. */ log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Console Buffer Size initially set to %d characters."), vWriteToConsoleBufferSize); } } va_start(vargs, lpszFmt); /* The only way I could figure out how to write to the console * returned by AllocConsole when running as a service was to * do all of this special casing and use the handle to the new * console's stdout and the WriteConsole function. If anyone * puzzling over this code knows a better way of doing this * let me know. * WriteConsole takes a fixed buffer and does not do any expansions * We need to prepare the string to be displayed ahead of time. * This means storing the message into a temporary buffer. The * following loop will expand the global buffer to hold the current * message. It will grow as needed to handle any arbitrarily large * user message. The buffer needs to be able to hold all available * characters + a null TCHAR. * The _vsntprintf function will fill all available space and only * terminate the string if there is room. Because of this we need * to make sure and reserve room for the null terminator and add it * if needed below. */ while ((cnt = _vsntprintf(vWriteToConsoleBuffer, vWriteToConsoleBufferSize - 1, lpszFmt, vargs)) < 0) { #ifdef DEBUG_CONSOLE_OUTPUT _tprintf(TEXT("writeToConsole ProcessCount=%d\n"), cnt); #endif /* Expand the size of the buffer */ free(vWriteToConsoleBuffer); /* Increase the buffer by the CONSOLE_BLOCK_SIZE or an additional 10%, which ever is larger. * The goal here is to grow the buffer size quickly, but not waste too much memory. */ vWriteToConsoleBufferSize = __max(vWriteToConsoleBufferSize + CONSOLE_BLOCK_SIZE, vWriteToConsoleBufferSize + vWriteToConsoleBufferSize / 10); vWriteToConsoleBuffer = malloc(sizeof(TCHAR) * vWriteToConsoleBufferSize); if (!vWriteToConsoleBuffer) { _tprintf(TEXT("Out of memory in logging code (%s)\n"), TEXT("WTC2")); va_end( vargs ); return FALSE; } #ifdef DEBUG_CONSOLE_OUTPUT _tprintf(TEXT("Console Buffer Size = %d (Increased Size) ****************************************\n"), vWriteToConsoleBufferSize); #endif if (logBufferGrowth) { /* This is queued as we can't use direct logging here. */ log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Console Buffer Size increased to %d characters."), vWriteToConsoleBufferSize); } } #ifdef DEBUG_CONSOLE_OUTPUT _tprintf(TEXT("writeToConsole ProcessCount=%d\n"), cnt); #endif if (cnt == (vWriteToConsoleBufferSize - 1)) { /* The maximum number of characters were read. All of the characters fit in the available space, but because of the way the API works, the string was not null terminated. */ vWriteToConsoleBuffer[vWriteToConsoleBufferSize - 1] = '\0'; } va_end(vargs); #ifdef DEBUG_CONSOLE_OUTPUT _tprintf(TEXT("writeToConsole BufferSize=%d, MessageLen=%d, Message=[%s]\n"), vWriteToConsoleBufferSize, _tcslen(vWriteToConsoleBuffer), vWriteToConsoleBuffer); #endif /* The WriteConsole API is a nasty little beast. * It can accept a buffer that is up to 64KB in size, but they can't tell us exactly how much before hand. * The size on tests on a 64-bit XP system appear to be around 25000 characters. * Windows 7 returns success, but starts writing garbled characters after around 31397 characters. (Not sure if this number is system specific however.) * The problem is that this is highly dependent on the current system state. * We used to start with 32000, but now use 30000 to avoid problems on Windows 7. (Not sure if this is small enough to avoid the issue on all systems.) * Start with a large size for efficiency, but then reduce it automatically in a sticky way in 5% increments to get to a size that works. */ fullLen = _tcslen(vWriteToConsoleBuffer); remainLen = fullLen; offset = 0; while (remainLen > 0) { thisLen = __min(remainLen, vWriteToConsoleMaxHeapBufferSize); #ifdef DEBUG_CONSOLE_OUTPUT _tprintf(TEXT("writeToConsole write %d of %d characters\n"), thisLen, fullLen); #endif if (WriteConsole(hdl, &(vWriteToConsoleBuffer[offset]), (DWORD)thisLen, &wrote, NULL)) { #ifdef DEBUG_CONSOLE_OUTPUT _tprintf(TEXT("\nwriteToConsole (WriteConsole wrote %d of requested %d characters)\n"), wrote, thisLen); #endif /* Success. */ offset += thisLen; remainLen -= thisLen; #ifdef DEBUG_CONSOLE_OUTPUT if (remainLen > 0) { /* We have not written out the whole line which means there was no line feed. Add one or the debug output will be hard to read. */ _tprintf(TEXT("\nwriteToConsole (Previous line was incomplete)\n")); } #endif } else { /* Failed. */ #ifdef DEBUG_CONSOLE_OUTPUT _tprintf(TEXT("\nwriteToConsole (Fail WriteConsole wrote %d of requested %d characters)\n"), wrote, thisLen); #endif switch (getLastError()) { case ERROR_NOT_ENOUGH_MEMORY: /* This means that the max heap buffer size is too large and needs to be reduced. */ if (vWriteToConsoleMaxHeapBufferSize < 100) { _tprintf(TEXT("Not enough available HEAP to write to console.\n")); return FALSE; } vWriteToConsoleMaxHeapBufferSize = vWriteToConsoleMaxHeapBufferSize - vWriteToConsoleMaxHeapBufferSize / 20; #ifdef DEBUG_CONSOLE_OUTPUT _tprintf(TEXT("Usable Console HEAP Buffer Size reduced to = %d ****************************************\n"), vWriteToConsoleMaxHeapBufferSize); #endif if (logBufferGrowth) { /* This is queued as we can't use direct logging here. */ log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Usable Console HEAP Buffer Size decreased to %d characters."), vWriteToConsoleMaxHeapBufferSize); } break; case ERROR_INVALID_FUNCTION: case ERROR_INVALID_HANDLE: /* This is a fairly normal thing to happen if the Wrapper is run without an actual console. * ERROR_INVALID_FUNCTION happens when we launch a forked elevated Wrapper. * ERROR_INVALID_HANDLE happens when the Wrapper is launched without its own console. * Log to debug so there is a note, but it is fine if this does not show up in commands where debug output can't be enabled. */ if (currentConsoleLevel <= LEVEL_DEBUG) { _tprintf(TEXT("A console does not exist. Disabling direct console output and falling back to using pipes.\n")); } consoleDirect = FALSE; return FALSE; default: _tprintf(TEXT("Failed to write to console: %s\n"), getLastErrorText()); return FALSE; } } } #ifdef DEBUG_CONSOLE_OUTPUT _tprintf(TEXT("writeToConsole END\n")); #endif return TRUE; } #endif /** * Does a search for all files matching the specified pattern and deletes all * but the most recent 'count' files. The files are sorted by their names. */ void limitLogFileCount(const TCHAR *current, const TCHAR *pattern, int sortMode, int count) { TCHAR **files; int index; int foundCurrent; #ifdef _DEBUG _tprintf(TEXT("limitLogFileCount(%s, %s, %d, %d)\n"), current, pattern, sortMode, count); #endif files = loggerFileGetFiles(pattern, sortMode); if (!files) { /* Failed */ return; } /* When this loop runs we keep the first COUNT files in the list and everything thereafter is deleted. */ foundCurrent = FALSE; index = 0; while (files[index]) { if (index < count) { #ifdef _DEBUG _tprintf(TEXT("Keep files[%d] %s\n"), index, files[index]); #endif if (_tcscmp(current, files[index]) == 0) { /* This is the current file, as expected. */ #ifdef _DEBUG _tprintf(TEXT(" Current\n")); #endif foundCurrent = TRUE; } } else { #ifdef _DEBUG _tprintf(TEXT("Delete files[%d] %s\n"), index, files[index]); #endif if (_tcscmp(current, files[index]) == 0) { /* This is the current file, we don't want to delete it. */ _tprintf(TEXT("Log file sort order would result in current log file being deleted: %s\n"), current); foundCurrent = TRUE; } else if (_tremove(files[index])) { _tprintf(TEXT("Unable to delete old log file: %s (%s)\n"), files[index], getLastErrorText()); } } index++; } /* Now if we did not find the current file, and there are files still in the directory, then we want to also delete the oldest one. Otherwise, the addition of the current file would result in too many files. */ if (!foundCurrent) { if (index >= count) { #ifdef _DEBUG _tprintf(TEXT("Delete files[%d] %s\n"), count - 1, files[count - 1]); #endif if (_tremove(files[count - 1])) { _tprintf(TEXT("Unable to delete old log file: %s (%s)\n"), files[count - 1], getLastErrorText()); } } } loggerFileFreeFiles(files); } /** * Sets the current uptime in seconds. * * @param uptime Uptime in seconds. * @param flipped TRUE when the uptime is no longer meaningful. */ void setUptime(int uptime, int flipped) { uptimeSeconds = uptime; uptimeFlipped = flipped; } /** * Rolls log files using the ROLLNUM system. * * @param nowStr String representation of the date at the format 'YYYYMMDD'. * If NULL, the current date will be used. */ void rollLogs(const TCHAR *nowStr) { static int failureLogged = FALSE; int i; TCHAR rollNum[11]; #if defined(WIN32) && !defined(WIN64) struct _stat64i32 fileStat; #else struct stat fileStat; #endif int result; struct tm *nowTM; TCHAR nowDateBuff[9]; const TCHAR* nowDate; time_t now; #ifdef WIN32 struct _timeb timebNow; #else struct timeval timevalNow; #endif #ifdef _DEBUG _tprintf(TEXT("rollLogs()\n")); #endif if (!logFilePath) { return; } if (!nowStr) { #ifdef WIN32 _ftime(&timebNow); now = (time_t)timebNow.time; #else gettimeofday(&timevalNow, NULL); now = (time_t)timevalNow.tv_sec; #endif nowTM = localtime(&now); _sntprintf(nowDateBuff, 9, TEXT("%04d%02d%02d"), nowTM->tm_year + 1900, nowTM->tm_mon + 1, nowTM->tm_mday); nowDate = (const TCHAR*)nowDateBuff; } else { nowDate = nowStr; } /* If the log file is currently open, it needs to be closed. */ if (logfileFP != NULL) { #ifdef _DEBUG _tprintf(TEXT("Closing logfile so it can be rolled...\n")); #endif fclose(logfileFP); logfileFP = NULL; currentLogFileName[0] = TEXT('\0'); } else { /* Don't roll if the current log file doesn't exist or can't be accessed. */ generateLogFileName(currentLogFileName, currentLogFileNameSize, logFilePath, nowDate, NULL); if (_tstat(currentLogFileName, &fileStat) != 0) { currentLogFileName[0] = TEXT('\0'); return; } } #ifdef _DEBUG _tprintf(TEXT("Rolling log files... (failureLogged=%d)\n"), failureLogged); #endif /* We don't know how many log files need to be rotated yet, so look. */ i = 0; do { i++; _sntprintf(rollNum, 11, TEXT("%d"), i); generateLogFileName(workLogFileName, currentLogFileNameSize, logFilePath, nowDate, rollNum); result = _tstat(workLogFileName, &fileStat); #ifdef _DEBUG if (result == 0) { _tprintf(TEXT("Rolled log file %s exists.\n"), workLogFileName); } #endif } while (result == 0); /* Now, starting at the highest file rename them up by one index. */ for (; i > 1; i--) { _tcsncpy(currentLogFileName, workLogFileName, _tcslen(logFilePath) + 11); _sntprintf(rollNum, 11, TEXT("%d"), i - 1); generateLogFileName(workLogFileName, currentLogFileNameSize, logFilePath, nowDate, rollNum); if ((logFileMaxLogFiles > 0) && (i > logFileMaxLogFiles) && (!logFilePurgePattern)) { /* The file needs to be deleted rather than rolled. If a purge pattern was not specified, * then the files will be deleted here. Otherwise they will be deleted below. */ #ifdef _DEBUG _tprintf(TEXT("Remove old log file %s\n"), workLogFileName); #endif if (_tremove(workLogFileName)) { #ifdef _DEBUG _tprintf(TEXT("Failed to remove old log file %s. err=%d\n"), workLogFileName, getLastError()); #endif if (getLastError() == 2) { /* The file did not exist. */ } else if (getLastError() == 3) { /* The path did not exist. */ } else { if (!failureLogged) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to delete old log file: %s (%s)"), workLogFileName, getLastErrorText()); failureLogged = TRUE; } generateLogFileName(currentLogFileName, currentLogFileNameSize, logFilePath, nowDate, NULL); /* Set the name back so we don't cause a logfile name changed event. */ return; } } else { /* On Windows, in some cases if the file can't be deleted, we still get here without an error. Double check. */ if (_tstat(workLogFileName, &fileStat) == 0) { /* The file still existed. */ #ifdef _DEBUG _tprintf(TEXT("Failed to remove old log file %s\n"), workLogFileName); #endif if (!failureLogged) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to delete old log file: %s"), workLogFileName); failureLogged = TRUE; } generateLogFileName(currentLogFileName, currentLogFileNameSize, logFilePath, nowDate, NULL); /* Set the name back so we don't cause a logfile name changed event. */ return; } #ifdef _DEBUG else { _tprintf(TEXT("Deleted %s\n"), workLogFileName); } #endif } } else { if (_trename(workLogFileName, currentLogFileName) != 0) { if (!failureLogged) { #ifdef WIN32 if (errno == EACCES) { /* This access denied message is treated as a special case, but the use by other applications issue only happens on Windows. */ /* Don't log this as with other errors as that would cause recursion. */ log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to rename log file %s to %s. File is in use by another application."), workLogFileName, currentLogFileName); } else { #endif /* Don't log this as with other errors as that would cause recursion. */ log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to rename log file %s to %s. (%s)"), workLogFileName, currentLogFileName, getLastErrorText()); #ifdef WIN32 } #endif failureLogged = TRUE; } generateLogFileName(currentLogFileName, currentLogFileNameSize, logFilePath, nowDate, NULL); /* Set the name back so we don't cause a logfile name changed event. */ return; } #ifdef _DEBUG else { _tprintf(TEXT("Renamed %s to %s\n"), workLogFileName, currentLogFileName); } #endif } } /* Rename the current file to the #1 index position */ generateLogFileName(currentLogFileName, currentLogFileNameSize, logFilePath, nowDate, NULL); if (_trename(currentLogFileName, workLogFileName) != 0) { if (!failureLogged) { if (getLastError() == 2) { /* File does not yet exist. */ } else if (getLastError() == 3) { /* Path does not yet exist. */ } else if (errno == 13) { /* Don't log this as with other errors as that would cause recursion. */ log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to rename log file %s to %s. File is in use by another application."), currentLogFileName, workLogFileName); } else { /* Don't log this as with other errors as that would cause recursion. */ log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to rename log file %s to %s. (%s)"), currentLogFileName, workLogFileName, getLastErrorText()); } failureLogged = TRUE; } generateLogFileName(currentLogFileName, currentLogFileNameSize, logFilePath, nowDate, NULL); /* Set the name back so we don't cause a logfile name changed event. */ return; } #ifdef _DEBUG else { _tprintf(TEXT("Renamed %s to %s\n"), currentLogFileName, workLogFileName); } #endif /* Now limit the number of files using the standard method. */ if (logFileMaxLogFiles > 0) { if (logFilePurgePattern) { limitLogFileCount(currentLogFileName, logFilePurgePattern, logFilePurgeSortMode, logFileMaxLogFiles + 1); } } if (failureLogged) { /* We made it here, but the failureLogged flag had been previously set. Make a note that we are back and then continue. */ log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Logfile rolling is working again.")); failureLogged = FALSE; } /* Reset the current log file name as it is not being used yet. */ currentLogFileName[0] = TEXT('\0'); /* Log file was rolled, so we want to cause a logfile change event. */ } #ifdef LINUX /** * Get description found in a release file. * This function will only check for the first line because there is only one line in these files. * * @return TRUE if the description could be found. */ int getReleaseDescription(TCHAR **description, const TCHAR *releaseFile) { #if defined(WIN32) && !defined(WIN64) struct _stat64i32 fileStat; #else struct stat fileStat; #endif FILE *file = NULL; int result = FALSE; /* check if the file exists */ if (_tstat(releaseFile, &fileStat) == 0) { file = _tfopen(releaseFile, TEXT("r")); if (file != NULL) { *description = malloc(100 * sizeof(TCHAR)); if (*description == NULL) { outOfMemoryQueued(TEXT("GRD"), 1); } else if (_fgetts(*description, 100, file) != NULL) { /* got the release description inside the file */ result = TRUE; } else { /* _fgetts failed but *description remains unchanged. Free it up. */ free(*description); *description = NULL; } fclose(file); } } return result; } static TCHAR distroDescription[100]; const TCHAR *centosPattern = TEXT("CentOS Linux"); const TCHAR *amiPattern = TEXT("Amazon Linux AMI"); const TCHAR *rhelPattern = TEXT("Red Hat Enterprise Linux"); const TCHAR *fedoraPattern = TEXT("Fedora release"); const TCHAR *linuxPattern = TEXT("Linux"); /** * Get a description of the linux distribution. */ const TCHAR *getDistro() { static int firstCall = TRUE; TCHAR *sysDescription = NULL; TCHAR *rhelDescription = NULL; int foundSysDescription; int foundRhDescription; if (firstCall) { firstCall = FALSE; foundSysDescription = getReleaseDescription(&sysDescription, TEXT("/etc/system-release")); foundRhDescription = getReleaseDescription(&rhelDescription, TEXT("/etc/redhat-release")); if ((foundRhDescription && _tcsstr(rhelDescription, centosPattern) != NULL) || (foundSysDescription && _tcsstr(sysDescription, centosPattern) != NULL)) { _tcsncpy(distroDescription, centosPattern, 100); } else if ((foundRhDescription && _tcsstr(rhelDescription, rhelPattern) != NULL) || (foundSysDescription && _tcsstr(sysDescription, rhelPattern) != NULL)) { _tcsncpy(distroDescription, rhelPattern, 100); } else if ((foundRhDescription && _tcsstr(rhelDescription, fedoraPattern) != NULL) || (foundSysDescription && _tcsstr(sysDescription, fedoraPattern) != NULL)) { _tcsncpy(distroDescription, fedoraPattern, 100); } else if (foundSysDescription && _tcsstr(sysDescription, amiPattern) != NULL) { _tcsncpy(distroDescription, amiPattern, 100); } else { _tcsncpy(distroDescription, linuxPattern, 100); } if (sysDescription) { free(sysDescription); } if (rhelDescription) { free(rhelDescription); } } return distroDescription; } int isCentos() { static int result = -1; return result != -1 ? result : (result = (_tcsicmp(getDistro(), centosPattern) == 0)); } int isAMI() { static int result = -1; return result != -1 ? result : (result = (_tcsicmp(getDistro(), amiPattern) == 0)); } int isRHEL() { static int result = -1; return result != -1 ? result : (result = (_tcsicmp(getDistro(), rhelPattern) == 0)); } int isFedora() { static int result = -1; return result != -1 ? result : (result = (_tcsicmp(getDistro(), fedoraPattern) == 0)); } /** * Check if the glibc version of the user is upper to given numbers. */ int wrapperAssertGlibcUserBis(unsigned int maj, unsigned int min, unsigned int rev) { unsigned int vmaj=0; unsigned int vmin=0; unsigned int vrev=0; TCHAR versionW[10]; mbstowcs(versionW, gnu_get_libc_version(), 10); _stscanf(versionW, TEXT("%d.%d.%d"), &vmaj, &vmin, &vrev); return ((vmaj == maj && vmin == min && vrev >= rev) || (vmaj == maj && vmin > min) || vmaj > maj); } #endif /** * Check if the system is Centos with glibc < 2.21 as there is a memory leak issue under these conditions. */ int doesFtellCauseMemoryLeak() { static int result = -1; #ifdef LINUX if (result == -1) { if ((isCentos() || isAMI() || isRHEL() || isFedora()) && !wrapperAssertGlibcUserBis(2, 21, 0)){ result = 1; } else { result = 0; } } #endif return (result > 0); } /** * Check to see whether or not the log file needs to be rolled. * This is only called when synchronized. */ void checkAndRollLogs(const TCHAR *nowDate, size_t printBufferSize) { size_t position; int result; #if defined(WIN32) && !defined(WIN64) struct _stat64i32 fileStat; #else struct stat fileStat; #endif static size_t unflushedBufferSize = 0; static size_t previousFileSize = 0; /* Depending on the roll mode, decide how to roll the log file. */ if (logFileRollMode & ROLL_MODE_SIZE) { /* Roll based on the size of the file. */ if (logFileMaxSize <= 0) { return; } /* Find out the current size of the file. If the file is currently open then we need to * use ftell to make sure that the buffered data is also included. */ if (logfileFP != NULL && !doesFtellCauseMemoryLeak()) { /* File is open */ if ((result = ftell(logfileFP)) < 0) { _tprintf(TEXT("Unable to get the current logfile size with ftell: %s\n"), getLastErrorText()); return; } position = (size_t)result; } else { /* File is not open or we can't use ftell because of memory leak issue */ generateLogFileName(currentLogFileName, currentLogFileNameSize, logFilePath, nowDate, NULL); if (_tstat(currentLogFileName, &fileStat) != 0) { if (getLastError() == 2) { /* File does not yet exist. */ position = 0; } else if (getLastError() == 3) { /* Path does not exist. */ position = 0; } else { _tprintf(TEXT("Unable to get the current logfile size with stat: %s\n"), getLastErrorText()); return; } } else { position = fileStat.st_size; if (doesFtellCauseMemoryLeak()) { if (previousFileSize != position) { /* the file has been flushed */ previousFileSize = position; unflushedBufferSize = 0; } unflushedBufferSize += printBufferSize; position += unflushedBufferSize; } } } /* Does the log file need to rotated? */ if ((int)position - 2 >= logFileMaxSize) { /* -2: no carriage return for the last message being logged. */ rollLogs(nowDate); } } /* Roll based on the date of the log entry. */ if (logFilePathHasDateToken && _tcscmp(nowDate, logFileLastNowDate) != 0) { /* The date has changed. Close the file. */ if (logfileFP != NULL) { #ifdef _DEBUG _tprintf(TEXT("Closing logfile because the date changed...\n")); #endif fclose(logfileFP); logfileFP = NULL; } /* Always reset the name so the the log file name will be regenerated correctly. */ currentLogFileName[0] = TEXT('\0'); /* This will happen just before a new log file is created. * Check the maximum file count. */ if (logFileMaxLogFiles > 0) { /* We will check for too many files here and then clear the current log file name so it will be set later. */ generateLogFileName(currentLogFileName, currentLogFileNameSize, logFilePath, nowDate, NULL); /* If logFilePurgeSortMode = NAMES_SMART, then logFilePurgePattern should not be NULL (see setLogfilePurgePattern()). */ if (logFilePurgePattern) { limitLogFileCount(currentLogFileName, logFilePurgePattern, logFilePurgeSortMode, logFileMaxLogFiles + 1); } else { /* This case can happen if wrapper.logfile.purge.pattern was left empty and wrapper.logfile.purge.sort is not NAMES_SMART. * We still need to remove old files, so generate a purge pattern and clean them using the default NAMES_SMART method. */ generateLogFilePattern(workLogFileName, currentLogFileNameSize); limitLogFileCount(currentLogFileName, workLogFileName, LOGGER_FILE_SORT_MODE_NAMES_SMART, logFileMaxLogFiles + 1); } currentLogFileName[0] = TEXT('\0'); workLogFileName[0] = TEXT('\0'); } } } void log_printf_queue( int useQueue, int source_id, int level, const TCHAR *lpszFmt, ... ) { int threadId; int localWriteIndex; int localReadIndex; va_list vargs; int count; #if defined(UNICODE) && !defined(WIN32) TCHAR *format; size_t i; size_t len; #endif TCHAR *buffer; /* Start by processing any arguments so that we can store a simple string. */ #ifdef _DEBUG_QUEUE _tprintf(TEXT("log_printf_queue(%d, %d, %d, %S)\n"), useQueue, source_id, level, lpszFmt); #endif #if defined(UNICODE) && !defined(WIN32) if (wcsstr(lpszFmt, TEXT("%s")) != NULL) { /* On UNIX platforms string tokens must always use "%S" variables and not "%s". We can * not safely use malloc here as the call may have originated from a signal handler. * Copy the template into the formatMessages string reserved for this thread, replace * the tokens and then continue using that. This is a bit of overhead, but these async * messages are fairly rare and this greatly simplifies the code throughout the rest of * the application by making it possible to always use the "%s" syntax. */ threadId = getThreadId(); _tcsncpy(formatMessages[threadId], lpszFmt, QUEUED_BUFFER_SIZE); /* Terminate just in case the format was too long. */ formatMessages[threadId][QUEUED_BUFFER_SIZE - 1] = TEXT('\0'); format = formatMessages[threadId]; /* Replace the tokens. */ #ifdef _DEBUG_QUEUE _tprintf(TEXT("Replacing string tokens.\n")); _tprintf(TEXT(" From: %S\n"), format); #endif len = wcslen(format); if (len > 0) { for (i = 0; i < len; i++) { if ((i > 0) && (format[i - 1] == TEXT('%')) && (format[i] == TEXT('s'))) { /* Make sure the '%' was not escaped. */ if ((i > 1) && (format[i - 2] == TEXT('%'))) { /* Escaped. Do nothing. */ } else { /* 's' needs to be changed to 'S' */ format[i] = TEXT('S'); } } } } #ifdef _DEBUG_QUEUE _tprintf(TEXT(" To: %S\n"), format); #endif lpszFmt = format; } #endif if (!isLogInitialized()) { useQueue = FALSE; } /** For queued logging, we have a fixed length buffer to work with. Just to make it easy to catch * problems, always use the same sized fixed buffer even if we will be using the non-queued logging. */ if (useQueue) { /* Care needs to be taken both with this code and the code below to get done as quick as possible. * It is generally safe because each thread has its own queue. The only danger is if a message is * being queued while that thread is interrupted by a signal. If things are setup correctly however * then non-signal threads should not be here in the first place. */ threadId = getThreadId(); localWriteIndex = queueWriteIndex[threadId]; localReadIndex = queueReadIndex[threadId]; if ((localWriteIndex == localReadIndex - 1) || ((localWriteIndex == QUEUE_SIZE - 1) && (localReadIndex == 0))) { _tprintf(TEXT("WARNING log queue overflow for thread[%d]:%d:%d dropping entry: %s\n"), threadId, localWriteIndex, localReadIndex, lpszFmt); return; } /* Get a reference to the message buffer we will use. */ buffer = queueMessages[threadId][queueWriteIndex[threadId]]; } else { /* This will not be queued so we can use malloc to create a new buffer. */ buffer = malloc(sizeof(TCHAR) * QUEUED_BUFFER_SIZE); if (!buffer) { _tprintf(TEXT("Out of memory in logging code (%s)\n"), TEXT("PQ1")); return; } /* For compiler */ threadId = -1; localWriteIndex = -1; } /* Now actually generate our buffer. */ va_start(vargs, lpszFmt); count = _vsntprintf(buffer, QUEUED_BUFFER_SIZE_USABLE, lpszFmt, vargs); va_end(vargs); /* vswprintf returns -1 on overflow. */ if ((count < 0) || (count >= QUEUED_BUFFER_SIZE_USABLE - 1)) { /* The expanded message was too big to fit into the buffer. * On Windows, it writes as much as it can so we can make it look pretty. * But on other platforms, nothing is written so we need a message. * It is illegal to do any mallocs in here, so there is nothing we can really do on UNIX. */ #if defined(WIN32) /* To be safe, make sure we are null terminated. */ buffer[QUEUED_BUFFER_SIZE_USABLE - 1] = 0; _tcsncat(buffer, TEXT("..."), QUEUED_BUFFER_SIZE); #else /* Write an error string that we know will fit. This doesn't need to be localized as it should be caught in testing. */ _sntprintf(buffer, QUEUED_BUFFER_SIZE, TEXT("(Message too long to be logged as a queued message. Please report this.)")); #endif } if (useQueue) { #ifdef _DEBUG_QUEUE _tprintf(TEXT("LOG ENQUEUE[%d] Thread[%d]: %s\n"), localWriteIndex, threadId, buffer); #endif /* Store additional information about the call. */ queueSourceIds[threadId][localWriteIndex] = source_id; queueLevels[threadId][localWriteIndex] = level; /* Lastly increment and wrap the write index. */ queueWriteIndex[threadId]++; if (queueWriteIndex[threadId] >= QUEUE_SIZE) { queueWriteIndex[threadId] = 0; queueWrapped[threadId] = 1; } } else { if (isLogInitialized()) { /* Make a normal logging call with our new buffer. Parameters are already expanded. */ log_printf(source_id, level, #if defined(UNICODE) && !defined(WIN32) TEXT("%S"), #else TEXT("%s"), #endif buffer); } else { /* The best we can do is print something on the screen. */ _tprintf( #if defined(UNICODE) && !defined(WIN32) TEXT("%S\n"), #else TEXT("%s\n"), #endif buffer); } free(buffer); } } /** * Perform any required logger maintenance at regular intervals. * * One operation is to log any queued messages. This must be done very * carefully as it is possible that a signal handler could be thrown at * any time as this function is being executed. */ void maintainLogger() { int localWriteIndex; int source_id; int level; int threadId; TCHAR *buffer; int logFileChanged; TCHAR *logFileCopy; /* Check to see if there is a pending log file change notification. Do this first as we could * generate our own here as well. It is important that we do our best to keep them in order. * Grab it and clear the reference quick in case another is set. This order is thread safe. */ if (pendingLogFileChange) { /* Lock the logging mutex. */ if (lockLoggingMutex()) { return; } logFileCopy = pendingLogFileChange; pendingLogFileChange = NULL; /* Release the lock we have on the logging mutex so that other threads can get in. */ if (releaseLoggingMutex()) { return; } /* Now see if a log file name was queued, using our local copy. */ if (logFileCopy) { #ifdef _DEBUG _tprintf(TEXT("Sending notification of queued log file name change: %s\n"), logFileCopy); #endif if (!logFilePathSetFromConf) { logFileChangedCallback(logFileCopy); } free(logFileCopy); logFileCopy = NULL; } } for (threadId = 0; threadId < WRAPPER_THREAD_COUNT; threadId++) { /* NOTE - The queue variables are not synchronized so we need to access them * carefully and assume that data could possibly be corrupted. */ localWriteIndex = queueWriteIndex[threadId]; /* Snapshot the value to maintain a constant reference. */ if ( queueReadIndex[threadId] != localWriteIndex ) { logFileCopy = NULL; /* Lock the logging mutex. */ if (lockLoggingMutex()) { return; } /* Empty the queue of any logged messages. */ localWriteIndex = queueWriteIndex[threadId]; /* Snapshot the value to maintain a constant reference. */ while (queueReadIndex[threadId] != localWriteIndex) { /* Snapshot the values in the queue at that index. */ source_id = queueSourceIds[threadId][queueReadIndex[threadId]]; level = queueLevels[threadId][queueReadIndex[threadId]]; buffer = queueMessages[threadId][queueReadIndex[threadId]]; /* The buffer is static in the queue and will be reused. */ #ifdef _DEBUG_QUEUE _tprintf(TEXT("LOG QUEUED[%d]: %s\n"), queueReadIndex[threadId], buffer ); #endif logFileChanged = log_printf_message(source_id, level, threadId, TRUE, buffer, TRUE); if (logFileChanged) { if (logFileCopy) { /* This can happen if there are multiple changes while printing the queued messages * (for example if the files are rolled with a very low size limit). * To keep it simple, we will reuse logFileCopy and report only the last change. */ free(logFileCopy); } /* We need to make a copy of currentLogFileName because we will call logFileChangedCallback() outside of the semaphore. */ logFileCopy = malloc(sizeof(TCHAR) * (_tcslen(currentLogFileName) + 1)); if (!logFileCopy) { _tprintf(TEXT("Out of memory in logging code (%s)\n"), TEXT("ML1")); } else { _tcsncpy(logFileCopy, currentLogFileName, _tcslen(currentLogFileName) + 1); } } #ifdef _DEBUG_QUEUE _tprintf(TEXT(" Queue lw=%d, qw=%d, qr=%d\n"), localWriteIndex, queueWriteIndex[threadId], queueReadIndex[threadId]); #endif /* Clear the string we just wrote. */ buffer[0] = TEXT('\0'); queueReadIndex[threadId]++; if ( queueReadIndex[threadId] >= QUEUE_SIZE ) { queueReadIndex[threadId] = 0; } } /* Release the lock we have on the logging mutex so that other threads can get in. */ if (releaseLoggingMutex()) { if (logFileCopy) { free(logFileCopy); } return; } /* Register the change of the logfile. This can be a long operation so do it when we are no longer in the semaphore. */ if (logFileCopy) { if (!logFilePathSetFromConf) { logFileChangedCallback(logFileCopy); } free(logFileCopy); } } } } wrapper_3.5.51_src/src/c/logger.h100644 0 0 32530 14333053650 14033 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ /** * Author: * Johan Sorlin * Leif Mortenson */ #ifndef _LOGGER_H #define _LOGGER_H /* If defined, output debug information about console output. */ /*#define DEBUG_CONSOLE_OUTPUT*/ #ifdef _DEBUG #define _DEBUG_QUEUE #endif #ifdef WIN32 #include #define LOG_USER (1<<3) #endif #ifndef DWORD #define DWORD unsigned long #endif /* Define constants that may not exist in WinError.h (e.g. on Windows IA). */ #ifndef ERROR_MUI_FILE_NOT_FOUND #define ERROR_MUI_FILE_NOT_FOUND 0x3AFC #endif #ifndef ERROR_MUI_INVALID_FILE #define ERROR_MUI_INVALID_FILE 0x3AFD #endif #ifndef ERROR_MUI_INVALID_RC_CONFIG #define ERROR_MUI_INVALID_RC_CONFIG 0x3AFE #endif #ifndef ERROR_MUI_INVALID_LOCALE_NAME #define ERROR_MUI_INVALID_LOCALE_NAME 0x3AFF #endif #ifndef ERROR_MUI_INVALID_ULTIMATEFALLBACK_NAME #define ERROR_MUI_INVALID_ULTIMATEFALLBACK_NAME 0x3B00 #endif #ifndef ERROR_MUI_FILE_NOT_LOADED #define ERROR_MUI_FILE_NOT_LOADED 0x3B01 #endif #include "logger_base.h" /* * * Log source constants * * */ #define WRAPPER_SOURCE_WRAPPER -1 #define WRAPPER_SOURCE_PROTOCOL -2 #define WRAPPER_SOURCE_JVM -3 #define WRAPPER_SOURCE_JVM_VERSION 0 /* The value '0' is important for log_printf to print this output as a direct message (otherwise any '%' in the output will cause memory issues) */ /* * * Log thread constants * * */ /* These are indexes in an array so they must be sequential, start * with zero and be one less than the final WRAPPER_THREAD_COUNT */ #define WRAPPER_THREAD_CURRENT -1 #define WRAPPER_THREAD_SIGNAL 0 #define WRAPPER_THREAD_MAIN 1 #define WRAPPER_THREAD_SRVMAIN 2 #define WRAPPER_THREAD_TIMER 3 #ifdef WIN32 #define WRAPPER_THREAD_MESSAGE 4 #define WRAPPER_THREAD_JAVAIO 5 #else #define WRAPPER_THREAD_JAVAIN 4 #define WRAPPER_THREAD_JAVAIO 5 #endif #define WRAPPER_THREAD_STARTUP (WRAPPER_THREAD_JAVAIO+1) #define WRAPPER_THREAD_COUNT (WRAPPER_THREAD_STARTUP+1) #define MAX_LOG_SIZE 4096 #ifdef WIN32 #else /* A special prefix on log messages that can be bassed through from a forked process so the parent will handle the log message correctly. */ #define LOG_FORK_MARKER TEXT("#!#WrApPeR#!#") #define LOG_SPECIAL_MARKER TEXT("#!#WrApPeRsPeCiAl#!#") #endif /* Default log formats */ #define LOG_FORMAT_LOGFILE_DEFAULT TEXT("LPTM") #define LOG_FORMAT_CONSOLE_DEFAULT TEXT("PM") #define LOG_DESTINATION_FILE 0x01 #define LOG_DESTINATION_CONSOLE 0x02 #define LOG_DESTINATION_SYSLOG 0x04 #define LOG_DESTINATION_ALL 0xFF /* * * Log file roll mode constants * * */ #define ROLL_MODE_UNKNOWN 0 #define ROLL_MODE_NONE 1 #define ROLL_MODE_SIZE 2 #define ROLL_MODE_WRAPPER 4 #define ROLL_MODE_JVM 8 #define ROLL_MODE_SIZE_OR_WRAPPER ROLL_MODE_SIZE + ROLL_MODE_WRAPPER #define ROLL_MODE_SIZE_OR_JVM ROLL_MODE_SIZE + ROLL_MODE_JVM #define ROLL_MODE_DATE 16 #define ROLL_MODE_DATE_TOKEN TEXT("YYYYMMDD") /* Any log messages generated within signal handlers must be stored until we * have left the signal handler to avoid deadlocks in the logging code. * Messages are stored in a round robin buffer of log messages until * maintainLogger is next called. * When we are inside of a signal, and thus when calling log_printf_queue, * we know that it is safe to modify the queue as needed. But it is possible * that a signal could be fired while we are in maintainLogger, so case is * taken to make sure that volatile changes are only made in log_printf_queue. */ #define QUEUE_SIZE 20 /* The size of QUEUED_BUFFER_SIZE_USABLE is arbitrary as the largest size which can be logged in full, * but to avoid crashes due to a bug in the HPUX libc (version < 1403), the length of the buffer passed to _vsntprintf must have a length of 1 + N, where N is a multiple of 8. */ #define QUEUED_BUFFER_SIZE_USABLE (512 + 1) #define QUEUED_BUFFER_SIZE (QUEUED_BUFFER_SIZE_USABLE + 4) /* This can be called from within logging code that would otherwise get stuck in recursion. * Log to the console exactly when it happens and then also try to get it into the log * file at the next oportunity. */ extern void outOfMemoryQueued(const TCHAR *context, int id); extern void outOfMemory(const TCHAR *context, int id); #ifdef _DEBUG /** * Used to dump memory directly to the log file in both HEX and readable format. * Useful in debugging applications to track down memory overflows etc. * * @param label A label that will be prepended on all lines of output. * @param memory The memory to be dumped. * @param len The length of the memory to be dumped. */ extern void log_dumpHex(TCHAR *label, TCHAR *memory, size_t len); #endif /** * Sets the number of milliseconds to allow logging to take before a warning is logged. * Defaults to 0 for no limit. Possible values 0 to 3600000. * * @param threshold Warning threashold. */ extern void setLogWarningThreshold(int threshold); /** * Enable or disable log destinations. * At any time, the logging can be turned off for all or a set of destinations. * Even when a destination is disabled, the code can continue to set its log level but * the value will be stored without re-enabling it. This allows the configuration to * be loaded normally and to be used later when the destination will be reactivated. * The same function can be used to re-enable a set of destinations. * * @param currentDestinationsMask Mask of destinations to toggle. * @param enable Wether the destinations should be enabled or disabled. */ extern void toggleLogDestinations(int currentDestinationsMask, int enable); /** * Sets the console log levels to a simple format for help and usage messages. */ extern void setSimpleLogLevels(); #ifdef WIN32 /** * This sets a flag which tells the logger that alternate source labels should be used to indicate that the current process is a launcher. */ extern void setLauncherSource(); #endif /** * Used for testing to set a pause into the next log entry made. * * @param pauseTime Number of seconds to pause, 0 pauses indefinitely. */ extern void setPauseTime(int pauseTime); /** * Set to true to cause changes in internal buffer sizes to be logged. Useful for debugging. * * @param log TRUE if changes should be logged. */ void setLogBufferGrowth(int log); extern int getLoggingIsPreload(); extern void setLoggingIsPreload(int value); /* * Logfile functions * */ extern int isLogfileAccessed(); extern int resolveDefaultLogFilePath(); /** * Sets the log file to be used. If the specified file is not absolute then * it will be resolved into an absolute path. If there are any problems with * the path, like a directory not existing then the call will fail and the * cause will be written to the existing log. * * @param log_file_path Log file to start using. * @param isConfigured The value comes from the configuration file. * @param preload TRUE if this is a preload pass. * * @return TRUE if there were any problems. */ extern int setLogfilePath( const TCHAR *log_file_path, int isConfigured, int preload); /** * Returns the default logfile. */ extern const TCHAR *getDefaultLogfilePath(); /** * Returns a reference to the currect log file path. * This return value may be changed at any time if the log file is rolled. */ extern const TCHAR *getLogfilePath(); /** * Returns a snapshot of the current log file path. This call safely gets the current path * and returns a copy. It is the responsibility of the caller to free up the memory on * return. Could return null if there was an error. */ extern TCHAR *getCurrentLogfilePath(); #ifndef WIN32 extern int checkLogfileDir(int checkDefaultDir); #endif extern int getLogfileRollModeForName( const TCHAR *logfileRollName ); extern void setLogfileRollMode(int log_file_roll_mode); extern int getLogfileRollMode(); extern void setLogfileUmask(int log_file_umask); #ifndef WIN32 extern void setLogfileGroup(gid_t log_file_group); #endif extern void setLogfileFormat( const TCHAR *log_file_format ); extern void setLogfileLevelInt(int log_file_level); extern int getLogfileLevelInt(); extern void setLogfileLevel( const TCHAR *log_file_level ); extern void setLogfileMaxFileSize( const TCHAR *max_file_size ); extern void setLogfileMaxLogFiles(int max_log_files); extern void setLogfilePurgePattern(const TCHAR *pattern, int* outIsGenerated); extern void setLogfilePurgeSortMode(int sortMode); extern DWORD getLogfileActivity(); /** Sets the auto flush log file flag. */ extern void setLogfileAutoFlush(int autoFlush); /** Sets the auto close log file flag. */ extern void setLogfileAutoClose(int autoClose); /** Closes the logfile if it is open. */ extern void closeLogfile(); /** Flushes any buffered logfile output to the disk. */ extern void flushLogfile(); /* * Console functions * */ extern void setConsoleLogFormat( const TCHAR *console_log_format ); extern void setConsoleLogLevelInt(int console_log_level); extern int getConsoleLogLevelInt(); extern void setConsoleLogLevel( const TCHAR *console_log_level ); extern void setConsoleFlush(int flush); #ifdef WIN32 extern void setConsoleDirect(int direct); #endif extern void setConsoleFatalToStdErr(int toStdErr); extern void setConsoleErrorToStdErr(int toStdErr); extern void setConsoleWarnToStdErr(int toStdErr); /* * Syslog/eventlog functions * */ extern void setSyslogLevelInt(int loginfo_level); extern int getSyslogLevelInt(); extern void setSyslogLevel( const TCHAR *loginfo_level ); extern void setSyslogSplitMessages(int splitMessages); #ifdef WIN32 extern void setSyslogRegister(int sysRegister); extern int getSyslogRegister(); extern TCHAR* getSyslogEventSourceName(); #endif #ifndef WIN32 extern void setSyslogFacility( const TCHAR *loginfo_level ); #endif extern void setSyslogEventSourceName( const TCHAR *event_source_name ); extern void setThreadMessageBufferInitialSize(int initialValue); extern void disableSysLog(int silent); extern int syslogMessageFileRegistered(int silent); extern int registerSyslogMessageFile(int forceInstall, int silent); extern int unregisterSyslogMessageFile(int silent); extern void resetDuration(); extern int getLowLogLevel(); /* * General log functions * */ extern int isLogInitialized(); extern int initLogging(void (*logFileChanged)(const TCHAR *logFile)); extern int disposeLogging(); extern void setUptime(int uptime, int flipped); extern void rollLogs(const TCHAR *nowStr); extern int getLogLevelForName( const TCHAR *logLevelName ); #ifndef WIN32 extern int getLogFacilityForName( const TCHAR *logFacilityName ); extern void generateCurrentLogFileName(TCHAR** pFileName); #endif extern void logRegisterThread(int thread_id); extern void logRegisterFormatCallbacks(int (*countCallback)(const TCHAR format, size_t *reqSize), int (*printCallback)(const TCHAR format, size_t printSize, TCHAR** pBuffer)); void printToConsole(const TCHAR *printBuffer, FILE *target); void printToConsoleLn(const TCHAR *printBuffer, FILE *target); /** * The log_printf function logs a message to the configured log targets. * * This method can be used safely in most cases. See the log_printf_queue * funtion for the exceptions. */ extern void log_printf( int source_id, int level, const TCHAR *lpszFmt, ... ); /** * The log_printf_queue function is less efficient than the log_printf * function and will cause logged messages to be logged out of order from * those logged with log_printf because the messages are queued and then * logged from another thread. * * Use of this function is required in cases where the thread may possibly * be a signal callback. In these cases, it is possible for the original * thread to have been suspended within a log_printf call. If the signal * thread then attempted to call log_printf, it would result in a deadlock. */ extern void log_printf_queue( int useQueue, int source_id, int level, const TCHAR *lpszFmt, ... ); extern void maintainLogger(); extern void invalidMultiByteSequence(const TCHAR *context, int id); #ifdef WIN32 extern void setLogSysLangId(int id); #endif #endif wrapper_3.5.51_src/src/c/logger_base.h100644 0 0 4013 14333053650 15000 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ #ifndef _LOGGER_BASE_H #define _LOGGER_BASE_H #include "wrapper_i18n.h" /* * * Log level constants * * */ /* No logging at all. */ #define LEVEL_NONE 9 /* Notice messages which should always be displayed. These never go to the syslog. */ #define LEVEL_NOTICE 8 /* Advisor messages which should always be displayed. These never go to the syslog. */ #define LEVEL_ADVICE 7 /* Too many restarts, unable to start etc. Case when the Wrapper is forced to exit. */ #define LEVEL_FATAL 6 /* JVM died, hung messages */ #define LEVEL_ERROR 5 /* Warning messages. */ #define LEVEL_WARN 4 /* Started, Stopped, Restarted messages. */ #define LEVEL_STATUS 3 /* Copyright message. and logged console output. */ #define LEVEL_INFO 2 /* Current debug messages */ #define LEVEL_DEBUG 1 /* Unknown level */ #define LEVEL_UNKNOWN 0 /* The maximum length of a source string, not including the null character. */ #define MAX_SOURCE_LENGTH 8 /* * * Function predeclaration * * */ /** * Returns a textual error message of the last error encountered. * * @return The last error message. */ extern const TCHAR* getLastErrorText(); /** * Returns a textual error message of a given error number. * * @param errorNum Error code. * @param handle (for Windows only) A module handle containing the message-table resource(s) to search. If NULL, the current process's application image file will be searched. * * @return The error message. */ extern const TCHAR* getErrorText(int errorNum, void* handle); /** * Returns the last error number. * * @return The last error number. */ extern int getLastError(); extern void outOfMemory(const TCHAR *context, int id); #endif wrapper_3.5.51_src/src/c/logger_file.c100644 0 0 73450 14333053650 15033 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * Author: * Tanuki Software Development Team */ #ifdef WIN32 #include #include #else #include #include #include #endif #ifdef WIN32 #include #endif #include "logger.h" #include "logger_file.h" #include "wrapper_i18n.h" #define FILES_CHUNK 5 #ifndef TRUE #define TRUE -1 #endif #ifndef FALSE #define FALSE 0 #endif /** * Returns a valid sort mode given a name: "TIMES", "NAMES_ASC", "NAMES_DEC", "NAMES_SMART". * In the event of an invalid value, TIMES will be returned. */ int loggerFileGetSortMode(const TCHAR *modeName) { if (strcmpIgnoreCase(modeName, TEXT("NAMES_ASC")) == 0) { return LOGGER_FILE_SORT_MODE_NAMES_ASC; } else if (strcmpIgnoreCase(modeName, TEXT("NAMES_DEC")) == 0) { return LOGGER_FILE_SORT_MODE_NAMES_DEC; } else if (strcmpIgnoreCase(modeName, TEXT("NAMES_SMART")) == 0) { return LOGGER_FILE_SORT_MODE_NAMES_SMART; } else { return LOGGER_FILE_SORT_MODE_TIMES; } } #ifdef WIN32 int sortFilesTimes(TCHAR **files, __time64_t *fileTimes, int cnt) { #else int sortFilesTimes(TCHAR **files, time_t *fileTimes, int cnt) { #endif int i, j; TCHAR *temp; #ifdef WIN32 __time64_t tempTime; #else time_t tempTime; #endif for (i = 0; i < cnt; i++) { for (j = 0; j < cnt - 1; j++) { if (fileTimes[j] < fileTimes[j + 1]) { temp = files[j + 1]; tempTime = fileTimes[j + 1]; files[j + 1] = files[j]; fileTimes[j + 1] = fileTimes[j]; files[j] = temp; fileTimes[j] = tempTime; } } } return TRUE; } /** * Compares two strings. Returns 0 if they are equal, -1 if file1 is bigger, 1 if file2 is bigger. */ int compareFileNames(const TCHAR *file1, const TCHAR *file2) { int pos1, pos2; TCHAR c1, c2; int numeric1, numeric2; long int num1, num2; int afterNumber = FALSE; pos1 = 0; pos2 = 0; while (TRUE) { c1 = file1[pos1]; c2 = file2[pos2]; /*printf(" file1[%d]=%d, file2[%d]=%d\n", pos1, c1, pos2, c2);*/ /* Did we find the null. */ if (c1 == 0) { if (c2 == 0) { return 0; } else { return 1; } } else { if (c2 == 0) { return -1; } else { /* Continue. */ } } /* We have two characters. */ numeric1 = ((c1 >= TEXT('0')) && (c1 <= TEXT('9'))); numeric2 = ((c2 >= TEXT('0')) && (c2 <= TEXT('9'))); /* See if one or both of the strings is numeric. */ if (numeric1) { if (numeric2) { /* Both are numeric, we need to start comparing the two file names as integer values. */ num1 = c1 - TEXT('0'); c1 = file1[pos1 + 1]; while ((c1 >= TEXT('0')) && (c1 <= TEXT('9'))) { num1 = num1 * 10 + (c1 - TEXT('0')); pos1++; c1 = file1[pos1 + 1]; } num2 = c2 - TEXT('0'); c2 = file2[pos2 + 1]; while ((c2 >= TEXT('0')) && (c2 <= TEXT('9'))) { num2 = num2 * 10 + (c2 - TEXT('0')); pos2++; c2 = file2[pos2 + 1]; } /*printf(" num1=%ld, num2=%ld\n", num1, num2);*/ if (num1 > num2) { return -1; } else if (num2 > num1) { return 1; } else { /* Equal, continue. */ } afterNumber = TRUE; } else { /* 1 is numeric, 2 is not. */ if (afterNumber) { return -1; } else { return 1; } } } else { if (numeric2) { /* 1 is not, 2 is numeric. */ if (afterNumber) { return 1; } else { return -1; } } else { /* Neither is numeric. */ } } /* Compare the characters as is. */ if (c1 > c2) { return -1; } else if (c2 > c1) { return 1; } else { /* Equal, continue. */ if ((c1 == TEXT('.')) || (c1 == TEXT('-')) || (c1 == TEXT('_'))) { } else { afterNumber = FALSE; } } pos1++; pos2++; } } int compareFileNamesIndex(const TCHAR *file1, const TCHAR *file2, int startIndex, int stopIndex, int startCountFromEnd, int stopCountFromEnd) { TCHAR *file1_; TCHAR *file2_; int start; int len; int result; start = (startCountFromEnd ? (int)_tcslen(file1) - startIndex : startIndex); len = (stopCountFromEnd ? (int)_tcslen(file1) - stopIndex : stopIndex) - start; file1_ = malloc(sizeof(TCHAR) * (len + 1)); if (!file1_) { outOfMemoryQueued(TEXT("CFNI"), 1); return 0; } _tcsncpy(file1_, file1 + start, len); file1_[len] = 0; start = (startCountFromEnd ? (int)_tcslen(file2) - startIndex : startIndex); len = (stopCountFromEnd ? (int)_tcslen(file2) - stopIndex : stopIndex) - start; file2_ = malloc(sizeof(TCHAR) * (len + 1)); if (!file2_) { free(file1_); outOfMemoryQueued(TEXT("CFNI"), 2); return 0; } _tcsncpy(file2_, file2 + start, len); file2_[len] = 0; result = compareFileNames(file1_, file2_); free(file1_); free(file2_); return result; } int sortFilesNamesAsc(TCHAR **files, int cnt) { int i, j; TCHAR *temp; int cmp; for (i = 0; i < cnt; i++) { for (j = 0; j < cnt - 1; j++) { cmp = compareFileNames(files[j], files[j+1]); if (cmp < 0) { temp = files[j + 1]; files[j + 1] = files[j]; files[j] = temp; } } } return TRUE; } int sortFilesNamesDec(TCHAR **files, int cnt) { int i, j; TCHAR *temp; int cmp; for (i = 0; i < cnt; i++) { for (j = 0; j < cnt - 1; j++) { cmp = compareFileNames(files[j], files[j+1]); if (cmp > 0) { temp = files[j + 1]; files[j + 1] = files[j]; files[j] = temp; } } } return TRUE; } int sortFilesNamesDecIndex(TCHAR **files, int cnt, int startIndex, int stopIndex, int startCountFromEnd, int stopCountFromEnd) { int i, j; TCHAR *temp; int cmp; for (i = 0; i < cnt; i++) { for (j = 0; j < cnt - 1; j++) { cmp = compareFileNamesIndex(files[j], files[j+1], startIndex, stopIndex, startCountFromEnd, stopCountFromEnd); if (cmp > 0) { temp = files[j + 1]; files[j + 1] = files[j]; files[j] = temp; } } } return TRUE; } /** * This function allows to sort filenames with the following logic: * - if the given pattern contains a ???????? (date) token, the files are first sorted by date descending. * - if the given pattern contains a * (num) token, the files are sorted by ascending numbers (for equivalent dates). * The first file being the one without number. * NOTE: The function assumes that there is at most one token of each! * * @param pattern pattern used to figure out how to sort the files. * @param files list of files to sort. * @param cnt number of files to sort. */ int sortFilesNamesSmart(const TCHAR* pattern, TCHAR **files, int cnt) { int i, j; TCHAR *temp; int cmp; TCHAR* numToken; TCHAR* dateToken; int numStartIndex, numStopIndex; int dateStartIndex = 0; int dateStopIndex = 0; int startCountFromEnd = 0; int stopCountFromEnd = 0; /* First sort by date. */ dateToken = _tcsstr(pattern, TEXT("?")); numToken = _tcsstr(pattern, TEXT("*")); if (dateToken) { if (!numToken || (dateToken < numToken)) { dateStartIndex = (int)(dateToken - pattern); dateStopIndex = dateStartIndex + 8; startCountFromEnd = FALSE; stopCountFromEnd = FALSE; } else { /* There is a num token before the date. So the length before the date is not fixed. Calculate the index from the end. */ dateStartIndex = (int)_tcslen(pattern) - (int)(dateToken - pattern); dateStopIndex = dateStartIndex - 8; startCountFromEnd = TRUE; stopCountFromEnd = TRUE; } sortFilesNamesDecIndex(files, cnt, dateStartIndex, dateStopIndex, startCountFromEnd, stopCountFromEnd); } if (numToken) { numStartIndex = (int)(numToken - pattern); numStopIndex = (int)_tcslen(pattern) - (numStartIndex + 1); for (i = 0; i < cnt; i++) { for (j = 0; j < cnt - 1; j++) { if (dateToken) { /* Make sure that the dates are equals. */ cmp = compareFileNamesIndex(files[j], files[j+1], dateStartIndex, dateStopIndex, startCountFromEnd, stopCountFromEnd); if (cmp != 0) { continue; } } /* Sort by ascending name. */ cmp = compareFileNamesIndex(files[j], files[j+1], numStartIndex, numStopIndex, FALSE, TRUE); if (cmp < 0) { temp = files[j + 1]; files[j + 1] = files[j]; files[j] = temp; } } } } return TRUE; } /** * Returns a NULL terminated list of file names within the specified pattern. * The files will be sorted new to old for TIMES. Then incremental ordering * for NAMES. The numeric components of the names will be treated as * numbers and sorted accordingly. */ TCHAR** loggerFileGetFiles(const TCHAR* pattern, int sortMode) { int cnt; int filesSize; TCHAR **files; #ifdef WIN32 int i; size_t dirLen; TCHAR *c; TCHAR *dirPart; intptr_t handle; struct _tfinddata64_t fblock; size_t fileLen; TCHAR **newFiles; __time64_t *fileTimes; __time64_t *newFileTimes; #else #ifdef WRAPPER_FILE_DEBUG int i; #endif int result; glob_t g; int findex; time_t *fileTimes; struct stat fileStat; #endif #ifdef WRAPPER_FILE_DEBUG _tprintf(TEXT("loggerFileGetFiles(%s, %d)\n"), pattern, sortMode); #endif #ifdef WIN32 cnt = 0; /* Initialize the files array. */ filesSize = FILES_CHUNK; files = malloc(sizeof(TCHAR *) * filesSize); if (!files) { outOfMemoryQueued(TEXT("WFGF"), 1); return NULL; } memset(files, 0, sizeof(TCHAR *) * filesSize); fileTimes = malloc(sizeof(__time64_t) * filesSize); if (!fileTimes) { outOfMemoryQueued(TEXT("WFGF"), 2); free(files); return NULL; } memset(fileTimes, 0, sizeof(__time64_t) * filesSize); /* Extract any path information from the beginning of the file */ c = max(_tcsrchr(pattern, TEXT('\\')), _tcsrchr(pattern, TEXT('/'))); if (c == NULL) { /* No directory component */ dirPart = malloc(sizeof(TCHAR) * 1); if (!dirPart) { outOfMemoryQueued(TEXT("WFGF"), 3); return NULL; } dirPart[0] = TEXT('\0'); dirLen = 0; } else { /* extract the directory. */ dirLen = c - pattern + 1; dirPart = malloc(sizeof(TCHAR) * (dirLen + 1)); if (!dirPart) { outOfMemoryQueued(TEXT("WFGF"), 4); return NULL; } _tcsncpy(dirPart, pattern, dirLen); dirPart[dirLen] = TEXT('\0'); } #ifdef WRAPPER_FILE_DEBUG _tprintf(TEXT(" dirPart=[%s]\n"), dirPart); #endif /* Get the first file. */ #ifdef _IA64_ /* On Itanium, the first parameter is not a "const". If you don't cast it, then you have a warning */ if ((handle = _tfindfirst64((TCHAR *)pattern, &fblock)) > 0) { #else if ((handle = _tfindfirst64(pattern, &fblock)) > 0) { #endif if ((_tcscmp(fblock.name, TEXT(".")) != 0) && (_tcscmp(fblock.name, TEXT("..")) != 0)) { fileLen = _tcslen(fblock.name); files[cnt] = malloc((_tcslen(dirPart) + _tcslen(fblock.name) + 1) * sizeof(TCHAR)); if (!files[cnt]) { outOfMemoryQueued(TEXT("WFGF"), 5); free(fileTimes); loggerFileFreeFiles(files); free(dirPart); return NULL; } _sntprintf(files[cnt], _tcslen(dirPart) + _tcslen(fblock.name) + 1, TEXT("%s%s"), dirPart, fblock.name); fileTimes[cnt] = fblock.time_write; #ifdef WRAPPER_FILE_DEBUG _tprintf(TEXT(" files[%d]=%s, %ld\n"), cnt, files[cnt], fileTimes[cnt]); #endif cnt++; } /* Look for additional files. */ while (_tfindnext64(handle, &fblock) == 0) { if ((_tcscmp(fblock.name, TEXT(".")) != 0) && (_tcscmp(fblock.name, TEXT("..")) != 0)) { /* Make sure we have enough room in the files array. */ if (cnt >= filesSize - 1) { newFiles = malloc(sizeof(TCHAR *) * (filesSize + FILES_CHUNK)); if (!newFiles) { outOfMemoryQueued(TEXT("WFGF"), 6); free(fileTimes); loggerFileFreeFiles(files); free(dirPart); return NULL; } memset(newFiles, 0, sizeof(TCHAR *) * (filesSize + FILES_CHUNK)); newFileTimes = malloc(sizeof(__time64_t) * (filesSize + FILES_CHUNK)); if (!newFileTimes) { outOfMemoryQueued(TEXT("WFGF"), 7); free(newFiles); free(fileTimes); loggerFileFreeFiles(files); free(dirPart); return NULL; } memset(newFileTimes, 0, sizeof(__time64_t) * (filesSize + FILES_CHUNK)); for (i = 0; i < filesSize; i++) { newFiles[i] = files[i]; newFileTimes[i] = fileTimes[i]; } free(files); free(fileTimes); files = newFiles; fileTimes = newFileTimes; filesSize += FILES_CHUNK; #ifdef WRAPPER_FILE_DEBUG _tprintf(TEXT(" increased files to %d\n"), filesSize); #endif } fileLen = _tcslen(fblock.name); files[cnt] = malloc((_tcslen(dirPart) + _tcslen(fblock.name) + 1) * sizeof(TCHAR)); if (!files[cnt]) { outOfMemoryQueued(TEXT("WFGF"), 8); free(fileTimes); loggerFileFreeFiles(files); free(dirPart); return NULL; } _sntprintf(files[cnt], _tcslen(dirPart) + _tcslen(fblock.name) + 1, TEXT("%s%s"), dirPart, fblock.name); fileTimes[cnt] = fblock.time_write; #ifdef WRAPPER_FILE_DEBUG _tprintf(TEXT(" files[%d]=%s, %ld\n"), cnt, files[cnt], fileTimes[cnt]); #endif cnt++; } } /* Close the file search */ _findclose(handle); } if (cnt <= 0) { if (errno == ENOENT) { /* No files matched. */ #ifdef WRAPPER_FILE_DEBUG _tprintf(TEXT(" No files matched.\n")); #endif } else { /* Encountered an error of some kind. */ log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Error listing files, %s: %s"), pattern, getLastErrorText()); free(fileTimes); loggerFileFreeFiles(files); return NULL; } } #else /* Unix */ #ifdef UNICODE char* cPattern; size_t req; req = wcstombs(NULL, pattern, 0) + 1; cPattern = malloc(req); if(!cPattern) { outOfMemoryQueued(TEXT("WFGF"), 8); return NULL; } wcstombs(cPattern, pattern, req); result = glob(cPattern, GLOB_MARK | GLOB_NOSORT, NULL, &g); free(cPattern); #else result = glob(pattern, GLOB_MARK | GLOB_NOSORT, NULL, &g); #endif cnt = 0; if (!result) { if (g.gl_pathc > 0) { filesSize = g.gl_pathc + 1; files = malloc(sizeof(TCHAR *) * filesSize); if (!files) { outOfMemoryQueued(TEXT("WFGF"), 9); return NULL; } memset(files, 0, sizeof(TCHAR *) * filesSize); fileTimes = malloc(sizeof(time_t) * filesSize); if (!fileTimes) { outOfMemoryQueued(TEXT("WFGF"), 10); loggerFileFreeFiles(files); return NULL; } memset(fileTimes, 0, sizeof(time_t) * filesSize); for (findex = 0; findex < g.gl_pathc; findex++) { #ifdef UNICODE req = mbstowcs(NULL, g.gl_pathv[findex], 0); if (req == (size_t)-1) { invalidMultiByteSequence(TEXT("GLET"), 1); } files[cnt] = malloc((req + 1) * sizeof(TCHAR)); if (!files[cnt]) { outOfMemoryQueued(TEXT("WFGF"), 11); free(fileTimes); loggerFileFreeFiles(files); return NULL; } mbstowcs(files[cnt], g.gl_pathv[findex], req + 1); #else files[cnt] = malloc((strlen(g.gl_pathv[findex]) + 1)); if (!files[cnt]) { outOfMemoryQueued(TEXT("WFGF"), 11); free(fileTimes); loggerFileFreeFiles(files); return NULL; } strncpy(files[cnt], g.gl_pathv[findex], strlen(g.gl_pathv[findex]) + 1); #endif /* Only try to get the modified time if it is really necessary. */ if (sortMode == LOGGER_FILE_SORT_MODE_TIMES) { if (!_tstat(files[cnt], &fileStat)) { fileTimes[cnt] = fileStat.st_mtime; } else { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Failed to stat %s: %s"), files[cnt], getLastErrorText()); } } #ifdef WRAPPER_FILE_DEBUG _tprintf(TEXT(" files[%d]=%s, %ld\n"), cnt, files[cnt], fileTimes[cnt]); #endif cnt++; } } else { #ifdef WRAPPER_FILE_DEBUG printf(" No files matched.\n"); #endif /* No files, but we still need the array. */ filesSize = 1; files = malloc(sizeof(TCHAR *) * filesSize); if (!files) { outOfMemoryQueued(TEXT("WFGF"), 12); return NULL; } memset(files, 0, sizeof(TCHAR *) * filesSize); fileTimes = malloc(sizeof(time_t) * filesSize); if (!fileTimes) { free(files); outOfMemoryQueued(TEXT("WFGF"), 13); return NULL; } memset(fileTimes, 0, sizeof(time_t) * filesSize); } globfree(&g); } else if (result == GLOB_NOMATCH) { #ifdef WRAPPER_FILE_DEBUG _tprintf(TEXT(" No files matched.\n")); #endif /* No files, but we still need the array. */ filesSize = 1; files = malloc(sizeof(TCHAR *) * filesSize); if (!files) { outOfMemoryQueued(TEXT("WFGF"), 14); return NULL; } memset(files, 0, sizeof(TCHAR *) * filesSize); fileTimes = malloc(sizeof(time_t) * filesSize); if (!fileTimes) { free(files); outOfMemoryQueued(TEXT("WFGF"), 15); return NULL; } memset(fileTimes, 0, sizeof(time_t) * filesSize); } else { /* Encountered an error of some kind. */ log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Error listing files, %s: %s"), pattern, getLastErrorText()); return NULL; } #endif if (sortMode == LOGGER_FILE_SORT_MODE_TIMES) { if (!sortFilesTimes(files, fileTimes, cnt)) { /* Failed. Reported. */ free(fileTimes); loggerFileFreeFiles(files); return NULL; } } else if (sortMode == LOGGER_FILE_SORT_MODE_NAMES_SMART) { if (!sortFilesNamesSmart(pattern, files, cnt)) { /* Failed. Reported. */ free(fileTimes); loggerFileFreeFiles(files); return NULL; } } else if (sortMode == LOGGER_FILE_SORT_MODE_NAMES_DEC) { if (!sortFilesNamesDec(files, cnt)) { /* Failed. Reported. */ free(fileTimes); loggerFileFreeFiles(files); return NULL; } } else { /* LOGGER_FILE_SORT_MODE_NAMES_ASC */ if (!sortFilesNamesAsc(files, cnt)) { /* Failed. Reported. */ free(fileTimes); loggerFileFreeFiles(files); return NULL; } } #ifdef WRAPPER_FILE_DEBUG _tprintf(TEXT(" Sorted:\n")); for (i = 0; i < cnt; i++) { _tprintf(TEXT(" files[%d]=%s, %ld\n"), i, files[i], fileTimes[i]); } _tprintf(TEXT("loggerFileGetFiles(%s, %d) END\n"), pattern, sortMode); #endif free(fileTimes); return files; } /** * Frees the array of file names returned by loggerFileGetFiles() */ void loggerFileFreeFiles(TCHAR** files) { int i; i = 0; while (files[i]) { free(files[i]); i++; } free(files); } /** * Combines two paths and take care to add only one separator between them. * * The returned string must be freed by the caller. * * @param path1 base path * @param path2 relative path. * * @return The resulting path, or NULL if there were any problems. */ TCHAR *combinePath(const TCHAR *path1, const TCHAR *path2) { TCHAR* result; TCHAR* tempPath1 = NULL; TCHAR* tempPath2 = NULL; TCHAR* tempPath2Ptr; TCHAR c1; TCHAR c2; size_t len1 = _tcslen(path1); size_t len2 = _tcslen(path2); size_t len = len1 + len2; int i = 0; #ifdef WIN32 const TCHAR bad_sep = TEXT('/'); const TCHAR good_sep = TEXT('\\'); #else const TCHAR bad_sep = TEXT('\\'); const TCHAR good_sep = TEXT('/'); #endif if (len1 > 0) { tempPath1 = malloc(sizeof(TCHAR) * (len1 + 1)); if (!tempPath1) { outOfMemoryQueued(TEXT("CP"), 1); return NULL; } _tcsncpy(tempPath1, path1, len1 + 1); } if (len2 > 0) { tempPath2 = malloc(sizeof(TCHAR) * (len2 + 1)); if (!tempPath2) { outOfMemoryQueued(TEXT("CP"), 2); free(tempPath1); return NULL; } _tcsncpy(tempPath2, path2, len2 + 1); } if (!tempPath1 && !tempPath2) { result = NULL; } else if (tempPath1 && !tempPath2) { result = tempPath1; } else if (!tempPath1 && tempPath2) { result = tempPath2; } else { tempPath2Ptr = tempPath2; /* first replace all directory separators by their standard according to the platform. * we want to avoid that the two paths use different separators. */ while (tempPath1[i] != TEXT('\0')) { if (tempPath1[i] == bad_sep) { tempPath1[i] = good_sep; } i++; } i = 0; while (tempPath2[i] != TEXT('\0')) { if (tempPath2[i] == bad_sep) { tempPath2[i] = good_sep; } i++; } c1 = tempPath1[len1 - 1]; c2 = tempPath2[0]; if (c1 == good_sep) { if (c2 == good_sep) { tempPath2Ptr++; } else { len += 1; } result = malloc(sizeof(TCHAR) * len); if (!result) { outOfMemoryQueued(TEXT("CP"), 3); free(tempPath1); free(tempPath2); return NULL; } _sntprintf(result, len, TEXT("%s%s"), tempPath1, tempPath2Ptr); } else { if (c2 == good_sep) { tempPath2Ptr++; len += 1; } else { len += 2; } result = malloc(sizeof(TCHAR) * len); if (!result) { outOfMemoryQueued(TEXT("CP"), 4); free(tempPath1); free(tempPath2); return NULL; } _sntprintf(result, len, TEXT("%s%c%s"), tempPath1, good_sep, tempPath2Ptr); } free(tempPath1); free(tempPath2); } return result; } /** * Given a path, resolve a real absolute path which has resolved all relative and symbolic links. * * The returned string must be freed by the caller. * * @param path The source path. * @param pathDesc A description of the path used for error messages. * @param errorLevel Level to log errors at. * * @return The absolute path, or NULL if there were any problems. */ TCHAR *getRealPath(const TCHAR *path, const TCHAR *pathDesc, int errorLevel, int useQueue) { TCHAR *realPath; #ifdef WIN32 DWORD len; #else size_t len; TCHAR *tempPath; #endif #ifdef WIN32 len = GetFullPathName(path, 0, NULL, NULL); if (!len) { if (errorLevel != LEVEL_NONE) { log_printf_queue(useQueue, WRAPPER_SOURCE_WRAPPER, errorLevel, TEXT("Unable to resolve the %s '%s': %s"), pathDesc, path, getLastErrorText()); } return NULL; } realPath = malloc(sizeof(TCHAR) * len); if (!realPath) { if (useQueue) { outOfMemoryQueued(TEXT("GRP"), 1); } else { outOfMemory(TEXT("GRP"), 1); } return NULL; } if (!GetFullPathName(path, len, realPath, NULL)) { if (errorLevel != LEVEL_NONE) { log_printf_queue(useQueue, WRAPPER_SOURCE_WRAPPER, errorLevel, TEXT("Unable to resolve the %s '%s': %s"), pathDesc, path, getLastErrorText()); } free(realPath); return NULL; } #else /* The solaris implementation of realpath will return a relative path if a relative * path is provided. We always need an absolute path here. So build up one and * then use realpath to remove any .. or other relative references. */ tempPath = malloc(sizeof(TCHAR) * (PATH_MAX + 1)); if (!tempPath) { if (useQueue) { outOfMemoryQueued(TEXT("GRP"), 2); } else { outOfMemory(TEXT("GRP"), 2); } return NULL; } if (_trealpathN(path, tempPath, PATH_MAX + 1) == NULL) { if (errorLevel != LEVEL_NONE) { log_printf_queue(useQueue, WRAPPER_SOURCE_WRAPPER, errorLevel, TEXT("Unable to resolve the %s '%s': %s"), pathDesc, path, getLastErrorText()); } free(tempPath); return NULL; } /* Now that we know how big the resulting string is, put it into a buffer of the correct size to avoid waste. */ len = _tcslen(tempPath) + 1; realPath = malloc(sizeof(TCHAR) * len); if (!realPath) { if (useQueue) { outOfMemoryQueued(TEXT("GRP"), 3); } else { outOfMemory(TEXT("GRP"), 3); } free(tempPath); return NULL; } _tcsncpy(realPath, tempPath, len); free(tempPath); #endif return realPath; } /** * Returns the absolute path of a file even if the file is not yet created. * The folder containing the file must exist. * * The returned string must be freed by the caller. * * @param path The source path. * @param pathDesc A description of the path used for error messages. * @param errorLevel Level to log errors at. * * @return The absolute path, or NULL if there were any problems. */ TCHAR* getAbsolutePathOfFile(const TCHAR* path, const TCHAR *pathDesc, int errorLevel, int useQueue) { TCHAR* ptr; TCHAR* dir; const TCHAR* file; TCHAR* result = NULL; TCHAR* pathCpy; pathCpy = malloc(sizeof(TCHAR) * (_tcslen(path) + 1)); if (!pathCpy) { outOfMemoryQueued(TEXT("GAPOF"), 1); } else { _tcsncpy(pathCpy, path, _tcslen(path) + 1); ptr = __max(_tcsrchr(pathCpy, TEXT('\\')), _tcsrchr(pathCpy, TEXT('/'))); if (ptr) { *ptr = 0; ptr++; dir = getRealPath(pathCpy, pathDesc, errorLevel, useQueue); file = ptr; } else { dir = getRealPath(TEXT("."), pathDesc, errorLevel, useQueue); file = pathCpy; } if (dir) { result = combinePath(dir, file); free(dir); } free(pathCpy); } return result; } #ifdef LOGGER_FILE_DEBUG void loggerFileTests() { TCHAR** files; printf("Start loggerFileTests\n"); files = loggerFileGetFiles((TEXT("../logs/*.log*"), LOGGER_FILE_SORT_MODE_TIMES); if (files) { loggerFileFreeFiles(files); } files = loggerFileGetFiles(TEXT("../logs/*.log*"), LOGGER_FILE_SORT_MODE_NAMES_ASC); if (files) { loggerFileFreeFiles(files); } files = loggerFileGetFiles(TEXT("../logs/*.log*"), LOGGER_FILE_SORT_MODE_NAMES_DEC); if (files) { loggerFileFreeFiles(files); } printf("End loggerFileTests\n"); } #endif wrapper_3.5.51_src/src/c/logger_file.h100644 0 0 3325 14333053650 15012 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * Author: * Tanuki Software Development Team */ #ifndef _LOGGER_FILE_H #define _LOGGER_FILE_H #ifdef WIN32 #include #endif #include "wrapper_i18n.h" /*#define LOGGER_FILE_DEBUG*/ #define LOGGER_FILE_SORT_MODE_TIMES 100 #define LOGGER_FILE_SORT_MODE_NAMES_ASC 101 #define LOGGER_FILE_SORT_MODE_NAMES_DEC 102 #define LOGGER_FILE_SORT_MODE_NAMES_SMART 103 /** * Returns a valid sort mode given a name: "TIMES", "NAMES_ASC", "NAMES_DEC", "NAMES_SMART". * In the event of an invalid value, TIMES will be returned. */ extern int loggerFileGetSortMode(const TCHAR *modeName); /** * Returns a NULL terminated list of file names within the specified pattern. * The files will be sorted new to old for TIMES. Then incremental ordering * for NAMES. The numeric components of the names will be treated as * numbers and sorted accordingly. */ extern TCHAR** loggerFileGetFiles(const TCHAR* pattern, int sortMode); /** * Frees the array of file names returned by wrapperFileGetFiles() */ extern void loggerFileFreeFiles(TCHAR** files); extern TCHAR *combinePath(const TCHAR *path1, const TCHAR *path2); extern TCHAR *getRealPath(const TCHAR *path, const TCHAR *pathDesc, int errorLevel, int useQueue); TCHAR* getAbsolutePathOfFile(const TCHAR* path, const TCHAR *pathDesc, int errorLevel, int useQueue); #endif wrapper_3.5.51_src/src/c/loggerjni.c100644 0 0 21745 14333053650 14535 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ #include #include #ifdef WIN32 #include #include #else #include #endif #include #include "loggerjni.h" void outOfMemory(const TCHAR *context, int id) { log_printf(TEXT("WrapperJNI Error: Out of memory (%s%02d). %s"), context, id, getLastErrorText()); } void invalidMultiByteSequence(const TCHAR *context, int id) { log_printf(TEXT("WrapperJNI Error: Invalid multibyte Sequence found in (%s%02d). %s"), context, id, getLastErrorText()); } #define LAST_ERROR_TEXT_BUFFER_SIZE 1024 /** Buffer holding the last error message. * TODO: This needs to be made thread safe, meaning that we need a buffer for each thread. */ TCHAR lastErrorTextBufferW[LAST_ERROR_TEXT_BUFFER_SIZE]; /** * Returns a textual error message of the last error encountered. * * @return The last error message. */ const TCHAR* getLastErrorText() { int errorNum; #ifdef WIN32 DWORD dwRet; TCHAR* lpszTemp = NULL; #else char* lastErrorTextMB; size_t req; #endif #ifdef WIN32 errorNum = GetLastError(); dwRet = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ARGUMENT_ARRAY, NULL, GetLastError(), LANG_NEUTRAL, (TCHAR*)&lpszTemp, 0, NULL); /* supplied buffer is not long enough */ if (!dwRet) { /* There was an error calling FormatMessage. */ _sntprintf(lastErrorTextBufferW, LAST_ERROR_TEXT_BUFFER_SIZE, TEXT("Failed to format system error message (Error: %d) (Original Error: 0x%x)"), GetLastError(), errorNum); } else if ((long)LAST_ERROR_TEXT_BUFFER_SIZE - 1 < (long)dwRet + 14) { _sntprintf(lastErrorTextBufferW, LAST_ERROR_TEXT_BUFFER_SIZE, TEXT("System error message is too large to convert (Required size: %d) (Original Error: 0x%x)"), dwRet, errorNum); } else { lpszTemp[lstrlen(lpszTemp)-2] = TEXT('\0'); /*remove cr and newline character */ _sntprintf(lastErrorTextBufferW, LAST_ERROR_TEXT_BUFFER_SIZE, TEXT("%s (0x%x)"), lpszTemp, errorNum); } /* following the documentation of FormatMessage, LocalFree should be called to free the output buffer. */ if (lpszTemp) { LocalFree(lpszTemp); } #else errorNum = errno; lastErrorTextMB = strerror(errorNum); req = mbstowcs(NULL, lastErrorTextMB, MBSTOWCS_QUERY_LENGTH); if (req == (size_t)-1) { invalidMultiByteSequence(TEXT("GLET"), 1); _sntprintf(lastErrorTextBufferW, LAST_ERROR_TEXT_BUFFER_SIZE, TEXT("System error message could not be decoded (Error 0x%x)"), errorNum); } else if (req >= LAST_ERROR_TEXT_BUFFER_SIZE) { _sntprintf(lastErrorTextBufferW, LAST_ERROR_TEXT_BUFFER_SIZE, TEXT("System error message too large to convert (Require size: %d) (Original Error: 0x%x)"), req, errorNum); } else { mbstowcs(lastErrorTextBufferW, lastErrorTextMB, LAST_ERROR_TEXT_BUFFER_SIZE); } #endif /* Always reterminate the buffer just to be sure it is safe because badly encoded characters can cause issues. */ lastErrorTextBufferW[LAST_ERROR_TEXT_BUFFER_SIZE - 1] = TEXT('\0'); return lastErrorTextBufferW; } /** * Returns the last error number. * * @return The last error number. */ int getLastError() { #ifdef WIN32 return GetLastError(); #else return errno; #endif } static int (*printMessageCallback)(const TCHAR* message) = NULL; /** * Set a method to print log messages. * * @param callback the method to call to print the message. */ void setPrintMessageCallback(int (*callback)(const TCHAR* message)) { printMessageCallback = callback; } /** * Prints the contents of a buffer to all configured targets. * * Must be called while locked. */ void log_printf_message(TCHAR *message) { TCHAR *subMessage; TCHAR *nextLF; FILE *target; /* If the message contains line feeds then break up the line into substrings and recurse. */ subMessage = message; nextLF = _tcschr(subMessage, TEXT('\n')); if (nextLF) { /* This string contains more than one line. Loop over the strings. It is Ok to corrupt this string because it is only used once. */ while (nextLF) { nextLF[0] = TEXT('\0'); log_printf_message(subMessage); /* Locate the next one. */ subMessage = &(nextLF[1]); nextLF = _tcschr(subMessage, TEXT('\n')); } /* The rest of the buffer will be the final line. */ log_printf_message(subMessage); return; } if (!printMessageCallback || printMessageCallback(message)) { /* We failed at some point. Print the message even if the encoding may be wrong. The string is already localized. Can we get the original string? */ target = stdout; _ftprintf(target, TEXT("%s\n"), message); /* As this is JNI, we always need to flush the output. */ fflush(target); } } /** * The tLog_printf function logs a message to the configured log targets. * * This method can be used safely in most cases. See the tLog_printf_queue * funtion for the exceptions. */ void log_printf(const TCHAR *lpszFmt, ...) { va_list vargs; int count; #ifndef WIN32 TCHAR *msg = NULL; int i, flag; #endif TCHAR* messageBuffer = NULL; size_t messageBufferSize = 1024; #ifndef WIN32 if (wcsstr(lpszFmt, TEXT("%s")) != NULL) { msg = malloc(sizeof(wchar_t) * (wcslen(lpszFmt) + 1)); if (msg) { /* Loop over the format and convert all '%s' patterns to %S' so the UNICODE displays correctly. */ if (wcslen(lpszFmt) > 0) { for (i = 0; i < _tcslen(lpszFmt); i++) { msg[i] = lpszFmt[i]; if ((lpszFmt[i] == TEXT('%')) && (i < _tcslen(lpszFmt)) && (lpszFmt[i + 1] == TEXT('s')) && ((i == 0) || (lpszFmt[i - 1] != TEXT('%')))) { msg[i+1] = TEXT('S'); i++; } } } msg[wcslen(lpszFmt)] = TEXT('\0'); } else { _tprintf(TEXT("Out of memory (P1)\n")); return; } flag = TRUE; } else { msg = (TCHAR*) lpszFmt; flag = FALSE; } #endif /* Loop until the buffer is large enough that we are able to successfully * print into it. Once the buffer has grown to the largest message size, * smaller messages will pass through this code without looping. */ do { if (messageBuffer == 0) { /* No buffer yet. Allocate one to get started. */ messageBuffer = malloc(sizeof(TCHAR) * messageBufferSize); if (!messageBuffer) { _tprintf(TEXT("Out of memory (P2)\n")); messageBufferSize = 0; #ifndef WIN32 if (flag == TRUE) { free(msg); } #endif return; } } /* Try writing to the buffer. */ va_start(vargs, lpszFmt); #ifndef WIN32 count = _vsntprintf(messageBuffer, messageBufferSize, msg, vargs); #else count = _vsntprintf(messageBuffer, messageBufferSize, lpszFmt, vargs); #endif va_end(vargs); if ((count < 0) || (count >= (int)messageBufferSize)) { /* If the count is exactly equal to the buffer size then a null TCHAR was not written. * It must be larger. * Windows will return -1 if the buffer is too small. If the number is * exact however, we still need to expand it to have room for the null. * UNIX will return the required size. */ /* Free the old buffer for starters. */ free(messageBuffer); /* Decide on a new buffer size. */ if (count <= (int)messageBufferSize) { messageBufferSize += 100; } else { messageBufferSize = count + 1; } messageBuffer = malloc(sizeof(TCHAR) * messageBufferSize); if (!messageBuffer) { _tprintf(TEXT("Out of memory (P3)\n")); messageBufferSize = 0; #ifndef WIN32 if (flag == TRUE) { free(msg); } #endif return; } /* Always set the count to -1 so we will loop again. */ count = -1; } } while (count < 0); #ifndef WIN32 if (flag == TRUE) { free(msg); } #endif /* Actually log the message. */ log_printf_message(messageBuffer); free(messageBuffer); } wrapper_3.5.51_src/src/c/loggerjni.h100644 0 0 1070 14333053650 14507 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ #ifndef _LOGGERJNI_H #define _LOGGERJNI_H #include "logger_base.h" void setPrintMessageCallback(int (*callback)(const TCHAR* message)); void log_printf(const TCHAR *lpszFmt, ...); #endif wrapper_3.5.51_src/src/c/makefile.inc100644 0 0 4511 14333053647 14637 0ustar 0 0 # makefile.inc -- Include this file into existing makefile at the very top. # _VC_MANIFEST_INC specifies whether build is incremental (1 - incremental). # _VC_MANIFEST_BASENAME specifies name of a temporary resource file. _VC_BIN_DIR=..\..\bin\\ _VC_LIB_DIR=..\..\lib\\ !if "$(DEBUG)" == "1" CPPFLAGS=$(CPPFLAGS) /MDd LFLAGS=$(LFLAGS) /INCREMENTAL _VC_MANIFEST_INC=1 _VC_MANIFEST_BASENAME=__VC90.Debug !else CPPFLAGS=$(CPPFLAGS) /MD _VC_MANIFEST_INC=0 _VC_MANIFEST_BASENAME=__VC90 !endif #################################################### # _VC_MANIFEST_EMBED_EXE - command to embed manifest in wrapper.EXE: # _VC_MANIFEST_EMBED_DLL - command to embed manifest in wrapper.DLL: !if "$(_VC_MANIFEST_INC)" == "1" #MT_SPECIAL_RETURN=1090650113 #MT_SPECIAL_SWITCH=-notify_resource_update MT_SPECIAL_RETURN=0 MT_SPECIAL_SWITCH= _VC_MANIFEST_EMBED_EXE= \ if exist $@.manifest mt.exe -manifest $@.manifest -out:$(_VC_BIN_DIR)$(_VC_MANIFEST_BASENAME).auto.manifest $(MT_SPECIAL_SWITCH) & \ if "%ERRORLEVEL%" == "$(MT_SPECIAL_RETURN)" \ rc /r $(_VC_BIN_DIR)$(_VC_MANIFEST_BASENAME).auto.rc & \ link $** /out:$(_VC_BIN_DIR)$@ $(LFLAGS) _VC_MANIFEST_EMBED_DLL= \ if exist $@.manifest mt.exe -manifest $@.manifest -out:$(_VC_LIB_DIR)$(_VC_MANIFEST_BASENAME).auto.manifest $(MT_SPECIAL_SWITCH) & \ if "%ERRORLEVEL%" == "$(MT_SPECIAL_RETURN)" \ rc /r $(_VC_LIB_DIR)$(_VC_MANIFEST_BASENAME).auto.rc & \ link $** /out:$(_VC_LIB_DIR)$@ $(LFLAGS) !else _VC_MANIFEST_EMBED_EXE= \ if exist $@.manifest mt.exe -manifest $@.manifest -outputresource:$(_VC_BIN_DIR)$@;1 _VC_MANIFEST_EMBED_DLL= \ if exist $@.manifest mt.exe -manifest $@.manifest -outputresource:$(_VC_LIB_DIR)$@;2 !endif #################################################### # _VC_MANIFEST_CLEAN - command to clean resources files generated temporarily: !if "$(_VC_MANIFEST_INC)" == "1" _VC_MANIFEST_CLEAN=-del $(_VC_BIN_DIR)$(_VC_MANIFEST_BASENAME).auto.res \ $(_VC_BIN_DIR)$(_VC_MANIFEST_BASENAME).auto.rc \ $(_VC_BIN_DIR)$(_VC_MANIFEST_BASENAME).auto.manifest \ $(_VC_LIB_DIR)$(_VC_MANIFEST_BASENAME).auto.res \ $(_VC_LIB_DIR)$(_VC_MANIFEST_BASENAME).auto.rc \ $(_VC_LIB_DIR)$(_VC_MANIFEST_BASENAME).auto.manifest !else _VC_MANIFEST_CLEAN= !endif # End of makefile.inc ####################################################wrapper_3.5.51_src/src/c/makefile.targ.inc100644 0 0 2753 14333053647 15601 0ustar 0 0 # makefile.targ.inc - include this at the very bottom of the existing makefile #################################################### # Commands to generate initial empty manifest file and the RC file # that references it, and for generating the .res file: $(_VC_BIN_DIR)$(_VC_MANIFEST_BASENAME).auto.res : $(_VC_BIN_DIR)$(_VC_MANIFEST_BASENAME).auto.rc $(_VC_BIN_DIR)$(_VC_MANIFEST_BASENAME).auto.rc : $(_VC_BIN_DIR)$(_VC_MANIFEST_BASENAME).auto.manifest type <<$@ #include 1RT_MANIFEST"$(_VC_BIN_DIR)$(_VC_MANIFEST_BASENAME).auto.manifest" 1RT_MANIFEST"$(_VC_BIN_DIR)$(_VC_MANIFEST_BASENAME).auto.manifest" << KEEP $(_VC_BIN_DIR)$(_VC_MANIFEST_BASENAME).auto.manifest : type <<$@ << KEEP $(_VC_LIB_DIR)$(_VC_MANIFEST_BASENAME).auto.res : $(_VC_LIB_DIR)$(_VC_MANIFEST_BASENAME).auto.rc $(_VC_LIB_DIR)$(_VC_MANIFEST_BASENAME).auto.rc : $(_VC_LIB_DIR)$(_VC_MANIFEST_BASENAME).auto.manifest type <<$@ #include 1RT_MANIFEST"$(_VC_LIB_DIR)$(_VC_MANIFEST_BASENAME).auto.manifest" 1RT_MANIFEST"$(_VC_LIB_DIR)$(_VC_MANIFEST_BASENAME).auto.manifest" << KEEP $(_VC_LIB_DIR)$(_VC_MANIFEST_BASENAME).auto.manifest : type <<$@ << KEEP # end of makefile.targ.incwrapper_3.5.51_src/src/c/messages.h100644 0 0 4732 14333053650 14346 0ustar 0 0 // // Values are 32 bit values layed out as follows: // // 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 // 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 // +---+-+-+-----------------------+-------------------------------+ // |Sev|C|R| Facility | Code | // +---+-+-+-----------------------+-------------------------------+ // // where // // Sev - is the severity code // // 00 - Success // 01 - Informational // 10 - Warning // 11 - Error // // C - is the Customer code flag // // R - is a reserved bit // // Facility - is the facility code // // Code - is the facility's status code // // // Define the facility codes // // // Define the severity codes // // // MessageId: MSG_EVENT_LOG_MESSAGE // // MessageText: // // %2 // #define MSG_EVENT_LOG_MESSAGE 0x00000064L // // MessageId: MSG_EVENT_LOG_CATEGORY_JVM1 // // MessageText: // // jvm1 // #define MSG_EVENT_LOG_CATEGORY_JVM1 0x00000001L // // MessageId: MSG_EVENT_LOG_CATEGORY_JVM2 // // MessageText: // // jvm2 // #define MSG_EVENT_LOG_CATEGORY_JVM2 0x00000002L // // MessageId: MSG_EVENT_LOG_CATEGORY_JVM3 // // MessageText: // // jvm3 // #define MSG_EVENT_LOG_CATEGORY_JVM3 0x00000003L // // MessageId: MSG_EVENT_LOG_CATEGORY_JVM4 // // MessageText: // // jvm4 // #define MSG_EVENT_LOG_CATEGORY_JVM4 0x00000004L // // MessageId: MSG_EVENT_LOG_CATEGORY_JVM5 // // MessageText: // // jvm5 // #define MSG_EVENT_LOG_CATEGORY_JVM5 0x00000005L // // MessageId: MSG_EVENT_LOG_CATEGORY_JVM6 // // MessageText: // // jvm6 // #define MSG_EVENT_LOG_CATEGORY_JVM6 0x00000006L // // MessageId: MSG_EVENT_LOG_CATEGORY_JVM7 // // MessageText: // // jvm7 // #define MSG_EVENT_LOG_CATEGORY_JVM7 0x00000007L // // MessageId: MSG_EVENT_LOG_CATEGORY_JVM8 // // MessageText: // // jvm8 // #define MSG_EVENT_LOG_CATEGORY_JVM8 0x00000008L // // MessageId: MSG_EVENT_LOG_CATEGORY_JVM9 // // MessageText: // // jvm9 // #define MSG_EVENT_LOG_CATEGORY_JVM9 0x00000009L // // MessageId: MSG_EVENT_LOG_CATEGORY_JVMXX // // MessageText: // // jvmxx // #define MSG_EVENT_LOG_CATEGORY_JVMXX 0x0000000AL // // MessageId: MSG_EVENT_LOG_CATEGORY_WRAPPER // // MessageText: // // wrapper // #define MSG_EVENT_LOG_CATEGORY_WRAPPER 0x0000000BL // // MessageId: MSG_EVENT_LOG_CATEGORY_PROTOCOL // // MessageText: // // wrapperp // #define MSG_EVENT_LOG_CATEGORY_PROTOCOL 0x0000000CL wrapper_3.5.51_src/src/c/org_tanukisoftware_wrapper_WrapperManager.h100644 0 0 52171 14333053650 23227 0ustar 0 0 /* DO NOT EDIT THIS FILE - it is machine generated */ #include /* Header for class org_tanukisoftware_wrapper_WrapperManager */ #ifndef _Included_org_tanukisoftware_wrapper_WrapperManager #define _Included_org_tanukisoftware_wrapper_WrapperManager #ifdef __cplusplus extern "C" { #endif #undef org_tanukisoftware_wrapper_WrapperManager_DEFAULT_PORT #define org_tanukisoftware_wrapper_WrapperManager_DEFAULT_PORT 15003L #undef org_tanukisoftware_wrapper_WrapperManager_DEFAULT_CPU_TIMEOUT #define org_tanukisoftware_wrapper_WrapperManager_DEFAULT_CPU_TIMEOUT 10000L #undef org_tanukisoftware_wrapper_WrapperManager_TICK_MS #define org_tanukisoftware_wrapper_WrapperManager_TICK_MS 100L #undef org_tanukisoftware_wrapper_WrapperManager_TIMER_FAST_THRESHOLD #define org_tanukisoftware_wrapper_WrapperManager_TIMER_FAST_THRESHOLD 1728000L #undef org_tanukisoftware_wrapper_WrapperManager_TIMER_SLOW_THRESHOLD #define org_tanukisoftware_wrapper_WrapperManager_TIMER_SLOW_THRESHOLD 1728000L #undef org_tanukisoftware_wrapper_WrapperManager_BACKEND_TYPE_UNKNOWN #define org_tanukisoftware_wrapper_WrapperManager_BACKEND_TYPE_UNKNOWN 0L #undef org_tanukisoftware_wrapper_WrapperManager_BACKEND_TYPE_SOCKET_V4 #define org_tanukisoftware_wrapper_WrapperManager_BACKEND_TYPE_SOCKET_V4 1L #undef org_tanukisoftware_wrapper_WrapperManager_BACKEND_TYPE_SOCKET_V6 #define org_tanukisoftware_wrapper_WrapperManager_BACKEND_TYPE_SOCKET_V6 2L #undef org_tanukisoftware_wrapper_WrapperManager_BACKEND_TYPE_PIPE #define org_tanukisoftware_wrapper_WrapperManager_BACKEND_TYPE_PIPE 4L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_START #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_START 100L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_STOP #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_STOP 101L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_RESTART #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_RESTART 102L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_PING #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_PING 103L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_STOP_PENDING #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_STOP_PENDING 104L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_START_PENDING #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_START_PENDING 105L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_STARTED #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_STARTED 106L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_STOPPED #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_STOPPED 107L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_JAVA_PID #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_JAVA_PID 108L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_KEY #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_KEY 110L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_BADKEY #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_BADKEY 111L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_LOW_LOG_LEVEL #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_LOW_LOG_LEVEL 112L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_PING_TIMEOUT #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_PING_TIMEOUT 113L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_SERVICE_CONTROL_CODE #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_SERVICE_CONTROL_CODE 114L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_PROPERTIES #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_PROPERTIES 115L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_LOG #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_LOG 116L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_CHILD_LAUNCH #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_CHILD_LAUNCH -124L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_CHILD_TERM #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_CHILD_TERM -123L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_LOGFILE #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_LOGFILE -122L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_CHECK_DEADLOCK #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_CHECK_DEADLOCK -121L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_DEADLOCK #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_DEADLOCK -120L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_APPEAR_ORPHAN #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_APPEAR_ORPHAN -119L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_PAUSE #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_PAUSE -118L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_RESUME #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_RESUME -117L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_GC #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_GC -116L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_FIRE_USER_EVENT #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_FIRE_USER_EVENT -115L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_SECOND_INVOCATION_EVENT #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_SECOND_INVOCATION_EVENT -114L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_FIRE_CTRL_EVENT #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_FIRE_CTRL_EVENT -113L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_SUSPEND_TIMEOUTS #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_SUSPEND_TIMEOUTS -112L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_RESUME_TIMEOUTS #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_MSG_RESUME_TIMEOUTS -111L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_C_EVENT #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_C_EVENT 200L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_CLOSE_EVENT #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_CLOSE_EVENT 201L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_LOGOFF_EVENT #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_LOGOFF_EVENT 202L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_SHUTDOWN_EVENT #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_SHUTDOWN_EVENT 203L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_TERM_EVENT #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_TERM_EVENT 204L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_HUP_EVENT #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_HUP_EVENT 205L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_USR1_EVENT #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_USR1_EVENT 206L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_USR2_EVENT #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_USR2_EVENT 207L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_LOG_LEVEL_DEBUG #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_LOG_LEVEL_DEBUG 1L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_LOG_LEVEL_INFO #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_LOG_LEVEL_INFO 2L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_LOG_LEVEL_STATUS #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_LOG_LEVEL_STATUS 3L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_LOG_LEVEL_WARN #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_LOG_LEVEL_WARN 4L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_LOG_LEVEL_ERROR #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_LOG_LEVEL_ERROR 5L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_LOG_LEVEL_FATAL #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_LOG_LEVEL_FATAL 6L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_LOG_LEVEL_ADVICE #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_LOG_LEVEL_ADVICE 7L #undef org_tanukisoftware_wrapper_WrapperManager_WRAPPER_LOG_LEVEL_NOTICE #define org_tanukisoftware_wrapper_WrapperManager_WRAPPER_LOG_LEVEL_NOTICE 8L #undef org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_START #define org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_START 65536L #undef org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_STOP #define org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_STOP 1L #undef org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_PAUSE #define org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_PAUSE 2L #undef org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_CONTINUE #define org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_CONTINUE 3L #undef org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_INTERROGATE #define org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_INTERROGATE 4L #undef org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_SHUTDOWN #define org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_SHUTDOWN 5L #undef org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_POWEREVENT_QUERYSUSPEND #define org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_POWEREVENT_QUERYSUSPEND 3328L #undef org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_POWEREVENT_QUERYSUSPENDFAILED #define org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_POWEREVENT_QUERYSUSPENDFAILED 3330L #undef org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_POWEREVENT_SUSPEND #define org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_POWEREVENT_SUSPEND 3332L #undef org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_POWEREVENT_RESUMECRITICAL #define org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_POWEREVENT_RESUMECRITICAL 3334L #undef org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_POWEREVENT_RESUMESUSPEND #define org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_POWEREVENT_RESUMESUSPEND 3335L #undef org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_POWEREVENT_BATTERYLOW #define org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_POWEREVENT_BATTERYLOW 3337L #undef org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_POWEREVENT_POWERSTATUSCHANGE #define org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_POWEREVENT_POWERSTATUSCHANGE 3338L #undef org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_POWEREVENT_OEMEVENT #define org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_POWEREVENT_OEMEVENT 3339L #undef org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_POWEREVENT_RESUMEAUTOMATIC #define org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_POWEREVENT_RESUMEAUTOMATIC 3346L /* Inaccessible static: m_out */ /* Inaccessible static: m_err */ /* Inaccessible static: m_outInfo */ /* Inaccessible static: m_outError */ /* Inaccessible static: m_outDebug */ /* Inaccessible static: m_os */ /* Inaccessible static: m_arch */ /* Inaccessible static: m_windows */ /* Inaccessible static: m_macosx */ /* Inaccessible static: m_aix */ /* Inaccessible static: m_zos */ /* Inaccessible static: m_securityManagerChecked */ /* Inaccessible static: m_disposed */ /* Inaccessible static: m_starting */ /* Inaccessible static: m_started */ /* Inaccessible static: m_instance */ /* Inaccessible static: m_hook */ /* Inaccessible static: m_hookTriggered */ /* Inaccessible static: m_hookRemoveFailed */ /* Inaccessible static: m_shutdownJVMComplete */ /* Inaccessible static: m_shutdownLockMap */ /* Inaccessible static: m_shutdownLocks */ /* Inaccessible static: m_runningExecs */ /* Inaccessible static: m_args */ /* Inaccessible static: m_backendType */ /* Inaccessible static: m_backendConnected */ /* Inaccessible static: m_backendOS */ /* Inaccessible static: m_backendIS */ /* Inaccessible static: m_port */ /* Inaccessible static: m_jvmPort */ /* Inaccessible static: m_jvmPortMin */ /* Inaccessible static: m_jvmPortMax */ /* Inaccessible static: m_wrapperPortAddress */ /* Inaccessible static: m_key */ /* Inaccessible static: m_soTimeout */ /* Inaccessible static: m_cpuTimeout */ /* Inaccessible static: m_startedTicks */ /* Inaccessible static: m_lowLogLevel */ /* Inaccessible static: m_ignoreSignals */ /* Inaccessible static: m_detachStarted */ /* Inaccessible static: m_commRunner */ /* Inaccessible static: m_commRunnerStarted */ /* Inaccessible static: m_eventRunner */ /* Inaccessible static: m_eventRunnerTicks */ /* Inaccessible static: m_startupRunner */ /* Inaccessible static: m_useSystemTime */ /* Inaccessible static: m_timerFastThreshold */ /* Inaccessible static: m_timerSlowThreshold */ /* Inaccessible static: m_disableTests */ /* Inaccessible static: m_listenerForceStop */ /* Inaccessible static: m_jvmBits */ /* Inaccessible static: m_ticks */ /* Inaccessible static: m_listener */ /* Inaccessible static: m_lastPingTicks */ /* Inaccessible static: m_backendSocket */ /* Inaccessible static: m_appearHung */ /* Inaccessible static: m_slowSeconds */ /* Inaccessible static: m_ignoreUserLogoffs */ /* Inaccessible static: m_service */ /* Inaccessible static: m_debug */ /* Inaccessible static: m_logFinalizer */ /* Inaccessible static: m_jvmId */ /* Inaccessible static: m_stoppingInit */ /* Inaccessible static: m_stopping */ /* Inaccessible static: m_stoppingThread */ /* Inaccessible static: m_stopped */ /* Inaccessible static: m_pendingStopMessage */ /* Inaccessible static: m_exitCode */ /* Inaccessible static: m_libraryOK */ /* Inaccessible static: m_libraryLoaded */ /* Inaccessible static: m_libraryVersionOk */ /* Inaccessible static: m_wrapperVersionOk */ /* Inaccessible static: m_commandBuffer */ /* Inaccessible static: m_logFile */ /* Inaccessible static: m_properties */ /* Inaccessible static: m_wrapperEventListenerMaskList */ /* Inaccessible static: m_wrapperEventListenerMasks */ /* Inaccessible static: m_produceCoreEvents */ /* Inaccessible static: m_res */ /* Inaccessible static: m_professionalEdition */ /* Inaccessible static: m_standardEdition */ /* Inaccessible static: PROPERTY_SEPARATOR */ /* Inaccessible static: m_backendReadBuffer */ /* Inaccessible static: class_00024org_00024tanukisoftware_00024wrapper_00024WrapperManager */ /* Inaccessible static: class_00024java_00024lang_00024String */ /* Inaccessible static: class_00024java_00024lang_00024Object */ /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeInit * Signature: (Z)V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeInit (JNIEnv *, jclass, jboolean); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeLoadWrapperProperties * Signature: ()V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeLoadWrapperProperties (JNIEnv *, jclass); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeDispose * Signature: (Z)V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeDispose (JNIEnv *, jclass, jboolean); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetLibraryVersion * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetLibraryVersion (JNIEnv *, jclass); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetJavaPID * Signature: ()I */ JNIEXPORT jint JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetJavaPID (JNIEnv *, jclass); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeIsProfessionalEdition * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeIsProfessionalEdition (JNIEnv *, jclass); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeIsStandardEdition * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeIsStandardEdition (JNIEnv *, jclass); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetControlEvent * Signature: ()I */ JNIEXPORT jint JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetControlEvent (JNIEnv *, jclass); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeRedirectPipes * Signature: ()I */ JNIEXPORT jint JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeRedirectPipes (JNIEnv *, jclass); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeRequestThreadDump * Signature: ()V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeRequestThreadDump (JNIEnv *, jclass); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: accessViolationInner * Signature: ()V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_accessViolationInner (JNIEnv *, jclass); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeRaiseExceptionInner * Signature: (I)V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeRaiseExceptionInner (JNIEnv *, jclass, jint); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeRaiseFailFastExceptionInner * Signature: ()V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeRaiseFailFastExceptionInner (JNIEnv *, jclass); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeSetConsoleTitle * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeSetConsoleTitle (JNIEnv *, jclass, jstring); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetUser * Signature: (Z)Lorg/tanukisoftware/wrapper/WrapperUser; */ JNIEXPORT jobject JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetUser (JNIEnv *, jclass, jboolean); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetInteractiveUser * Signature: (Z)Lorg/tanukisoftware/wrapper/WrapperUser; */ JNIEXPORT jobject JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetInteractiveUser (JNIEnv *, jclass, jboolean); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeListServices * Signature: ()[Lorg/tanukisoftware/wrapper/WrapperWin32Service; */ JNIEXPORT jobjectArray JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeListServices (JNIEnv *, jclass); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeSendServiceControlCode * Signature: (Ljava/lang/String;I)Lorg/tanukisoftware/wrapper/WrapperWin32Service; */ JNIEXPORT jobject JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeSendServiceControlCode (JNIEnv *, jclass, jstring, jint); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeExec * Signature: ([Ljava/lang/String;Ljava/lang/String;Lorg/tanukisoftware/wrapper/WrapperProcessConfig;Z)Lorg/tanukisoftware/wrapper/WrapperProcess; */ JNIEXPORT jobject JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeExec (JNIEnv *, jclass, jobjectArray, jstring, jobject, jboolean); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeWrapperGetEnv * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeWrapperGetEnv (JNIEnv *, jclass, jstring); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeLoadWrapperResources * Signature: (Ljava/lang/String;Ljava/lang/String;Z)Lorg/tanukisoftware/wrapper/WrapperResources; */ JNIEXPORT jobject JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeLoadWrapperResources (JNIEnv *, jclass, jstring, jstring, jboolean); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeCheckDeadLocks * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeCheckDeadLocks (JNIEnv *, jclass); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetPortStatus * Signature: (ILjava/lang/String;I)I */ JNIEXPORT jint JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetPortStatus (JNIEnv *, jclass, jint, jstring, jint); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetDpiScale * Signature: ()I */ JNIEXPORT jint JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetDpiScale (JNIEnv *, jclass); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetDpiAwareness * Signature: ()I */ JNIEXPORT jint JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetDpiAwareness (JNIEnv *, jclass); /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeSetDpiAwareness * Signature: (I)V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeSetDpiAwareness (JNIEnv *, jclass, jint); #ifdef __cplusplus } #endif #endif wrapper_3.5.51_src/src/c/property.c100644 0 0 307751 14333053650 14465 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.comment * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ #include #include #include #include #ifdef WIN32 #include /* MS Visual Studio 8 went and deprecated the POXIX names for functions. * Fixing them all would be a big headache for UNIX versions. */ #pragma warning(disable : 4996) #else #include #include #include #include #endif #include "wrapper_i18n.h" #include "logger.h" #include "logger_file.h" #include "property.h" #include "wrapper.h" #include "wrapper_file.h" #define MAX_INCLUDE_DEPTH 10 /* The largest possible "name+'='+value" property pair length on Windows. */ #define MAX_ENV_PAIR_LEN 32767 EnvSrc *baseEnvSrc = NULL; /** Stores the time that the property file began to be loaded. */ struct tm loadPropertiesTM; const TCHAR **escapedPropertyNames = NULL; void setInnerProperty(Properties *properties, Property *property, const TCHAR *propertyValue, int warnUndefinedVars); /** * @param warnUndefinedVars Log warnings about missing environment variables. */ void prepareProperty(Properties *properties, Property *property, int warnUndefinedVars) { TCHAR *oldValue; if (_tcsstr(property->value_inner, TEXT("%"))) { /* Reset the property. If the unreplaced environment variables are now available * setting it again will cause it to be replaced correctly. If not this will * only waste time. The value will be freed in the process so we need to * keep it around. */ #ifdef _DEBUG _tprintf( TEXT("Unreplaced property %s=%s\n"), property->name, property->value_inner ); #endif oldValue = malloc(sizeof(TCHAR) * (_tcslen(property->value_inner) + 1)); if (!oldValue) { outOfMemory(TEXT("PP"), 1); } else { _tcsncpy(oldValue, property->value_inner, _tcslen(property->value_inner) + 1); setInnerProperty(properties, property, oldValue, warnUndefinedVars); free(oldValue); } #ifdef _DEBUG _tprintf( TEXT(" -> property %s=%s\n"), property->name, property->value_inner ); #endif } } /** * Private function to find a Property structure. */ Property* getInnerProperty(Properties *properties, const TCHAR *propertyName, int warnUndefinedVars) { Property *property; int cmp; /* Loop over the properties which are in order and look for the specified property. */ property = properties->first; while (property != NULL) { cmp = strcmpIgnoreCase(property->name, propertyName); if (cmp > 0) { /* This property would be after the one being looked for, so it does not exist. */ return NULL; } else if (cmp == 0) { /* We found it. */ prepareProperty(properties, property, warnUndefinedVars && properties->logWarnings); return property; } /* Keep looking */ property = property->next; } /* We did not find the property being looked for. */ return NULL; } void addInnerProperty(Properties *properties, Property *newProperty) { newProperty->previous = properties->last; if (properties->last == NULL) { /* This will be the first property. */ properties->first = newProperty; } else { /* Point the old last property to the new last property. */ properties->last->next = newProperty; } properties->last = newProperty; newProperty->next = NULL; } void insertInnerProperty(Properties *properties, Property *newProperty) { Property *property; int cmp; /* Loop over the properties which are in order and look for the specified property. */ /* This function assumes that Property is not already in properties. */ property = properties->first; while (property != NULL) { cmp = strcmpIgnoreCase(property->name, newProperty->name); if (cmp > 0) { /* This property would be after the new property, so insert it here. */ newProperty->previous = property->previous; newProperty->next = property; if (property->previous == NULL) { /* This was the first property */ properties->first = newProperty; } else { property->previous->next = newProperty; } property->previous = newProperty; /* We are done, so return */ return; } property = property->next; } /* The new property needs to be added at the end */ addInnerProperty(properties, newProperty); } Property* createInnerProperty() { Property *property; property = malloc(sizeof(Property)); if (!property) { outOfMemory(TEXT("CIP"), 1); return NULL; } property->name = NULL; property->next = NULL; property->previous = NULL; property->value = NULL; property->value_inner = NULL; property->filePath = NULL; property->lineNumber = 0; property->definitions = 1; property->isGenerated = FALSE; property->isVariable = FALSE; property->lastDefinitionDepth = -1; return property; } /** * Private function to dispose a Property structure. Assumes that the * Property is disconnected already. */ void disposeInnerProperty(Property *property) { if (property->name) { free(property->name); } if (property->value) { /* The memory may contain sensitive data or passwords and must be erased. */ wrapperSecureFreeStrW(property->value); } if (property->value_inner) { /* The memory may contain sensitive data or passwords and must be erased. */ wrapperSecureFreeStrW(property->value_inner); } if (property->filePath) { free(property->filePath); } free(property); } TCHAR generateValueBuffer[256]; /** * This function returns a reference to a static buffer and is NOT thread safe. * It is currently called only when loading a property file and when firing an event. * Both happen in the main thread. * The largest return value can be 15+1 characters. */ TCHAR* generateTimeValue(const TCHAR* format, struct tm *timeTM) { if (strcmpIgnoreCase(format, TEXT("YYYYMMDDHHIISS")) == 0) { _sntprintf(generateValueBuffer, 256, TEXT("%04d%02d%02d%02d%02d%02d"), timeTM->tm_year + 1900, timeTM->tm_mon + 1, timeTM->tm_mday, timeTM->tm_hour, timeTM->tm_min, timeTM->tm_sec ); } else if (strcmpIgnoreCase(format, TEXT("YYYYMMDD_HHIISS")) == 0) { _sntprintf(generateValueBuffer, 256, TEXT("%04d%02d%02d_%02d%02d%02d"), timeTM->tm_year + 1900, timeTM->tm_mon + 1, timeTM->tm_mday, timeTM->tm_hour, timeTM->tm_min, timeTM->tm_sec ); } else if (strcmpIgnoreCase(format, TEXT("YYYYMMDDHHII")) == 0) { _sntprintf(generateValueBuffer, 256, TEXT("%04d%02d%02d%02d%02d"), timeTM->tm_year + 1900, timeTM->tm_mon + 1, timeTM->tm_mday, timeTM->tm_hour, timeTM->tm_min ); } else if (strcmpIgnoreCase(format, TEXT("YYYYMMDDHH")) == 0) { _sntprintf(generateValueBuffer, 256, TEXT("%04d%02d%02d%02d"), timeTM->tm_year + 1900, timeTM->tm_mon + 1, timeTM->tm_mday, timeTM->tm_hour ); } else if (strcmpIgnoreCase(format, TEXT("YYYYMMDD")) == 0) { _sntprintf(generateValueBuffer, 256, TEXT("%04d%02d%02d"), timeTM->tm_year + 1900, timeTM->tm_mon + 1, timeTM->tm_mday); } else { _sntprintf(generateValueBuffer, 256, TEXT("{INVALID}")); } return generateValueBuffer; } /** * This function returns a reference to a static buffer and is NOT thread safe. * It is currently called only when loading a property file and when firing an event. * Both happen in the main thread. * The largest return value can be 9+1 characters. */ TCHAR* generateRandValue(const TCHAR* format) { if (strcmpIgnoreCase(format, TEXT("N")) == 0) { _sntprintf(generateValueBuffer, 256, TEXT("%01d"), rand() % 10); } else if (strcmpIgnoreCase(format, TEXT("NN")) == 0) { _sntprintf(generateValueBuffer, 256, TEXT("%02d"), rand() % 100); } else if (strcmpIgnoreCase(format, TEXT("NNN")) == 0) { _sntprintf(generateValueBuffer, 256, TEXT("%03d"), rand() % 1000); } else if (strcmpIgnoreCase(format, TEXT("NNNN")) == 0) { _sntprintf(generateValueBuffer, 256, TEXT("%04d"), rand() % 10000); } else if (strcmpIgnoreCase(format, TEXT("NNNNN")) == 0) { _sntprintf(generateValueBuffer, 256, TEXT("%04d%01d"), rand() % 10000, rand() % 10); } else if (strcmpIgnoreCase(format, TEXT("NNNNNN")) == 0) { _sntprintf(generateValueBuffer, 256, TEXT("%04d%02d"), rand() % 10000, rand() % 100); } else { _sntprintf(generateValueBuffer, 256, TEXT("{INVALID}")); } return generateValueBuffer; } /** * Parses a property value and populates any environment variables. If the expanded * environment variable would result in a string that is longer than bufferLength * the value is truncated. * * @param propertyValue The property value to be parsed. * @param buffer output buffer where the expanded string will be copied. * @param bufferLength size of the buffer. * @param warnUndefinedVars Log warnings about missing environment variables. * @param warnedUndefVarMap Map of variables which have previously been logged, may be NULL if warnUndefinedVars false. * @param warnLogLevel Log level at which any warnings will be logged. * @param ignoreVarMap Map of environment variables that should not be expanded. * @param pHasPercentage Pointer to a variable which will be set to TRUE if a %WRAPPER_PERCENTAGE% variable was found. * - If a non-NULL pointer is passed, the variable will not be expanded and no warning will be reported. * - If NULL is passed, the variable will be expanded to '%'. */ void evaluateEnvironmentVariables(const TCHAR *propertyValue, TCHAR *buffer, int bufferLength, int warnUndefinedVars, PHashMap warnedUndefVarMap, int warnLogLevel, PHashMap ignoreVarMap, int *pHasPercentage) { const TCHAR *in; TCHAR *out; TCHAR *envName; TCHAR *envValue; int envValueNeedFree; TCHAR *start; TCHAR *end; size_t len; size_t outLen; size_t bufferAvailable; const TCHAR* ignore; #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("evaluateEnvironmentVariables(properties, '%s', buffer, %d, %d)"), propertyValue, bufferLength, warnUndefinedVars); #endif buffer[0] = TEXT('\0'); in = propertyValue; out = buffer; bufferAvailable = bufferLength - 1; /* Reserver room for the null terminator */ /* Loop until we hit the end of string. */ while (in[0] != TEXT('\0')) { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" initial='%s', buffer='%s'"), propertyValue, buffer); #endif start = _tcschr(in, TEXT('%')); if (start != NULL) { end = _tcschr(start + 1, TEXT('%')); if (end != NULL) { /* A pair of '%' characters was found. An environment */ /* variable name should be between the two. */ len = (int)(end - start - 1); envName = malloc(sizeof(TCHAR) * (len + 1)); if (envName == NULL) { outOfMemory(TEXT("EEV"), 1); return; } _tcsncpy(envName, start + 1, len); envName[len] = TEXT('\0'); /* See if it is a special dynamic environment variable */ envValueNeedFree = FALSE; if (_tcsstr(envName, TEXT("WRAPPER_TIME_")) == envName) { /* Found a time value. */ envValue = generateTimeValue(envName + 13, &loadPropertiesTM); } else if (_tcsstr(envName, TEXT("WRAPPER_RAND_")) == envName) { /* Found a time value. */ envValue = generateRandValue(envName + 13); } else if (_tcsstr(envName, TEXT("WRAPPER_PERCENTAGE")) == envName) { envValue = NULL; if (pHasPercentage) { /* Do not warn about this variable. Do not expand it. We will expand it later. */ hashMapPutKWVW(warnedUndefVarMap, envName, envName); *pHasPercentage = TRUE; } else { envValue = TEXT("%"); } } else { if (ignoreVarMap) { /* Can return NULL if missing or "TRUE" or "FALSE". */ ignore = hashMapGetKWVW(ignoreVarMap, envName); } else { ignore = NULL; } if (!ignore || strcmpIgnoreCase(ignore, TEXT("TRUE")) != 0) { /* Try looking up the environment variable. */ envValue = _tgetenv(envName); #if !defined(WIN32) && defined(UNICODE) envValueNeedFree = TRUE; #endif } else { envValue = NULL; envValueNeedFree = FALSE; } } if (envValue != NULL) { /* An envvar value was found. */ /* Copy over any text before the envvar */ outLen = (int)(start - in); if (bufferAvailable < outLen) { outLen = bufferAvailable; } if (outLen > 0) { _tcsncpy(out, in, outLen); out += outLen; bufferAvailable -= outLen; } /* Copy over the env value */ outLen = _tcslen(envValue); if (bufferAvailable < outLen) { outLen = bufferAvailable; } if (outLen > 0) { _tcsncpy(out, envValue, outLen); out += outLen; bufferAvailable -= outLen; } if (envValueNeedFree) { free(envValue); } /* Terminate the string */ out[0] = TEXT('\0'); /* Set the new in pointer */ in = end + 1; } else { /* Not found. So copy over the input up until the */ /* second '%'. Leave it in case it is actually the */ /* start of an environment variable name */ outLen = len = end - in + 1; if (bufferAvailable < outLen) { outLen = bufferAvailable; } if (outLen > 0) { _tcsncpy(out, in, outLen); out += outLen; bufferAvailable -= outLen; } in += len; /* Terminate the string */ out[0] = TEXT('\0'); if (warnUndefinedVars) { if (hashMapGetKWVW(warnedUndefVarMap, envName) == NULL) { /* This is the first time this environment variable was noticed, so report it to the user then remember so we don't report it again. */ log_printf(WRAPPER_SOURCE_WRAPPER, warnLogLevel, TEXT("The '%s' environment variable was referenced but has not been defined."), envName); hashMapPutKWVW(warnedUndefVarMap, envName, envName); } } } free(envName); } else { /* Only a single '%' TCHAR was found. Leave it as is. */ outLen = len = _tcslen(in); if (bufferAvailable < outLen) { outLen = bufferAvailable; } if (outLen > 0) { _tcsncpy(out, in, outLen); out += outLen; bufferAvailable -= outLen; } in += len; /* Terminate the string */ out[0] = TEXT('\0'); } } else { /* No more '%' chars in the string. Copy over the rest. */ outLen = len = _tcslen(in); if (bufferAvailable < outLen) { outLen = bufferAvailable; } if (outLen > 0) { _tcsncpy(out, in, outLen); out += outLen; bufferAvailable -= outLen; } in += len; /* Terminate the string */ out[0] = TEXT('\0'); } } #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" final buffer='%s'"), buffer); #endif } /** * * @param warnUndefinedVars Log warnings about missing environment variables. */ void setInnerProperty(Properties *properties, Property *property, const TCHAR *propertyValue, int warnUndefinedVars) { int i, count; /* The property value is expanded into a large buffer once, but that is temporary. The actual * value is stored in the minimum required size. */ TCHAR *buffer; int hasPercentage = FALSE; /* Free any existing value */ if (property->value != NULL) { free(property->value); property->value = NULL; } if (property->value_inner != NULL) { free(property->value_inner); property->value_inner = NULL; } /* Set the new value using a copy of the provided value. */ if (propertyValue != NULL) { buffer = malloc(MAX_PROPERTY_VALUE_LENGTH * sizeof(TCHAR)); if (buffer) { evaluateEnvironmentVariables(propertyValue, buffer, MAX_PROPERTY_VALUE_LENGTH, warnUndefinedVars, properties->warnedVarMap, properties->logWarningLogLevel, properties->ignoreVarMap, &hasPercentage); /* We need to keep a copy of the value without expanding %WRAPPER_PERCENTAGE% (see the function description of evaluateEnvironmentVariables()) */ property->value_inner = malloc(sizeof(TCHAR) * (_tcslen(buffer) + 1)); if (!property->value_inner) { outOfMemoryQueued(TEXT("SIP"), 1); } else { /* Strip any non valid characters like control characters. Some valid characters are * less than 0 when the TCHAR is unsigned. */ for (i = 0, count = 0; i < (int)_tcslen(buffer); i++) { /* Only add valid characters, skipping any control characters EXCEPT for a line feed. */ if ((buffer[i] == TEXT('\n')) || (!_istcntrl(buffer[i]))) { property->value_inner[count++] = buffer[i]; } } /* Crop string to new size */ property->value_inner[count] = TEXT('\0'); if (hasPercentage) { /* Now expand the percentages */ evaluateEnvironmentVariables(property->value_inner, buffer, MAX_PROPERTY_VALUE_LENGTH, warnUndefinedVars, properties->warnedVarMap, properties->logWarningLogLevel, properties->ignoreVarMap, NULL); property->value = malloc(sizeof(TCHAR) * (_tcslen(buffer) + 1)); if (!property->value) { outOfMemoryQueued(TEXT("SIP"), 2); } else { _tcsncpy(property->value, buffer, _tcslen(buffer) + 1); } } if (!property->value) { property->value = malloc(sizeof(TCHAR) * (_tcslen(property->value_inner) + 1)); if (!property->value) { outOfMemoryQueued(TEXT("SIP"), 3); } else { _tcsncpy(property->value, property->value_inner, _tcslen(property->value_inner) + 1); } } } free(buffer); } else { outOfMemoryQueued(TEXT("SIP"), 4); } } } /** * Check if the given buffer matches the syntax of a property (to be used before actually creating the property). * The buffer should contain a '=' and the name on its left should not contain any space once it has been trimmed. * A null termination character will be inserted before the first '=' in the line. * * @param buffer The full line to be checked. * * @return a pointer to the string representation of the value (i.e the part of the buffer after the '=') * or NULL if the line did not match the syntax of a property. */ static TCHAR* checkPropertySyntax(TCHAR* buffer) { TCHAR *keyTrim; TCHAR *d; /* The buffer should contain a '='. */ if ((d = _tcschr(buffer, TEXT('='))) != NULL) { *d = TEXT('\0'); d++; keyTrim = malloc(sizeof(TCHAR) * (_tcslen(buffer) + 1)); if (!keyTrim) { outOfMemory(TEXT("CPS"), 1); return NULL; } trim(buffer, keyTrim); /* The trimmed key should not contain any space. */ if (_tcschr(keyTrim, TEXT(' ')) == NULL) { free(keyTrim); return d; } free(keyTrim); } return NULL; } static int loadPropertiesCallback(void *callbackParam, const TCHAR *fileName, int lineNumber, int depth, TCHAR *config, int exitOnOverwrite, int logLevelOnOverwrite) { Properties *properties = (Properties *)callbackParam; TCHAR *d; properties->exitOnOverwrite = exitOnOverwrite; properties->logLevelOnOverwrite = logLevelOnOverwrite; /* special case where the callback should only update the properties structure */ if ((fileName == NULL) && (lineNumber == -1) && (config == NULL)) { return TRUE; } if (_tcsstr(config, TEXT("include")) == config) { /* Users sometimes remove the '#' from include statements. Add a warning to help them notice the problem. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("Include file reference missing leading '#': %s"), config); } else if ((d = checkPropertySyntax(config))) { addProperty(properties, fileName, lineNumber, depth, config, d, FALSE, FALSE, TRUE, FALSE); } return TRUE; } /** * Create a Properties structure loaded in from the specified file. * Must call disposeProperties to free up allocated memory. * * @param properties Properties structure to load into. * @param filename File to load the properties from. * @param preload TRUE if this is a preload call that should have supressed error output. * @param originalWorkingDir Working directory of the binary at the moment it was launched. * @param fileRequired TRUE if the file specified by filename is required, FALSE if a missing * file will silently fail. * @param readFilterCallback Pointer to a callback funtion which will be used to filter some * lines that should not be processed. * * @return CONFIG_FILE_READER_SUCCESS if the file was read successfully, * CONFIG_FILE_READER_OPEN_FAIL if the file could not be found or opened. * CONFIG_FILE_READER_FAIL if there were any problems at all, or * CONFIG_FILE_READER_HARD_FAIL if the problem should cascaded all the way up. */ int loadProperties(Properties *properties, const TCHAR* filename, int preload, const TCHAR *originalWorkingDir, int fileRequired, ConfigFileReader_ReadFilterCallbackMB readFilterCallback) { /* Store the time that the property file began to be loaded. */ #ifdef WIN32 struct _timeb timebNow; #else struct timeval timevalNow; #endif time_t now; struct tm *nowTM; #ifdef WIN32 _ftime(&timebNow); now = (time_t)timebNow.time; #else gettimeofday(&timevalNow, NULL); now = (time_t)timevalNow.tv_sec; #endif nowTM = localtime(&now); memcpy(&loadPropertiesTM, nowTM, sizeof(struct tm)); return configFileReader(filename, fileRequired, loadPropertiesCallback, properties, readFilterCallback, TRUE, preload, originalWorkingDir, properties->warnedVarMap, properties->ignoreVarMap, properties->logWarnings, properties->logWarningLogLevel); } /** * Get the log level of the messages reported when properties are overwritten. * * @param properties * * @return log level, or -1 if AUTO */ int GetLogLevelOnOverwrite(Properties *properties) { /* Should be at least LEVEL_FATAL if exitOnOverwrite is set to TRUE */ if (properties) { if (properties->exitOnOverwrite) { return __max(properties->logLevelOnOverwrite, LEVEL_FATAL); } return properties->logLevelOnOverwrite; } return LEVEL_UNKNOWN; } Properties* createProperties(int debug, int logLevelOnOverwrite, int exitOnOverwrite) { Properties *properties = malloc(sizeof(Properties)); if (!properties) { outOfMemory(TEXT("CP"), 1); return NULL; } properties->debugProperties = debug; properties->exitOnOverwrite = exitOnOverwrite; properties->logLevelOnOverwrite = logLevelOnOverwrite; properties->overwrittenPropertyCausedExit = FALSE; properties->logWarnings = TRUE; properties->logWarningLogLevel = LEVEL_WARN; properties->first = NULL; properties->last = NULL; properties->warnedVarMap = newHashMap(8); properties->ignoreVarMap = newHashMap(8); properties->dumpFormat = NULL; if ((!properties->warnedVarMap) || (!properties->ignoreVarMap)) { outOfMemory(TEXT("CP"), 2); disposeProperties(properties); return NULL; } return properties; } void disposeProperties(Properties *properties) { /* Loop and dispose any Property structures */ Property *tempProperty; Property *property; if (properties) { property = properties->first; properties->first = NULL; properties->last = NULL; while (property != NULL) { /* Save the next property */ tempProperty = property->next; /* Clean up the current property */ disposeInnerProperty(property); property = NULL; /* set the current property to the next. */ property = tempProperty; } if (properties->dumpFormat) { free(properties->dumpFormat); } if (properties->warnedVarMap) { freeHashMap(properties->warnedVarMap); } if (properties->ignoreVarMap) { freeHashMap(properties->ignoreVarMap); } /* Dispose the Properties structure */ free(properties); properties = NULL; } } /** * This method cleans the environment at shutdown. */ void disposeEnvironment() { EnvSrc *current, *previous; if (baseEnvSrc) { current = baseEnvSrc; while (current != NULL) { free(current->name); previous = current; current = current->next; free(previous); } baseEnvSrc = NULL; } } void disconnectProperty(Properties *properties, Property *property) { Property *next; Property *previous; next = property->next; previous = property->previous; if (next == NULL) { /* This was the last property */ properties->last = previous; } else { next->previous = property->previous; } if (previous == NULL) { /* This was the first property */ properties->first = next; } else { previous->next = property->next; } } /** * Remove a single Property from a Properties. All associated memory is * freed up. * * @return TRUE if the property was found, FALSE otherwise. */ int removeProperty(Properties *properties, const TCHAR *propertyName) { Property *property; /* Look up the property */ property = getInnerProperty(properties, propertyName, FALSE); if (property == NULL) { /* The property did not exist, so nothing to do. */ } else { /* Disconnect the property */ disconnectProperty(properties, property); /* Now that property is disconnected, if can be disposed. */ disposeInnerProperty(property); return TRUE; } return FALSE; } /** * Sets an environment variable with the specified value. * The function will only set the variable if its value is changed, but if * it does, the call will result in a memory leak the size of the string: * "name=value". * * For Windows, the putenv_s funcion looks better, but it is not available * on some older SDKs and non-pro versions of XP. * * @param name Name of the variable being set. * @param value Value to be set, NULL to clear it. * * Return TRUE if there were any problems, FALSE otherwise. */ int setEnvInner(const TCHAR *name, const TCHAR *value) { int result = FALSE; TCHAR *oldVal; #ifdef WIN32 #if !defined(WRAPPER_USE_PUTENV_S) size_t len; TCHAR *envBuf; #endif #endif #if defined(WRAPPER_USE_PUTENV) size_t len; TCHAR *envBuf; #endif /* Get the current environment variable value so we can avoid allocating and * setting the variable if it has not changed its value. */ #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("setEnvInner '%s=%s'."), name, value); #endif oldVal = _tgetenv(name); if (value == NULL) { /*_tprintf("clear %s=\n", name);*/ /* Only clear the variable if it is actually set to avoid unnecessary leaks. */ if (oldVal != NULL) { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("setEnvInner (CLEAR) '%s=%s'."), name, value); #endif #ifdef WIN32 #if defined(WRAPPER_USE_PUTENV_S) if (_tputenv_s(name, TEXT("")) == EINVAL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to clear the '%s' environment variable."), name); result = TRUE; } #else len = _tcslen(name) + 1 + 1; envBuf = malloc(sizeof(TCHAR) * len); if (!envBuf) { outOfMemory(TEXT("SEI"), 1); result = TRUE; } else { _sntprintf(envBuf, len, TEXT("%s="), name); /* The memory pointed to by envBuf should only be freed if this is UNICODE. */ if (_tputenv(envBuf)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to clear the '%s' environment variable."), name); result = TRUE; } } #endif #else #if defined(WRAPPER_USE_PUTENV) len = _tcslen(name) + 1 + 1; envBuf = malloc(sizeof(TCHAR) * len); if (!envBuf) { outOfMemory(TEXT("SEI"), 1); result = TRUE; } else { _sntprintf(envBuf, len, TEXT("%s="), name); /* The memory pointed to by envBuf should only be freed if this is UNICODE. */ if (_tputenv(envBuf)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to clear the '%s' environment variable."), name); result = TRUE; } #ifdef UNICODE free(envBuf); #endif } #else _tunsetenv(name); #endif #endif } } else { /*_tprintf("set %s=%s\n", name, value);*/ if ((oldVal == NULL) || (_tcscmp(oldVal, value) != 0)) { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("setEnvInner (SET) '%s=%s'."), name, value); #endif #ifdef WIN32 #if defined(WRAPPER_USE_PUTENV_S) if (_tputenv_s(name, value) == EINVAL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to set the '%s' environment variable to: %s"), name, value); result = TRUE; } #else len = _tcslen(name) + 1 + _tcslen(value) + 1; if (len > MAX_ENV_PAIR_LEN) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to set the '%s' environment variable because total pair length of %d is longer than maximum for the OS of %d."), name, len, MAX_ENV_PAIR_LEN); result = TRUE; } else { envBuf = malloc(sizeof(TCHAR) * len); if (!envBuf) { outOfMemory(TEXT("SEI"), 2); result = TRUE; } else { _sntprintf(envBuf, len, TEXT("%s=%s"), name, value); /* The memory pointed to by envBuf should only be freed if this is UNICODE. */ if (_tputenv(envBuf)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to set environment variable: %s=%s"), name, value); result = TRUE; } } } #endif #else #if defined(WRAPPER_USE_PUTENV) len = _tcslen(name) + 1 + _tcslen(value) + 1; envBuf = malloc(sizeof(TCHAR) * len); if (!envBuf) { outOfMemory(TEXT("SEI"), 2); result = TRUE; } else { _sntprintf(envBuf, len, TEXT("%s=%s"), name, value); /* The memory pointed to by envBuf should only be freed if this is UNICODE. */ if (_tputenv(envBuf)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to set the '%s' environment variable to: %s"), name, value); result = TRUE; } #ifdef UNICODE free(envBuf); #endif } #else if (_tsetenv(name, value, TRUE)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to set the '%s' environment variable to: %s"), name, value); result = TRUE; } #endif #endif } } #if !defined(WIN32) && defined(UNICODE) if (oldVal != NULL) { free(oldVal); } #endif return result; } /** * Sets an environment variable with the specified value. * The function will only set the variable if its value is changed, but if * it does, the call will result in a memory leak the size of the string: * "name=value". * * @param name Name of the variable being set. * @param value Value to be set, NULL to clear it. * @param source Where the variable came from. * Must be one of ENV_SOURCE_PARENT, ENV_SOURCE_APPLICATION, ENV_SOURCE_CONFIG, * or ENV_SOURCE_REG_SYSTEM or ENV_SOURCE_REG_ACCOUNT on Windows. * If value is ENV_SOURCE_PARENT then the value may be NULL and will never be * set to the environment. * * Return TRUE if there were any problems, FALSE otherwise. */ int setEnv(const TCHAR *name, const TCHAR *value, int source) { EnvSrc **thisEnvSrcRef; EnvSrc *thisEnvSrc; size_t len; TCHAR *nameCopy; EnvSrc *newEnvSrc; int cmpRes; #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("setEnv(%s, %s, %d)"), name, value, source); #endif thisEnvSrcRef = &baseEnvSrc; thisEnvSrc = baseEnvSrc; /* Create a copy of the name so we can store it. */ len = _tcslen(name) + 1; nameCopy = malloc(sizeof(TCHAR) * len); if (!nameCopy) { outOfMemory(TEXT("SE"), 1); return TRUE; } _sntprintf(nameCopy, len, TEXT("%s"), name); /* Figure out where we want to set the value. */ while (thisEnvSrc) { cmpRes = strcmpIgnoreCase(thisEnvSrc->name, name); if (cmpRes == 0) { /* This is the same value. It is being changed. */ /* The nameCopy is not needed so free it up. */ free(nameCopy); thisEnvSrc->source |= source; if (source != ENV_SOURCE_PARENT) { return setEnvInner(name, value); } return FALSE; } else if (cmpRes > 0) { /* This EnvSrc would be after the one being set, so we need to insert it. */ newEnvSrc = malloc(sizeof(EnvSrc)); if (!newEnvSrc) { outOfMemory(TEXT("SEV"), 2); return TRUE; } newEnvSrc->source = source; newEnvSrc->name = nameCopy; newEnvSrc->next = thisEnvSrc; *thisEnvSrcRef = newEnvSrc; if (source != ENV_SOURCE_PARENT) { return setEnvInner(name, value); } return FALSE; } else { /* This EnvSrc would be before the one being set, so keep looking. */ thisEnvSrcRef = &(thisEnvSrc->next); thisEnvSrc = thisEnvSrc->next; } } /* If we get here then we are at the end of the list. */ thisEnvSrc = malloc(sizeof(EnvSrc)); if (!thisEnvSrc) { outOfMemory(TEXT("SEV"), 3); return TRUE; } thisEnvSrc->source = source; thisEnvSrc->name = nameCopy; thisEnvSrc->next = NULL; *thisEnvSrcRef = thisEnvSrc; if (source != ENV_SOURCE_PARENT) { return setEnvInner(name, value); } return FALSE; } /** * Used to set a NULL terminated list of property names whose values should be * escaped when read in from a file. '\\' will become '\' and '\n' will * become '^J', all other characters following '\' will be left as is. * * @param propertyNames NULL terminated list of property names. Property names * can contain a single '*' wildcard which will match 0 or * more characters. */ void setEscapedProperties(const TCHAR **propertyNames) { escapedPropertyNames = propertyNames; } /** * Returns true if the specified property matches one of the property names * previously set in a call to setEscapedProperties() * * @param propertyName Property name to test. * * @return TRUE if the property should be escaped. FALSE otherwise. */ int isEscapedProperty(const TCHAR *propertyName) { size_t nameLen; size_t i; const TCHAR *pattern; TCHAR *wildPos; size_t headLen; size_t tailLen; int matched; size_t patternI; size_t nameI; if (escapedPropertyNames) { nameLen = _tcslen(propertyName); i = 0; while (escapedPropertyNames[i]) { pattern = escapedPropertyNames[i]; if (strcmpIgnoreCase(pattern, propertyName) == 0) { /* Direct Match. */ #ifdef _DEBUG _tprintf(TEXT("Property %s matched pattern %s\n"), propertyName, pattern); #endif return TRUE; } else { wildPos = _tcschr(pattern, TEXT('*')); if (wildPos) { /* The string contains a wildcard. */ /* Try to match the head of the property name. */ headLen = wildPos - pattern; if (headLen < nameLen) { matched = TRUE; patternI = 0; nameI = 0; while (patternI < headLen) { if (pattern[patternI] != propertyName[nameI]) { matched = FALSE; break; } patternI++; nameI++; } if (matched) { tailLen = _tcslen(pattern) - headLen - 1; if (tailLen < nameLen - headLen) { matched = TRUE; patternI = headLen + 1; nameI = nameLen - tailLen; while (nameI < nameLen) { if (pattern[patternI] != propertyName[nameI]) { matched = FALSE; break; } patternI++; nameI++; } if (matched) { #ifdef _DEBUG _tprintf(TEXT("Property %s matched pattern %s\n"), propertyName, pattern); #endif return TRUE; } } } } } } i++; } } return FALSE; } /** * Expands escaped characters and returns a newly malloced string with the result. * '\n' replaced with '^J' * '\\' replaced with '\' * Other escaped characters will show as is. * * @param buffer Original buffer containing escaped characters. * * @return The new expanded buffer. It is the responsibility of the caller to free memory later. */ TCHAR *expandEscapedCharacters(const TCHAR* buffer) { size_t inPos; size_t outPos; TCHAR *outBuffer; int i; TCHAR c1, c2; /* First count the length of the required output buffer to hold the current line. Use the same code twice to avoid maintenance problems. */ outBuffer = NULL; for (i = 0; i < 2; i++) { inPos = 0; outPos = 0; do { c1 = buffer[inPos]; /* The real backslash is #92. The yen mark from files loaded from ShiftJIS is #165. */ if ((c1 == TEXT('\\')) || (c1 == 165)) { /* Escape. */ c2 = buffer[inPos + 1]; if (c2 == TEXT('n')) { /* Line feed. */ inPos++; if (outBuffer) { outBuffer[outPos] = TEXT('\n'); } outPos++; } else if ((c2 == TEXT('\\')) || (c2 == 165)) { /* Back slash. */ inPos++; if (outBuffer) { outBuffer[outPos] = c1; } outPos++; } else if (c2 == 0) { /* Premature End of buffer. Show the backslash. */ if (outBuffer) { outBuffer[outPos] = c1; } outPos++; c1 = 0; } else { /* Unknown char, show the unescaped backslash. */ inPos++; if (outBuffer) { outBuffer[outPos] = c1; outBuffer[outPos + 1] = c2; } outPos += 2; } inPos++; } else if (c1 == 0) { /* End of buffer. */ } else { /* Normal character. */ if (outBuffer) { outBuffer[outPos] = c1; } outPos++; inPos++; } } while (c1 != 0); /* string terminator. */ if (outBuffer) { outBuffer[outPos] = TEXT('\0'); } outPos++; if (outBuffer) { /* We have have full outBuffer. Fall through. */ } else { /* First pass. We need to allocate the outBuffer. */ outBuffer = malloc(outPos * sizeof(TCHAR)); if (!outBuffer) { outOfMemory(TEXT("ELF"), 1); return NULL; } } } return outBuffer; } /** * Return TRUE if the value of the property should be displayed as '' when printed in the logs. * * @property property to check. * * @return TRUE if the value should be hidden. */ int isSecretValue(Property *property) { TCHAR* propName; int result = TRUE; propName = toLower(property->name); if (propName) { result = (_tcsstr(propName, TEXT(".password")) == (propName + ((int)_tcslen(propName) - 9))); free(propName); } return result; } /** * Allocate a string which is used to display the value of a property in the logs. * * @value property value. * @hidden whether the value should be hidden or not. * * @return the allocated value. Should be freed by the caller. */ TCHAR* getDisplayValue(const TCHAR *value, int hidden) { int i, j; TCHAR* buffer; size_t len; /* We need to malloc the buffer! Before, the buffer was received as an argument, but its big * size caused the stack to overflow on certain platforms (I experienced this on HPUX-IA). * I could have used static variables to force creating the buffers on the heap, but they * would remain in memory all the time the Wrapper is running. */ if (hidden) { buffer = malloc(sizeof(TCHAR) * 9); if (!buffer) { outOfMemory(TEXT("GDV"), 1); return NULL; } _tcsncpy(buffer, TEXT(""), 9); } else { /* Count all newlines if any. */ for(i = 0, j = 0; i < (int)_tcslen(value); i++) { if ((value)[i] == TEXT('\n')) { j++; } } len = _tcslen(value) + j + 1; buffer = malloc(sizeof(TCHAR) * len); if (!buffer) { outOfMemory(TEXT("GDV"), 2); return NULL; } if (j > 0) { /* Replace all newlines with '\n'. */ for(i = 0, j = 0; i < (int)_tcslen(value) + 1; i++) { if ((value)[i] == TEXT('\n')) { buffer[j++] = TEXT('\\'); buffer[j++] = TEXT('n'); } else { buffer[j++] = value[i]; } } } else { _tcsncpy(buffer, value, len); } } return buffer; } /** * Adds a single property to the properties structure. * * @param properties Properties structure to add to. * @param filename Name of the file from which the property was loaded. NULL, if not from a file. * @param lineNum Line number of the property declaration in the file. Ignored if filename is NULL. * @param depth Depth of the configuration file where the property was declared. Ignored if filename is NULL. * @param propertyName Name of the new Property. * @param propertyValue Initial property value. * @param finalValue TRUE if the property should be set as static. * @param quotable TRUE if the property could contain quotes. * @param escapable TRUE if the propertyValue can be escaped if its propertyName * is in the list set with setEscapedProperties(). * @param internal TRUE if the property is a Wrapper internal property. * * @return The newly created Property, or NULL if there was a reported error. */ Property* addProperty(Properties *properties, const TCHAR* filename, int lineNum, int depth, const TCHAR *propertyName, const TCHAR *propertyValue, int finalValue, int quotable, int escapable, int internal) { int setValue; Property *property; TCHAR *oldVal; TCHAR *propertyNameTrim; const TCHAR *propertyValueNotNull; TCHAR *propertyValueTrim; TCHAR *propertyExpandedValue; int logLevelOnOverwrite; int hidden; TCHAR *dispValue1; TCHAR *dispValue2; int overwriteWarnId; int isShownAsInternal; propertyValueNotNull = propertyValue ? propertyValue : TEXT(""); #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("addProperty(properties, %s, '%s', '%s', %d, %d, %d, %d)"), (filename ? filename : TEXT("")), propertyName, propertyValue, finalValue, quotable, escapable, internal); #endif /* It is possible that the propertyName and or properyValue contains extra spaces. */ propertyNameTrim = malloc(sizeof(TCHAR) * (_tcslen(propertyName) + 1)); if (!propertyNameTrim) { outOfMemory(TEXT("AP"), 1); return NULL; } trim(propertyName, propertyNameTrim); propertyValueTrim = malloc(sizeof(TCHAR) * ( _tcslen(propertyValueNotNull) + 1)); if (!propertyValueTrim) { outOfMemory(TEXT("AP"), 2); free(propertyNameTrim); return NULL; } trim(propertyValueNotNull, propertyValueTrim); #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" trimmed name='%s', value='%s'"), propertyNameTrim, propertyValueTrim); #endif /* See if the property already exists */ setValue = TRUE; property = getInnerProperty(properties, propertyNameTrim, FALSE); if (property == NULL) { /* This is a new property */ property = createInnerProperty(); if (!property) { free(propertyNameTrim); free(propertyValueTrim); return NULL; } /* Store a copy of the name */ property->name = malloc(sizeof(TCHAR) * (_tcslen(propertyNameTrim) + 1)); if (!property->name) { outOfMemory(TEXT("AP"), 3); disposeInnerProperty(property); free(propertyNameTrim); free(propertyValueTrim); return NULL; } _tcsncpy(property->name, propertyNameTrim, _tcslen(propertyNameTrim) + 1); /* Insert this property at the correct location. Value will still be null. */ insertInnerProperty(properties, property); } else { /* The property was already set. Only change it if non final and non internal */ if (property->finalValue || (property->internal && !internal)) { setValue = FALSE; } property->definitions++; /* On preload we set properties->debugProperties to false as we don't want to log anything nor to stop the Wrapper at this stage. */ if (properties->debugProperties) { /* Preload was already done so the logging system is ready. */ logLevelOnOverwrite = GetLogLevelOnOverwrite(properties); isShownAsInternal = property->internal; if (logLevelOnOverwrite == -1) { /* Log level on overwrite is AUTO. */ if (((property->lastDefinitionDepth >= depth) && (!property->finalValue)) || /* if the new property is referenced in a file with a lower inclusion depth and not overriding a command property. */ (finalValue && (!setValue)) || /* if the new property is a command property that can't be set. */ (isShownAsInternal && (!internal))) { /* if there is any attempt to override an internal property. */ logLevelOnOverwrite = LEVEL_WARN; } else { logLevelOnOverwrite = LEVEL_DEBUG; } } if (property->isVariable) { if (internal) { /* Never show a warning when the property is overriden internally. */ logLevelOnOverwrite = LEVEL_NONE; } else if (property->internal && (logLevelOnOverwrite < LEVEL_WARN)) { /* Always show a warning when the user is overriding an internal variable. */ logLevelOnOverwrite = LEVEL_WARN; } } if ((getLowLogLevel() <= logLevelOnOverwrite) && (logLevelOnOverwrite != LEVEL_NONE)) { overwriteWarnId = 0; /* From version 3.5.27, the Wrapper will also log messages if the command line contains duplicated properties or attempts to set an internal environment variable. */ if (finalValue) { if (isShownAsInternal) { overwriteWarnId = 1; } else if (property->finalValue) { overwriteWarnId = 2; } } else { if (isShownAsInternal) { overwriteWarnId = 4; } else if (property->finalValue) { overwriteWarnId = 5; } else { overwriteWarnId = 6; } } if (overwriteWarnId > 0) { hidden = isSecretValue(property); dispValue1 = getDisplayValue(property->value, hidden); if (!dispValue1) { free(propertyNameTrim); free(propertyValueTrim); return NULL; } dispValue2 = getDisplayValue(propertyValueTrim, hidden); if (!dispValue2) { free(dispValue1); free(propertyNameTrim); free(propertyValueTrim); return NULL; } switch (overwriteWarnId) { case 1: log_printf(WRAPPER_SOURCE_WRAPPER, logLevelOnOverwrite, TEXT("The \"%s\" property is defined by the Wrapper internally and can not be overwritten.\n Ignoring redefinition on the Wrapper command line.\n Fixed Value %s=%s\n Ignored Value %s=%s"), propertyNameTrim, propertyNameTrim, dispValue1, propertyNameTrim, dispValue2); break; case 2: log_printf(WRAPPER_SOURCE_WRAPPER, logLevelOnOverwrite, TEXT("The \"%s\" property was already defined on the Wrapper command line and can not be overwritten.\n Ignoring redefinition on the Wrapper command line.\n Fixed Value %s=%s\n Ignored Value %s=%s"), propertyNameTrim, propertyNameTrim, dispValue1, propertyNameTrim, dispValue2); break; case 4: log_printf(WRAPPER_SOURCE_WRAPPER, logLevelOnOverwrite, TEXT("The \"%s\" property is defined by the Wrapper internally and can not be overwritten.\n Ignoring redefinition on line #%d of configuration file: %s\n Fixed Value %s=%s\n Ignored Value %s=%s"), propertyNameTrim, lineNum, (filename ? filename : TEXT("")), propertyNameTrim, dispValue1, propertyNameTrim, dispValue2); break; case 5: log_printf(WRAPPER_SOURCE_WRAPPER, logLevelOnOverwrite, TEXT("The \"%s\" property was defined on the Wrapper command line and can not be overwritten.\n Ignoring redefinition on line #%d of configuration file: %s\n Fixed Value %s=%s\n Ignored Value %s=%s"), propertyNameTrim, lineNum, (filename ? filename : TEXT("")), propertyNameTrim, dispValue1, propertyNameTrim, dispValue2); break; case 6: log_printf(WRAPPER_SOURCE_WRAPPER, logLevelOnOverwrite, TEXT("The \"%s\" property was redefined on line #%d of configuration file: %s\n Old Value %s=%s\n New Value %s=%s"), propertyNameTrim, lineNum, (filename ? filename : TEXT("")), propertyNameTrim, dispValue1, propertyNameTrim, dispValue2); break; } free(dispValue1); free(dispValue2); } } if (properties->exitOnOverwrite) { properties->overwrittenPropertyCausedExit = TRUE; } } } free(propertyNameTrim); if (setValue) { if (escapable && isEscapedProperty(property->name)) { /* Expand the value. */ #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("expanding value of %s"), property->name); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" value : %s"), propertyValueTrim); #endif propertyExpandedValue = expandEscapedCharacters(propertyValueTrim); if (!propertyExpandedValue) { free(propertyValueTrim); return NULL; } #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" expanded: %s"), propertyExpandedValue); #endif /* Set the property value. */ setInnerProperty(properties, property, propertyExpandedValue, FALSE); free(propertyExpandedValue); } else { /* Set the property value. */ setInnerProperty(properties, property, propertyValueTrim, FALSE); } if (property->value == NULL) { free(propertyValueTrim); return NULL; } /* Store the final flag */ property->finalValue = finalValue; /* Store the quotable flag. */ property->quotable = quotable; /* Store the internal flag. */ property->internal = internal; /* Store the include depth. */ property->lastDefinitionDepth = depth; /* Prepare the property by expanding any environment variables that are defined. */ prepareProperty(properties, property, FALSE); /* Store the file name if any. */ if (property->filePath != NULL) { free(property->filePath); property->filePath = NULL; } if (filename) { property->filePath = malloc(sizeof(TCHAR) * (_tcslen(filename) + 1)); _tcsncpy(property->filePath, filename, _tcslen(filename) + 1); } /* Store the line number. */ property->lineNumber = lineNum; /* See if this is a variable definition */ if ((_tcslen(property->name) > 12) && (_tcsstr(property->name, TEXT("set.default.")) == property->name)) { /* This property is an environment variable definition that should only * be set if the environment variable does not already exist. Get the * value back out of the property as it may have had environment * replacements. */ property->isVariable = TRUE; oldVal = _tgetenv(property->name + 12); if (oldVal == NULL) { /* Only set the variable if the new value is not NULL. */ if (propertyValue) { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("set default env('%s', '%s')"), property->name + 12, property->value); #endif setEnv(property->name + 12, property->value, (internal ? ENV_SOURCE_APPLICATION : ENV_SOURCE_CONFIG)); } } else { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT( "not setting default env('%s', '%s'), already set to '%s'"), property->name + 12, property->value, oldVal); #endif #if !defined(WIN32) && defined(UNICODE) free(oldVal); #endif } } else if ((_tcslen(property->name) > 4) && (_tcsstr(property->name, TEXT("set.")) == property->name)) { /* This property is an environment variable definition. Get the * value back out of the property as it may have had environment * replacements. */ property->isVariable = TRUE; if (propertyValue) { /* Set the variable if the new value is not NULL. */ #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("set env('%s', '%s')"), property->name + 4, property->value); #endif setEnv(property->name + 4, property->value, (internal ? ENV_SOURCE_APPLICATION : ENV_SOURCE_CONFIG)); } else { oldVal = _tgetenv(property->name + 4); if (oldVal) { /* Clear the variable if the new value is NULL and the environment was not NULL. */ #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("set env('%s', '')"), property->name + 4); #endif setEnv(property->name + 4, NULL, (internal ? ENV_SOURCE_APPLICATION : ENV_SOURCE_CONFIG)); #if !defined(WIN32) && defined(UNICODE) free(oldVal); #endif } } } } free(propertyValueTrim); return property; } /** * Takes a name/value pair in the form = and attempts to add * it to the specified properties table. * * @param properties Properties structure to add to. * @param filename Name of the file from which the property was loaded. NULL, if not from a file. * @param lineNum Line number of the property declaration in the file. Ignored if filename is NULL. * @param propertyNameValue The "name=value" pair to create the property from. * @param finalValue TRUE if the property should be set as static. * @param quotable TRUE if the property could contain quotes. * @param internal TRUE if the property is a Wrapper internal property. * * Returns 0 if successful, otherwise 1 */ int addPropertyPair(Properties *properties, const TCHAR* filename, int lineNum, const TCHAR *propertyNameValue, int finalValue, int quotable, int internal) { TCHAR buffer[MAX_PROPERTY_NAME_VALUE_LENGTH]; TCHAR *d; /* Make a copy of the pair that we can edit */ if (_tcslen(propertyNameValue) + 1 >= MAX_PROPERTY_NAME_VALUE_LENGTH) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("The following property name value pair is too large. Need to increase the internal buffer size: %s"), propertyNameValue); return 1; } _tcsncpy(buffer, propertyNameValue, MAX_PROPERTY_NAME_VALUE_LENGTH); if ((d = checkPropertySyntax(buffer))) { if (addProperty(properties, filename, lineNum, 0, buffer, d, finalValue, quotable, FALSE, internal) != NULL) { return 0; } } return 1; } /** * Register an internal variable and add it to the properties structure in order to get correct warnings. * * @param properties The Properties structure. * @param varName The variable name. * @param varValue The value of the variable. * @param finalValue TRUE if the variable can be changed after the configuration is loaded. * @param ignore TRUE if the variable should be added to properties->ignoreVarMap * which means it should not be expanded until its value is turned to FALSE. */ void setInternalVarProperty(Properties *properties, const TCHAR *varName, const TCHAR *varValue, int finalValue, int ignore) { TCHAR* propertyName; /* A variable that was never set as ignored does not need to be added in the Hashmap. * A variable that was previously set as ignored can be kept in the Hashmap but we need to set the hash value to "FALSE". */ if (ignore) { hashMapPutKWVW(properties->ignoreVarMap, varName, TEXT("TRUE")); } else if (hashMapGetKWVW(properties->ignoreVarMap, varName)) { hashMapPutKWVW(properties->ignoreVarMap, varName, TEXT("FALSE")); } /* Do not warn about this variable */ hashMapPutKWVW(properties->warnedVarMap, varName, TEXT("INTERNAL")); propertyName = malloc(sizeof(TCHAR) * (4 + _tcslen(varName) + 1)); if (!propertyName) { outOfMemory(TEXT("SIVP"), 1); return; } _sntprintf(propertyName, 4 + _tcslen(varName) + 1, TEXT("set.%s"), varName); /* Always add the variable as a property to prevent user from overriding it and to get the correct warning, but pass a NULL value if the variable should not be set or cleared. */ addProperty(properties, NULL, 0, 0, propertyName, varValue, finalValue, FALSE, FALSE, TRUE); free(propertyName); } const TCHAR* getStringProperty(Properties *properties, const TCHAR *propertyName, const TCHAR *defaultValue) { Property *property; property = getInnerProperty(properties, propertyName, TRUE); if (property == NULL) { if (defaultValue != NULL) { property = addProperty(properties, NULL, 0, 0, propertyName, defaultValue, FALSE, FALSE, FALSE, FALSE); if (property) { property->isGenerated = TRUE; return property->value; } else { /* We failed to add the property, but still return the default. */ return defaultValue; } } else { return NULL; } } else { return property->value; } } const TCHAR* getNotEmptyStringProperty(Properties *properties, const TCHAR *propertyName, const TCHAR *defaultValue) { const TCHAR* result = getStringProperty(properties, propertyName, defaultValue); if (result && (_tcslen(result) > 0)) { return result; } return defaultValue; } const TCHAR* getFileSafeStringProperty(Properties *properties, const TCHAR *propertyName, const TCHAR *defaultValue) { Property *property; TCHAR *buffer; int i; property = getInnerProperty(properties, propertyName, TRUE); if (property == NULL) { if (defaultValue != NULL) { property = addProperty(properties, NULL, 0, 0, propertyName, defaultValue, FALSE, FALSE, FALSE, FALSE); if (property) { property->isGenerated = TRUE; } } return defaultValue; } else { buffer = property->value; if (_tcschr(buffer, TEXT('%'))) { i = 0; while (buffer[i]) { if (buffer[i] == TEXT('%')) { buffer[i] = TEXT('_'); } i++; } } return buffer; } } /** * Does a quick sort of the property values, keeping the values together. */ void sortStringProperties(long unsigned int *propertyIndices, TCHAR **propertyNames, TCHAR **propertyValues, int low, int high) { int i = low; int j = high; long int tempIndex; TCHAR *tempName; TCHAR *tempValue; long unsigned int x = propertyIndices[(low + high) / 2]; do { while (propertyIndices[i] < x) { i++; } while (propertyIndices[j] > x) { j--; } if (i <= j) { /* Swap i and j values. */ tempIndex = propertyIndices[i]; tempName = propertyNames[i]; tempValue = propertyValues[i]; propertyIndices[i] = propertyIndices[j]; propertyNames[i] = propertyNames[j]; propertyValues[i] = propertyValues[j]; propertyIndices[j] = tempIndex; propertyNames[j] = tempName; propertyValues[j] = tempValue; i++; j--; } } while (i <= j); /* Recurse */ if (low < j) { sortStringProperties(propertyIndices, propertyNames, propertyValues, low, j); } if (i < high) { sortStringProperties(propertyIndices, propertyNames, propertyValues, i, high); } } /** * Returns a sorted array of all properties beginning with {propertyNameBase}. * Only numerical characters can be returned between the two. * * The calling code must always call freeStringProperties to make sure that the * malloced propertyNames, propertyValues, and propertyIndices arrays are freed * up correctly. This is only necessary if the function returns 0. * * @param properties The full properties structure. * @param propertyNameHead All matching properties must begin with this value. * @param propertyNameTail All matching properties must end with this value. * @param all If FALSE then the array will start with #1 and loop up until the * next property is not found, if TRUE then all properties will be * returned, even if there are gaps in the series. * @param matchAny If FALSE only numbers are allowed as placeholder, * If TRUE any strings (including empty string) are allowed and * propertyIndices is not set. * @param propertyNames Returns a pointer to a NULL terminated array of * property names. * @param propertyValues Returns a pointer to a NULL terminated array of * property values. * @param propertyIndices Returns a pointer to a 0 terminated array of * the index numbers used in each property name of * the propertyNames array. * * @return 0 if successful, -1 if there was an error. */ int getStringProperties(Properties *properties, const TCHAR *propertyNameHead, const TCHAR *propertyNameTail, int all, int matchAny, TCHAR ***propertyNames, TCHAR ***propertyValues, long unsigned int **propertyIndices) { int j; int k; size_t headLen; size_t tailLen; size_t thisLen; TCHAR *thisHead; TCHAR *thisTail; size_t i; Property *property; size_t indexLen; TCHAR indexS[16]; int ok; TCHAR c; int count; int firstPass = TRUE; if (!matchAny) { *propertyIndices = NULL; } headLen = _tcslen(propertyNameHead); tailLen = _tcslen(propertyNameTail); for (j = 0; j < 2; j++) { count = 0; property = properties->first; while (property != NULL) { thisLen = _tcslen(property->name); if (matchAny && (thisLen < headLen + tailLen)) { /* Too short, not what we are looking for. */ } else if (!matchAny && (thisLen < headLen + tailLen + 1)) { /* Too short, not what we are looking for. */ } else { thisHead = malloc(sizeof(TCHAR) * (headLen + 1)); if (!thisHead) { outOfMemory(TEXT("GSPS"), 1); } else { _tcsncpy(thisHead, property->name, headLen); thisHead[headLen] = 0; if (strcmpIgnoreCase(thisHead, propertyNameHead) == 0) { /* Head matches. */ thisTail = malloc(sizeof(TCHAR) * (tailLen + 1)); if (!thisTail) { outOfMemory(TEXT("GSPS"), 2); } else { _tcsncpy(thisTail, property->name + thisLen - tailLen, tailLen + 1); if (strcmpIgnoreCase(thisTail, propertyNameTail) == 0) { /* Tail matches. */ indexLen = thisLen - headLen - tailLen; if (indexLen <= 15) { ok = TRUE; if (!matchAny) { _tcsncpy(indexS, property->name + headLen, indexLen); indexS[indexLen] = 0; for (i = 0; i < indexLen; i++) { c = indexS[i]; if ((c < '0') || (c > '9')) { ok = FALSE; break; } } } if (ok) { if (!firstPass) { prepareProperty(properties, property, FALSE); if (!matchAny) { (*propertyIndices)[count] = _tcstoul(indexS, NULL, 10); } (*propertyNames)[count] = property->name; (*propertyValues)[count] = property->value; } count++; } } } free(thisTail); } } free(thisHead); } } /* Keep looking */ property = property->next; } if (firstPass) { firstPass = FALSE; *propertyNames = malloc(sizeof(TCHAR *) * (count + 1)); if (!(*propertyNames)) { outOfMemory(TEXT("GSPS"), 3); *propertyNames = NULL; *propertyValues = NULL; if (!matchAny) { *propertyIndices = NULL; } return -1; } *propertyValues = malloc(sizeof(TCHAR *) * (count + 1)); if (!(*propertyValues)) { outOfMemory(TEXT("GSPS"), 4); free(*propertyNames); *propertyNames = NULL; *propertyValues = NULL; if (!matchAny) { *propertyIndices = NULL; } return -1; } if (!matchAny) { *propertyIndices = malloc(sizeof(long unsigned int) * (count + 1)); if (!(*propertyIndices)) { outOfMemory(TEXT("GSPS"), 5); free(*propertyNames); free(*propertyValues); *propertyNames = NULL; *propertyValues = NULL; *propertyIndices = NULL; return -1; } } if (count == 0) { /* The count is 0 so no need to continue through the loop again. */ (*propertyNames)[0] = NULL; (*propertyValues)[0] = NULL; if (!matchAny) { (*propertyIndices)[0] = 0; } return 0; } } else { /* Second pass */ (*propertyNames)[count] = NULL; (*propertyValues)[count] = NULL; if (!matchAny) { (*propertyIndices)[count] = 0; sortStringProperties(*propertyIndices, *propertyNames, *propertyValues, 0, count - 1); /* If we don't want all of the properties then we need to remove the extra ones. * Names and values are not allocated, so setting them to NULL is fine.*/ if (!all) { for (k = 0; k < count; k++) { if ((*propertyIndices)[k] != k + 1) { (*propertyNames)[k] = NULL; (*propertyValues)[k] = NULL; (*propertyIndices)[k] = 0; } } } } /* for (k = 0; k < count; k++) { if ((*propertyNames)[k]) { _tprintf("[%d] #%lu: %s=%s\n", k, (*propertyIndices)[k], (*propertyNames)[k], (*propertyValues)[k]); } } */ return 0; } } /* For compiler */ return 0; } /** * Frees up an array of properties previously returned by getStringProperties(). */ void freeStringProperties(TCHAR **propertyNames, TCHAR **propertyValues, long unsigned int *propertyIndices) { /* The property names are not malloced. */ free(propertyNames); /* The property values are not malloced. */ free(propertyValues); free(propertyIndices); } int getIntProperty(Properties *properties, const TCHAR *propertyName, int defaultValue) { TCHAR buffer[16]; Property *property; int i; TCHAR c; int value; property = getInnerProperty(properties, propertyName, TRUE); if (property == NULL) { _sntprintf(buffer, 16, TEXT("%d"), defaultValue); property = addProperty(properties, NULL, 0, 0, propertyName, buffer, FALSE, FALSE, FALSE, FALSE); if (property) { property->isGenerated = TRUE; } return defaultValue; } else { value = (int)_tcstol(property->value, NULL, 0); /* Make sure that the property does not contain invalid characters. */ i = 0; do { c = property->value[i]; if ((i > 0) && (c == TEXT('\0'))) { /* Fall through */ } else if ((i == 0) && (c == TEXT('-'))) { /* Negative number. This is Ok. */ } else if ((c < TEXT('0')) || (c > TEXT('9'))) { if (i == 0) { /* If the bad character is the first character then use the default value. */ value = defaultValue; } if (properties->logWarnings) { log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("Encountered an invalid numerical value for configuration property %s=%s. Resolving to %d."), propertyName, property->value, value); } break; } i++; } while (c != TEXT('\0')); return value; } } int getBooleanProperty(Properties *properties, const TCHAR *propertyName, int defaultValue) { const TCHAR *defaultValueS; Property *property; const TCHAR *propertyValue; if (defaultValue) { defaultValueS = TEXT("TRUE"); } else { defaultValueS = TEXT("FALSE"); } property = getInnerProperty(properties, propertyName, TRUE); if (property == NULL) { property = addProperty(properties, NULL, 0, 0, propertyName, defaultValueS, FALSE, FALSE, FALSE, FALSE); if (property) { property->isGenerated = TRUE; } propertyValue = defaultValueS; } else { propertyValue = property->value; } if (strcmpIgnoreCase(propertyValue, TEXT("TRUE")) == 0) { return TRUE; } else if (strcmpIgnoreCase(propertyValue, TEXT("FALSE")) == 0) { return FALSE; } else { if (properties->logWarnings) { log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("Encountered an invalid boolean value for configuration property %s=%s. Resolving to %s."), propertyName, propertyValue, defaultValueS); } return defaultValue; } } /** * Build sorted arrays of all boolean properties beginning with {propertyNameBase}. * Only numerical characters can be returned between the two. * * The calling code must always call freeBooleanProperties to make sure that the * malloced propertyNames, propertyValues, and propertyIndices arrays are freed * up correctly. This is only necessary if the function returns 0. * * @param properties The full properties structure. * @param propertyNameHead All matching properties must begin with this value. * @param propertyNameTail All matching properties must end with this value. * @param all If FALSE then the array will start with #1 and loop up until the * next property is not found, if TRUE then all properties will be * returned, even if there are gaps in the series. * @param matchAny If FALSE only numbers are allowed as placeholder * @param propertyNames Returns a pointer to a NULL terminated array of * property names. * @param propertyValues Returns a pointer to a NULL terminated array of * property values. * @param propertyIndices Returns a pointer to a 0 terminated array of * the index numbers used in each property name of * the propertyNames array. * * @return 0 if successful, -1 if there was an error. */ int getBooleanProperties(Properties *properties, const TCHAR *propertyNameHead, const TCHAR *propertyNameTail, int all, int matchAny, TCHAR ***propertyNames, int **propertyValues, long unsigned int **propertyIndices, int defaultValue) { TCHAR **strPropertyValues; int i = 0; int count = 0; int result; result = getStringProperties(properties, propertyNameHead, propertyNameTail, all, matchAny, propertyNames, &strPropertyValues, propertyIndices); if (result == -1) return result; while (strPropertyValues[i]) { count++; i++; } *propertyValues = malloc(sizeof(TCHAR *) * (count + 1)); i = 0; while (strPropertyValues[i]) { if (strcmpIgnoreCase(strPropertyValues[i], TEXT("TRUE")) == 0) { (*propertyValues)[i] = TRUE; } else if (strcmpIgnoreCase(strPropertyValues[i], TEXT("FALSE")) == 0) { (*propertyValues)[i] = FALSE; } else { if (properties->logWarnings) { log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("Encountered an invalid boolean value for configuration property %s=%s. Resolving to %s."), (*propertyNames)[i], strPropertyValues[i], defaultValue ? TEXT("TRUE") : TEXT("FALSE")); } (*propertyValues)[i] = defaultValue; } i++; } free(strPropertyValues); return 0; } void freeBooleanProperties(TCHAR **propertyNames, int *propertyValues, long unsigned int *propertyIndices) { /* The property names are not malloced. */ free(propertyNames); free(propertyValues); free(propertyIndices); } const TCHAR* getPropertyFilePath(Properties *properties, const TCHAR* propertyName) { Property* property; property = getInnerProperty(properties, propertyName, FALSE); if (property) { return property->filePath; } return NULL; } static const TCHAR* getStatusStr(int status) { TCHAR *name; switch (status) { case STATUS_ENABLED: name = TEXT("ENABLED"); break; case STATUS_DISABLED: name = TEXT("DISABLED"); break; default: name = TEXT("UNCHANGED"); break; } return name; } int getStatusProperty(Properties *properties, const TCHAR *propertyName, int defaultValue) { const TCHAR* valueStr = getStringProperty(properties, propertyName, NULL); if (valueStr) { if (strcmpIgnoreCase(valueStr, TEXT("UNCHANGED")) == 0) { return STATUS_UNCHANGED; } else if (strcmpIgnoreCase(valueStr, TEXT("ENABLED")) == 0) { return STATUS_ENABLED; } else if (strcmpIgnoreCase(valueStr, TEXT("DISABLED")) == 0) { return STATUS_DISABLED; } else if (properties->logWarnings) { log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("Encountered an invalid value for configuration property %s=%s. Resolving to %s."), propertyName, valueStr, getStatusStr(defaultValue)); } } return defaultValue; } /** * Indicates if a property was generated by the Wrapper or written in the configuration. * ATTENTION: The value returned by this function should never be used in a condition * that will affect the Wrapper behaviour. We want the configuration to be * loaded the same way if a property is set with a default value or not set. * This function should only be used for logging purpose, for example to print * a property name making sure it actually exists in the configuration file. * * @param properties The full properties structure. * @param propertyName The name of the property to check. * * @return TRUE if the property was generated by the Wrapper, FALSE if it exists in the configuration. */ int isGeneratedProperty(Properties *properties, const TCHAR *propertyName) { Property *property; property = getInnerProperty(properties, propertyName, FALSE); if (property == NULL) { return FALSE; } else { return property->isGenerated; } } int isQuotableProperty(Properties *properties, const TCHAR *propertyName) { Property *property; property = getInnerProperty(properties, propertyName, FALSE); if (property == NULL) { return FALSE; } else { return property->quotable; } } /** * Return a code indicating how the property can be dumped. * * @property property to check. * * @return 0 if the property should not be dumped. * 1 if the property can be logged normally. */ int propertyDumpFilter(Property *property) { if (property->isGenerated || property->internal) { return 0; } /* if (_tcsstr(propName, TEXT(".license."))) { free(propName); return 0; } */ return 1; } const TCHAR* getPropertySourceName(Property *property) { if (property->finalValue) { return TEXT("COMMAND "); } else if (property->isGenerated) { return TEXT("WRAPPER "); } else { return TEXT("FILE "); } } TCHAR getPropertySourceShortName(Property *property) { if (property->finalValue) { return TEXT('C'); } else if (property->isGenerated) { return TEXT('W'); } else { return TEXT('F'); } } /* Returns the number of columns with variable sizes and the required size. */ int getColumnsAndReqVarSizeForPropertyDump(TCHAR* value, TCHAR* format, size_t *reqSize) { int numColumns; int i; for(i = 0, numColumns = 0; i < (int)_tcslen(format); i++ ) { switch(format[i]) { case TEXT('V'): case TEXT('v'): *reqSize += _tcslen(value) + 3; numColumns++; break; } } return numColumns; } /* Returns the number of columns with constant sizes, and retrieve the sizes that should be calculated taking into account all properties. */ int getColumnsAndReqConstSizesForPropertyDump(Properties *properties, TCHAR* format, size_t *reqSize, size_t *reqPropNameSize, size_t *reqConfPathSize, size_t *reqConfNameSize) { Property *property; int dumpFilter; int addPropNameSize = FALSE; int addConfPathSize = FALSE; int addConfNameSize = FALSE; int numColumns; int i; *reqSize = 0; *reqPropNameSize = 0; *reqConfPathSize = 0; *reqConfNameSize = 0; for(i = 0, numColumns = 0; i < (int)_tcslen(format); i++ ) { switch(format[i]) { case TEXT('S'): case TEXT('s'): *reqSize += 1 + 3; numColumns++; break; case TEXT('Z'): case TEXT('z'): *reqSize += 8 + 3; /* FILE|EMBEDDED|COMMAND|WRAPPER */ numColumns++; break; case TEXT('F'): case TEXT('f'): *reqSize += 1 + 3; numColumns++; break; case TEXT('P'): case TEXT('p'): addConfPathSize = TRUE; numColumns++; break; case TEXT('C'): case TEXT('c'): addConfNameSize = TRUE; numColumns++; break; case TEXT('L'): case TEXT('l'): *reqSize += 4 + 3; numColumns++; break; case TEXT('I'): case TEXT('i'): *reqSize += 1 + 3; numColumns++; break; case TEXT('N'): case TEXT('n'): addPropNameSize = TRUE; numColumns++; break; } } if (addPropNameSize || addConfPathSize || addConfNameSize) { property = properties->first; while (property != NULL) { dumpFilter = propertyDumpFilter(property); if (dumpFilter > 0) { if (addPropNameSize) { *reqPropNameSize = __max(_tcslen(property->name), *reqPropNameSize); } /* We assume that the conf file name and path are written with characters that display on a single char width. * We would need a smarter function to calculate the length of strings containing full width Japanese characters. */ if (addConfPathSize && property->filePath) { *reqConfPathSize = __max(_tcslen(property->filePath), *reqConfPathSize); } if (addConfNameSize && property->filePath) { *reqConfNameSize = __max(_tcslen(getFileName(property->filePath)), *reqConfNameSize); } } property = property->next; } if (addPropNameSize) { *reqSize += *reqPropNameSize + 3; } if (addConfPathSize) { *reqSize += *reqConfPathSize + 3; } if (addConfNameSize) { *reqSize += *reqConfNameSize + 3; } } return numColumns; } TCHAR* buildPropertyDumpBuffer(Property *property, TCHAR* format, int numConstColumns, size_t reqConstTotSize, size_t reqPropNameSize, size_t reqConfPathSize, size_t reqConfNameSize) { int i; size_t reqSize; int numColumns; TCHAR *pos; int currentColumn; int handledFormat; int temp = 0; int len = 0; TCHAR* printBuffer; TCHAR* propValue; #if defined(UNICODE) && !defined(WIN32) const TCHAR* leftAlignStrFormat = TEXT("%-*S"); #else const TCHAR* leftAlignStrFormat = TEXT("%-*s"); #endif propValue = getDisplayValue(property->value, isSecretValue(property)); if (!propValue) { return NULL; } /* The required size for the columns with a constant width, as well as their number, are calculated once and for all properties. */ reqSize = reqConstTotSize; numColumns = numConstColumns; /* Then add the variable columns. */ numColumns += getColumnsAndReqVarSizeForPropertyDump(propValue, format, &reqSize); if (reqSize == 0) { free(propValue); /* Invalid format - this should not happen because we checked that the format was correct before calling this function. */ return NULL; } /* Always add room for the null. */ reqSize += 1; printBuffer = malloc(sizeof(TCHAR) * reqSize); if (!printBuffer) { outOfMemory(TEXT("BPDB"), 1); free(propValue); return NULL; } pos = printBuffer; /* Indent with two characters to display like when dumping environment variables. */ temp = _sntprintf(pos, 3, TEXT(" ")); pos += temp; len += temp; reqSize += len; for(i = 0, currentColumn = 0; i < (int)_tcslen(format); i++) { handledFormat = TRUE; switch(format[i]) { case TEXT('S'): case TEXT('s'): temp = _sntprintf(pos, reqSize - len, TEXT("%c"), getPropertySourceShortName(property)); currentColumn++; break; case TEXT('Z'): case TEXT('z'): temp = _sntprintf(pos, reqSize - len, TEXT("%s"), getPropertySourceName(property)); currentColumn++; break; case TEXT('F'): case TEXT('f'): temp = _sntprintf(pos, reqSize - len, TEXT("%c"), (property->finalValue ? TEXT('F') : TEXT(' '))); currentColumn++; break; case TEXT('P'): case TEXT('p'): temp = _sntprintf(pos, reqSize - len, leftAlignStrFormat, reqConfPathSize, property->filePath ? property->filePath : TEXT("")); currentColumn++; break; case TEXT('C'): case TEXT('c'): temp = _sntprintf(pos, reqSize - len, leftAlignStrFormat, reqConfNameSize, property->filePath ? getFileName(property->filePath) : TEXT("")); currentColumn++; break; case TEXT('L'): case TEXT('l'): if (getPropertySourceShortName(property) != TEXT('F')) { temp = _sntprintf(pos, reqSize - len, TEXT(" ")); } else if (property->lineNumber > 9999) { temp = _sntprintf(pos, reqSize - len, TEXT("****")); } else { temp = _sntprintf(pos, reqSize - len, TEXT("%4d"), property->lineNumber); } currentColumn++; break; case TEXT('I'): case TEXT('i'): if (property->definitions > 9) { temp = _sntprintf(pos, reqSize - len, TEXT("*")); } else if (property->definitions > 1) { /* Only show the number of definitions if it's more than 1, to make those cases appear clearly. */ temp = _sntprintf(pos, reqSize - len, TEXT("%d"), property->definitions); } else { /* In the future we may want to show a '-' if the property is not defined anywhere (0 definitions). For now it is just not listed. */ temp = _sntprintf(pos, reqSize - len, TEXT(" ")); } currentColumn++; break; case TEXT('N'): case TEXT('n'): temp = _sntprintf(pos, reqSize - len, leftAlignStrFormat, reqPropNameSize, property->name); currentColumn++; break; case TEXT('V'): case TEXT('v'): temp = _sntprintf(pos, reqSize - len, TEXT("%s"), propValue); currentColumn++; break; default: handledFormat = FALSE; } if (handledFormat) { pos += temp; len += temp; /* Add separator chars */ if (currentColumn != numColumns) { temp = _sntprintf(pos, reqSize - len, TEXT(" | ")); pos += temp; len += temp; } } } free(propValue); /* Return the print buffer to the caller. */ return printBuffer; } void dumpProperties(Properties *properties) { Property *property; int dumpFilter; int numConstColumns; size_t reqConstTotSize; size_t reqPropNameSize; size_t reqConfPathSize; size_t reqConfNameSize; TCHAR* printBuffer; if ((getLowLogLevel() <= properties->dumpLogLevel) && (properties->dumpLogLevel != LEVEL_NONE)) { property = properties->first; /* The required size for the columns with a constant width, as well as their number, are calculated once and for all properties. */ numConstColumns = getColumnsAndReqConstSizesForPropertyDump(properties, properties->dumpFormat, &reqConstTotSize, &reqPropNameSize, &reqConfPathSize, &reqConfNameSize); if ((numConstColumns == 0) && (getColumnsAndReqVarSizeForPropertyDump(TEXT(""), properties->dumpFormat, &reqConstTotSize) == 0)) { /* No columns or invalid format - use the default format instead. */ log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("Encountered an invalid format for configuration property %s=%s. Resolving to '%s'."), TEXT("wrapper.properties.dump.format"), properties->dumpFormat, PROPERTIES_DUMP_FORMAT_DEFAULT); setPropertiesDumpFormat(properties, PROPERTIES_DUMP_FORMAT_DEFAULT); /* Recalculate the size for the columns with a constant width and their number. */ numConstColumns = getColumnsAndReqConstSizesForPropertyDump(properties, properties->dumpFormat, &reqConstTotSize, &reqPropNameSize, &reqConfPathSize, &reqConfNameSize); } log_printf(WRAPPER_SOURCE_WRAPPER, properties->dumpLogLevel, TEXT("")); log_printf(WRAPPER_SOURCE_WRAPPER, properties->dumpLogLevel, TEXT("Wrapper configuration properties BEGIN:")); while (property != NULL) { dumpFilter = propertyDumpFilter(property); if (dumpFilter > 0) { printBuffer = buildPropertyDumpBuffer(property, properties->dumpFormat, numConstColumns, reqConstTotSize, reqPropNameSize, reqConfPathSize, reqConfNameSize); if (printBuffer) { log_printf(WRAPPER_SOURCE_WRAPPER, properties->dumpLogLevel, TEXT("%s"), printBuffer); free(printBuffer); } } property = property->next; } log_printf(WRAPPER_SOURCE_WRAPPER, properties->dumpLogLevel, TEXT("Wrapper configuration properties END:")); log_printf(WRAPPER_SOURCE_WRAPPER, properties->dumpLogLevel, TEXT("")); } } /** * Level at which properties will be dumped. */ void setPropertiesDumpLogLevel(Properties *properties, int logLevel) { properties->dumpLogLevel = logLevel; } /** * Format used when dumping properties. */ void setPropertiesDumpFormat(Properties *properties, const TCHAR* format) { if (properties->dumpFormat) { free(properties->dumpFormat); } properties->dumpFormat = malloc(sizeof(TCHAR) * (_tcslen(format) + 1)); _tcsncpy(properties->dumpFormat, format, (_tcslen(format) + 1)); } /** * Set to TRUE if warnings about property values should be logged. */ void setLogPropertyWarnings(Properties *properties, int logWarnings) { properties->logWarnings = logWarnings; } /** * Level at which any property warnings are logged. */ void setLogPropertyWarningLogLevel(Properties *properties, int logLevel) { properties->logWarningLogLevel = logLevel; } /** * Returns the minimum value. This is used in place of the __min macro when the parameters should not be called more than once. */ int propIntMin(int value1, int value2) { if (value1 < value2) { return value1; } else { return value2; } } /** * Returns the maximum value. This is used in place of the __max macro when the parameters should not be called more than once. */ int propIntMax(int value1, int value2) { if (value1 > value2) { return value1; } else { return value2; } } /** Creates a linearized representation of all of the properties. * The returned buffer must be freed by the calling code. */ TCHAR *linearizeProperties(Properties *properties, TCHAR separator) { Property *property; size_t size; TCHAR *c; TCHAR *fullBuffer; TCHAR *work, *buffer; /* First we need to figure out how large a buffer will be needed to linearize the properties. */ size = 0; property = properties->first; while (property != NULL) { /* Add the length of the basic property. */ size += _tcslen(property->name); size++; /* '=' */ size += _tcslen(property->value); /* Handle any characters that will need to be escaped. */ c = property->name; while ((c = _tcschr(c, separator)) != NULL) { size++; c++; } c = property->value; while ((c = _tcschr(c, separator)) != NULL) { size++; c++; } size++; /* separator */ property = property->next; } size++; /* null terminated. */ /* Now that we know how much space this will all take up, allocate a buffer. */ fullBuffer = buffer = calloc(sizeof(TCHAR) , size); if (!fullBuffer) { outOfMemory(TEXT("LP"), 1); return NULL; } /* Now actually build up the output. Any separator characters need to be escaped with themselves. */ property = properties->first; while (property != NULL) { /* name */ work = property->name; while ((c = _tcschr(work, separator)) != NULL) { _tcsncpy(buffer, work, c - work + 1); buffer += c - work + 1; buffer[0] = separator; buffer++; work = c + 1; } _tcsncpy(buffer, work, size - _tcslen(fullBuffer)); buffer += _tcslen(work); /* equals */ buffer[0] = TEXT('='); buffer++; /* value */ work = property->value; while ((c = _tcschr(work, separator)) != NULL) { _tcsncpy(buffer, work, c - work + 1); buffer += c - work + 1; buffer[0] = separator; buffer++; work = c + 1; } _tcsncpy(buffer, work, size - _tcslen(fullBuffer)); buffer += _tcslen(work); /* separator */ buffer[0] = separator; buffer++; property = property->next; } /* null terminate. */ buffer[0] = 0; buffer++; return fullBuffer; } wrapper_3.5.51_src/src/c/property.h100644 0 0 42160 14333053650 14440 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ #ifndef _PROPERTY_H #define _PROPERTY_H #include "wrapper_hashmap.h" #ifndef TRUE #define TRUE -1 #endif #ifndef FALSE #define FALSE 0 #endif /* This defines the largest environment variable that we are able * to work with. It can be expanded if needed. */ #define MAX_PROPERTY_NAME_LENGTH 512 #define MAX_PROPERTY_VALUE_LENGTH 16384 #define MAX_PROPERTY_NAME_VALUE_LENGTH MAX_PROPERTY_NAME_LENGTH + 1 + MAX_PROPERTY_VALUE_LENGTH #define ENV_SOURCE_PARENT 1 #define ENV_SOURCE_APPLICATION 2 #define ENV_SOURCE_CONFIG 4 #ifdef WIN32 #define ENV_SOURCE_REG_SYSTEM 8 #define ENV_SOURCE_REG_ACCOUNT 16 #endif #define PROPERTIES_DUMP_FORMAT_DEFAULT TEXT("SFNV") typedef struct EnvSrc EnvSrc; struct EnvSrc { int source; /* Source of the variable. */ TCHAR* name; /* Name of the environment variable. */ EnvSrc *next; /* Next variable in the chain. */ }; extern EnvSrc *baseEnvSrc; typedef struct Property Property; struct Property { TCHAR *name; /* The name of the property. */ TCHAR *value; /* The value of the property. */ TCHAR *value_inner; /* The value of the property (with %WRAPPER_PERCENTAGE% kept unexpanded). */ TCHAR *filePath; /* The file in which the property was declared. NULL is not defined in a conf file. */ int lineNumber; /* The line number at which the property was declared. 0 if not defined in a conf file. */ int definitions; /* Counter for the number of time the property was defined. */ int finalValue; /* TRUE if the Property can not be changed. */ int quotable; /* TRUE if quotes can be optionally added around the value. */ int internal; /* TRUE if the Property is internal. */ int isGenerated; /* TRUE if the property did not exist in the configuration and was generated using a default value. */ int isVariable; /* TRUE if the property the definition of a variable (starting with 'set.'). */ int lastDefinitionDepth; /* Depth of the configuration file in which the property was previously defined, or -1 if not yet defined. */ Property *next; /* Pointer to the next Property in a linked list */ Property *previous; /* Pointer to the previous Property in a linked list */ }; typedef struct Properties Properties; struct Properties { int debugProperties; /* TRUE if debug information on Properties should be shown. */ int exitOnOverwrite; /* If TRUE, causes the wrapper to exit when any property is overwritten in the config files. */ int logLevelOnOverwrite; /* Defines the log level of the messages reported when properties are overwritten (the value may change each time a directive is encountered as the configuration is loading). */ int overwrittenPropertyCausedExit; /* Flag to keep trace whether at least one property was overridden */ int logWarnings; /* Flag that controls whether or not warnings will be logged. */ int logWarningLogLevel; /* Log level at which any log warnings will be logged. */ int dumpLogLevel; /* Log level at which properties will be dumped. */ TCHAR* dumpFormat; /* Format used when dumping properties. */ Property *first; /* Pointer to the first property. */ Property *last; /* Pointer to the last property. */ PHashMap warnedVarMap; /* Map of undefined environment variables for which the user was warned. */ PHashMap ignoreVarMap; /* Map of environment variables that should not be expanded. */ }; /** * Get the log level of the messages reported when properties are overwritten. * * @param properties * * @return log level */ extern int GetLogLevelOnOverwrite(Properties *properties); /** * Sets an environment variable with the specified value. * The function will only set the variable if its value is changed, but if * it does, the call will result in a memory leak the size of the string: * "name=value". * * @param name Name of the variable being set. * @param value Value to be set, NULL to clear it. * @param source Where the variable came from. * Must be one of ENV_SOURCE_PARENT, ENV_SOURCE_APPLICATION, ENV_SOURCE_CONFIG, * or ENV_SOURCE_REG_SYSTEM or ENV_SOURCE_REG_ACCOUNT on Windows. * If value is ENV_SOURCE_PARENT then the value may be NULL and will never be * set to the environment. * * Return TRUE if there were any problems, FALSE otherwise. */ extern int setEnv(const TCHAR *name, const TCHAR *value, int source); /** * Parses a property value and populates any environment variables. If the expanded * environment variable would result in a string that is longer than bufferLength * the value is truncated. * * @param propertyValue The property value to be parsed. * @param buffer output buffer where the expanded string will be copied. * @param bufferLength size of the buffer. * @param warnUndefinedVars Log warnings about missing environment variables. * @param warnedUndefVarMap Map of variables which have previously been logged, may be NULL if warnUndefinedVars false. * @param warnLogLevel Log level at which any warnings will be logged. * @param ignoreVarMap Map of environment variables that should not be expanded. * @param pHasPercentage Pointer to a variable which will be set to TRUE if a %WRAPPER_PERCENTAGE% variable was found. * - If a non-NULL pointer is passed, the variable will not be expanded and no warning will be reported. * - If NULL is passed, the variable will be expanded to '%'. */ extern void evaluateEnvironmentVariables(const TCHAR *propertyValue, TCHAR *buffer, int bufferLength, int warnUndefinedVars, PHashMap warnedUndefVarMap, int warnLogLevel, PHashMap ignoreVarMap, int *hasPercentage); /** * This function returns a reference to a static buffer and is NOT thread safe. * Check implementation notes before using. */ extern TCHAR* generateTimeValue(const TCHAR* format, struct tm *timeTM); /** * This function returns a reference to a static buffer and is NOT thread safe. * Check implementation notes before using. */ extern TCHAR* generateRandValue(const TCHAR* format); /** * Callback declaration of a function to filter lines read from the configuration file. */ typedef int (*ConfigFileReader_ReadFilterCallbackMB)(const char *bufferMB); /** * Callback declaration of a function to filter lines read from the configuration file. */ typedef int (*ConfigFileReader_ReadFilterCallbackW)(const TCHAR *bufferMB); /** * Create a Properties structure loaded in from the specified file. * Must call disposeProperties to free up allocated memory. * * @param properties Properties structure to load into. * @param filename File to load the properties from. * @param preload TRUE if this is a preload call that should have supressed error output. * @param originalWorkingDir Working directory of the binary at the moment it was launched. * @param fileRequired TRUE if the file specified by filename is required, FALSE if a missing * file will silently fail. * @param readFilterCallback Pointer to a callback funtion which will be used to filter some * lines that should not be processed. * * @return CONFIG_FILE_READER_SUCCESS if the file was read successfully, * CONFIG_FILE_READER_OPEN_FAIL if the file could not be found or opened. * CONFIG_FILE_READER_FAIL if there were any problems at all, or * CONFIG_FILE_READER_HARD_FAIL if the problem should cascaded all the way up. */ extern int loadProperties(Properties *properties, const TCHAR* filename, int preload, const TCHAR *originalWorkingDir, int fileRequired, ConfigFileReader_ReadFilterCallbackMB readFilterCallback); /** * Create a Properties structure. Must call disposeProperties to free up * allocated memory. */ extern Properties* createProperties(int debug, int logLevelOnOverwrite, int exitOnOverwrite); /** * Free all memory allocated by a Properties structure. The properties * pointer will no longer be valid. */ extern void disposeProperties(Properties *properties); /** * Free all memory allocated by a Properties structure. The properties * pointer will no longer be valid. */ extern void disposeEnvironment(); /** * Remove a single Property from a Properties. All associated memory is * freed up. * * @return TRUE if the property was found, FALSE otherwise. */ extern int removeProperty(Properties *properties, const TCHAR *propertyName); /** * Used to set a NULL terminated list of property names whose values should be * escaped when read in from a file. '\\' will become '\' and '\n' will * become '^J', all other characters following '\' will be left as is. * * @param propertyNames NULL terminated list of property names. Property names * can contain a single '*' wildcard which will match 0 or * more characters. */ extern void setEscapedProperties(const TCHAR **propertyNames); /** * Returns true if the specified property matches one of the property names * previously set in a call to setEscapedProperties() * * @param propertyName Property name to test. * * @return TRUE if the property should be escaped. FALSE otherwise. */ extern int isEscapedProperty(const TCHAR *propertyName); /** * Adds a single property to the properties structure. * * @param properties Properties structure to add to. * @param filename Name of the file from which the property was loaded. NULL, if not from a file. * @param lineNum Line number of the property declaration in the file. Ignored if filename is NULL. * @param depth Depth of the configuration file where the property was declared. Ignored if filename is NULL. * @param propertyName Name of the new Property. * @param propertyValue Initial property value. * @param finalValue TRUE if the property should be set as static. * @param quotable TRUE if the property could contain quotes. * @param escapable TRUE if the propertyValue can be escaped if its propertyName * is in the list set with setEscapedProperties(). * @param internal TRUE if the property is a Wrapper internal property. * * @return The newly created Property, or NULL if there was a reported error. */ extern Property* addProperty(Properties *properties, const TCHAR* filename, int lineNum, int depth, const TCHAR *propertyName, const TCHAR *propertyValue, int finalValue, int quotable, int escapable, int internal); /** * Takes a name/value pair in the form = and attempts to add * it to the specified properties table. * * @param properties Properties structure to add to. * @param filename Name of the file from which the property was loaded. NULL, if not from a file. * @param lineNum Line number of the property declaration in the file. Ignored if filename is NULL. * @param propertyNameValue The "name=value" pair to create the property from. * @param finalValue TRUE if the property should be set as static. * @param quotable TRUE if the property could contain quotes. * @param internal TRUE if the property is a Wrapper internal property. * * Returns 0 if successful, otherwise 1 */ extern int addPropertyPair(Properties *properties, const TCHAR* filename, int lineNum, const TCHAR *propertyNameValue, int finalValue, int quotable, int internal); extern void setInternalVarProperty(Properties *properties, const TCHAR *varName, const TCHAR *varValue, int finalValue, int ignore); extern const TCHAR* getStringProperty(Properties *properties, const TCHAR *propertyName, const TCHAR *defaultValue); extern const TCHAR* getNotEmptyStringProperty(Properties *properties, const TCHAR *propertyName, const TCHAR *defaultValue); extern const TCHAR* getFileSafeStringProperty(Properties *properties, const TCHAR *propertyName, const TCHAR *defaultValue); /** * Returns a sorted array of all properties beginning with {propertyNameBase}. * Only numerical characters can be returned between the two. * * The calling code must always call freeStringProperties to make sure that the * malloced propertyNames, propertyValues, and propertyIndices arrays are freed * up correctly. This is only necessary if the function returns 0. * * @param properties The full properties structure. * @param propertyNameHead All matching properties must begin with this value. * @param propertyNameTail All matching properties must end with this value. * @param all If FALSE then the array will start with #1 and loop up until the * next property is not found, if TRUE then all properties will be * returned, even if there are gaps in the series. * @param matchAny If FALSE only numbers are allowed as placeholder * @param propertyNames Returns a pointer to a NULL terminated array of * property names. * @param propertyValues Returns a pointer to a NULL terminated array of * property values. * @param propertyIndices Returns a pointer to a 0 terminated array of * the index numbers used in each property name of * the propertyNames array. * * @return 0 if successful, -1 if there was an error. */ extern int getStringProperties(Properties *properties, const TCHAR *propertyNameHead, const TCHAR *propertyNameTail, int all, int matchAny, TCHAR ***propertyNames, TCHAR ***propertyValues, long unsigned int **propertyIndices); /** * Frees up an array of properties previously returned by getStringProperties(). */ extern void freeStringProperties(TCHAR **propertyNames, TCHAR **propertyValues, long unsigned int *propertyIndices); extern int getIntProperty(Properties *properties, const TCHAR *propertyName, int defaultValue); extern int getBooleanProperty(Properties *properties, const TCHAR *propertyName, int defaultValue); extern int getBooleanProperties(Properties *properties, const TCHAR *propertyNameHead, const TCHAR *propertyNameTail, int all, int matchAny, TCHAR ***propertyNames, int **propertyValues, long unsigned int **propertyIndices, int defaultValue); extern void freeBooleanProperties(TCHAR **propertyNames, int *propertyValues, long unsigned int *propertyIndices); extern const TCHAR* getPropertyFilePath(Properties *properties, const TCHAR* propertyName); extern int getStatusProperty(Properties *properties, const TCHAR *propertyName, int defaultValue); extern int isGeneratedProperty(Properties *properties, const TCHAR *propertyName); extern int isQuotableProperty(Properties *properties, const TCHAR *propertyName); extern void dumpProperties(Properties *properties); /** * Level at which properties will be dumped. */ extern void setPropertiesDumpLogLevel(Properties *properties, int logLevel); /** * Format used when dumping properties. */ extern void setPropertiesDumpFormat(Properties *properties, const TCHAR* format); /** * Set to TRUE if warnings about property values should be logged. */ extern void setLogPropertyWarnings(Properties *properties, int logWarnings); /** * Level at which any property warnings are logged. */ extern void setLogPropertyWarningLogLevel(Properties *properties, int logLevel); /** * Returns the minimum value. This is used in place of the __min macro when the parameters should not be called more than once. */ extern int propIntMin(int value1, int value2); /** * Returns the maximum value. This is used in place of the __max macro when the parameters should not be called more than once. */ extern int propIntMax(int value1, int value2); /** Creates a linearized representation of all of the properties. * The returned buffer must be freed by the calling code. */ extern TCHAR *linearizeProperties(Properties *properties, TCHAR separator); #endif wrapper_3.5.51_src/src/c/resource.h100644 0 0 667 14333053650 14351 0ustar 0 0 //{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by Wrapper.rc // #define IDI_WRAPPER 102 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 106 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1002 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif wrapper_3.5.51_src/src/c/runnmake.bat100644 0 0 1044 14333053647 14675 0ustar 0 0 @echo off setlocal rem Copyright (c) 1999, 2022 Tanuki Software Inc. rem rem Java Service Wrapper windows build script. This script is designed to be rem called by the ant build.xml file. rem rem %1 Makefile name rem %2 Visual Studio environment script rem %3 script argument rem %4 script argument rem %5 script argument echo Configuring the Visual Studio environment... echo call %2 %3 %4 %5 call %2 %3 %4 %5 echo Using SDK in %WindowsSdkDir% echo. echo Run the make file... echo nmake /f %1 /c all nmake /f %1 /c all wrapper_3.5.51_src/src/c/test_example.c100644 0 0 3007 14333053650 15216 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ #include #include #include #include "CUnit/Basic.h" #include "property.h" /******************************************************************** * Example Tests *******************************************************************/ /** * The suite initialization function. * Opens the temporary file used by the tests. * Returns zero on success, non-zero otherwise. */ int tsEX_init_suite1(void) { return 0; } /** * The suite cleanup function. * Closes the temporary file used by the tests. * Returns zero on success, non-zero otherwise. */ int tsEX_clean_suite1(void) { return 0; } /** * Simple test that passes. */ void tsEX_testPass(void) { CU_ASSERT_EQUAL(0, 0); } /** * Simple test that passes. */ void tsEX_testFail(void) { CU_ASSERT_NOT_EQUAL(0, 1); } int tsEX_suiteExample() { CU_pSuite exampleSuite; exampleSuite = CU_add_suite("Example Suite", tsEX_init_suite1, tsEX_clean_suite1); if (NULL == exampleSuite) { return CU_get_error(); } CU_add_test(exampleSuite, "Pass", tsEX_testPass); CU_add_test(exampleSuite, "Fail", tsEX_testFail); return FALSE; } wrapper_3.5.51_src/src/c/test_filter.c100644 0 0 13063 14333053650 15073 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ #include #include #include #include "CUnit/Basic.h" #include "logger.h" #include "property.h" #include "wrapper.h" /******************************************************************** * Filter Tests *******************************************************************/ #define TSFLTR_WORK_BUFFER_LEN 4096 TCHAR tsFLTR_workBuffer[TSFLTR_WORK_BUFFER_LEN]; void tsFLTR_dummyLogFileChanged(const TCHAR *logFile) { } int tsFLTR_init_wrapper(void) { initLogging(tsFLTR_dummyLogFileChanged); logRegisterThread(WRAPPER_THREAD_MAIN); setLogfileLevelInt(LEVEL_NONE); setConsoleLogFormat(TEXT("LPM")); setConsoleLogLevelInt(LEVEL_DEBUG); setConsoleFlush(TRUE); setSyslogLevelInt(LEVEL_NONE); return 0; } int tsFLTR_clean_wrapper(void) { disposeLogging(); return 0; } void tsFLTR_subTestWrapperWildcardMatch(const TCHAR *pattern, const TCHAR *text, size_t expectedMinLen, int expectedMatch) { size_t minLen; int matched; minLen = wrapperGetMinimumTextLengthForPattern(pattern); if (minLen != expectedMinLen) { _sntprintf(tsFLTR_workBuffer, TSFLTR_WORK_BUFFER_LEN, TEXT("wrapperGetMinimumTextLengthForPattern(\"%s\") returned %d rather than expected %d."), pattern, minLen, expectedMinLen); _tprintf(TEXT("%s\n"), tsFLTR_workBuffer); CU_FAIL(tsFLTR_workBuffer); } else { _sntprintf(tsFLTR_workBuffer, TSFLTR_WORK_BUFFER_LEN, TEXT("wrapperGetMinimumTextLengthForPattern(\"%s\") returned %d."), pattern, minLen); CU_PASS(tsFLTR_workBuffer); } matched = wrapperWildcardMatch(text, pattern, expectedMinLen); if (matched != expectedMatch) { _sntprintf(tsFLTR_workBuffer, TSFLTR_WORK_BUFFER_LEN, TEXT("wrapperWildcardMatch(\"%s\", \"%s\", %d) returned %s rather than expected %s."), text, pattern, expectedMinLen, (matched ? TEXT("TRUE") : TEXT("FALSE")), (expectedMatch ? TEXT("TRUE") : TEXT("FALSE"))); _tprintf(TEXT("%s\n"), tsFLTR_workBuffer); CU_FAIL(tsFLTR_workBuffer); } else { _sntprintf(tsFLTR_workBuffer, TSFLTR_WORK_BUFFER_LEN, TEXT("wrapperWildcardMatch(\"%s\", \"%s\", %d) returned %s."), text, pattern, expectedMinLen, (matched ? TEXT("TRUE") : TEXT("FALSE"))); CU_PASS(tsFLTR_workBuffer); } } void tsFLTR_testWrapperWildcardMatch() { tsFLTR_subTestWrapperWildcardMatch(TEXT("a"), TEXT("a"), 1, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("a"), TEXT("b"), 1, FALSE); tsFLTR_subTestWrapperWildcardMatch(TEXT("a"), TEXT(""), 1, FALSE); tsFLTR_subTestWrapperWildcardMatch(TEXT("a"), TEXT("abc"), 1, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("b"), TEXT("abc"), 1, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("c"), TEXT("abc"), 1, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("d"), TEXT("abc"), 1, FALSE); tsFLTR_subTestWrapperWildcardMatch(TEXT("?"), TEXT("a"), 1, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("?"), TEXT(""), 1, FALSE); tsFLTR_subTestWrapperWildcardMatch(TEXT("*"), TEXT(""), 0, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("*"), TEXT("a"), 0, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("*"), TEXT("abc"), 0, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("*a"), TEXT("a"), 1, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("*a"), TEXT("abc"), 1, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("*b"), TEXT("abc"), 1, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("a*"), TEXT("a"), 1, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("a*"), TEXT("abc"), 1, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("b*"), TEXT("abc"), 1, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("*a*"), TEXT("a"), 1, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("*a*"), TEXT("abc"), 1, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("*b*"), TEXT("abc"), 1, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("HEAD*TAIL"), TEXT("This is the HEAD and this is the TAIL....."), 8, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("HEAD**TAIL"), TEXT("This is the HEAD and this is the TAIL....."), 8, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("*HEAD*TAIL*"), TEXT("This is the HEAD and this is the TAIL....."), 8, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("HEAD*TAIL"), TEXT("This is the HEAD and this is the TaIL....."), 8, FALSE); tsFLTR_subTestWrapperWildcardMatch(TEXT("HEAD**TAIL"), TEXT("This is the HEAD and this is the TaIL....."), 8, FALSE); tsFLTR_subTestWrapperWildcardMatch(TEXT("*HEAD*TAIL*"), TEXT("This is the HEAD and this is the TaIL....."), 8, FALSE); tsFLTR_subTestWrapperWildcardMatch(TEXT("HEAD*TA?L"), TEXT("This is the HEAD and this is the TAIL....."), 8, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("HEAD**TA?L"), TEXT("This is the HEAD and this is the TAIL....."), 8, TRUE); tsFLTR_subTestWrapperWildcardMatch(TEXT("*HEAD*TA?L*"), TEXT("This is the HEAD and this is the TAIL....."), 8, TRUE); } int tsFLTR_suiteFilter() { CU_pSuite filterSuite; filterSuite = CU_add_suite("Filter Suite", tsFLTR_init_wrapper, tsFLTR_clean_wrapper); if (NULL == filterSuite) { return CU_get_error(); } CU_add_test(filterSuite, "wrapperWildcardMatch", tsFLTR_testWrapperWildcardMatch); return FALSE; } wrapper_3.5.51_src/src/c/test_hashmap.c100644 0 0 17174 14333053650 15236 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ #include #include #include #include "CUnit/Basic.h" #include "logger.h" #include "property.h" #include "wrapper_hashmap.h" /******************************************************************** * Hash Map Tests *******************************************************************/ TCHAR *tsHASH_randomChars = TEXT("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-"); #define TSHASH_WORK_BUFFER_LEN 4096 TCHAR tsHASH_workBuffer[TSHASH_WORK_BUFFER_LEN]; int tsHASH_getRandom(int min, int max) { int num; int rNum; num = max + 1 - min; if (num <= 0) { return min; } /* Some platforms use very large RAND_MAX values that cause overflow problems in our math */ if (RAND_MAX > 0x10000) { rNum = (int)((rand() >> 8) * num / (RAND_MAX >> 8)); } else { rNum = (int)(rand() * num / RAND_MAX); } return min + rNum; } /** * Creates a string of random characters that is within the specified range of lengths. * It is the responsibility of the caller to free up the string. * * @param minLen Minimum Length of the string. * @param maxLen Maximum Length of the string. * * @return the requested string, or NULL if out of memory. */ TCHAR *tsHASH_buildRandomString(int minLen, int maxLen) { int num; int len; TCHAR *str; int i; num = _tcslen(tsHASH_randomChars); len = tsHASH_getRandom(minLen, maxLen); str = malloc(sizeof(TCHAR) * (len + 1)); if (!str) { return NULL; } for (i = 0; i < len; i++) { str[i] = tsHASH_randomChars[tsHASH_getRandom(0, num - 1)]; } str[len] = TEXT('\0'); return str; } /** * Creates a string of random characters that is within the specified range of lengths. * It is the responsibility of the caller to free up the string. * * @param minLen Minimum Length of the string. * @param maxLen Maximum Length of the string. * * @return the requested string, or NULL if out of memory. */ TCHAR *tsHASH_buildRandomStringWithTail(int minLen, int maxLen, int tail) { int num; size_t len; size_t strLen; TCHAR *str; size_t i; TCHAR tailStr[32]; _sntprintf(tailStr, 32, TEXT("-%d"), tail); num = _tcslen(tsHASH_randomChars); len = tsHASH_getRandom(minLen, maxLen); strLen = len + _tcslen(tailStr) + 1; str = malloc(sizeof(TCHAR) * strLen); if (!str) { return NULL; } for (i = 0; i < len; i++) { str[i] = tsHASH_randomChars[tsHASH_getRandom(0, num - 1)]; } str[len] = TEXT('\0'); _tcsncat(str, tailStr, strLen); return str; } /** * Frees up an array and its contents. Depends on the values being NULL if they are not allocated. * * @param array Array to be freed. */ void tsHASH_freeTCHARArray(TCHAR **array, int len) { int i; if (array) { for (i = 0; i < len; i++) { if (array[i]) { free(array[i]); } } free(array); } } void tsHASH_hashMapCommon(int buckets, int valueCount) { PHashMap hashMap; int i; TCHAR **keys = NULL; TCHAR **values = NULL; const TCHAR *value; hashMap = newHashMap(buckets); if (valueCount > 0) { keys = malloc(sizeof(TCHAR*) * valueCount); if (!keys) { CU_FAIL(TEXT("Out of memory HMC1")); freeHashMap(hashMap); return; } memset(keys, 0, sizeof(TCHAR*) * valueCount); values = malloc(sizeof(TCHAR*) * valueCount); if (!values) { CU_FAIL(TEXT("Out of memory HMC2")); tsHASH_freeTCHARArray(keys, valueCount); freeHashMap(hashMap); return; } memset(values, 0, sizeof(TCHAR*) * valueCount); /* Generate and add key-value pairs. */ for (i = 0; i < valueCount; i++) { keys[i] = tsHASH_buildRandomStringWithTail(1, 20, i); if (!keys[i]) { CU_FAIL(TEXT("Out of memory HMC3")); tsHASH_freeTCHARArray(keys, valueCount); tsHASH_freeTCHARArray(values, valueCount); freeHashMap(hashMap); return; } values[i] = tsHASH_buildRandomString(1, 255); if (!values[i]) { CU_FAIL(TEXT("Out of memory HMC3")); tsHASH_freeTCHARArray(keys, valueCount); tsHASH_freeTCHARArray(values, valueCount); freeHashMap(hashMap); return; } hashMapPutKWVW(hashMap, keys[i], values[i]); } #ifdef _DEBUG_HASHMAP dumpHashMapStats(hashMap); #endif /* Now check to make sure all of the values were set correctly. */ for (i = 0; i < valueCount; i++) { value = hashMapGetKWVW(hashMap, keys[i]); if (value) { if (_tcscmp(values[i], value) != 0) { _sntprintf(tsHASH_workBuffer, TSHASH_WORK_BUFFER_LEN, TEXT("hashMapGetKWVW(map, \"%s\") returned \"%s\" rather than expected \"%s\"."), keys[i], value, values[i]); _tprintf(TEXT("%s\n"), tsHASH_workBuffer); CU_FAIL(tsHASH_workBuffer); } else { _sntprintf(tsHASH_workBuffer, TSHASH_WORK_BUFFER_LEN, TEXT("hashMapGetKWVW(map, \"%s\") returned \"%s\" as expected."), keys[i], value); CU_PASS(tsHASH_workBuffer); } } else { _sntprintf(tsHASH_workBuffer, TSHASH_WORK_BUFFER_LEN, TEXT("hashMapGetKWVW(map, \"%s\") returned NULL rather than expected \"%s\"."), keys[i], values[i]); _tprintf(TEXT("%s\n"), tsHASH_workBuffer); CU_FAIL(tsHASH_workBuffer); } } /* Check for a value that will not be in the map. */ value = hashMapGetKWVW(hashMap, TEXT("$")); if (value) { _sntprintf(tsHASH_workBuffer, TSHASH_WORK_BUFFER_LEN, TEXT("hashMapGetKWVW(map, \"$\") returned \"%s\" rather than expected NULL."), value); _tprintf(TEXT("%s\n"), tsHASH_workBuffer); CU_FAIL(tsHASH_workBuffer); } else { _sntprintf(tsHASH_workBuffer, TSHASH_WORK_BUFFER_LEN, TEXT("hashMapGetKWVW(map, \"$\") returned NULL as expected.")); CU_PASS(tsHASH_workBuffer); } tsHASH_freeTCHARArray(keys, valueCount); tsHASH_freeTCHARArray(values, valueCount); } freeHashMap(hashMap); } /** * Make sure we can create and destroy an empty hash map. */ void tsHASH_testHashMapEmpty() { tsHASH_hashMapCommon(100, 0); } /** * Make sure we can create and destroy an sparsely filled hash map that has many empty buckets. */ void tsHASH_testHashMapSparse() { tsHASH_hashMapCommon(100, 10); } /** * Make sure we can create and destroy an sparsely filled hash map that has many empty buckets. */ void tsHASH_testHashMapLarge() { tsHASH_hashMapCommon(100, 10000); } int tsHASH_suiteHashMap() { CU_pSuite hashMapSuite; hashMapSuite = CU_add_suite("HashMap Suite", NULL, NULL); if (NULL == hashMapSuite) { return CU_get_error(); } CU_add_test(hashMapSuite, "empty HashMap", tsHASH_testHashMapEmpty); CU_add_test(hashMapSuite, "sparce HashMap", tsHASH_testHashMapSparse); CU_add_test(hashMapSuite, "large HashMap", tsHASH_testHashMapLarge); return FALSE; } wrapper_3.5.51_src/src/c/test_javaadditionalparam.c100644 0 0 3407 14333053650 17562 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ #include #include #include #include "CUnit/Basic.h" #include "logger.h" #include "property.h" #include "wrapper.h" /******************************************************************** * JavaAdditionalParam Utilities *******************************************************************/ /* TODO: Currently the source of tsJAP_testJavaAdditionalParamSuite is in wrapper.c Should we move it in here? */ void tsJAP_dummyLogFileChanged(const TCHAR *logFile) { } int tsJAP_init_properties(void) { initLogging(tsJAP_dummyLogFileChanged); logRegisterThread(WRAPPER_THREAD_MAIN); setLogfileLevelInt(LEVEL_NONE); setConsoleLogFormat(TEXT("LPM")); setConsoleLogLevelInt(LEVEL_DEBUG); setConsoleFlush(TRUE); setSyslogLevelInt(LEVEL_NONE); properties = createProperties(FALSE, LEVEL_NONE, FALSE); return properties ? 0 : 1; } int tsJAP_clean_properties(void) { disposeLogging(); disposeProperties(properties); return 0; } int tsJAP_suiteJavaAdditionalParam() { CU_pSuite javaAdditionalParamSuite; javaAdditionalParamSuite = CU_add_suite("Java Additional Parameter Suite", tsJAP_init_properties, tsJAP_clean_properties); if (NULL == javaAdditionalParamSuite) { return CU_get_error(); } CU_add_test(javaAdditionalParamSuite, "loadJavaAdditionalCallback()", tsJAP_testJavaAdditionalParamSuite); return FALSE; } wrapper_3.5.51_src/src/c/testsuite.c100644 0 0 10460 14333053650 14576 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /* NOTES: * * See for ASSERTS: * http://cunit.sourceforge.net/doc/writing_tests.html#tests */ #include #include #include #include "CUnit/Automated.h" #include "CUnit/Basic.h" #include #include "testsuite.h" #include "logger.h" /******************************************************************** * Main *******************************************************************/ static void showHelp(TCHAR *app) { _tprintf(TEXT("Wrapper testsuite help.\n")); _tprintf(TEXT("%s \n"), app); _tprintf(TEXT("\n")); _tprintf(TEXT("Commands:\n")); _tprintf(TEXT(" --basic : Runs all tests in basic mode. Only summaries visible.\n")); _tprintf(TEXT(" --auto : Runs all tests in automatic mode. Output visible but results output to file.\n")); _tprintf(TEXT(" See CUnitAutomated-Results.xml for results.\n")); _tprintf(TEXT(" --console : Interactive mode.\n")); _tprintf(TEXT(" --help : This help.\n")); _tprintf(TEXT("\n")); } /* The main() function for setting up and running the tests. * Returns a CUE_SUCCESS on successful running, another * CUnit error code on failure. */ int main(int argc, char **cargv) { TCHAR **argv; int i; size_t req; int errorCode; argv = malloc(argc * sizeof * argv); if (!argv) { _tprintf(TEXT("Out of Memory in Main\n")); return 1; } for (i = 0; i < argc; i++) { req = mbstowcs(NULL, cargv[i], MBSTOWCS_QUERY_LENGTH); if (req == (size_t)-1) { _tprintf(TEXT("Encoding problem with arguments in Main\n")); while (--i > 0) { free(argv[i]); } free(argv); return 1; } argv[i] = malloc(sizeof(TCHAR) * (req + 1)); if (!argv[i]) { _tprintf(TEXT("Out of Memory in Main\n")); while (--i > 0) { free(argv[i]); } free(argv); return 1; } mbstowcs(argv[i], cargv[i], req + 1); argv[i][req] = TEXT('\0'); /* Avoid bufferflows caused by badly encoded characters. */ } /* Initialize the random seed. */ srand((unsigned)time(NULL)); /* initialize the CUnit test registry */ if (CUE_SUCCESS != CU_initialize_registry()) { errorCode = CU_get_error(); goto error; } /* add a suite to the registry */ if (tsEX_suiteExample()) { CU_cleanup_registry(); errorCode = CU_get_error(); goto error; } if (tsFLTR_suiteFilter()) { CU_cleanup_registry(); errorCode = CU_get_error(); goto error; } if (tsJAP_suiteJavaAdditionalParam()) { CU_cleanup_registry(); errorCode = CU_get_error(); goto error; } if (tsHASH_suiteHashMap()) { CU_cleanup_registry(); errorCode = CU_get_error(); goto error; } if (argc < 2) { showHelp(argv[0]); errorCode = 1; } else if (strcmpIgnoreCase(argv[1], TEXT("--basic")) == 0) { /* Run all tests using the CUnit Basic interface */ CU_set_output_filename("testsuite"); CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); CU_cleanup_registry(); errorCode = CU_get_error(); } else if (strcmpIgnoreCase(argv[1], TEXT("--auto")) == 0) { /* Run all tests using the CUnit Automated interface */ CU_list_tests_to_file(); CU_automated_run_tests(); CU_cleanup_registry(); errorCode = CU_get_error(); } else if (strcmpIgnoreCase(argv[1], TEXT("--console")) == 0) { /* Run all tests using the CUnit Console interface */ CU_console_run_tests(); CU_cleanup_registry(); errorCode = CU_get_error(); } else { showHelp(argv[0]); errorCode = 1; } error: for (i = 0; i < argc; i++) { free(argv[i]); } free(argv); return errorCode; } wrapper_3.5.51_src/src/c/testsuite.h100644 0 0 1160 14333053650 14560 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ #ifndef _TESTSUITE_H #define _TESTSUITE_H #include "CUnit/Basic.h" #include "wrapper_i18n.h" extern int tsEX_suiteExample(); extern int tsFLTR_suiteFilter(); extern int tsJAP_suiteJavaAdditionalParam(); extern int tsHASH_suiteHashMap(); #endif wrapper_3.5.51_src/src/c/wrapper.c100644 0 0 1642110 14333053650 14271 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ /** * Author: * Leif Mortenson * Ryan Shaw */ #ifdef WIN32 /* need the 2 following includes to use IPv6 and need wsock32.lib in the makefile */ #include #include #include #endif #include #include #include #include #include #include #include #ifdef CUNIT #include "CUnit/Basic.h" #endif #include "wrapper_i18n.h" #include "wrapperinfo.h" #include "wrapper.h" #include "logger.h" #include "logger_file.h" #include "wrapper_jvminfo.h" #include "wrapper_encoding.h" #include "wrapper_file.h" #ifndef WIN32 #include "wrapper_ulimit.h" #endif #ifdef WIN32 #include #include #include #include #include #include /* MS Visual Studio 8 went and deprecated the POXIX names for functions. * Fixing them all would be a big headache for UNIX versions. */ #pragma warning(disable : 4996) /* Defines for MS Visual Studio 6 */ #ifndef _INTPTR_T_DEFINED typedef long intptr_t; #define _INTPTR_T_DEFINED #endif #else /* UNIX */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define SOCKET int #define HANDLE int #define INVALID_HANDLE_VALUE -1 #define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #if defined(SOLARIS) #include #include #elif defined(AIX) || defined(HPUX) || defined(MACOSX) #elif defined(FREEBSD) #include #include #else /* LINUX */ #include #endif #endif /* WIN32 */ /* Define some common defines to make cross platform code a bit cleaner. */ #ifdef WIN32 #define WRAPPER_EADDRINUSE WSAEADDRINUSE #define WRAPPER_EWOULDBLOCK WSAEWOULDBLOCK #define WRAPPER_EACCES WSAEACCES #else #define WRAPPER_EADDRINUSE EADDRINUSE #define WRAPPER_EWOULDBLOCK EWOULDBLOCK #define WRAPPER_EACCES EACCES #endif WrapperConfig *wrapperData; char packetBufferMB[MAX_LOG_SIZE + 1]; TCHAR packetBufferW[MAX_LOG_SIZE + 1]; TCHAR *keyChars = TEXT("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-"); /* Properties structure loaded in from the configuration file. */ Properties *properties = NULL; /* Mutex for synchronization of the tick timer. */ #ifdef WIN32 HANDLE tickMutexHandle = NULL; #else pthread_mutex_t tickMutex = PTHREAD_MUTEX_INITIALIZER; #endif /* Server Pipe Handles. */ HANDLE protocolActiveServerPipeIn = INVALID_HANDLE_VALUE; HANDLE protocolActiveServerPipeOut = INVALID_HANDLE_VALUE; /* Flag for indicating the connected pipes */ int protocolActiveServerPipeConnected = FALSE; /* Server Socket. */ SOCKET protocolActiveServerSD = INVALID_SOCKET; /* Client Socket. */ SOCKET protocolActiveBackendSD = INVALID_SOCKET; #ifndef IN6ADDR_LOOPBACK_INIT /* even if I include ws2ipdef.h, it doesn't define IN6ADDR_LOOPBACK_INIT, so that's why I define it here */ #define IN6ADDR_LOOPBACK_INIT { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } #endif #define LOOPBACK_IPv4 "127.0.0.1" #ifdef HPUX_IA /* on HPUX ia, gcc reports a warning "missing braces around initializer" when using IN6ADDR_LOOPBACK_INIT (/usr/include/netinet/in6.h:241). So I add braces for the struct and braces for the union. */ struct in6_addr LOOPBACK_IPv6 = {{IN6ADDR_LOOPBACK_INIT}}; #else struct in6_addr LOOPBACK_IPv6 = IN6ADDR_LOOPBACK_INIT; #endif int disposed = FALSE; int handleSignals = TRUE; int loadConfiguration(); #define READ_BUFFER_BLOCK_SIZE 1024 static char *wrapperChildWorkBuffer = NULL; static size_t wrapperChildWorkBufferSize = 0; static size_t wrapperChildWorkBufferLen = 0; static time_t wrapperChildWorkLastDataTime = 0; static int wrapperChildWorkLastDataTimeMillis = 0; static int wrapperChildWorkIsNewLine = TRUE; /** * Constructs a tm structure from a pair of Strings like "20091116" and "1514". * The time returned will be in the local time zone. This is not 100% accurate * as it doesn't take into account the time zone in which the dates were * originally set. */ struct tm getInfoTime(const TCHAR *date, const TCHAR *time) { struct tm buildTM; TCHAR temp[5]; memset(&buildTM, 0, sizeof(struct tm)); /* Year */ _tcsncpy( temp, date, 4 ); temp[4] = 0; buildTM.tm_year = _ttoi( temp ) - 1900; /* Month */ _tcsncpy( temp, date + 4, 2 ); temp[2] = 0; buildTM.tm_mon = _ttoi( temp ) - 1; /* Day */ _tcsncpy( temp, date + 6, 2 ); temp[2] = 0; buildTM.tm_mday = _ttoi( temp ); /* Hour */ _tcsncpy( temp, time, 2 ); temp[2] = 0; buildTM.tm_hour = _ttoi( temp ); /* Minute */ _tcsncpy( temp, time + 2, 2 ); temp[2] = 0; buildTM.tm_min = _ttoi( temp ); return buildTM; } struct tm wrapperGetReleaseTime() { return getInfoTime(wrapperReleaseDate, wrapperReleaseTime); } struct tm wrapperGetBuildTime() { return getInfoTime(wrapperBuildDate, wrapperBuildTime); } /** * Adds default properties used to set global environment variables. * * These are done by setting properties rather than call setEnv directly * so that it will be impossible for users to override their values by * creating a "set.XXX=NNN" property in the configuration file. */ void wrapperAddDefaultProperties(Properties *props) { TCHAR buffer[11]; /* should be large enough to contain the pid and lang (increase the buffer size if more variables are needed) */ TCHAR* confDirTemp; #ifdef WIN32 int work, pos2; TCHAR pathSep = TEXT('\\'); #else TCHAR pathSep = TEXT('/'); #endif int pos; #ifdef WIN32 const TCHAR* fileSeparator = TEXT("\\"); const TCHAR* pathSeparator = TEXT(";"); #else const TCHAR* fileSeparator = TEXT("/"); const TCHAR* pathSeparator = TEXT(":"); #endif if (wrapperData->confDir == NULL) { if (_tcsrchr(wrapperData->argConfFile, pathSep) != NULL) { pos = (int)(_tcsrchr(wrapperData->argConfFile, pathSep) - wrapperData->argConfFile); } else { pos = -1; } #ifdef WIN32 if (_tcsrchr(wrapperData->argConfFile, TEXT('/')) != NULL) { pos2 = (int)(_tcsrchr(wrapperData->argConfFile, TEXT('/')) - wrapperData->argConfFile); } else { pos2 = -1; } pos = __max(pos, pos2); #endif if (pos == -1) { confDirTemp = malloc(sizeof(TCHAR) * 2); if (!confDirTemp) { outOfMemory(TEXT("WADP"), 1); return; } _tcsncpy(confDirTemp, TEXT("."), 2); } else if (pos == 0) { confDirTemp = malloc(sizeof(TCHAR) * 2); if (!confDirTemp) { outOfMemory(TEXT("WADP"), 2); return; } _sntprintf(confDirTemp, 2, TEXT("%c"), pathSep); } else { confDirTemp = malloc(sizeof(TCHAR) * (pos + 1)); if (!confDirTemp) { outOfMemory(TEXT("WADP"), 3); return; } _tcsncpy(confDirTemp, wrapperData->argConfFile, pos); confDirTemp[pos] = TEXT('\0'); } #ifdef WIN32 /* Get buffer size, including '\0' */ work = GetFullPathName(confDirTemp, 0, NULL, NULL); if (!work) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to resolve the conf directory: %s"), getLastErrorText()); free(confDirTemp); return; } wrapperData->confDir = malloc(sizeof(TCHAR) * work); if (!wrapperData->confDir) { outOfMemory(TEXT("WADP"), 4); free(confDirTemp); return; } if (!GetFullPathName(confDirTemp, work, wrapperData->confDir, NULL)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to resolve the conf directory: %s"), getLastErrorText()); free(confDirTemp); return; } #else /* The solaris implementation of realpath will return a relative path if a relative * path is provided. We always need an absolute path here. So build up one and * then use realpath to remove any .. or other relative references. */ wrapperData->confDir = malloc(sizeof(TCHAR) * (PATH_MAX + 1)); if (!wrapperData->confDir) { outOfMemory(TEXT("WADP"), 5); free(confDirTemp); return; } if (_trealpathN(confDirTemp, wrapperData->confDir, PATH_MAX + 1) == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to resolve the original working directory: %s"), getLastErrorText()); free(confDirTemp); return; } #endif setEnv(TEXT("WRAPPER_CONF_DIR"), wrapperData->confDir, ENV_SOURCE_APPLICATION); free(confDirTemp); } _sntprintf(buffer, 3, TEXT("en")); setInternalVarProperty(props, TEXT("WRAPPER_LANG"), buffer, TRUE, FALSE); _sntprintf(buffer, 11, TEXT("%d"), wrapperData->wrapperPID); setInternalVarProperty(props, TEXT("WRAPPER_PID"), buffer, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_BASE_NAME"), wrapperData->baseName, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_BITS"), wrapperBits, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_ARCH"), wrapperArch, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_OS"), wrapperOS, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_VERSION"), wrapperVersionRoot, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_EDITION"), TEXT("Community"), TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_HOSTNAME"), wrapperData->hostName, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_HOST_NAME"), wrapperData->hostName, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_RUN_MODE"), wrapperData->isConsole ? TEXT("console") : TEXT("service"), TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_FILE_SEPARATOR"), fileSeparator, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_PATH_SEPARATOR"), pathSeparator, TRUE, FALSE); /* These variables don't need be set as an environment variables, but we should still register them to prevent users from defining them on their own. */ setInternalVarProperty(props, TEXT("WRAPPER_PERCENTAGE"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_RAND_N"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_RAND_NN"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_RAND_NNN"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_RAND_NNNN"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_RAND_NNNNN"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_RAND_NNNNNN"), NULL, TRUE, FALSE); /* NOTE: currently possible for the user to set WRAPPER_SYSTEM_

. In order to block it we would need to use another method because there is no fixed name * that we can add to the property list. One idea is to create a separated list of wildcarded properties and a method to loop over them checking that there * is no match before adding a user-defined property to the real list of properties. */ setInternalVarProperty(props, TEXT("WRAPPER_TIME_YYYYMMDDHHIISS"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_TIME_YYYYMMDD_HHIISS"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_TIME_YYYYMMDDHHII"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_TIME_YYYYMMDDHH"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_TIME_YYYYMMDD"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_JAVA_VERSION"), NULL, FALSE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_JAVA_VERSION_MAJOR"), NULL, FALSE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_JAVA_VERSION_MINOR"), NULL, FALSE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_JAVA_VERSION_REVISION"), NULL, FALSE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_JAVA_VENDOR"), NULL, FALSE, FALSE); #ifdef WIN32 /* Do not change the value of this variable as this would cause a memory leak on each JVM restart (see setEnvInner()). */ if (wrapperData->registry_java_home) { setInternalVarProperty(props, TEXT("WRAPPER_JAVA_HOME"), wrapperData->registry_java_home, FALSE, TRUE); } else { setInternalVarProperty(props, TEXT("WRAPPER_JAVA_HOME"), NULL, FALSE, FALSE); } #endif /* The following variables are never set as environment variables (no memory leak should happen). */ setInternalVarProperty(props, TEXT("WRAPPER_NAME"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_DISPLAYNAME"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_DESCRIPTION"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_EVENT_JVM_PID"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_EVENT_JVM_ID"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_EVENT_NAME"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_EVENT_RAND_N"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_EVENT_RAND_NN"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_EVENT_RAND_NNN"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_EVENT_RAND_NNNN"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_EVENT_RAND_NNNNN"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_EVENT_RAND_NNNNNN"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_EVENT_TIME_YYYYMMDDHHIISS"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_EVENT_TIME_YYYYMMDD_HHIISS"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_EVENT_TIME_YYYYMMDDHHII"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_EVENT_TIME_YYYYMMDDHH"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_EVENT_TIME_YYYYMMDD"), NULL, TRUE, FALSE); setInternalVarProperty(props, TEXT("WRAPPER_EVENT_WRAPPER_PID"), NULL, TRUE, FALSE); } /** * This function is here to help Community Edition users who are attempting * to generate a hostId. */ int showHostIds(int logLevel, int notUsed) { log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("")); log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("The Community Edition of the Java Service Wrapper does not implement\nHostIds.")); log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("")); log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("If you have requested a trial license, or purchased a license, you\nmay be looking for the Standard or Professional Editions of the Java\nService Wrapper. They can be downloaded here:")); log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT(" http://wrapper.tanukisoftware.com/download")); log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("")); return FALSE; } /** * Attempt to set the console title if it exists and is accessible. */ void wrapperSetConsoleTitle() { #ifdef WIN32 if (wrapperData->consoleTitle) { if (wrapperProcessHasVisibleConsole()) { /* The console should be visible. */ if (!SetConsoleTitle(wrapperData->consoleTitle)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Attempt to set the console title failed: %s"), getLastErrorText()); } } } #elif LINUX /* This works on all UNIX versions, but only Linux resets it * correctly when the wrapper process terminates. */ if (wrapperData->consoleTitle) { if (wrapperData->isConsole) { /* The console should be visible. */ _tprintf(TEXT("%c]0;%s%c"), TEXT('\033'), wrapperData->consoleTitle, TEXT('\007')); } } #endif } /** * Loads the current environment into a table so we can debug it later. * * @return TRUE if there were problems, FALSE if successful. */ int loadEnvironment() { size_t len; TCHAR *sourcePair; TCHAR *pair; TCHAR *equal; TCHAR *name; TCHAR *value; #ifdef WIN32 LPTCH lpvEnv; LPTSTR lpszVariable; #else /* The compiler won't let us reference environ directly in the for loop on OSX because it is actually a function. */ char **environment = environ; int i; #endif #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Loading Environment...")); #endif #ifdef WIN32 lpvEnv = GetEnvironmentStrings(); if (!lpvEnv) { _tprintf(TEXT("GetEnvironmentStrings failed (%s)\n"), getLastErrorText()); return TRUE; } lpszVariable = (LPTSTR)lpvEnv; while (lpszVariable[0] != '\0') { sourcePair = lpszVariable; #else i = 0; while (environment[i]) { len = mbstowcs(NULL, environment[i], MBSTOWCS_QUERY_LENGTH); if (len == (size_t)-1) { /* Invalid string. Skip. */ } else { sourcePair = malloc(sizeof(TCHAR) * (len + 1)); if (!sourcePair) { outOfMemory(TEXT("LE"), 1); _tprintf(TEXT(" Invalid character string: %s (%s)\n"), environment[i], getLastErrorText()); return TRUE; } mbstowcs(sourcePair, environment[i], len + 1); sourcePair[len] = TEXT('\0'); /* Avoid bufferflows caused by badly encoded characters. */ #endif len = _tcslen(sourcePair); /* We need a copy of the variable pair so we can split it. */ pair = malloc(sizeof(TCHAR) * (len + 1)); if (!pair) { outOfMemory(TEXT("LE"), 1); #ifdef WIN32 FreeEnvironmentStrings(lpvEnv); #else free(sourcePair); #endif return TRUE; } _sntprintf(pair, len + 1, TEXT("%s"), sourcePair); equal = _tcschr(pair, TEXT('=')); if (equal) { name = pair; value = &(equal[1]); equal[0] = TEXT('\0'); if (_tcslen(name) <= 0) { name = NULL; } if (_tcslen(value) <= 0) { value = NULL; } /* It is possible that the name was empty. */ if (name) { setEnv(name, value, ENV_SOURCE_PARENT); } } free(pair); #ifdef WIN32 lpszVariable += len + 1; #else free(sourcePair); } i++; #endif } #ifdef WIN32 FreeEnvironmentStrings(lpvEnv); #endif #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Loading Environment complete.")); #endif return FALSE; } /** * Updates a string value by making a copy of the original. Any old value is * first freed. */ void updateStringValue(TCHAR **ptr, const TCHAR *value) { if (*ptr != NULL) { free(*ptr); *ptr = NULL; } if (value != NULL) { *ptr = malloc(sizeof(TCHAR) * (_tcslen(value) + 1)); if (!(*ptr)) { outOfMemory(TEXT("USV"), 1); /* TODO: This is pretty bad. Not sure how to recover... */ } else { _tcsncpy(*ptr, value, _tcslen(value) + 1); } } } #ifndef WIN32 /* UNIX */ int getSignalMode(const TCHAR *modeName, int defaultMode) { if (!modeName) { return defaultMode; } if (strcmpIgnoreCase(modeName, TEXT("IGNORE")) == 0) { return WRAPPER_SIGNAL_MODE_IGNORE; } else if (strcmpIgnoreCase(modeName, TEXT("RESTART")) == 0) { return WRAPPER_SIGNAL_MODE_RESTART; } else if (strcmpIgnoreCase(modeName, TEXT("SHUTDOWN")) == 0) { return WRAPPER_SIGNAL_MODE_SHUTDOWN; } else if (strcmpIgnoreCase(modeName, TEXT("FORWARD")) == 0) { return WRAPPER_SIGNAL_MODE_FORWARD; } else if (strcmpIgnoreCase(modeName, TEXT("PAUSE")) == 0) { return WRAPPER_SIGNAL_MODE_PAUSE; } else if (strcmpIgnoreCase(modeName, TEXT("RESUME")) == 0) { return WRAPPER_SIGNAL_MODE_RESUME; } else if (strcmpIgnoreCase(modeName, TEXT("CLOSE_LOGFILE")) == 0) { return WRAPPER_SIGNAL_MODE_CLOSE_LOGFILE; } else { return defaultMode; } } void wrapperBuildUnixDaemonInfo() { if (!wrapperData->configured) { /** Configure the HUP signal handler. */ wrapperData->signalHUPMode = getSignalMode(getStringProperty(properties, TEXT("wrapper.signal.mode.hup"), NULL), WRAPPER_SIGNAL_MODE_FORWARD); /** Configure the USR1 signal handler. */ wrapperData->signalUSR1Mode = getSignalMode(getStringProperty(properties, TEXT("wrapper.signal.mode.usr1"), NULL), WRAPPER_SIGNAL_MODE_FORWARD); /** Configure the USR2 signal handler. */ wrapperData->signalUSR2Mode = getSignalMode(getStringProperty(properties, TEXT("wrapper.signal.mode.usr2"), NULL), WRAPPER_SIGNAL_MODE_FORWARD); } } #endif void setEnvironmentLogLevel(int logLevel) { wrapperData->environmentLogLevel = logLevel; } /** * Dumps the table of environment variables, and their sources. */ void dumpEnvironment() { EnvSrc *envSrc; TCHAR *envVal; int logLevel = wrapperData->environmentLogLevel; const TCHAR* ignore; log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("")); log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("Environment variables (Source | Name=Value) BEGIN:")); envSrc = baseEnvSrc; while (envSrc) { /* Do not display the variables that are not expanded. */ if (properties->ignoreVarMap) { /* Can return NULL if missing or "TRUE" or "FALSE". */ ignore = hashMapGetKWVW(properties->ignoreVarMap, envSrc->name); } else { ignore = NULL; } if (!ignore || strcmpIgnoreCase(ignore, TEXT("TRUE")) != 0) { envVal = _tgetenv(envSrc->name); log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT(" %c%c%c%c%c | %s=%s"), (envSrc->source & ENV_SOURCE_PARENT ? TEXT('P') : TEXT('-')), #ifdef WIN32 (envSrc->source & ENV_SOURCE_REG_SYSTEM ? TEXT('S') : TEXT('-')), (envSrc->source & ENV_SOURCE_REG_ACCOUNT ? TEXT('A') : TEXT('-')), #else TEXT('-'), TEXT('-'), #endif (envSrc->source & ENV_SOURCE_APPLICATION ? TEXT('W') : TEXT('-')), (envSrc->source & ENV_SOURCE_CONFIG ? TEXT('C') : TEXT('-')), envSrc->name, (envVal ? envVal : TEXT("")) ); #if !defined(WIN32) && defined(UNICODE) if (envVal) { free(envVal); } #endif } envSrc = envSrc->next; } log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("Environment variables END:")); log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("")); } #ifdef WIN32 /** * Check if the Wrapper is running under cygwin terminal. * I'm looking for the environment variable TERM to be equal to "xterm". * I tried with OSTYPE and MACHTYPE, but _tgetenv always returns NULL. * @return TRUE if under cygwin, otherwise returns FALSE */ int isCygwin() { TCHAR *osType; int retVal = FALSE; osType = _tgetenv(TEXT("TERM")); if ((osType != NULL) && (_tcscmp(osType, TEXT("xterm")) == 0)) { retVal = TRUE; } return retVal; } #endif /** * Return TRUE if the this is a prompt call made from the script (like --translate or --jvm_bits or --request_delta_binary_bits or --request_log_file or --request_default_log_file). * * @param argCommand the first arguement passed when launching the Wrapper */ int isPromptCallCommand(const TCHAR* argCommand) { if (!argCommand) { return FALSE; } return ((strcmpIgnoreCase(argCommand, TEXT("-translate")) == 0) || (strcmpIgnoreCase(argCommand, TEXT("-jvm_bits")) == 0) || #ifndef WIN32 (strcmpIgnoreCase(argCommand, TEXT("-request_log_file")) == 0) || (strcmpIgnoreCase(argCommand, TEXT("-request_default_log_file")) == 0) || #endif (strcmpIgnoreCase(argCommand, TEXT("-request_delta_binary_bits")) == 0)); } /** * Return TRUE if the this is a prompt call made from the script (like --translate or --jvm_bits or --request_delta_binary_bits or --request_log_file or --request_default_log_file). * This function must be called after the arguments have been parsed! */ int isPromptCall() { return isPromptCallCommand(wrapperData->argCommand); } int wrapperLoadLoggingProperties(int preload) { const TCHAR *logfilePath; int logfileRollMode; int defaultFlushTimeOut = 1; int loglevelTargetsSet = FALSE; #ifdef WIN32 int consoleDirect; int silent; #endif int isPurgePatternGenerated = FALSE; const TCHAR* confPurgePattern; setLoggingIsPreload(preload); setLogPropertyWarnings(properties, !preload); setLogPropertyWarningLogLevel(properties, getLogLevelForName(getStringProperty(properties, TEXT("wrapper.property_warning.loglevel"), TEXT("WARN")))); setPropertiesDumpLogLevel(properties, getLogLevelForName(getStringProperty(properties, TEXT("wrapper.properties.dump.loglevel"), TEXT("DEBUG")))); setPropertiesDumpFormat(properties, getStringProperty(properties, TEXT("wrapper.properties.dump.format"), PROPERTIES_DUMP_FORMAT_DEFAULT)); setEnvironmentLogLevel(getLogLevelForName(getStringProperty(properties, TEXT("wrapper.environment.dump.loglevel"), getBooleanProperty(properties, TEXT("wrapper.environment.dump"), FALSE) ? TEXT("INFO") : TEXT("DEBUG")))); setLogWarningThreshold(getIntProperty(properties, TEXT("wrapper.log.warning.threshold"), 0)); wrapperData->logLFDelayThreshold = propIntMax(propIntMin(getIntProperty(properties, TEXT("wrapper.log.lf_delay.threshold"), 500), 3600000), 0); if (resolveDefaultLogFilePath()) { /* The error has already been logged. This is not fatal, we will continue with the relative path. */ } logfilePath = getFileSafeStringProperty(properties, TEXT("wrapper.logfile"), TEXT("wrapper.log")); if (setLogfilePath(logfilePath, TRUE, preload)) { return TRUE; } logfileRollMode = getLogfileRollModeForName(getStringProperty(properties, TEXT("wrapper.logfile.rollmode"), TEXT("SIZE"))); if (logfileRollMode == ROLL_MODE_UNKNOWN) { if (!preload) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("wrapper.logfile.rollmode invalid. Disabling log file rolling.")); } logfileRollMode = ROLL_MODE_NONE; } else if (logfileRollMode == ROLL_MODE_DATE) { if (!_tcsstr(logfilePath, ROLL_MODE_DATE_TOKEN)) { if (!preload) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("wrapper.logfile must contain \"%s\" for a roll mode of DATE. Disabling log file rolling."), ROLL_MODE_DATE_TOKEN); } logfileRollMode = ROLL_MODE_NONE; } } setLogfileRollMode(logfileRollMode); /* Load log file format */ setLogfileFormat(getStringProperty(properties, TEXT("wrapper.logfile.format"), LOG_FORMAT_LOGFILE_DEFAULT)); /* Load log file log level (stay in silent mode on a translate call) */ setLogfileLevel(getStringProperty(properties, TEXT("wrapper.logfile.loglevel"), TEXT("INFO"))); /* Load max log filesize log level */ setLogfileMaxFileSize(getStringProperty(properties, TEXT("wrapper.logfile.maxsize"), TEXT("0"))); /* Load log files level */ setLogfileMaxLogFiles(getIntProperty(properties, TEXT("wrapper.logfile.maxfiles"), 0)); /* Load log file purge sort */ setLogfilePurgeSortMode(loggerFileGetSortMode(getStringProperty(properties, TEXT("wrapper.logfile.purge.sort"), TEXT("NAMES_SMART")))); /* Load log file purge pattern */ confPurgePattern = getFileSafeStringProperty(properties, TEXT("wrapper.logfile.purge.pattern"), TEXT("")); setLogfilePurgePattern(confPurgePattern, &isPurgePatternGenerated); if (preload) { if (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("c")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-console")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("s")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-service"))) { /* See if the logs should be rolled on Wrapper startup (console and service only). * This is done once during preload so that there is no output before the file is being rolled. */ if ((getLogfileRollMode() & ROLL_MODE_WRAPPER) || (getLogfileRollMode() & ROLL_MODE_JVM)) { rollLogs(NULL); } } } /* Get the close timeout. */ wrapperData->logfileCloseTimeout = propIntMax(propIntMin(getIntProperty(properties, TEXT("wrapper.logfile.close.timeout"), getIntProperty(properties, TEXT("wrapper.logfile.inactivity.timeout"), 1)), 3600), -1); setLogfileAutoClose(wrapperData->logfileCloseTimeout == 0); /* Get the flush timeout. */ /* We should stay autoflush until the main loop is reached. Keep the value for later. */ wrapperData->logfileFlushTimeout = propIntMax(propIntMin(getIntProperty(properties, TEXT("wrapper.logfile.flush.timeout"), defaultFlushTimeOut), 3600), 0); /* Load console format */ setConsoleLogFormat(getStringProperty(properties, TEXT("wrapper.console.format"), LOG_FORMAT_CONSOLE_DEFAULT)); /* Load console log level (stay in silent mode on a translate call) */ setConsoleLogLevel(getStringProperty(properties, TEXT("wrapper.console.loglevel"), TEXT("INFO"))); /* Load the console direct flag & console flush flag. */ #ifdef WIN32 consoleDirect = getBooleanProperty(properties, TEXT("wrapper.console.direct"), TRUE); if (isSecondary()) { setConsoleDirect(FALSE); setConsoleFlush(TRUE); if (consoleDirect) { /* When piping stdout and stderr from the child to the parent process, * the order of the messages between the 2 streams can't be guaranted. * - If wrapper.console.direct was set to TRUE, which is the default, * force using only stdout to avoid this issue. * - If wrapper.console.direct was set to FALSE, it's most likely that * the user wants to catch each stream separately, so the order * should not matter. */ setConsoleFatalToStdErr(FALSE); setConsoleErrorToStdErr(FALSE); setConsoleWarnToStdErr(FALSE); loglevelTargetsSet = TRUE; } } else { setConsoleDirect(consoleDirect); setConsoleFlush(getBooleanProperty(properties, TEXT("wrapper.console.flush"), isCygwin())); } #else setConsoleFlush(getBooleanProperty(properties, TEXT("wrapper.console.flush"), FALSE)); #endif /* Load the console loglevel targets. */ if (!loglevelTargetsSet) { setConsoleFatalToStdErr(getBooleanProperty(properties, TEXT("wrapper.console.fatal_to_stderr"), TRUE)); setConsoleErrorToStdErr(getBooleanProperty(properties, TEXT("wrapper.console.error_to_stderr"), TRUE)); setConsoleWarnToStdErr(getBooleanProperty(properties, TEXT("wrapper.console.warn_to_stderr"), FALSE)); } /* Get the debug status (Property is deprecated but flag is still used) */ wrapperData->isDebugging = getBooleanProperty(properties, TEXT("wrapper.debug"), FALSE); if (wrapperData->isDebugging) { /* For backwards compatability */ setConsoleLogLevelInt(LEVEL_DEBUG); setLogfileLevelInt(LEVEL_DEBUG); } else { if (getLowLogLevel() <= LEVEL_DEBUG) { wrapperData->isDebugging = TRUE; } } /* Get the adviser status */ wrapperData->isAdviserEnabled = getBooleanProperty(properties, TEXT("wrapper.adviser"), TRUE); /* The adviser is always enabled if debug is enabled. */ if (wrapperData->isDebugging) { wrapperData->isAdviserEnabled = TRUE; } /* Load syslog log level (stay in silent mode on a translate call) */ setSyslogLevel(getStringProperty(properties, TEXT("wrapper.syslog.loglevel"), TEXT("NONE"))); /* Load syslog split messages flag. */ setSyslogSplitMessages(getBooleanProperty(properties, TEXT("wrapper.syslog.split_messages"), FALSE)); #ifndef WIN32 /* Load syslog facility */ setSyslogFacility(getStringProperty(properties, TEXT("wrapper.syslog.facility"), TEXT("USER"))); #endif /* Load syslog event source name */ setSyslogEventSourceName(getStringProperty(properties, TEXT("wrapper.syslog.ident"), getStringProperty(properties, TEXT("wrapper.name"), getStringProperty(properties, TEXT("wrapper.ntservice.name"), TEXT("wrapper"))))); #ifdef WIN32 setSyslogRegister(getBooleanProperty(properties, TEXT("wrapper.syslog.ident.enable"), TRUE)); #endif #ifdef WIN32 /* Register or unregister an event source depending on the value of wrapper.syslog.ident.enable. * The syslog will be disabled if the application is not registered after calling the functions * to register or unregister, so this has to be done on preload. This has to be done again each * time the configuration is reloaded because wrapper.syslog.ident.enable may possibly change. */ /* Make sure we are not running in setup, teardown or install mode. * - Setup is automatically executed when installing a service - no need to do it here. * - TearDown is not executed when removing the service as this would remove the source * of existing messages in the event log. */ if (strcmpIgnoreCase(wrapperData->argCommand, TEXT("su")) && strcmpIgnoreCase(wrapperData->argCommand, TEXT("-setup")) && strcmpIgnoreCase(wrapperData->argCommand, TEXT("td")) && strcmpIgnoreCase(wrapperData->argCommand, TEXT("-teardown")) && strcmpIgnoreCase(wrapperData->argCommand, TEXT("i")) && strcmpIgnoreCase(wrapperData->argCommand, TEXT("-install")) && strcmpIgnoreCase(wrapperData->argCommand, TEXT("it")) && strcmpIgnoreCase(wrapperData->argCommand, TEXT("-installstart"))) { /* The functions below need to be called even if we don't have the permission to edit the registry. * They will eventually disable event logging if the application is not registered. * On preload use the silent mode to avoid double log outputs. */ silent = preload || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("r")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-remove")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("p")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-stop")); if (getSyslogRegister()) { /* Register the syslog message */ registerSyslogMessageFile(FALSE, silent); } else { /* Unregister the syslog message */ unregisterSyslogMessageFile(silent); } } #endif if (!preload && (confPurgePattern[0] != 0) && isPurgePatternGenerated) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Ignoring the value of wrapper.logfile.purge.pattern because\n wrapper.logfile.purge.sort was set to 'NAMES_SMART'.")); } return FALSE; } /** * Retrieve the configured exit code that should be returned when the Wrapper ends with an error. * This function should be called after the configuration has been loaded (after the logging * has been loaded if silent is FALSE). * * @param silent TRUE if log output should be disabled. */ void getConfiguredErrorExitCode(int silent) { wrapperData->errorExitCode = getIntProperty(properties, TEXT("wrapper.exit_code.error"), 1); if (wrapperData->errorExitCode < 1 || wrapperData->errorExitCode > 255) { wrapperData->errorExitCode = 1; if (!silent) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("%s must be in the range %d to %d. Changing to %d."), TEXT("wrapper.exit_code.error"), 1, 255, 1); } } } /** * This function provides a log file after proloading the properties. * It will load all configurations related to the logging (loglevel, format, etc.). * For standard editions, it helps us to resolve the language, if specified in the conf file. * * @param logLevelOnOverwriteProperties : use this parameter to keep the value of the last #properties.on_overwrite.loglevel found during the preload phase * @param exitOnOverwriteProperties : use this parameter to keep the value of the last #properties.on_overwrite.exit found during the preload phase * * @return TRUE if there was a FATAL error which will not be reported again during the second load. FALSE otherwise. * It is prefered to return FALSE and log the localized error on the second load if we know it will reappear. */ int wrapperPreLoadConfigurationProperties(int *logLevelOnOverwriteProperties, int *exitOnOverwriteProperties) { int returnVal = FALSE; #ifdef HPUX const TCHAR* fix_iconv_hpux; #endif /* The properties being loaded here will not be re-loaded, so enable warnings from here. */ setLogPropertyWarnings(properties, TRUE); #ifdef HPUX fix_iconv_hpux = getStringProperty(properties, TEXT("wrapper.fix_iconv_hpux"), NULL); if (fix_iconv_hpux && (strcmpIgnoreCase(fix_iconv_hpux, TEXT("ALWAYS")) == 0)) { /* If Iconv should be fixed, enable it as soon as possible to get the correct conversion when reloading the configuration. */ toggleIconvHpuxFix(TRUE); } #endif #ifdef WIN32 /* For the community edition, always try to display the system errors in English. */ setLogSysLangId((SUBLANG_ENGLISH_US << 10) + LANG_ENGLISH); /* Always read JVM output using the default Windows ANSI code page. */ wrapperData->jvm_stdout_codepage = GetACP(); #endif /* Load log file */ wrapperLoadLoggingProperties(TRUE); wrapperAddDefaultProperties(properties); /* The logging is ready. Print any queued messages generated while loading the localization. * If not done here and a FATAL error occurs on the second load, the queued messages will appear at last and make it confusing. */ maintainLogger(); /* If the working dir has been changed then we need to restore it before * the configuration can be reloaded. This is needed to support relative * references to include files. */ if (wrapperData->workingDir && wrapperData->originalWorkingDir) { if (wrapperSetWorkingDir(wrapperData->originalWorkingDir)) { /* Failed to restore the working dir. The configuration can't be reloaded correctly. Shutdown the Wrapper. */ returnVal = TRUE; } } if (!returnVal) { *logLevelOnOverwriteProperties = properties->logLevelOnOverwrite; *exitOnOverwriteProperties = properties->exitOnOverwrite; } if (properties) { disposeProperties(properties); properties = NULL; } return returnVal; } /** * Retrieve the original working directory and store it in wrapperData->originalWorkingDir. * * Return TRUE if there were any problems. */ int getOriginalWorkingDir() { #ifdef WIN32 int work; #endif if (wrapperData->originalWorkingDir) { free(wrapperData->originalWorkingDir); } #ifdef WIN32 /* Get buffer size, including '\0' */ work = GetFullPathName(TEXT("."), 0, NULL, NULL); if (!work) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to resolve the original working directory: %s"), getLastErrorText()); return TRUE; } wrapperData->originalWorkingDir = malloc(sizeof(TCHAR) * work); if (!wrapperData->originalWorkingDir) { outOfMemory(TEXT("WLCP"), 3); return TRUE; } if (!GetFullPathName(TEXT("."), work, wrapperData->originalWorkingDir, NULL)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to resolve the original working directory: %s"), getLastErrorText()); return TRUE; } #else /* The solaris implementation of realpath will return a relative path if a relative * path is provided. We always need an absolute path here. So build up one and * then use realpath to remove any .. or other relative references. */ wrapperData->originalWorkingDir = malloc(sizeof(TCHAR) * (PATH_MAX + 1)); if (!wrapperData->originalWorkingDir) { outOfMemory(TEXT("WLCP"), 4); return TRUE; } if (_trealpathN(TEXT("."), wrapperData->originalWorkingDir, PATH_MAX + 1) == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to resolve the original working directory: %s"), getLastErrorText()); return TRUE; } #endif return FALSE; } #ifndef WIN32 /** * Try getting getting the Group Name for a Group Id. * * @param groupId Id of the group. * * @return The group name of NULL if the group doesn't exist for the given Id. */ TCHAR* getGroupNameFromId(gid_t groupId) { struct group *gr; size_t size; TCHAR* groupName = NULL; if (groupId != -1) { gr = getgrgid(groupId); if (gr) { size = mbstowcs(NULL, gr->gr_name, MBSTOWCS_QUERY_LENGTH); if (size > (size_t)0) { groupName = malloc(sizeof(TCHAR) * (size + 1)); if (!groupName) { outOfMemory(TEXT("GGNFI"), 1); } else { mbstowcs(groupName, gr->gr_name, size + 1); } } } } return groupName; } /** * Prints in a buffer a list of group names given their Ids. * * @param groups list of group Ids. * @param ngroups size of the groups list. * @param buffer in which to print the group names. * @param bufferSize size of the buffer. * * @return the buffer. */ const TCHAR* getGroupListString(gid_t *groups, int ngroups, TCHAR* buffer, int bufferSize) { int len = 0; int totLen = 0; int i; TCHAR* pBuffer = buffer; TCHAR* groupName; buffer[0] = 0; if (groups) { for (i = 0; i < ngroups; i++) { groupName = getGroupNameFromId(groups[i]); if (groupName) { if (totLen + _tcslen(groupName) + 10 > bufferSize) { /* Should not happen! (+10: ' (ddddd), ') */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Skip group '%s'. Too long."), groupName); } else { _sntprintf(pBuffer, bufferSize - totLen, TEXT("%s (%d)"), groupName, groups[i]); len = _tcslen(pBuffer); totLen += len; pBuffer += len; if (i < ngroups - 1) { _tcsncpy(pBuffer, TEXT(", "), bufferSize - totLen); len = 2; totLen += len; pBuffer += len; } } free(groupName); } } buffer[bufferSize - 1] = 0; } return buffer; } /** * Try getting getting the Group Id for a Group Name. * * @param groupName Name of the group (Wide char). * @param pGroupId Pointer to the Group Id to collect. * * @return TRUE if there were any problems, FALSE if successful. */ int getGroupIdFromName(const TCHAR* groupName, gid_t *pGroupId) { struct group *gr; char *groupNameMB = NULL; size_t size; int result = TRUE; if (groupName && groupName[0] != 0) { size = wcstombs(NULL, groupName, 0); if (size > (size_t)0) { groupNameMB = malloc(size + 1); if (!groupNameMB) { outOfMemory(TEXT("GGIFN"), 1); } else { wcstombs(groupNameMB, groupName, size + 1); gr = getgrnam(groupNameMB); if (gr != NULL) { *pGroupId = gr->gr_gid; result = FALSE; } free(groupNameMB); } } } return result; } /** * Check if the Wrapper process has the right to assign the given group, * and retrieve a list of group Ids of the Wrapper process. * * @param groupId Id of the group to check. * @param pGroups Pointer to a list that will be malloced and filled with the group Ids of the Wrapper process * (in case the Wrapper is not allowed to assign the group). * @param pNgroups Pointer to an integer which be set to the number of group in group list (only set if the list is not NULL). * * @return TRUE if the Wrapper is not allowed to assign the group or if there is any error, FALSE if all is Ok. */ int checkGroupCurrentProcess(gid_t groupId, gid_t **pGroups, int *pNgroups) { int size; int i, j, k; gid_t egid; /* effective group Id */ int egidInGroups = FALSE; /* will be set to TRUE if the effective group Id is found in *pGroups. */ *pGroups = NULL; if (geteuid() == 0) { /* Root user: valid */ return FALSE; } egid = getegid(); if (egid == groupId) { /* The group Id is the effective group Id of the Wrapper process: valid */ return FALSE; } size = getgroups(0, NULL); if (size == -1) { /* Unable to check. Consider invalid! */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to retrieve the supplementary group IDs of the current process - %s."), getLastErrorText()); return TRUE; } *pGroups = (gid_t *)malloc((size + 1) * sizeof(gid_t)); if (*pGroups == NULL) { /* Unable to check. Consider invalid! */ outOfMemory(TEXT("CGCP"), 1); return TRUE; } if (size > 0) { size = getgroups(size, *pGroups); if (size == -1) { /* Unable to check. Consider invalid! */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to retrieve the supplementary group IDs of the current process - %s."), getLastErrorText()); free(*pGroups); *pGroups = NULL; return TRUE; } for (i = 0; i < size; i++) { if ((*pGroups)[i] == groupId) { /* The group Id is among the secondary groups of the Wrapper process: valid */ free(*pGroups); *pGroups = NULL; return FALSE; } } /* The doc says it is unspecified whether the effective group ID is included in list returned by getgroups(). * We need to be careful about the following cases: * 1) the effective group may be added as a duplicate of another supplementary group Id in the list. * 2) the effective group may be skipped even though it is not listed (we need to add it). */ for (i = 0; i < size; i++) { if ((*pGroups)[i] == egid) { /* Set a flag to not add the effective group to the list. */ egidInGroups = TRUE; /* Check if there is a duplicate later in the list. */ for (j = i + 1; j < size; j++) { if ((*pGroups)[j] == egid) { /* Duplicate found, remove it. */ for (k = j; k < size - 1; k++) { (*pGroups)[k] = (*pGroups)[k + 1]; } size--; /* There should be only one duplicate. */ break; } } break; } } } *pNgroups = size; if (!egidInGroups) { /* Add the effective group Id of the Wrapper process. */ (*pGroups)[size] = egid; (*pNgroups) += 1; } return TRUE; } #define GROUPS_BUFFER_LENGTH 1024 /** * Load a Group Id (used to change the group of a file). * * @param pGroupId Pointer to the Group Id to set. * @param propertyName Name of the property from which to load the group. * @param defaultValue Default Group Id to use. * @param defaultPropertyName Used for logging. The name of the default property, * or NULL if this is the default property. * @param preload TRUE if this is a preload call that should have supressed error output. * * @return TRUE if there were any problems, FALSE if successful. */ int loadGroupProperty(gid_t *pGroupId, const TCHAR* propertyName, gid_t defaultValue, const TCHAR* defaultPropertyName, int preload) { const TCHAR* propertyValue; TCHAR* groupName; gid_t groupId; TCHAR *endptr; int failed = FALSE; gid_t *wrapperGroups = NULL; int ngroups = 0; TCHAR buffer[GROUPS_BUFFER_LENGTH]; propertyValue = getStringProperty(properties, propertyName, NULL); if (!propertyValue || (propertyValue[0] == 0) || (strcmpIgnoreCase(propertyValue, TEXT("-UNCHANGED-")) == 0)) { /* Default value. */ *pGroupId = defaultValue; } else { errno = 0; groupId = (gid_t)_tcstoul(propertyValue, &endptr, 10); if ((errno == 0) && (propertyValue != endptr) && (!*endptr)) { /* 1) Numerical value: a group ID? */ /* no error && some digits were read && no additional characters remain */ if (getgrgid(groupId)) { /* This user Id exists */ *pGroupId = groupId; } else { if (!preload) { log_printf(WRAPPER_SOURCE_WRAPPER, (wrapperData->groupStrict ? LEVEL_FATAL : properties->logWarningLogLevel), TEXT("Encountered an invalid group Id for configuration property %s=%s."), propertyName, propertyValue); } *pGroupId = defaultValue; failed = TRUE; } } else { /* 2) String: a group name? */ if (getGroupIdFromName(propertyValue, pGroupId)) { /* Failed to retrieve the group Id. */ if (!preload) { log_printf(WRAPPER_SOURCE_WRAPPER, (wrapperData->groupStrict ? LEVEL_FATAL : properties->logWarningLogLevel), TEXT("Encountered an invalid group name for configuration property %s=%s."), propertyName, propertyValue); } *pGroupId = defaultValue; failed = TRUE; } } if (*pGroupId != defaultValue) { /* Check that the Wrapper has the right to change the group. */ if (checkGroupCurrentProcess(*pGroupId, &wrapperGroups, &ngroups)) { if (!preload) { log_printf(WRAPPER_SOURCE_WRAPPER, (wrapperData->groupStrict ? LEVEL_FATAL : properties->logWarningLogLevel), TEXT("The Wrapper process is not allowed to assign the group specified by configuration property %s=%s."), propertyName, propertyValue); if (wrapperData->isAdviserEnabled && wrapperGroups) { log_printf(WRAPPER_SOURCE_WRAPPER, (wrapperData->groupStrict ? LEVEL_FATAL : properties->logWarningLogLevel), TEXT("The Wrapper should either run as root or as a user which belongs to the group you want to assign.\n The Wrapper process is a member of the following groups:\n %s"), getGroupListString(wrapperGroups, ngroups, buffer, GROUPS_BUFFER_LENGTH)); } } *pGroupId = defaultValue; failed = TRUE; } free(wrapperGroups); } if (!preload && failed && !wrapperData->groupStrict) { if (defaultPropertyName) { groupName = getGroupNameFromId(*pGroupId); log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT(" Resolving to the value of %s (%s)."), defaultPropertyName, groupName ? groupName : TEXT("-UNCHANGED-")); free(groupName); } else { log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT(" Resolving to '-UNCHANGED-'.")); } } } return failed; } #endif static int isServicePasswordNeeded() { #ifdef WIN32 return (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("i")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-install")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("it")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-installstart")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-customize"))); #else return FALSE; #endif } /** * This callback is called by configFileReader_Read just after reading a line of the configuration file. * It makes it possible to skip some lines that should be ignored and not stored in memory. * * @param bufferMB the MB buffer containing the line currently read. * * Return TRUE if the line should be read, FALSE otherwise. */ int confReadFilterCallbackMB(const char *bufferMB) { const char* propName = "wrapper.ntservice.password"; char *ptr; /* If the service is being installed or the Wrapper being customized, the password property should be used. Otherwise ignore it. * In such modes, the Wrapper executes shortly and the memory will be erased when the property will be disposed. */ if (!isServicePasswordNeeded()) { ptr = strstr(bufferMB, propName); if (ptr) { ptr += strlen(propName); if ((*ptr == '=') || (*ptr == ' ') || (*ptr == '\t')) { /* We have exactly "wrapper.ntservice.password" (not "wrapper.ntservice.password.prompt" or any other property) */ #ifdef _DEBUG /* NOTE: the address of bufferMB is copied in memory when passed as a parameter to confReadFilterCallback(), * but the buffer itself is not copied (each character has the same address). */ printf("confReadFilterCallbackMB - address of bufferMB[0] (%c): %p\n", bufferMB[0], &(bufferMB[0])); printf("confReadFilterCallbackMB - address of bufferMB[1] (%c): %p\n", bufferMB[1], &(bufferMB[1])); #endif return FALSE; } } } return TRUE; } /** * Load the configuration. * * @param preload TRUE if the configuration is being preloaded. * * Return TRUE if there were any problems. */ int wrapperLoadConfigurationProperties(int preload) { static int logLevelOnOverwriteProperties = LEVEL_NONE; static int exitOnOverwriteProperties = FALSE; static int preloadFailed = FALSE; int i; int firstCall; #ifdef WIN32 int work; int defaultUMask; #else mode_t defaultUMask; gid_t logFileGroup; int loadGroupResult = FALSE; #endif const TCHAR* prop; int loadResult; if (preloadFailed) { /* The preload has failed with a FATAL error that will not be reported again on the second load. Exit. */ return TRUE; } /* Unless this is the first call, we need to dispose the previous properties object. */ if (properties) { firstCall = FALSE; disposeProperties(properties); properties = NULL; } else { firstCall = TRUE; /* This is the first time, so preserve the working directory. */ if (getOriginalWorkingDir()) { if (preload) { /* no need to try again as we would fail exactly the same on the second load. */ preloadFailed = TRUE; } return TRUE; } if (wrapperData->configFile) { free(wrapperData->configFile); } /* This is the first time, so preserve the full canonical location of the * configuration file. */ if (_tcscmp(wrapperData->argConfFile, TEXT("-")) == 0) { wrapperData->configFile = NULL; } else { #ifdef WIN32 work = GetFullPathName(wrapperData->argConfFile, 0, NULL, NULL); if (!work) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to resolve the full path of the configuration file, %s: %s"), wrapperData->argConfFile, getLastErrorText()); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Current working directory is: %s"), wrapperData->originalWorkingDir); if (preload) { /* no need to try again as we would fail exactly the same on the second load. */ preloadFailed = TRUE; } return TRUE; } wrapperData->configFile = malloc(sizeof(TCHAR) * work); if (!wrapperData->configFile) { outOfMemory(TEXT("WLCP"), 1); if (preload) { /* no need to try again as we would fail exactly the same on the second load. */ preloadFailed = TRUE; } return TRUE; } if (!GetFullPathName(wrapperData->argConfFile, work, wrapperData->configFile, NULL)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to resolve the full path of the configuration file, %s: %s"), wrapperData->argConfFile, getLastErrorText()); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT( "Current working directory is: %s"), wrapperData->originalWorkingDir); if (preload) { /* no need to try again as we would fail exactly the same on the second load. */ preloadFailed = TRUE; } return TRUE; } #else /* The solaris implementation of realpath will return a relative path if a relative * path is provided. We always need an absolute path here. So build up one and * then use realpath to remove any .. or other relative references. */ wrapperData->configFile = malloc(sizeof(TCHAR) * (PATH_MAX + 1)); if (!wrapperData->configFile) { outOfMemory(TEXT("WLCP"), 2); if (preload) { /* no need to try again as we would fail exactly the same on the second load. */ preloadFailed = TRUE; } return TRUE; } if (_trealpathN(wrapperData->argConfFile, wrapperData->configFile, PATH_MAX + 1) == NULL) { /* Most likely the file does not exist. The wrapperData->configFile has the first * file that could not be found. May not be the config file directly if symbolic * links are involved. */ if (wrapperData->argConfFileDefault) { /* The output buffer is likely to contain undefined data. * To be on the safe side and in order to report the error * below correctly we need to override the data first.*/ _sntprintf(wrapperData->configFile, PATH_MAX + 1, TEXT("%s"), wrapperData->argConfFile); /* This was the default config file name. We know that the working directory * could be resolved so the problem must be that the default config file does * not exist. This problem will be reported later and the wrapperData->configFile * variable will have the correct full path. * Fall through for now and the user will get a better error later. */ } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT( "Unable to open configuration file: %s (%s)\n Current working directory: %s"), wrapperData->argConfFile, getLastErrorText(), wrapperData->originalWorkingDir); if (preload) { preloadFailed = TRUE; } return TRUE; } } #endif } } /* Create a Properties structure. */ properties = createProperties(!preload && firstCall, logLevelOnOverwriteProperties, exitOnOverwriteProperties); logLevelOnOverwriteProperties = LEVEL_NONE; exitOnOverwriteProperties = FALSE; if (!properties) { if (preload) { /* OOM reported */ preloadFailed = TRUE; } return TRUE; } setLogPropertyWarnings(properties, !preload); wrapperAddDefaultProperties(properties); /* The argument prior to the argBase will be the configuration file, followed * by 0 or more command line properties. The command line properties need to be * loaded first, followed by the configuration file. */ if (!isPromptCall()) { for (i = 0; i < wrapperData->argCount; i++) { if (addPropertyPair(properties, NULL, 0, wrapperData->argValues[i], TRUE, TRUE, FALSE)) { if (!preload) { /* Only report this error on the second load, then shutdown. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("The argument '%s' is not a valid property name-value pair."), wrapperData->argValues[i]); return TRUE; } } } } if (!wrapperData->configFile) { /* This happens when "-" was specified (skip the configuration file). */ if ((!preload) && !(wrapperData->confFileOptional)) { /* On preload, even if we don't have a conf file, we still want to load the embedded configuration * and command line properties as they may affect the logging or creation of the log file. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Configuration file is required.")); return TRUE; } } else { /* Now load the configuration file. * When this happens, the working directory MUST be set to the original working dir. */ /* Only show log errors when this is not the default configuration file, otherwise the usage will be shown. */ #ifdef WIN32 loadResult = loadProperties(properties, wrapperData->configFile, preload, wrapperData->originalWorkingDir, !preload && !(wrapperData->argConfFileDefault), confReadFilterCallbackMB); #else loadResult = loadProperties(properties, wrapperData->configFile, (preload | wrapperData->daemonize), wrapperData->originalWorkingDir, !preload && !(wrapperData->argConfFileDefault), confReadFilterCallbackMB); #endif if (loadResult != CONFIG_FILE_READER_SUCCESS) { if (wrapperData->confFileOptional && wrapperData->argConfFileDefault && (loadResult == CONFIG_FILE_READER_OPEN_FAIL)) { /* The wrapper was launched without a config file in the arguments, but it is not required. This is normal not to find it. */ } else { /* If this was a default file name then we don't want to show this as * an error here. It will be handled by the caller. */ /* Debug is not yet available as the config file is not yet loaded. */ if (!preload) { if (!wrapperData->argConfFileDefault) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Failed to load configuration.")); } return TRUE; } } } else { /* Config file found. */ wrapperData->argConfFileFound = TRUE; } } /* The properties have just been loaded. */ if (preload == TRUE) { /* Get the error exit code just after the properties have been loaded in case we need to stop before the second load. * We will get it again in loadConfiguration() to allow the property to be reloaded. */ getConfiguredErrorExitCode(TRUE); } else if (properties->overwrittenPropertyCausedExit) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Found duplicated properties.")); return TRUE; /* will cause the wrapper to exit with error code 1 */ } if (firstCall) { /* If the working dir was configured, we need to extract it and preserve its value. * This must be done after the configuration has been completely loaded. * Skip this part when the configuration is reloaded for UNIX daemons (firstCall * == FALSE) as there is no reason wrapper.working.dir would have changed. */ /* If some environment variable could not be expanded we will fail on preload, * so we want to have proper warning before stopping. */ setLogPropertyWarnings(properties, preload); prop = getStringProperty(properties, TEXT("wrapper.working.dir"), TEXT(".")); /* Restore property warnings. */ setLogPropertyWarnings(properties, !preload); if (prop && (_tcslen(prop) > 0)) { if (wrapperData->workingDir) { free(wrapperData->workingDir); } /* Log any error during preload and stop as anyway the path to the log file and language packs will most likely be wrong (unless they are absolute). */ #ifdef WIN32 work = GetFullPathName(prop, 0, NULL, NULL); if (!work) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to resolve the working directory %s: %s"), prop, getLastErrorText()); if (preload) { preloadFailed = TRUE; } return TRUE; } wrapperData->workingDir = malloc(sizeof(TCHAR) * work); if (!wrapperData->workingDir) { outOfMemory(TEXT("WLCP"), 5); if (preload) { preloadFailed = TRUE; } return TRUE; } if (!GetFullPathName(prop, work, wrapperData->workingDir, NULL)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to resolve the working directory %s: %s"), prop, getLastErrorText()); if (preload) { preloadFailed = TRUE; } return TRUE; } #else /* The solaris implementation of realpath will return a relative path if a relative * path is provided. We always need an absolute path here. So build up one and * then use realpath to remove any .. or other relative references. */ wrapperData->workingDir = malloc(sizeof(TCHAR) * (PATH_MAX + 1)); if (!wrapperData->workingDir) { outOfMemory(TEXT("WLCP"), 6); if (preload) { preloadFailed = TRUE; } return TRUE; } if (_trealpathN(prop, wrapperData->workingDir, PATH_MAX + 1) == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to resolve the working directory %s: %s"), prop, getLastErrorText()); if (preload) { preloadFailed = TRUE; } return TRUE; } #endif } } /* Now that the configuration is loaded, we need to update the working directory if the user specified one. * This must be done now so that anything that references the working directory, including the log file * and language pack locations will work correctly. */ if (wrapperData->workingDir && wrapperSetWorkingDir(wrapperData->workingDir)) { if (preload) { preloadFailed = TRUE; } return TRUE; } if (wrapperData->umask == -1) { /** Get the umask value for the various files. */ #ifdef WIN32 defaultUMask = _umask(0); _umask(defaultUMask); #else defaultUMask = umask((mode_t)0); umask(defaultUMask); #endif wrapperData->umask = getIntProperty(properties, TEXT("wrapper.umask"), defaultUMask); } wrapperData->javaUmask = getIntProperty(properties, TEXT("wrapper.java.umask"), wrapperData->umask); wrapperData->pidFileUmask = getIntProperty(properties, TEXT("wrapper.pidfile.umask"), wrapperData->umask); wrapperData->lockFileUmask = getIntProperty(properties, TEXT("wrapper.lockfile.umask"), wrapperData->umask); wrapperData->javaPidFileUmask = getIntProperty(properties, TEXT("wrapper.java.pidfile.umask"), wrapperData->umask); wrapperData->javaIdFileUmask = getIntProperty(properties, TEXT("wrapper.java.idfile.umask"), wrapperData->umask); wrapperData->statusFileUmask = getIntProperty(properties, TEXT("wrapper.statusfile.umask"), wrapperData->umask); wrapperData->javaStatusFileUmask = getIntProperty(properties, TEXT("wrapper.java.statusfile.umask"), wrapperData->umask); wrapperData->anchorFileUmask = getIntProperty(properties, TEXT("wrapper.anchorfile.umask"), wrapperData->umask); setLogfileUmask(getIntProperty(properties, TEXT("wrapper.logfile.umask"), wrapperData->umask)); #ifndef WIN32 /** Get the group value for the various files. */ wrapperData->groupStrict = getBooleanProperty(properties, TEXT("wrapper.group.strict"), FALSE); /* On a strict mode, make sure to load everything before stopping to get all error messages. */ loadGroupResult |= loadGroupProperty(&wrapperData->group, TEXT("wrapper.group"), -1, NULL, preload); /* -1 means the group is unchanged */ /* loadGroupResult |= loadGroupProperty(&wrapperData->javaGroup, TEXT("wrapper.java.group"), wrapperData->group, TEXT("wrapper.group"), preload); */ /* TODO: can this exist? */ loadGroupResult |= loadGroupProperty(&wrapperData->pidFileGroup, TEXT("wrapper.pidfile.group"), wrapperData->group, TEXT("wrapper.group"), preload); loadGroupResult |= loadGroupProperty(&wrapperData->lockFileGroup, TEXT("wrapper.lockfile.group"), wrapperData->group, TEXT("wrapper.group"), preload); loadGroupResult |= loadGroupProperty(&wrapperData->javaPidFileGroup, TEXT("wrapper.java.pidfile.group"), wrapperData->group, TEXT("wrapper.group"), preload); loadGroupResult |= loadGroupProperty(&wrapperData->javaIdFileGroup, TEXT("wrapper.java.idfile.group"), wrapperData->group, TEXT("wrapper.group"), preload); loadGroupResult |= loadGroupProperty(&wrapperData->statusFileGroup, TEXT("wrapper.statusfile.group"), wrapperData->group, TEXT("wrapper.group"), preload); loadGroupResult |= loadGroupProperty(&wrapperData->javaStatusFileGroup, TEXT("wrapper.java.statusfile.group"), wrapperData->group, TEXT("wrapper.group"), preload); loadGroupResult |= loadGroupProperty(&wrapperData->anchorFileGroup, TEXT("wrapper.anchorfile.group"), wrapperData->group, TEXT("wrapper.group"), preload); loadGroupResult |= loadGroupProperty(&logFileGroup, TEXT("wrapper.logfile.group"), wrapperData->group, TEXT("wrapper.group"), preload); setLogfileGroup(logFileGroup); if (!preload && wrapperData->groupStrict && loadGroupResult) { return TRUE; } #endif /* We are only preloading */ if (preload == TRUE) { /* this affects basically language specific variables */ if (wrapperPreLoadConfigurationProperties(&logLevelOnOverwriteProperties, &exitOnOverwriteProperties)) { preloadFailed = TRUE; return TRUE; /* Actually the return code is ignored on preload. */ } return FALSE; } #ifndef WIN32 if (firstCall) { wrapperData->daemonize = getBooleanProperty(properties, TEXT("wrapper.daemonize"), FALSE); if (wrapperData->daemonize) { /** If in the first call here and the wrapper will daemonize, then we don't need to proceed * any further as the properties will be loaded properly at the second time... */ return FALSE; } } #endif /* Load the configuration. */ if ((strcmpIgnoreCase(wrapperData->argCommand, TEXT("-translate")) != 0) && loadConfiguration()) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Problem loading the Wrapper configuration file: %s"), wrapperData->configFile); return TRUE; } return FALSE; } void wrapperGetCurrentTime(struct timeb *timeBuffer) { #ifdef WIN32 ftime(timeBuffer); #else struct timeval tv; gettimeofday(&tv, NULL); timeBuffer->time = (time_t)tv.tv_sec; timeBuffer->millitm = (unsigned short)(tv.tv_usec / 1000); #endif } /** * This function stops the pipes (quite in a brutal way) */ void protocolStopServerPipe() { if (protocolActiveServerPipeIn != INVALID_HANDLE_VALUE) { #ifdef WIN32 CloseHandle(protocolActiveServerPipeIn); #else close(protocolActiveServerPipeIn); #endif protocolActiveServerPipeIn = INVALID_HANDLE_VALUE; log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_INFO, TEXT("backend pipe closed.")); } if (protocolActiveServerPipeOut != INVALID_HANDLE_VALUE) { #ifdef WIN32 CloseHandle(protocolActiveServerPipeOut); #else close(protocolActiveServerPipeOut); #endif protocolActiveServerPipeOut = INVALID_HANDLE_VALUE; log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_INFO, TEXT("backend pipe closed.")); } } /** * There is no difference between closing a socket IPv4 vs a socket IPv6 */ void protocolStopServerSocket() { int rc; /* Close the socket. */ if (protocolActiveServerSD != INVALID_SOCKET) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("closing backend server.")); } #ifdef WIN32 rc = closesocket(protocolActiveServerSD); #else /* UNIX */ rc = close(protocolActiveServerSD); #endif if (rc == SOCKET_ERROR) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("server socket close failed. (%d)"), wrapperGetLastError()); } } protocolActiveServerSD = INVALID_SOCKET; } wrapperData->actualPort = 0; } void protocolStopServer() { if (wrapperData->backendType == WRAPPER_BACKEND_TYPE_PIPE) { protocolStopServerPipe(); } else { protocolStopServerSocket(); } } int protocolActiveServerPipeStarted = FALSE; int protocolStartServerPipe() { size_t pipeNameLen; TCHAR *pipeName; #ifdef WIN32 pipeNameLen = 17 + 10 + 1 + 10 + 3; #else pipeNameLen = 12 + 10 + 1 + 10 + 3; #endif pipeName = malloc(sizeof(TCHAR) * (pipeNameLen + 1)); if (!pipeName) { outOfMemory(TEXT("PSSP"), 1); return TRUE; } #ifdef WIN32 _sntprintf(pipeName, pipeNameLen, TEXT("\\\\.\\pipe\\wrapper-%d-%d-out"), wrapperData->wrapperPID, wrapperData->jvmRestarts + 1); if ((protocolActiveServerPipeOut = CreateNamedPipe(pipeName, PIPE_ACCESS_OUTBOUND,/* + FILE_FLAG_FIRST_PIPE_INSTANCE, */ PIPE_TYPE_MESSAGE | /* message type pipe */ PIPE_READMODE_MESSAGE | /* message-read mode */ PIPE_NOWAIT, /* nonblocking mode */ 1, /* only allow 1 connection at a time */ 32768, 32768, 0, NULL)) == INVALID_HANDLE_VALUE) { #else _sntprintf(pipeName, pipeNameLen, TEXT("/tmp/wrapper-%d-%d-out"), wrapperData->wrapperPID, wrapperData->jvmRestarts + 1); if (_tmkfifo(pipeName, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH) == INVALID_HANDLE_VALUE) { #endif log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_ERROR, TEXT("Unable to create backend pipe: %s"), getLastErrorText()); free(pipeName); return TRUE; } if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("server listening on pipe %s."), pipeName); } #ifdef WIN32 _sntprintf(pipeName, pipeNameLen, TEXT("\\\\.\\pipe\\wrapper-%d-%d-in"), wrapperData->wrapperPID, wrapperData->jvmRestarts + 1); if ((protocolActiveServerPipeIn = CreateNamedPipe(pipeName, PIPE_ACCESS_INBOUND,/* + FILE_FLAG_FIRST_PIPE_INSTANCE,*/ PIPE_TYPE_MESSAGE | /* message type pipe */ PIPE_READMODE_MESSAGE | /* message-read mode*/ PIPE_NOWAIT, /* nonblocking mode*/ 1, 32768, 32768, 0, NULL)) == INVALID_HANDLE_VALUE) { #else _sntprintf(pipeName, pipeNameLen, TEXT("/tmp/wrapper-%d-%d-in"), wrapperData->wrapperPID, wrapperData->jvmRestarts + 1); if (_tmkfifo(pipeName, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH) == INVALID_HANDLE_VALUE) { #endif log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_ERROR, TEXT("Unable to create backend pipe: %s"), getLastErrorText()); free(pipeName); return TRUE; } if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("server listening on pipe %s."), pipeName); } protocolActiveServerPipeStarted = TRUE; free(pipeName); return FALSE; } #ifdef WIN32 /** * This function doesn't exist on Windows. Similar to inet_ntop that you can find on unix. * Convert IPv4 and IPv6 addresses from binary to text form * @param af The family type (AF_INET or AF_INET6) * @param src Network address structure * @param dst Pointer where to write the text form of the address * @return On success, inet_ntop() returns a non-NULL pointer to dst. NULL is returned if there was an error. */ const char* inet_ntop(int af, const void* src, char* dst, socklen_t cnt) { int result; struct sockaddr_in6 addr6; struct sockaddr_in addr4; if (af == AF_INET) { memset(&addr4, 0, sizeof(struct sockaddr_in)); memcpy(&(addr4.sin_addr), src, sizeof(addr4.sin_addr)); addr4.sin_family = af; /* here is the function to return a TCHAR. I keep it commented out just as a note */ /* result = WSAAddressToString((struct sockaddr*) &addr4, sizeof(struct sockaddr_in), 0, dst, (LPDWORD) &cnt); */ result = getnameinfo((struct sockaddr *)&addr4, sizeof(struct sockaddr_in), dst, cnt, NULL, 0, NI_NUMERICHOST); } else { memset(&addr6, 0, sizeof(struct sockaddr_in)); memcpy(&(addr6.sin6_addr), src, sizeof(addr6.sin6_addr)); addr6.sin6_family = af; /* here is the function to return a TCHAR. I keep it commented out just as a note */ /* result = WSAAddressToString((struct sockaddr*) &addr6, sizeof(struct sockaddr_in6), 0, dst, (LPDWORD) &cnt); */ result = getnameinfo((struct sockaddr *)&addr6, sizeof(struct sockaddr_in6), dst, cnt, NULL, 0, NI_NUMERICHOST); } if (result != 0) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("getnameinfo failed (%d): (%s)"), result, getLastErrorText()); return NULL; } return dst; } /** * This function doesn't exist on Windows. Similar to inet_pton that you can find on unix. * convert IPv4 and IPv6 addresses from text to binary form * @param af The family type (AF_INET or AF_INET6) * @param src The adress in text * @param dst Pointer where to write the binary form of the address * @return FALSE if no error. */ int inet_pton(int af, const char *src, void *dst) { struct addrinfo hints; struct addrinfo *res, *ressave; int result; int i = 0; struct sockaddr_in6 *sockaddr_ipv6; struct sockaddr_in *sockaddr_ipv4; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = af; result = getaddrinfo(src, NULL, &hints, &res); if (result != 0) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("getaddrinfo failed (%d): (%s)"), result, getLastErrorText()); return TRUE; } ressave = res; while (res) { switch (res->ai_family) { case AF_INET: sockaddr_ipv4 = (struct sockaddr_in *) res->ai_addr; memcpy(dst, &sockaddr_ipv4->sin_addr, sizeof(struct in_addr)); result = FALSE; break; case AF_INET6: sockaddr_ipv6 = (struct sockaddr_in6 *) res->ai_addr; memcpy(dst, &sockaddr_ipv6->sin6_addr, sizeof(struct in6_addr)); result = FALSE; break; default: log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("Ignore unsupported family type: %d"), res->ai_family); break; } res = res->ai_next; } freeaddrinfo(ressave); return result; } #endif /** * Start server using a socket * @param IPv4 if true then we use IPv4, otherwise we use IPv6 * @return FALSE if socket is created successfully. For some specific error, returns WRAPPER_BACKEND_ERROR_NEXT, for all the other errors returns TRUE */ int protocolStartServerSocket(int IPv4) { struct sockaddr_in addr_srv4; struct sockaddr_in6 addr_srv6; int rc; int port; int fixedPort; #ifdef UNICODE char* tempAddress; size_t len; #endif /*int optVal;*/ #ifdef WIN32 u_long dwNoBlock = TRUE; #endif /* Create the server socket. */ if (IPv4) { protocolActiveServerSD = socket(AF_INET, SOCK_STREAM, 0); } else { protocolActiveServerSD = socket(AF_INET6, SOCK_STREAM, 0); } if (protocolActiveServerSD == INVALID_SOCKET) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_ERROR, TEXT("server socket creation failed. (%s)"), getLastErrorText()); return WRAPPER_BACKEND_ERROR_NEXT; } /* Make sure the socket is reused. */ /* We actually do not want to do this as it makes it possible for more than one Wrapper * instance to bind to the same port. The second instance succeeds to bind, but any * attempts to connect to that port will go to the first Wrapper. This would of course * cause attempts to launch the second JVM to fail. * Leave this code here as a future development note. optVal = 1; #ifdef WIN32 if (setsockopt(protocolActiveServerSD, SOL_SOCKET, SO_REUSEADDR, (TCHAR *)&optVal, sizeof(optVal)) < 0) { #else if (setsockopt(protocolActiveServerSD, SOL_SOCKET, SO_REUSEADDR, &optVal, sizeof(optVal)) < 0) { #endif log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_ERROR, "server socket SO_REUSEADDR failed. (%s)", getLastErrorText()); wrapperProtocolClose(); protocolStopServer(); return; } */ /* Make the socket non-blocking */ #ifdef WIN32 rc = ioctlsocket(protocolActiveServerSD, FIONBIO, &dwNoBlock); #else /* UNIX */ rc = fcntl(protocolActiveServerSD, F_SETFL, O_NONBLOCK); #endif if (rc == SOCKET_ERROR) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_ERROR, TEXT("server socket ioctlsocket failed. (%s)"), getLastErrorText()); wrapperProtocolClose(); protocolStopServer(); return TRUE; } /* If a port was specified in the configuration file then we want to * try to use that port or find the next available port. If 0 was * specified, then we will silently start looking for an available * port starting at 32000. */ port = wrapperData->port; if (port <= 0) { port = wrapperData->portMin; fixedPort = FALSE; } else { fixedPort = TRUE; } tryagain: /* Cleanup the socket first */ if (IPv4) { memset(&addr_srv4, 0, sizeof(addr_srv4)); addr_srv4.sin_family = AF_INET; addr_srv4.sin_port = htons((u_short)port); } else { memset(&addr_srv6, 0, sizeof(addr_srv6)); addr_srv6.sin6_family = AF_INET6; addr_srv6.sin6_flowinfo = 0; /* htons switch the 2 bytes. For example: 32000 in binary: 1111101 00000000 After swap: 00000000 1111101 which is 125 in decimal */ addr_srv6.sin6_port = htons((u_short)port); } if (wrapperData->portAddress == NULL) { /* the user hasn't defined any address, so we use the loopback address */ if (IPv4) { addr_srv4.sin_addr.s_addr = inet_addr(LOOPBACK_IPv4); } else { addr_srv6.sin6_addr = LOOPBACK_IPv6; } } else { #ifdef UNICODE #ifdef WIN32 len = WideCharToMultiByte(CP_OEMCP, 0, wrapperData->portAddress, -1, NULL, 0, NULL, NULL); if (len <= 0) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_WARN, TEXT("Invalid multibyte sequence in port address \"%s\" : %s"), wrapperData->portAddress, getLastErrorText()); return TRUE; } tempAddress = malloc(len); if (!tempAddress) { outOfMemory(TEXT("PSSS"), 1); return TRUE; } WideCharToMultiByte(CP_OEMCP, 0, wrapperData->portAddress, -1, tempAddress, (int)len, NULL, NULL); #else len = wcstombs(NULL, wrapperData->portAddress, 0); if (len == (size_t)-1) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_WARN, TEXT("Invalid multibyte sequence in port address \"%s\" : %s"), wrapperData->portAddress, getLastErrorText()); return TRUE; } tempAddress = malloc(len + 1); if (!tempAddress) { outOfMemory(TEXT("PSSS"), 2); return TRUE; } wcstombs(tempAddress, wrapperData->portAddress, len + 1); #endif /* convert the adress from text to binary form */ if (IPv4) { inet_pton(AF_INET, (const char *)tempAddress, &(addr_srv4.sin_addr)); } else { inet_pton(AF_INET6, (const char *)tempAddress, &(addr_srv6.sin6_addr)); } free(tempAddress); #else if (IPv4) { inet_pton(AF_INET, (const char *)wrapperData->portAddress, &(addr_srv4.sin_addr)); } else { inet_pton(AF_INET6, (const char *)wrapperData->portAddress, &(addr_srv6.sin6_addr)); } #endif } #ifdef WIN32 if (IPv4) { rc = bind(protocolActiveServerSD, (struct sockaddr FAR *)&addr_srv4, sizeof(addr_srv4)); } else { rc = bind(protocolActiveServerSD, (struct sockaddr FAR *)&addr_srv6, sizeof(addr_srv6)); } #else /* UNIX */ if (IPv4) { rc = bind(protocolActiveServerSD, (struct sockaddr *)&addr_srv4, sizeof(addr_srv4)); } else { rc = bind(protocolActiveServerSD, (struct sockaddr *)&addr_srv6, sizeof(addr_srv6)); } #endif if (rc == SOCKET_ERROR) { rc = wrapperGetLastError(); /* The specified port could not be bound. */ if ((rc == WRAPPER_EADDRINUSE) || (rc == WRAPPER_EACCES)) { /* Address in use, try looking at the next one. */ if (fixedPort) { /* The last port checked was the defined fixed port, switch to the dynamic range. */ port = wrapperData->portMin; fixedPort = FALSE; goto tryagain; } else { port++; if (port <= wrapperData->portMax) { goto tryagain; } } } /* Log an error. This is fatal, so die. */ if (wrapperData->port <= 0) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_FATAL, TEXT("unable to bind listener to any port in the range %d to %d. (%s)"), wrapperData->portMin, wrapperData->portMax, getLastErrorText()); } else { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_FATAL, TEXT("unable to bind listener port %d, or any port in the range %d to %d. (%s)"), wrapperData->port, wrapperData->portMin, wrapperData->portMax, getLastErrorText()); } wrapperStopProcess(getLastError(), TRUE); wrapperProtocolClose(); protocolStopServer(); wrapperData->exitRequested = TRUE; wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_NO; return TRUE; } /* If we got here, then we are bound to the port */ if ((wrapperData->port > 0) && (port != wrapperData->port)) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_INFO, TEXT("port %d already in use, using port %d instead."), wrapperData->port, port); } wrapperData->actualPort = port; if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("server listening on port %d."), wrapperData->actualPort); } /* Tell the socket to start listening. */ rc = listen(protocolActiveServerSD, 1); if (rc == SOCKET_ERROR) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_ERROR, TEXT("server socket listen failed. (%d)"), wrapperGetLastError()); wrapperProtocolClose(); protocolStopServer(); return TRUE; } return FALSE; } /** * if backendType is 'auto', then it will try in this order: * - socket IPv4 * - socket IPv6 * - pipe * if backendType is 'socket', then it will try in this order: * - socket IPv4 * - socket IPv6 */ void protocolStartServer() { int useFallbackAuto = FALSE; int useFallbackSocket = FALSE; int result; if (wrapperData->backendType == WRAPPER_BACKEND_TYPE_AUTO) { useFallbackAuto = TRUE; } if (wrapperData->backendType == WRAPPER_BACKEND_TYPE_SOCKET) { useFallbackSocket = TRUE; } if (wrapperData->backendType & WRAPPER_BACKEND_TYPE_SOCKET_V4) { result = protocolStartServerSocket(TRUE); if (result == WRAPPER_BACKEND_ERROR_NEXT && (useFallbackAuto || useFallbackSocket)) { /* we failed to use Ipv4, so lets try with IPv6 */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Failed to start server using socket IPv4, will try with socket IPv6...")); } else if (result == FALSE) { /* success */ wrapperData->backendType = WRAPPER_BACKEND_TYPE_SOCKET_V4; return; } else { /* error message should have already be printed in protocolStartServerSocket */ } } if (wrapperData->backendType & WRAPPER_BACKEND_TYPE_SOCKET_V6) { result = protocolStartServerSocket(FALSE); if (result == WRAPPER_BACKEND_ERROR_NEXT && useFallbackAuto) { /* we failed to use Ipv6, so lets try with pipe */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Failed to start server socket IPv6, will try with Pipe...")); } else if (result == FALSE) { /* success */ wrapperData->backendType = WRAPPER_BACKEND_TYPE_SOCKET_V6; return; } else { /* error message should have already be printed in protocolStartServerSocket */ } } if ( wrapperData->backendType & WRAPPER_BACKEND_TYPE_PIPE) { result = protocolStartServerPipe(); if (result == WRAPPER_BACKEND_ERROR_NEXT && useFallbackAuto) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Failed to start server socket when trying with socket (IPv4 and IPv6) and pipe...")); } else if (result == FALSE) { /* success */ wrapperData->backendType = WRAPPER_BACKEND_TYPE_PIPE; return; } else { /* error message should have already be printed in protocolStartServerPipe */ } } /* if we reach this code, it means we couldn't create a socket or a pipe */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to start server socket.")); if (!useFallbackAuto) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("You can set wrapper.backend.type=AUTO, so the wrapper will try to connect to the JVM using ipv4, ipv6 and pipe.")); if (!useFallbackSocket) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("You can set wrapper.backend.type=SOCKET, so the wrapper will try to connect to the JVM using ipv4 and ipv6.")); } } return; } /* this functions connects the pipes once the other end is there */ void protocolOpenPipe() { #ifdef WIN32 int result; result = ConnectNamedPipe(protocolActiveServerPipeOut, NULL); if (GetLastError() == ERROR_PIPE_LISTENING) { return; } result = ConnectNamedPipe(protocolActiveServerPipeIn, NULL); if (GetLastError() == ERROR_PIPE_LISTENING) { return; } if ((result == 0) && (GetLastError() != ERROR_PIPE_CONNECTED) && (GetLastError() != ERROR_NO_DATA)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Pipe connect failed: %s"), getLastErrorText()); return; } #else size_t pipeNameLen; TCHAR *pipeName; pipeNameLen = 12 + 10 + 1 + 10 + 3; pipeName = malloc(sizeof(TCHAR) * (pipeNameLen + 1)); if (!pipeName) { outOfMemory(TEXT("PSSP"), 1); return; } _sntprintf(pipeName, pipeNameLen, TEXT("/tmp/wrapper-%d-%d-out"), wrapperData->wrapperPID, wrapperData->jvmRestarts); protocolActiveServerPipeOut = _topen(pipeName, O_WRONLY | O_NONBLOCK, S_IWUSR | S_IRUSR); if (protocolActiveServerPipeOut == INVALID_HANDLE_VALUE) { free(pipeName); return; } _sntprintf(pipeName, pipeNameLen, TEXT("/tmp/wrapper-%d-%d-in"), wrapperData->wrapperPID, wrapperData->jvmRestarts); protocolActiveServerPipeIn = _topen(pipeName, O_RDONLY | O_NONBLOCK, S_IRUSR); if (protocolActiveServerPipeIn == INVALID_HANDLE_VALUE) { free(pipeName); return; } free(pipeName); #endif protocolActiveServerPipeConnected = TRUE; } /** * @param IPv4 if true then we use IPv4, otherwise we use IPv6 */ void protocolOpenSocket(int IPv4) { struct sockaddr_in6 addr_srv6; struct sockaddr_in addr_srv4; int rc; TCHAR* socketSource; int req; #if defined(WIN32) u_long dwNoBlock = TRUE; u_long addr_srv_len; #elif defined(HPUX) && !defined(ARCH_IA) int addr_srv_len; #else socklen_t addr_srv_len; #endif SOCKET newBackendSD = INVALID_SOCKET; char straddr[256] = {0}; int port; /* Is the server socket open? */ if (protocolActiveServerSD == INVALID_SOCKET) { /* can't do anything yet. */ return; } /* Try accepting a socket. */ if (IPv4) { addr_srv_len = sizeof(addr_srv4); } else { addr_srv_len = sizeof(addr_srv6); } #ifdef WIN32 if (IPv4) { newBackendSD = accept(protocolActiveServerSD, (struct sockaddr FAR *)&addr_srv4, &addr_srv_len); } else { newBackendSD = accept(protocolActiveServerSD, (struct sockaddr FAR *)&addr_srv6, &addr_srv_len); } #else /* UNIX */ if (IPv4) { newBackendSD = accept(protocolActiveServerSD, (struct sockaddr *)&addr_srv4, &addr_srv_len); } else { newBackendSD = accept(protocolActiveServerSD, (struct sockaddr *)&addr_srv6, &addr_srv_len); } #endif if (newBackendSD == INVALID_SOCKET) { rc = wrapperGetLastError(); /* EWOULDBLOCK != EAGAIN on some platforms. */ if ((rc == WRAPPER_EWOULDBLOCK) || (rc == EAGAIN)) { /* There are no incomming sockets right now. */ return; } else { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("socket creation failed. (%s)"), getLastErrorText()); } return; } } /* get a human readable version of the address */ if (IPv4) { inet_ntop(AF_INET, &addr_srv4.sin_addr, straddr, sizeof(straddr)); } else { inet_ntop(AF_INET6, &addr_srv6.sin6_addr, straddr, sizeof(straddr)); } /* convert the address */ #ifdef WIN32 req = MultiByteToWideChar(CP_OEMCP, 0, straddr, -1, NULL, 0); if (req <= 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Invalid multibyte sequence in %s: %s"), TEXT("network address"), getLastErrorText()); return; } socketSource = malloc(sizeof(TCHAR) * (req + 1)); if (!socketSource) { outOfMemory(TEXT("PO"), 1); return; } MultiByteToWideChar(CP_OEMCP, 0, straddr, -1, socketSource, req + 1); #else req = mbstowcs(NULL, straddr, MBSTOWCS_QUERY_LENGTH); if (req == (size_t)-1) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_WARN, TEXT("Invalid multibyte sequence in %s: %s"), TEXT("network address"), getLastErrorText()); return; } socketSource = malloc(sizeof(TCHAR) * (req + 1)); if (!socketSource) { outOfMemory(TEXT("PO"), 2); return; } mbstowcs(socketSource, straddr, req + 1); socketSource[req] = TEXT('\0'); /* Avoid bufferflows caused by badly encoded characters. */ #endif /* Is it already open? */ if (protocolActiveBackendSD != INVALID_SOCKET) { if (IPv4) { port = ntohs(addr_srv4.sin_port); } else { port = ntohs(addr_srv6.sin6_port); } log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_WARN, TEXT("Ignoring unexpected backend socket connection from %s on port %d"), socketSource, port); free(socketSource); #ifdef WIN32 rc = closesocket(newBackendSD); #else /* UNIX */ rc = close(newBackendSD); #endif if (rc == SOCKET_ERROR) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("socket close failed. (%d)"), wrapperGetLastError()); } } return; } /* New connection, so continue. */ protocolActiveBackendSD = newBackendSD; /* Collect information about the remote end of the socket. */ if (wrapperData->isDebugging) { if (IPv4) { port = ntohs(addr_srv4.sin_port); } else { port = ntohs(addr_srv6.sin6_port); } log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("accepted a socket on port %d from %s at port %d"), wrapperData->actualPort, socketSource, port); } free(socketSource); /* Make the socket non-blocking */ #ifdef WIN32 rc = ioctlsocket(protocolActiveBackendSD, FIONBIO, &dwNoBlock); #else /* UNIX */ rc = fcntl(protocolActiveBackendSD, F_SETFL, O_NONBLOCK); #endif if (rc == SOCKET_ERROR) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("socket ioctlsocket failed. (%s)"), getLastErrorText()); } wrapperProtocolClose(); return; } /* We got an incoming connection, so close down the listener to prevent further connections. */ protocolStopServer(); } /** * Attempt to accept a connection from a JVM client. */ void protocolOpen() { if (wrapperData->backendType == WRAPPER_BACKEND_TYPE_PIPE) { protocolOpenPipe(); } else if (wrapperData->backendType == WRAPPER_BACKEND_TYPE_SOCKET_V6) { protocolOpenSocket(FALSE); } else { protocolOpenSocket(TRUE); } } void protocolClosePipe() { #ifndef WIN32 size_t pipeNameLen; TCHAR *pipeName; pipeNameLen = 12 + 10 + 1 + 10 + 3; #endif if (protocolActiveServerPipeConnected) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("Closing backend pipe.")); } #ifdef WIN32 if (protocolActiveServerPipeIn != INVALID_HANDLE_VALUE && !CloseHandle(protocolActiveServerPipeIn)) { #else if (close(protocolActiveServerPipeIn) == -1) { #endif log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_ERROR, TEXT("Failed to close backend pipe: %s"), getLastErrorText()); } #ifdef WIN32 if (protocolActiveServerPipeOut != INVALID_HANDLE_VALUE && !CloseHandle(protocolActiveServerPipeOut)) { #else if (close(protocolActiveServerPipeOut) == -1) { #endif log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_ERROR, TEXT("Failed to close backend pipe: %s"), getLastErrorText()); } #ifndef WIN32 pipeName = malloc(sizeof(TCHAR) * (pipeNameLen + 1)); if (!pipeName) { outOfMemory(TEXT("PCP"), 1); return; } _sntprintf(pipeName, pipeNameLen, TEXT("/tmp/wrapper-%d-%d-in"), wrapperData->wrapperPID, wrapperData->jvmRestarts); _tunlink(pipeName); _sntprintf(pipeName, pipeNameLen, TEXT("/tmp/wrapper-%d-%d-out"), wrapperData->wrapperPID, wrapperData->jvmRestarts); _tunlink(pipeName); #endif protocolActiveServerPipeConnected = FALSE; protocolActiveServerPipeStarted = FALSE; protocolActiveServerPipeIn = INVALID_HANDLE_VALUE; protocolActiveServerPipeOut = INVALID_HANDLE_VALUE; } } void protocolCloseSocket() { int rc; /* Close the socket. */ if (protocolActiveBackendSD != INVALID_SOCKET) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("Closing backend socket.")); } #ifdef WIN32 rc = closesocket(protocolActiveBackendSD); #else /* UNIX */ rc = close(protocolActiveBackendSD); #endif if (rc == SOCKET_ERROR) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("socket close failed. (%d)"), wrapperGetLastError()); } } protocolActiveBackendSD = INVALID_SOCKET; } } /** * Close the backend socket. */ void wrapperProtocolClose() { if (wrapperData->backendType == WRAPPER_BACKEND_TYPE_PIPE) { protocolClosePipe(); } else { protocolCloseSocket(); } } /** * Returns the name of a given function code for debug purposes. */ TCHAR *wrapperProtocolGetCodeName(char code) { static TCHAR unknownBuffer[14]; TCHAR *name; switch (code) { case WRAPPER_MSG_START: name = TEXT("START"); break; case WRAPPER_MSG_STOP: name = TEXT("STOP"); break; case WRAPPER_MSG_RESTART: name = TEXT("RESTART"); break; case WRAPPER_MSG_PING: name = TEXT("PING"); break; case WRAPPER_MSG_STOP_PENDING: name = TEXT("STOP_PENDING"); break; case WRAPPER_MSG_START_PENDING: name = TEXT("START_PENDING"); break; case WRAPPER_MSG_STARTED: name = TEXT("STARTED"); break; case WRAPPER_MSG_STOPPED: name = TEXT("STOPPED"); break; case WRAPPER_MSG_JAVA_PID: name = TEXT("JAVA_PID"); break; case WRAPPER_MSG_KEY: name = TEXT("KEY"); break; case WRAPPER_MSG_BADKEY: name = TEXT("BADKEY"); break; case WRAPPER_MSG_LOW_LOG_LEVEL: name = TEXT("LOW_LOG_LEVEL"); break; case WRAPPER_MSG_PING_TIMEOUT: /* No longer used. */ name = TEXT("PING_TIMEOUT"); break; case WRAPPER_MSG_SERVICE_CONTROL_CODE: name = TEXT("SERVICE_CONTROL_CODE"); break; case WRAPPER_MSG_PROPERTIES: name = TEXT("PROPERTIES"); break; case WRAPPER_MSG_LOG + LEVEL_DEBUG: name = TEXT("LOG(DEBUG)"); break; case WRAPPER_MSG_LOG + LEVEL_INFO: name = TEXT("LOG(INFO)"); break; case WRAPPER_MSG_LOG + LEVEL_STATUS: name = TEXT("LOG(STATUS)"); break; case WRAPPER_MSG_LOG + LEVEL_WARN: name = TEXT("LOG(WARN)"); break; case WRAPPER_MSG_LOG + LEVEL_ERROR: name = TEXT("LOG(ERROR)"); break; case WRAPPER_MSG_LOG + LEVEL_FATAL: name = TEXT("LOG(FATAL)"); break; case WRAPPER_MSG_LOG + LEVEL_ADVICE: name = TEXT("LOG(ADVICE)"); break; case WRAPPER_MSG_LOG + LEVEL_NOTICE: name = TEXT("LOG(NOTICE)"); break; case WRAPPER_MSG_LOGFILE: name = TEXT("LOGFILE"); break; case WRAPPER_MSG_APPEAR_ORPHAN: /* No longer used. */ name = TEXT("APPEAR_ORPHAN"); break; case WRAPPER_MSG_PAUSE: name = TEXT("PAUSE"); break; case WRAPPER_MSG_RESUME: name = TEXT("RESUME"); break; case WRAPPER_MSG_GC: name = TEXT("GC"); break; #ifdef WIN32 case WRAPPER_MSG_FIRE_CTRL_EVENT: name = TEXT("FIRE_CTRL_EVENT"); break; #endif default: _sntprintf(unknownBuffer, 14, TEXT("UNKNOWN(%d)"), code); name = unknownBuffer; break; } return name; } /* Mutex for synchronization of the wrapperProtocolFunction function. */ #ifdef WIN32 HANDLE protocolMutexHandle = NULL; #else pthread_mutex_t protocolMutex = PTHREAD_MUTEX_INITIALIZER; #endif /** Obtains a lock on the protocol mutex. */ int lockProtocolMutex() { #ifdef WIN32 switch (WaitForSingleObject(protocolMutexHandle, INFINITE)) { case WAIT_ABANDONED: _tprintf(TEXT("Protocol mutex was abandoned.\n")); fflush(NULL); return -1; case WAIT_FAILED: _tprintf(TEXT("Protocol mutex wait failed.\n")); fflush(NULL); return -1; case WAIT_TIMEOUT: _tprintf(TEXT("Protocol mutex wait timed out.\n")); fflush(NULL); return -1; default: /* Ok */ break; } #else if (pthread_mutex_lock(&protocolMutex)) { _tprintf(TEXT("Failed to lock the Protocol mutex. %s\n"), getLastErrorText()); return -1; } #endif return 0; } /** Releases a lock on the protocol mutex. */ int releaseProtocolMutex() { #ifdef WIN32 if (!ReleaseMutex(protocolMutexHandle)) { _tprintf(TEXT("Failed to release Protocol mutex. %s\n"), getLastErrorText()); fflush(NULL); return -1; } #else if (pthread_mutex_unlock(&protocolMutex)) { _tprintf(TEXT("Failed to unlock the Protocol mutex. %s\n"), getLastErrorText()); return -1; } #endif return 0; } size_t protocolSendBufferSize = 0; char *protocolSendBuffer = NULL; /** * Sends a command to the JVM process. * * @param function The command to send. (This is intentionally an 8-bit char.) * @param message Message to send along with the command. * * @return TRUE if there were any problems. */ int wrapperProtocolFunction(char function, const TCHAR *messageW) { #ifdef UNICODE #ifdef WIN32 TCHAR buffer[16]; UINT cp; #endif #endif int rc; int cnt; int sendCnt; int inWritten; size_t len; TCHAR *logMsgW; const TCHAR *messageTemplate; char *messageMB = NULL; int returnVal = FALSE; int ok = TRUE; size_t sent; #ifdef WIN32 int maxSendSize; #else TCHAR* errorW; const char* outputEncoding = MB_UTF8; #endif /* It is important than there is never more than one thread allowed in here at a time. */ if (lockProtocolMutex()) { return TRUE; } if (ok) { /* We will be transmitting a MultiByte string of characters. So we need to convert the messageW. */ if (messageW) { #ifdef UNICODE /* We handle the backend communication in UTF-8 to allow support of all characters. * This is needed when sending properties to the JVM as the system encoding * may not support certain characters of the configuration file. */ #ifdef WIN32 GetLocaleInfo(GetThreadLocale(), LOCALE_IDEFAULTANSICODEPAGE, buffer, sizeof(buffer)); cp = _ttoi(buffer); len = WideCharToMultiByte(CP_UTF8, 0, messageW, -1, NULL, 0, NULL, NULL); if (len <= 0) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_WARN, TEXT("Invalid multibyte sequence in %s \"%s\" : %s"), TEXT("protocol message"), messageW, getLastErrorText()); returnVal = TRUE; ok = FALSE; } else { messageMB = malloc(len); if (!messageMB) { outOfMemory(TEXT("WPF"), 2); returnVal = TRUE; ok = FALSE; } else { WideCharToMultiByte(CP_UTF8, 0, messageW, -1, messageMB, (int)len, NULL, NULL); } } #else if (converterWideToMB(messageW, &messageMB, outputEncoding) < 0) { if (messageMB) { /* An error message is stored in messageMB (we need to convert it to wide chars to display it). */ if (converterMBToWide(messageMB, NULL, &errorW, TRUE)) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_WARN, TEXT("Unexpected conversion error in %s \"%s\""), TEXT("protocol message"), messageW); } else { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_WARN, errorW); } if (errorW) { free(errorW); } free(messageMB); } else { outOfMemory(TEXT("WPF"), 3); } returnVal = TRUE; ok = FALSE; } #endif #else len = _tscslen(messageW) + 1; messageMB = malloc(len); if (!messageMB) { outOfMemory(TEXT("WPF"), 4); returnVal = TRUE; ok = FALSE; } else { _tcsncpy(messageMB, messageW, len); } #endif } else { messageMB = NULL; } } /* We don't want to show the full properties log message. It is quite long and distracting. */ if (function == WRAPPER_MSG_PROPERTIES) { messageTemplate = TEXT("(Property Values, Size=%d)"); len = _tcslen(messageTemplate) + 16 + 1; logMsgW = malloc(sizeof(TCHAR) * len); if (!logMsgW) { outOfMemory(TEXT("WPF"), 1); /* Fallback to raw message. Not ideal but Ok. */ logMsgW = (TCHAR*)messageW; /* Strip the const, but will never be modified. */ } else { /* messageMB should never be NULL, but for code checker be careful. */ _sntprintf(logMsgW, len, messageTemplate, (messageMB ? strlen(messageMB) : 0)); } } else { logMsgW = (TCHAR*)messageW; /* Strip the const, but will never be modified. */ } if (wrapperData->stopPacketReceived) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("Stopping, so not sending %s : %s"), wrapperProtocolGetCodeName(function), (logMsgW == NULL ? TEXT("NULL") : logMsgW)); ok = FALSE; returnVal = FALSE; } if (ok) { /* We need to construct a single string that will be used to transmit the command + message. */ if (messageMB) { len = 1 + strlen(messageMB) + 1; } else { len = 2; } if (protocolSendBufferSize < len) { if (protocolSendBuffer) { free(protocolSendBuffer); } protocolSendBuffer = malloc(sizeof(char) * len); if (!protocolSendBuffer) { outOfMemory(TEXT("WPF"), 4); returnVal = TRUE; ok = FALSE; } else { /* Build the packet */ protocolSendBuffer[0] = function; if (messageMB) { strncpy(&(protocolSendBuffer[1]), messageMB, len - 1); } else { protocolSendBuffer[1] = 0; } } } if (messageMB) { free(messageMB); } } if (ok) { if (((protocolActiveBackendSD == INVALID_SOCKET) && (wrapperData->backendType == WRAPPER_BACKEND_TYPE_SOCKET)) || ((protocolActiveServerPipeConnected == FALSE) && (wrapperData->backendType == WRAPPER_BACKEND_TYPE_PIPE))) { /* A socket was not opened */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("Socket not open, so packet not sent %s : %s"), wrapperProtocolGetCodeName(function), (logMsgW == NULL ? TEXT("NULL") : logMsgW)); } returnVal = TRUE; } else { if (wrapperData->isDebugging) { if ((function == WRAPPER_MSG_PING) && messageW && (_tcsstr(messageW, TEXT("silent")) == messageW)) { /* log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("Send a silent ping packet %s : %s"), wrapperProtocolGetCodeName(function), (logMsgW == NULL ? TEXT("NULL") : logMsgW)); */ } else { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("Send a packet %s : %s"), wrapperProtocolGetCodeName(function), (logMsgW == NULL ? TEXT("NULL") : logMsgW)); } } /* When actually sending the packet, we need to be careful to make sure that the entire packet gets sent. * There isssues on both sockets and Pipes where the send will fail if the packet is too large. * In such cases, it needs to be broken up into multiple calls. * This is currently only an issue with the PROPERTIES packet. */ if (wrapperData->backendType == WRAPPER_BACKEND_TYPE_PIPE) { sent = 0; cnt = 0; sendCnt = 0; #ifdef WIN32 maxSendSize = 40000; #endif while ((sent < len) && (cnt < 200)) { if (cnt > 0) { wrapperSleep(10); } #ifdef WIN32 /* Send a maximum of 32000 characters per call as larger values appear to fail without error. */ if (WriteFile(protocolActiveServerPipeOut, protocolSendBuffer + sent, __min(maxSendSize, (int)(sizeof(char) * (len - sent))), &inWritten, NULL) == FALSE) { #else if ((inWritten = write(protocolActiveServerPipeOut, protocolSendBuffer + sent, sizeof(char) * (int)(len - sent))) == -1) { #endif log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_FATAL, TEXT("Writing to the backend pipe failed (%d): %s"), wrapperGetLastError(), getLastErrorText()); returnVal = TRUE; break; #ifdef WIN32 } else if (inWritten == 0) { /* Didn't write anything, but not an error. * Have not found this documented anywhere, but it happens if the size is larger than some hidden limit. */ maxSendSize = __max(512, (int)(maxSendSize * 0.90)); sendCnt++; #endif } else { /* Write N characters */ if (((sent + inWritten < len) || (sendCnt > 0)) && wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT(" Sent %d bytes, %d remaining."), inWritten, len - sent - inWritten ); } sent += inWritten; sendCnt++; } cnt++; } if (sent < len) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_ERROR, TEXT("%s send failed. Incomplete. Sent %d of %d bytes."), TEXT("Pipe"), sent, len); returnVal = TRUE; } } else { sent = 0; cnt = 0; sendCnt = 0; rc = 0; while ((sent < len) && (cnt < 200)) { if (cnt > 0) { wrapperSleep(10); } rc = send(protocolActiveBackendSD, protocolSendBuffer + sent, sizeof(char) * (int)(len - sent), 0); if (rc == SOCKET_ERROR) { if (wrapperGetLastError() == WRAPPER_EWOULDBLOCK) { /* The output buffer is simply full right now. Try again in a bit. */ } else { break; } } else if (rc < 0) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_ERROR, TEXT("Send unexpectedly returned %d"), rc); break; } else { /* Wrote N characters. */ if (((sent + rc < len) || (sendCnt > 0)) && wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT(" Sent %d bytes, %d remaining."), rc, len - sent - rc ); } sent += rc; sendCnt++; } cnt++; } if (rc == SOCKET_ERROR) { if (wrapperGetLastError() == WRAPPER_EWOULDBLOCK) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_WARN, TEXT("Socket send failed. Blocked for 2 seconds. %s"), getLastErrorText()); #ifdef WIN32 } else if (wrapperGetLastError() == WSAECONNRESET) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_ERROR, TEXT("Socket send failed. %s"), getLastErrorText()); #endif } else { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("Socket send failed. %s"), getLastErrorText()); } } /* On Windows, when the write side of a socket encounters a failure, the read side will also start failing even if there is unread data in the buffer. * Go ahead an close the connection here to avoid another error leter. */ wrapperProtocolClose(); returnVal = TRUE; } else if (sent < len) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_ERROR, TEXT("%s send failed. Incomplete. Sent %d of %d bytes."), TEXT("Socket"), sent, len); returnVal = TRUE; } else { returnVal = FALSE; } } } } /* Free up the logMsgW if we allocated it. */ if (logMsgW != messageW) { free(logMsgW); } /* Always make sure the mutex is released. */ if (releaseProtocolMutex()) { returnVal = TRUE; } return returnVal; } /** * Checks the status of the server backend. * * The backend will be initialized if the JVM is in a state where it should * be up, otherwise the backend will be left alone. * * If the forceOpen flag is set then an attempt will be made to initialize * the backend regardless of the JVM state. * * Returns TRUE if the backend is open and ready on return, FALSE if not. */ int wrapperCheckServerBackend(int forceOpen) { if (((wrapperData->backendType & WRAPPER_BACKEND_TYPE_SOCKET) && (protocolActiveServerSD == INVALID_SOCKET)) || ((wrapperData->backendType == WRAPPER_BACKEND_TYPE_PIPE) && (protocolActiveServerPipeStarted == FALSE)) ) { /* The backend is not currently open and needs to be started, * unless the JVM is DOWN or in a state where it is not needed. */ if ((!forceOpen) && ((wrapperData->jState == WRAPPER_JSTATE_DOWN_CLEAN) || (wrapperData->jState == WRAPPER_JSTATE_LAUNCH_DELAY) || (wrapperData->jState == WRAPPER_JSTATE_RESTART) || (wrapperData->jState == WRAPPER_JSTATE_STOPPED) || (wrapperData->jState == WRAPPER_JSTATE_KILLING) || (wrapperData->jState == WRAPPER_JSTATE_KILL) || (wrapperData->jState == WRAPPER_JSTATE_KILLED) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_CHECK) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH_STDIN) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH))) { /* The JVM is down or in a state where the backend is not needed. */ return FALSE; } else { /* The backend should be open, try doing so. */ protocolStartServer(); if ( ((wrapperData->backendType & WRAPPER_BACKEND_TYPE_SOCKET) && (protocolActiveServerSD == INVALID_SOCKET)) || ((wrapperData->backendType == WRAPPER_BACKEND_TYPE_PIPE) && (protocolActiveServerPipeStarted == FALSE)) ) { /* Failed. */ return FALSE; } else { return TRUE; } } } else { /* Backend is ready. */ return TRUE; } } /** * Simple function to parse hexidecimal numbers into a TICKS */ TICKS hexToTICKS(TCHAR *buffer) { TICKS value = 0; TCHAR c; int pos = 0; while (TRUE) { c = buffer[pos]; if ((c >= TEXT('a')) && (c <= TEXT('f'))) { value = (value << 4) + (10 + c - TEXT('a')); } else if ((c >= TEXT('A')) && (c <= TEXT('F'))) { value = (value << 4) + (10 + c - TEXT('A')); } else if ((c >= TEXT('0')) && (c <= TEXT('9'))) { value = (value << 4) + (c - TEXT('0')); } else { /* Any other character or null is the end of the number. */ return value; } pos++; } } /** * Read any data sent from the JVM. This function will loop and read as many * packets are available. The loop will only be allowed to go for 250ms to * ensure that other functions are handled correctly. * * Returns 0 if all available data has been read, 1 if more data is waiting. */ int wrapperProtocolRead() { char c; char code; int len; #ifdef WIN32 int maxlen; #endif int pos; TCHAR *tc; int err; struct timeb timeBuffer; time_t startTime; int startTimeMillis; time_t now; int nowMillis; time_t durr; #ifdef WIN32 size_t req; #else TCHAR* packetW; #endif wrapperGetCurrentTime(&timeBuffer); startTime = now = timeBuffer.time; startTimeMillis = nowMillis = timeBuffer.millitm; /* log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("now=%ld, nowMillis=%d"), now, nowMillis); */ while((durr = (now - startTime) * 1000 + (nowMillis - startTimeMillis)) < 250) { /* log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("durr=%ld"), durr); */ /* If we have an open client backend, then use it. */ if (((wrapperData->backendType & WRAPPER_BACKEND_TYPE_SOCKET) && (protocolActiveBackendSD == INVALID_SOCKET)) || ((wrapperData->backendType == WRAPPER_BACKEND_TYPE_PIPE) && (protocolActiveServerPipeConnected == FALSE))) { /* A Client backend is not open */ /* Is the server backend open? */ if (!wrapperCheckServerBackend(FALSE)) { /* Backend is down. We can not read any packets. */ return 0; } /* Try accepting a connection */ protocolOpen(); if (((wrapperData->backendType & WRAPPER_BACKEND_TYPE_SOCKET) && (protocolActiveBackendSD == INVALID_SOCKET)) || ((wrapperData->backendType == WRAPPER_BACKEND_TYPE_PIPE) && (protocolActiveServerPipeConnected == FALSE))) { return 0; } } if (wrapperData->backendType & WRAPPER_BACKEND_TYPE_SOCKET) { /* Try receiving a packet code */ len = recv(protocolActiveBackendSD, (void*) &c, 1, 0); if (len == SOCKET_ERROR) { err = wrapperGetLastError(); if ((err != WRAPPER_EWOULDBLOCK) && /* Windows - Would block. */ (err != EAGAIN)) { /* UNIX - Would block. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("Socket read failed. %s"), getLastErrorText()); } wrapperProtocolClose(); } return 0; } else if (len != 1) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("Socket read no code (eof).")); } wrapperProtocolClose(); return 0; } code = (char)c; /* Read in any message */ pos = 0; do { len = recv(protocolActiveBackendSD, (void*) &c, 1, 0); if (len == 1) { if (c == 0) { /* End of string */ len = 0; } else if (pos < MAX_LOG_SIZE) { packetBufferMB[pos] = c; pos++; } } else { len = 0; } } while (len == 1); /* terminate the string; */ packetBufferMB[pos] = '\0'; } else if (wrapperData->backendType == WRAPPER_BACKEND_TYPE_PIPE) { #ifdef WIN32 err = PeekNamedPipe(protocolActiveServerPipeIn, NULL, 0, NULL, &maxlen, NULL); if ((err == 0) && (GetLastError() == ERROR_BROKEN_PIPE)) { /* ERROR_BROKEN_PIPE - the client has closed the pipe. So most likely it just exited */ protocolActiveServerPipeIn = INVALID_HANDLE_VALUE; } if (maxlen == 0) { /*no data available */ return 0; } if (ReadFile(protocolActiveServerPipeIn, &c, 1, &len, NULL) == TRUE || GetLastError() == ERROR_MORE_DATA) { code = (char)c; --maxlen; pos = 0; do { ReadFile(protocolActiveServerPipeIn, &c, 1, &len, NULL); if (len == 1) { if (c == 0) { /* End of string */ len = 0; } else if (pos < MAX_LOG_SIZE) { packetBufferMB[pos] = c; pos++; } } else { len = 0; } } while (len == 1 && maxlen-- >= 0); packetBufferMB[pos] = '\0'; } else { if (GetLastError() == ERROR_INVALID_HANDLE) { return 0; } else { wrapperProtocolClose(); return 0; } } #else len = read(protocolActiveServerPipeIn, (void*) &c, 1); if (len == SOCKET_ERROR) { err = wrapperGetLastError(); if ((err != WRAPPER_EWOULDBLOCK) && /* Windows - Would block. */ (err != EAGAIN)) { /* UNIX - Would block. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("pipe read failed. (%s)"), getLastErrorText()); } wrapperProtocolClose(); } return 0; } else if (len == 0) { /*nothing read...*/ return 0; } code = (char)c; /* Read in any message */ pos = 0; do { len = read(protocolActiveServerPipeIn, (void*) &c, 1); if (len == 1) { if (c == 0) { /* End of string */ len = 0; } else if (pos < MAX_LOG_SIZE) { packetBufferMB[pos] = c; pos++; } } else { len = 0; } } while (len == 1); /* terminate the string; */ packetBufferMB[pos] = '\0'; #endif } else { /* Should not reach this part because wrapperData->backendType should always have a valid value */ return 0; } /* Convert the multi-byte packetBufferMB buffer into a wide-character string. */ /* Source message is always smaller than the MAX_LOG_SIZE so the output will be as well. */ /* While the packets sent to the JVM are UTF-8 encoded, it is better to handle the communication * from the JVM to the native Wrapper in the same encoding as stdout (by default the locale encoding). */ #ifdef WIN32 req = MultiByteToWideChar(getJvmOutputCodePage(), 0, packetBufferMB, -1, packetBufferW, MAX_LOG_SIZE + 1); if (req <= 0) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_WARN, TEXT("Invalid multibyte sequence in %s: %s"), TEXT("protocol message"), getLastErrorText()); packetBufferW[0] = TEXT('\0'); } #else if (converterMBToWide(packetBufferMB, getJvmOutputEncodingMB(), &packetW, TRUE)) { if (packetW) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_WARN, packetW); free(packetW); } else { outOfMemory(TEXT("WPR"), 1); } packetBufferW[0] = TEXT('\0'); } else { _sntprintf(packetBufferW, MAX_LOG_SIZE + 1, packetW); packetBufferW[MAX_LOG_SIZE] = TEXT('\0'); free(packetW); } #endif if (wrapperData->isDebugging) { if ((code == WRAPPER_MSG_PING) && (_tcsstr(packetBufferW, TEXT("silent")) == packetBufferW)) { /* log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("read a silent ping packet %s : %s"), wrapperProtocolGetCodeName(code), packetBufferW); */ } else { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("read a packet %s : %s"), wrapperProtocolGetCodeName(code), packetBufferW); } } switch (code) { case WRAPPER_MSG_STOP: wrapperStopRequested(_ttoi(packetBufferW)); break; case WRAPPER_MSG_RESTART: wrapperRestartRequested(); break; case WRAPPER_MSG_PING: #ifdef MACOSX if (wrapperData->jvmStopped) { /* On macOS, we can't use the condition '(sigInfo->si_code == CLD_CONTINUED)' to set * the wrapperData->signalChildContinuedTrapped flag. But we can know that the JVM * is back when we receive a ping. Then call wrapperGetProcessStatus() to log a * message that the JVM was continued. */ wrapperGetProcessStatus(wrapperGetTicks(), TRUE); } #endif /* Because all versions of the wrapper.jar simply bounce back the ping message, the pingSendTicks should always exist. */ tc = _tcschr(packetBufferW, TEXT(' ')); if (tc) { /* A pingSendTicks should exist. Parse the id following the space. It will be in the format 0xffffffff. */ wrapperPingResponded(hexToTICKS(&tc[1]), TRUE); } else { /* Should not happen, but just in case use the current ticks. */ wrapperPingResponded(wrapperGetTicks(), FALSE); } break; case WRAPPER_MSG_STOP_PENDING: wrapperStopPendingSignaled(_ttoi(packetBufferW)); break; case WRAPPER_MSG_STOPPED: wrapperStoppedSignaled(); break; case WRAPPER_MSG_START_PENDING: wrapperStartPendingSignaled(_ttoi(packetBufferW)); break; case WRAPPER_MSG_STARTED: wrapperStartedSignaled(); break; case WRAPPER_MSG_JAVA_PID: wrapperCheckMonitoredProcess(_ttoi(packetBufferW)); break; case WRAPPER_MSG_KEY: wrapperKeyRegistered(packetBufferW); break; case WRAPPER_MSG_LOG + LEVEL_DEBUG: case WRAPPER_MSG_LOG + LEVEL_INFO: case WRAPPER_MSG_LOG + LEVEL_STATUS: case WRAPPER_MSG_LOG + LEVEL_WARN: case WRAPPER_MSG_LOG + LEVEL_ERROR: case WRAPPER_MSG_LOG + LEVEL_FATAL: wrapperLogSignaled(code - WRAPPER_MSG_LOG, packetBufferW); break; case WRAPPER_MSG_APPEAR_ORPHAN: /* No longer used. This is still here in case a mix of versions are used. */ break; default: if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("received unknown packet (%d:%s)"), code, packetBufferW); } break; } /* Get the time again */ wrapperGetCurrentTime(&timeBuffer); now = timeBuffer.time; nowMillis = timeBuffer.millitm; } /* log_printf(WRAPPER_SOURCE_PROTOCOL, LEVEL_DEBUG, TEXT("done durr=%ld"), durr); */ if ((durr = (now - startTime) * 1000 + (nowMillis - startTimeMillis)) < 250) { return 0; } else { return 1; } } /****************************************************************************** * Wrapper inner methods. *****************************************************************************/ /** * IMPORTANT - Any logging done in here needs to be queued or it would cause a recursion problem. * * It is also critical that this is NEVER called from within the protocol function because it * would cause a deadlock with the protocol semaphore. This means that it can never be called * from within log_printf(...). */ void wrapperLogFileChanged(const TCHAR *logFile) { if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Active log file changed: %s"), logFile); } /* On startup, this function will always be called the first time the log file is set, * we don't want to send the command in this case as it clutters the debug log output. * Besides, the JVM will not be running anyway. */ if (wrapperData->jState != WRAPPER_JSTATE_DOWN_CLEAN) { wrapperProtocolFunction(WRAPPER_MSG_LOGFILE, logFile); } } /** * Return the size of the PID */ int wrapperGetPidSize(int pid, int minSize) { if (pid < pow(10, minSize + 1)) { return minSize; } /* Slower method but it should not happen often. */ return (int)floor (log10 ((double)abs (pid))) + 1; } /* In the future, we may load this value from configuration properties. */ #define PID_DEFAULT_SIZE 7 #define PID_NA TEXT("-------") /* Must be the same size as PID_DEFAULT_SIZE */ static int jPidSize = PID_DEFAULT_SIZE; static int wPidSize = 0; /** * Calculates the size required to display the Wrapper PID column or the Java PID column. * The size of each column can grow on its own as needed and never go back down. */ int wrapperLogFormatCount(const TCHAR format, size_t *reqSize) { switch( format ) { case TEXT('J'): case TEXT('j'): jPidSize = wrapperGetPidSize((int)wrapperData->javaPID, jPidSize); *reqSize += jPidSize + 3; return 1; case TEXT('W'): case TEXT('w'): if (wPidSize == 0) { /* The Wrapper PID can't change. Calculate its width only one time. */ wPidSize = wrapperGetPidSize((int)wrapperData->wrapperPID, PID_DEFAULT_SIZE); } *reqSize += wPidSize + 3; return 1; } return 0; } /** * Print the Wrapper PID column or the Java PID column. */ int wrapperLogFormatPrint(const TCHAR format, size_t printSize, TCHAR** pBuffer) { switch( format ) { case TEXT('J'): case TEXT('j'): if (wrapperData->javaPID) { return _sntprintf( *pBuffer, printSize, TEXT("%*d"), jPidSize, wrapperData->javaPID); } else { return _sntprintf( *pBuffer, printSize, PID_NA); } case TEXT('W'): case TEXT('w'): return _sntprintf( *pBuffer, printSize, TEXT("%*d"), wPidSize, wrapperData->wrapperPID); } return 0; } /** * Pre initialize the wrapper. */ int wrapperInitialize() { #ifdef WIN32 int maxPathLen = _MAX_PATH; #else int maxPathLen = PATH_MAX; #endif /* Initialize the properties variable. */ properties = NULL; /* Initialize the random seed. */ srand((unsigned)time(NULL)); /* Make sure all values are reliably set to 0. All required values should also be * set below, but this extra step will protect against future changes. Some * platforms appear to initialize maloc'd memory to 0 while others do not. */ wrapperData = malloc(sizeof(WrapperConfig)); if (!wrapperData) { _tprintf(TEXT("Out of memory (%s)\n"), TEXT("WIZ1")); return 1; } memset(wrapperData, 0, sizeof(WrapperConfig)); /* Setup the initial values of required properties. */ wrapperData->configured = FALSE; wrapperData->isConsole = TRUE; wrapperSetWrapperState(WRAPPER_WSTATE_STARTING); wrapperSetJavaState(WRAPPER_JSTATE_DOWN_CLEAN, 0, -1); wrapperData->lastPingTicks = wrapperGetTicks(); wrapperData->lastLoggedPingTicks = wrapperGetTicks(); wrapperData->jvmVersionCommand = NULL; wrapperData->jvmCommand = NULL; wrapperData->jvmDefaultLogLevel = LEVEL_INFO; wrapperData->jvmSource = WRAPPER_SOURCE_JVM; wrapperData->exitRequested = FALSE; wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_INITIAL; /* The first JVM needs to be started. */ wrapperData->exitCode = 0; wrapperData->errorExitCode = 1; wrapperData->jvmRestarts = 0; wrapperData->jvmLaunchTicks = wrapperGetTicks(); wrapperData->failedInvocationCount = 0; wrapperData->originalWorkingDir = NULL; wrapperData->configFile = NULL; wrapperData->workingDir = NULL; wrapperData->outputFilterCount = 0; wrapperData->confDir = NULL; wrapperData->umask = -1; wrapperData->portAddress = NULL; wrapperData->pingTimedOut = FALSE; wrapperData->shutdownActionPropertyName = NULL; wrapperData->javaVersion = NULL; wrapperData->jvmBits = JVM_BITS_UNKNOWN; wrapperData->jvmVendor = JVM_VENDOR_UNKNOWN; #ifdef WIN32 wrapperData->registry_java_home = NULL; if (!(tickMutexHandle = CreateMutex(NULL, FALSE, NULL))) { printf("Failed to create tick mutex. %s\n", getLastErrorText()); return 1; } /* Initialize control code queue. */ wrapperData->ctrlCodeQueue = malloc(sizeof(int) * CTRL_CODE_QUEUE_SIZE); if (!wrapperData->ctrlCodeQueue) { _tprintf(TEXT("Out of memory (%s)\n"), TEXT("WIZ2")); return 1; } wrapperData->ctrlCodeQueueWriteIndex = 0; wrapperData->ctrlCodeQueueReadIndex = 0; wrapperData->ctrlCodeQueueWrapped = FALSE; #endif if (initLogging(wrapperLogFileChanged)) { return 1; } /* This will only be called by the main thread on startup. * Immediately register this thread with the logger. * This has to happen after the logging is initialized. */ logRegisterThread(WRAPPER_THREAD_MAIN); logRegisterFormatCallbacks(wrapperLogFormatCount, wrapperLogFormatPrint); setLogfileFormat(TEXT("LPTM")); setLogfileLevelInt(LEVEL_DEBUG); setConsoleLogFormat(TEXT("LPM")); setConsoleLogLevelInt(LEVEL_DEBUG); setSyslogLevelInt(LEVEL_NONE); setLogfilePath(TEXT("wrapper.log"), FALSE, FALSE); /* Setting the logfile path may cause some output, so always set the levels and formats first. */ setLogfileRollMode(ROLL_MODE_SIZE); setLogfileAutoClose(FALSE); setConsoleFlush(TRUE); /* Always flush immediately until the logfile is configured to make sure that problems are in a consistent location. */ #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Wrapper Initializing... Minimum logging configured.")); #endif /** Remember what the initial user directory was when the Wrapper was launched. */ wrapperData->initialPath = (TCHAR *)malloc((maxPathLen + 1) * sizeof(TCHAR)); if (!wrapperData->initialPath) { outOfMemory(TEXT("WIZ"), 3); return 1; } else { if (!(wrapperData->initialPath = _tgetcwd((TCHAR*)wrapperData->initialPath, maxPathLen + 1))) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Failed to get the initial directory. (%s)"), getLastErrorText()); return 1; } } /* Set a variable to the initial working directory. */ setEnv(TEXT("WRAPPER_INIT_DIR"), wrapperData->initialPath, ENV_SOURCE_APPLICATION); #ifdef WIN32 if (!(protocolMutexHandle = CreateMutex(NULL, FALSE, NULL))) { _tprintf(TEXT("Failed to create protocol mutex. %s\n"), getLastErrorText()); fflush(NULL); return 1; } #endif /* This is a sanity check to make sure that the datatype used for tick counts is correct. */ if (sizeof(TICKS) != 4) { printf("Tick size incorrect %d != 4\n", (int)sizeof(TICKS)); fflush(NULL); return 1; } if (loadEnvironment()) { return 1; } #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Wrapper Initialization complete.")); #endif return 0; } void wrapperDataDispose() { int i; if (wrapperData->pingActionList) { free(wrapperData->pingActionList); wrapperData->pingActionList = NULL; } if (wrapperData->workingDir) { free(wrapperData->workingDir); wrapperData->workingDir = NULL; } if (wrapperData->originalWorkingDir) { free(wrapperData->originalWorkingDir); wrapperData->originalWorkingDir = NULL; } if (wrapperData->configFile) { free(wrapperData->configFile); wrapperData->configFile = NULL; } if (wrapperData->initialPath) { free(wrapperData->initialPath); wrapperData->initialPath = NULL; } if (wrapperData->baseName) { free(wrapperData->baseName); wrapperData->baseName = NULL; } if (wrapperData->classpath) { free(wrapperData->classpath); wrapperData->classpath = NULL; } if (wrapperData->portAddress) { free(wrapperData->portAddress); wrapperData->portAddress = NULL; } #ifdef WIN32 if (wrapperData->jvmVersionCommand) { free(wrapperData->jvmVersionCommand); wrapperData->jvmVersionCommand = NULL; } if (wrapperData->jvmCommand) { free(wrapperData->jvmCommand); wrapperData->jvmCommand = NULL; } if (wrapperData->userName) { free(wrapperData->userName); wrapperData->userName = NULL; } if (wrapperData->domainName) { free(wrapperData->domainName); wrapperData->domainName = NULL; } if (wrapperData->ntServiceLoadOrderGroup) { free(wrapperData->ntServiceLoadOrderGroup); wrapperData->ntServiceLoadOrderGroup = NULL; } if (wrapperData->ntServiceDependencies) { free(wrapperData->ntServiceDependencies); wrapperData->ntServiceDependencies = NULL; } if (wrapperData->ntServiceAccount) { free(wrapperData->ntServiceAccount); wrapperData->ntServiceAccount = NULL; } if (wrapperData->ntServicePassword) { wrapperSecureFreeStrW(wrapperData->ntServicePassword); wrapperData->ntServicePassword = NULL; } if (wrapperData->ctrlCodeQueue) { free(wrapperData->ctrlCodeQueue); wrapperData->ctrlCodeQueue = NULL; } #else if(wrapperData->jvmVersionCommand) { for (i = 0; wrapperData->jvmVersionCommand[i] != NULL; i++) { free(wrapperData->jvmVersionCommand[i]); wrapperData->jvmVersionCommand[i] = NULL; } free(wrapperData->jvmVersionCommand); wrapperData->jvmVersionCommand = NULL; } if(wrapperData->jvmCommand) { for (i = 0; wrapperData->jvmCommand[i] != NULL; i++) { free(wrapperData->jvmCommand[i]); wrapperData->jvmCommand[i] = NULL; } free(wrapperData->jvmCommand); wrapperData->jvmCommand = NULL; } #endif if(wrapperData->shutdownActionPropertyName) { free(wrapperData->shutdownActionPropertyName); wrapperData->shutdownActionPropertyName = NULL; } if (wrapperData->outputFilterCount > 0) { for (i = 0; i < wrapperData->outputFilterCount; i++) { if (wrapperData->outputFilters[i]) { free(wrapperData->outputFilters[i]); wrapperData->outputFilters[i] = NULL; } if (wrapperData->outputFilterActionLists[i]) { free(wrapperData->outputFilterActionLists[i]); wrapperData->outputFilterActionLists[i] = NULL; } } if (wrapperData->outputFilters) { free(wrapperData->outputFilters); wrapperData->outputFilters = NULL; } if (wrapperData->outputFilterActionLists) { free(wrapperData->outputFilterActionLists); wrapperData->outputFilterActionLists = NULL; } if (wrapperData->outputFilterMessages) { free(wrapperData->outputFilterMessages); wrapperData->outputFilterMessages = NULL; } if (wrapperData->outputFilterAllowWildFlags) { free(wrapperData->outputFilterAllowWildFlags); wrapperData->outputFilterAllowWildFlags = NULL; } if (wrapperData->outputFilterMinLens) { free(wrapperData->outputFilterMinLens); wrapperData->outputFilterMinLens = NULL; } } if (wrapperData->pidFilename) { free(wrapperData->pidFilename); wrapperData->pidFilename = NULL; } if (wrapperData->lockFilename) { free(wrapperData->lockFilename); wrapperData->lockFilename = NULL; } if (wrapperData->javaPidFilename) { free(wrapperData->javaPidFilename); wrapperData->javaPidFilename = NULL; } if (wrapperData->javaIdFilename) { free(wrapperData->javaIdFilename); wrapperData->javaIdFilename = NULL; } if (wrapperData->statusFilename) { free(wrapperData->statusFilename); wrapperData->statusFilename = NULL; } if (wrapperData->javaStatusFilename) { free(wrapperData->javaStatusFilename); wrapperData->javaStatusFilename = NULL; } if (wrapperData->commandFilename) { free(wrapperData->commandFilename); wrapperData->commandFilename = NULL; } if (wrapperData->consoleTitle) { free(wrapperData->consoleTitle); wrapperData->consoleTitle = NULL; } if (wrapperData->serviceName) { free(wrapperData->serviceName); wrapperData->serviceName = NULL; } if (wrapperData->serviceDisplayName) { free(wrapperData->serviceDisplayName); wrapperData->serviceDisplayName = NULL; } if (wrapperData->serviceDescription) { free(wrapperData->serviceDescription); wrapperData->serviceDescription = NULL; } if (wrapperData->hostName) { free(wrapperData->hostName); wrapperData->hostName = NULL; } if (wrapperData->confDir) { free(wrapperData->confDir); wrapperData->confDir = NULL; } if (wrapperData->argConfFileDefault && wrapperData->argConfFile) { free(wrapperData->argConfFile); wrapperData->argConfFile = NULL; } disposeJavaVersion(wrapperData->javaVersionMin); disposeJavaVersion(wrapperData->javaVersionMax); disposeJavaVersion(wrapperData->javaVersion); #ifdef WIN32 if (wrapperData->registry_java_home) { free(wrapperData->registry_java_home); wrapperData->registry_java_home = NULL; } #endif if (wrapperData) { free(wrapperData); wrapperData = NULL; } } /** Common wrapper cleanup code. */ void wrapperDispose(int exitCode) { /* Make sure not to dispose twice. This should not happen, but check for safety. */ if (disposed) { /* Don't use log_printf here as the second call may have already disposed logging. */ _tprintf(TEXT("wrapperDispose was called more than once.\n")); return; } disposed = TRUE; #ifdef WIN32 if (protocolMutexHandle) { if (!CloseHandle(protocolMutexHandle)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to close protocol mutex handle. %s"), getLastErrorText()); } } /* Make sure that the startup thread has completed. */ disposeStartup(); disposeSystemPath(); #endif disposeHashMapJvmEncoding(); #ifndef WIN32 /* Clean up the javaIN thread. */ disposeJavaIN(); #endif /* Clean up the javaIO thread. This should be done before the timer thread. */ if (wrapperData->useJavaIOThread) { disposeJavaIO(); } /* Clean up the timer thread. */ if (!wrapperData->useSystemTime) { disposeTimer(); } /* Clean up the properties structure. */ disposeProperties(properties); properties = NULL; disposeEnvironment(); if (wrapperChildWorkBuffer) { free(wrapperChildWorkBuffer); wrapperChildWorkBuffer = NULL; } if (protocolSendBuffer) { free(protocolSendBuffer); protocolSendBuffer = NULL; } /* Note: It is important that all other threads completed at that point, as we are going to dispose the logging. */ /* We will dispose the logging, so wrapperSleep should not be allowed to log anymore. */ wrapperData->isSleepOutputEnabled = FALSE; /* Always call maintain logger once to make sure that all queued messages are logged before we exit. */ maintainLogger(); if (wrapperData->runCommonStarted) { /* Log the exit code to help with debugging. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Exit code: %d"), exitCode); } /* This will be the last message. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("<-- Wrapper Stopped")); } /* Stop handling signals (the handler is using wrapperData and the logging and would crash if they are already disposed). * Note: Once this flag is set, CTRL+C will be handled by the parent session and interrupt the process immediately (when * running as a console). We maintained the logger before to make sure that all output can be printed. */ handleSignals = FALSE; /* Clean up the logging system. Should happen near last. */ disposeLogging(); /* clean up the main wrapper data structure. This must be done last.*/ wrapperDataDispose(); } /** * Returns the file name base as a newly malloced TCHAR *. The resulting * base file name will have any path and extension stripped. * If the 'os-arch-bit' pattern is found, it will also be stripped. * * @param fileName file name or path. * @param pBaseName pointer to output buffer. Should be long enough to always * contain the base name (_tcslen(fileName) + 1) is safe. */ void wrapperGetFileBase(const TCHAR *fileName, TCHAR **pBaseName) { const TCHAR *start; const TCHAR *end; const TCHAR *c; TCHAR buffer[32]; if ((c = _tcsrchr(fileName, FILE_SEPARATOR_C)) != NULL) { /* Strip off any path. */ start = c + 1; } else { start = fileName; } _sntprintf(buffer, 32, TEXT("-%s-%s-%s"), wrapperOS, wrapperArch, wrapperBits); buffer[31] = 0; if ((c = _tcsstr(start, buffer)) != NULL) { /* Strip off '-os-arch-bit'. */ end = c; } else if ((c = _tcsrchr(start, TEXT('.'))) != NULL) { /* Strip off any extension. */ end = c; } else { end = &start[_tcslen(start)]; } /* Now create the new base name. */ _tcsncpy(*pBaseName, start, end - start); (*pBaseName)[end - start] = TEXT('\0'); } /** * Returns a buffer containing a multi-line version banner. It is the responsibility of the caller * to make sure it gets freed. */ TCHAR *generateVersionBanner() { TCHAR *banner = TEXT("Java Service Wrapper %s Edition %s-bit %s\n Copyright (C) 1999-%s Tanuki Software, Ltd. All Rights Reserved.\n http://wrapper.tanukisoftware.com"); TCHAR *product = TEXT("Community"); TCHAR *copyright = TEXT("2022"); TCHAR *buffer; size_t len; len = _tcslen(banner) + _tcslen(product) + _tcslen(wrapperBits) + _tcslen(wrapperVersionRoot) + _tcslen(copyright) + 1; buffer = malloc(sizeof(TCHAR) * len); if (!buffer) { outOfMemory(TEXT("GVB"), 1); return NULL; } _sntprintf(buffer, len, banner, product, wrapperBits, wrapperVersionRoot, copyright); return buffer; } /** * Output the version. */ void wrapperVersionBanner() { TCHAR *banner = generateVersionBanner(); if (!banner) { return; } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, banner); free(banner); } /** * Output the application usage. */ void wrapperUsage(TCHAR *appName) { TCHAR *confFileBase; confFileBase = malloc(sizeof(TCHAR) * (_tcslen(appName) + 1)); if (!confFileBase) { outOfMemory(TEXT("WU"), 1); return; } wrapperGetFileBase(appName, &confFileBase); setSimpleLogLevels(); wrapperVersionBanner(); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Usage:")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" %s [configuration properties] [...]"), appName); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" %s [configuration properties] [...]"), appName); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" ( implicitly '-c')")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" %s "), appName); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" ( implicitly '%s.conf')"), confFileBase); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" %s"), appName); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" ( implicitly '-c' and '%s.conf')"), confFileBase); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("where can be one of:")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" -c --console run as a Console application")); #ifdef WIN32 log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" -su --setup SetUp the Wrapper")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" -td --teardown TearDown the Wrapper")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" -t --start starT an NT service")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" -a --pause pAuse a running NT service")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" -e --resume rEsume a paused NT service")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" -p --stop stoP a started NT service")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" -i --install Install as an NT service")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" -it --installstart Install and sTart as an NT service")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" -r --remove Uninstall/Remove as an NT service")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" -l= --controlcode= send a user controL Code to a running NT service")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" -d --dump request a thread Dump")); /** Return mask: installed:1 running:2 interactive:4 automatic:8 manual:16 disabled:32 */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" -q[=serviceName] --query[=serviceName] Query the current status of the service")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" -qs[=serviceName] --querysilent[=serviceName] Silently Query the current status of the service")); /* Omit '-s' option from help as it is only used by the service manager. */ /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" -s --service used by service manager")); */ #endif log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" -v --version print the Wrapper's Version information")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" -? --help print this help message")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" -- mark the end of Wrapper arguments. All arguments after the\n '--' will be passed through unmodified to the java application.")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" is the conf file to use. Filename must be absolute or")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" relative to the location of %s"), appName); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("[configuration properties] are configuration name-value pairs which override values")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" in the Wrapper configuration file. For example:")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" wrapper.debug=true")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Please note that any file references must be absolute or relative to the location\n of the Wrapper executable.")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("")); free(confFileBase); } int wrapperSetDefaultConfFile(const TCHAR *defaultConfFile) { size_t len; if (defaultConfFile) { len = _tcslen(defaultConfFile) + 1; wrapperData->argConfFile = malloc(len * sizeof(TCHAR)); if (!wrapperData->argConfFile) { outOfMemory(TEXT("WSDCF"), 1); return FALSE; } _tcsncpy(wrapperData->argConfFile, defaultConfFile, len); } else { len = _tcslen(wrapperData->baseName) + 5 + 1; wrapperData->argConfFile = malloc(len * sizeof(TCHAR)); if (!wrapperData->argConfFile) { outOfMemory(TEXT("WSDCF"), 2); return FALSE; } _sntprintf(wrapperData->argConfFile, len, TEXT("%s.conf"), wrapperData->baseName); } return TRUE; } /** * Parse the main arguments. * * Returns FALSE if the application should exit with an error. A message will * already have been logged. */ int wrapperParseArguments(int argc, TCHAR **argv) { TCHAR *c; int delimiter, wrapperArgCount; TCHAR *defaultConfFile = NULL; wrapperData->javaArgValueCount = 0; delimiter = 1; if (argc > 1 ) { for (delimiter = 0; delimiter < argc ; delimiter++) { if ( _tcscmp(argv[delimiter], TEXT("--")) == 0) { #if !defined(WIN32) && defined(UNICODE) free(argv[delimiter]); #endif argv[delimiter] = NULL; wrapperData->javaArgValueCount = argc - delimiter - 1; if (delimiter + 1 < argc) { wrapperData->javaArgValues = &argv[delimiter + 1]; } break; } } } /* Store the name of the binary.*/ wrapperData->argBinary = argv[0]; wrapperData->baseName = malloc(sizeof(TCHAR) * (_tcslen(argv[0]) + 1)); if (!wrapperData->baseName) { outOfMemory(TEXT("WPA"), 2); return FALSE; } wrapperGetFileBase(argv[0], &wrapperData->baseName); wrapperArgCount = delimiter; if (wrapperArgCount > 1) { if (argv[1][0] == TEXT('-')) { /* Syntax 1 or 3 */ /* A command appears to have been specified. */ wrapperData->argCommand = &argv[1][1]; /* Strip off the '-' */ if (wrapperData->argCommand[0] == TEXT('\0')) { wrapperUsage(argv[0]); return FALSE; } /* Does the argument have a value? */ c = _tcschr(wrapperData->argCommand, TEXT('=')); if (c == NULL) { wrapperData->argCommandArg = NULL; } else { wrapperData->argCommandArg = (TCHAR *)(c + 1); c[0] = TEXT('\0'); } if (wrapperArgCount > 2) { if (_tcsncmp(wrapperData->argCommand, TEXT("-translate"), 5) == 0) { if (wrapperArgCount > 3) { wrapperData->argConfFile = argv[3]; wrapperData->argCount = wrapperArgCount - 4; wrapperData->argValues = &argv[4]; } return TRUE; } else if ((_tcscmp(wrapperData->argCommand, TEXT("-jvm_bits")) == 0) || #ifdef WIN32 (_tcscmp(wrapperData->argCommand, TEXT("-request_log_file")) == 0) || (_tcscmp(wrapperData->argCommand, TEXT("-request_default_log_file")) == 0) || #endif (_tcscmp(wrapperData->argCommand, TEXT("-request_delta_binary_bits")) == 0)) { wrapperData->argConfFile = argv[2]; wrapperData->argCount = wrapperArgCount - 3; wrapperData->argValues = &argv[3]; /* If no configuration file is specified, we will go to case 'Syntax 3'. */ return TRUE; } /* Syntax 1 */ /* A command and conf file were specified. */ wrapperData->argConfFile = argv[2]; wrapperData->argCount = wrapperArgCount - 3; wrapperData->argValues = &argv[3]; } else { /* Syntax 3 */ /* Only a command was specified. Assume a default config file name. */ wrapperSetDefaultConfFile(defaultConfFile); wrapperData->argConfFileDefault = TRUE; wrapperData->argCount = wrapperArgCount - 2; wrapperData->argValues = &argv[2]; } } else { /* Syntax 2 */ /* A command was not specified, but there may be a config file. */ wrapperData->argCommand = TEXT("c"); wrapperData->argCommandArg = NULL; wrapperData->argConfFile = argv[1]; wrapperData->argCount = wrapperArgCount - 2; wrapperData->argValues = &argv[2]; } } else { /* Systax 4 */ /* A config file was not specified. Assume a default config file name. */ wrapperData->argCommand = TEXT("c"); wrapperData->argCommandArg = NULL; wrapperSetDefaultConfFile(defaultConfFile); wrapperData->argConfFileDefault = TRUE; wrapperData->argCount = wrapperArgCount - 1; wrapperData->argValues = &argv[1]; } return TRUE; } /** * Performs the specified action, * * @param actionList An array of action Ids ending with a value ACTION_LIST_END. * Negative values are standard actions, positive are user * custom events. * @param triggerMsg The reason the actions are being fired. * @param actionSourceCode Tracks where the action originated. * @param actionPropertyIndex Index of the property where the action was configured. Ignored if the type of action is not configured with a -component property. * @param logForActionNone Flag stating whether or not a message should be logged * for the NONE action. * @param exitCode Error code to use in case the action results in a shutdown. */ void wrapperProcessActionList(int *actionList, const TCHAR *triggerMsg, int actionSourceCode, int actionPropertyIndex, int logForActionNone, int exitCode) { int i; int action; TCHAR propertyName[52]; if (actionList) { i = 0; while ((action = actionList[i]) != ACTION_LIST_END) { switch(action) { case ACTION_RESTART: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s %s"), triggerMsg, wrapperGetRestartProcessMessage()); wrapperRestartProcess(); if (actionSourceCode == WRAPPER_ACTION_SOURCE_CODE_PING_TIMEOUT) { wrapperKillProcess(TRUE); } break; case ACTION_SHUTDOWN: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s Shutting down."), triggerMsg); wrapperData->shutdownActionTriggered = TRUE; switch (actionSourceCode) { case WRAPPER_ACTION_SOURCE_CODE_FILTER: _sntprintf(propertyName, 52, TEXT("wrapper.filter.action.%d"), actionPropertyIndex); break; case WRAPPER_ACTION_SOURCE_CODE_PING_TIMEOUT: _sntprintf(propertyName, 52, TEXT("wrapper.ping.timeout.action")); break; default: _sntprintf(propertyName, 52, TEXT("")); } updateStringValue(&(wrapperData->shutdownActionPropertyName), propertyName); if (actionSourceCode == WRAPPER_ACTION_SOURCE_CODE_PING_TIMEOUT) { wrapperData->exitCode = exitCode; wrapperKillProcess(TRUE); } else { wrapperStopProcess(exitCode, FALSE); } break; case ACTION_DUMP: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s Requesting thread dump."), triggerMsg); wrapperRequestDumpJVMState(); break; case ACTION_DEBUG: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s Debugging."), triggerMsg); break; case ACTION_PAUSE: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s %s"), triggerMsg, wrapperGetPauseProcessMessage()); wrapperPauseProcess(actionSourceCode); break; case ACTION_RESUME: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s %s"), triggerMsg, wrapperGetResumeProcessMessage()); wrapperResumeProcess(actionSourceCode); break; #if defined(MACOSX) case ACTION_ADVICE_NIL_SERVER: if (wrapperData->isAdviserEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT( "--------------------------------------------------------------------")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT( "Advice:")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT( "MACOSX is known to have problems displaying GUIs from processes\nrunning as a daemon launched from launchd. The above\n\"Returning nil _server\" means that you are encountering this\nproblem. This usually results in a long timeout which is affecting\nthe performance of your application.")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT( "--------------------------------------------------------------------")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("")); } break; #endif case ACTION_NONE: if (logForActionNone) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s"), triggerMsg); } /* Do nothing but masks later filters */ break; case ACTION_SUCCESS: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s Application has signaled success, consider this application started successful..."), triggerMsg); wrapperData->failedInvocationCount = 0; break; case ACTION_GC: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s Requesting GC..."), triggerMsg); wrapperRequestJVMGC(actionSourceCode); break; default: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unknown action type: %d"), action); break; } i++; } } } /** * Function that will recursively attempt to match two strings where the * pattern can contain '?' or '*' wildcard characters. This function requires * that the pattern be matched from the beginning of the text. * * @param text Text to be searched. * @param textLen Length of the text. * @param pattern Pattern to search for. * @param patternLen Length of the pattern. * @param minTextLen Minimum number of characters that the text needs to possibly match the pattern. * * @return TRUE if found, FALSE otherwise. * * 1) text=abcdefg textLen=7 pattern=a*d*efg patternLen=7 minTextLen=5 * 1.1) text=bcdefg textLen=6 pattern=d*efg patternLen=5 minTextLen=4 * 1.2) text=cdefg textLen=5 pattern=d*efg patternLen=5 minTextLen=4 * 1.3) text=defg textLen=4 pattern=d*efg patternLen=5 minTextLen=4 * 1.3.1) text=efg textLen=3 pattern=efg patternLen=3 minTextLen=3 */ int wildcardMatchInner(const TCHAR *text, size_t textLen, const TCHAR *pattern, size_t patternLen, size_t minTextLen) { size_t textIndex; size_t patternIndex; TCHAR patternChar; size_t textIndex2; TCHAR textChar; /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT(" wildcardMatchInner(\"%s\", %d, \"%s\", %d, %d)"), text, textLen, pattern, patternLen, minTextLen);*/ textIndex = 0; patternIndex = 0; while ((textIndex < textLen) && (patternIndex < patternLen)) { patternChar = pattern[patternIndex]; if (patternChar == TEXT('*')) { /* The pattern '*' can match 0 or more characters. This requires a bit of recursion to work it out. */ textIndex2 = textIndex; /* Loop over all possible starting locations. We know how many characters are needed to match (minTextLen - patternIndex) so we can stop there. */ while (textIndex2 < textLen - (minTextLen - (patternIndex + 1))) { if (wildcardMatchInner(&(text[textIndex2]), textLen - textIndex2, &(pattern[patternIndex + 1]), patternLen - (patternIndex + 1), minTextLen - patternIndex)) { /* Got a match in recursion. */ /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT(" wildcardMatchInner(\"%s\", %d, \"%s\", %d, %d) -> HERE1 textIndex=%d, patternIndex=%d, textIndex2=%d TRUE"), text, textLen, pattern, patternLen, minTextLen, textIndex, patternIndex, textIndex2);*/ return TRUE; } else { /* Failed to match. Try matching one more character against the '*'. */ textIndex2++; } } /* If we get here then all possible starting locations failed. */ /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT(" wildcardMatchInner(\"%s\", %d, \"%s\", %d, %d) -> HERE2 textIndex=%d, patternIndex=%d, textIndex2=%d FALSE"), text, textLen, pattern, patternLen, minTextLen, textIndex, patternIndex, textIndex2);*/ return FALSE; } else if (patternChar == TEXT('?')) { /* Match any character. */ patternIndex++; textIndex++; } else { textChar = text[textIndex]; if (patternChar == textChar) { /* Characters match. */ patternIndex++; textIndex++; } else { /* Characters do not match. We are done. */ /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT(" wildcardMatchInner(\"%s\", %d, \"%s\", %d, %d) -> HERE3 textIndex=%d, patternIndex=%d FALSE"), text, textLen, pattern, patternLen, minTextLen, textIndex, patternIndex);*/ return FALSE; } } } /* It is ok if there are text characters left over as we only need to match a substring, not the whole string. */ /* If there are any pattern chars left. Make sure that they are all wildcards. */ while (patternIndex < patternLen) { if (pattern[patternIndex] != TEXT('*')) { /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT(" wildcardMatchInner(\"%s\", %d, \"%s\", %d, %d) -> HERE4 pattern[%d]=%c FALSE"), text, textLen, pattern, patternLen, minTextLen, patternIndex, pattern[patternIndex]);*/ return FALSE; } patternIndex++; } /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT(" wildcardMatchInner(\"%s\", %d, \"%s\", %d, %d) -> HERE5 textIndex=%d, patternIndex=%d TRUE"), text, textLen, pattern, patternLen, minTextLen, textIndex, patternIndex);*/ return TRUE; } /** * Does any necessary post processing on the command string. * This function assumes that command has been malloced. It will either return * the string as is, or return a modified string. When a modified string is * returned the orignal command buffer will always be freed. * * 1) Replace the first instance of the %WRAPPER_COMMAND_FILLER_N% environment * variable so that the total command length will be equal to or greater than * the length specified by N. The padding will be the length plus a series of * Xs terminated by a single Y. This is mainly for testing. * * @param command The original command. * * @return The modifed command. */ TCHAR *wrapperPostProcessCommandElement(TCHAR *command) { TCHAR *pos1; TCHAR *pos2; size_t commandLen; size_t commandLen2; size_t commandLenLen; TCHAR commandLenBuffer[8]; size_t fillerLen; TCHAR *tempCommand; size_t index; /* If the special WRAPPER_COMMAND_FILLER_N environment variable is being used then expand it. * This is mainly used for testing. */ pos1 = _tcsstr(command, TEXT("%WRAPPER_COMMAND_FILLER_")); if (pos1 == NULL) { return command; } pos2 = _tcsstr(pos1 + 1, TEXT("%")); if (pos2 == NULL) { return command; } commandLen = _tcslen(command); commandLenLen = pos2 - pos1 - 24; if (commandLenLen >= 8) { /* Too long. invalid. */ return command; } memcpy(commandLenBuffer, pos1 + 24, sizeof(TCHAR) * commandLenLen); commandLenBuffer[commandLenLen] = TEXT('\0'); commandLen2 = __max((int)(commandLen - commandLenLen) - 25, __min(_ttoi(commandLenBuffer), 9999999)); fillerLen = commandLen2 - commandLen + commandLenLen + 25; tempCommand = malloc(sizeof(TCHAR) * (commandLen - commandLenLen - 25 + fillerLen + 1)); if (!tempCommand) { outOfMemory(TEXT("WBJC"), 3); return command; } memcpy(tempCommand, command, (pos1 - command) * sizeof(TCHAR)); index = pos1 - command; if (fillerLen > 11) { _sntprintf(&(tempCommand[index]), commandLen2 + 1 - index, TEXT("FILL-%d-"), fillerLen); fillerLen -= _tcslen(&tempCommand[index]); index += _tcslen(&tempCommand[index]); } while (fillerLen > 1) { tempCommand[index] = TEXT('X'); index++; fillerLen--; } if (fillerLen > 0) { tempCommand[index] = TEXT('Y'); index++; fillerLen--; } memcpy(&(tempCommand[index]), pos2 + 1, sizeof(TCHAR) * _tcslen(pos2 + 1)); tempCommand[commandLen2] = TEXT('\0'); free(command); return tempCommand; } /** * Test function to pause the current thread for the specified amount of time. * This is used to test how the rest of the Wrapper behaves when a particular * thread blocks for any reason. * * @param pauseTime Number of seconds to pause for. -1 will pause indefinitely. * @param threadName Name of the thread that will be logged prior to pausing. */ void wrapperPauseThread(int pauseTime, const TCHAR *threadName) { int i; if (pauseTime > 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Pausing the \"%s\" thread for %d seconds..."), threadName, pauseTime); for (i = 0; i < pauseTime; i++) { wrapperSleep(1000); } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Resuming the \"%s\" thread..."), threadName); } else if (pauseTime < 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Pausing the \"%s\" thread indefinitely."), threadName); while(TRUE) { wrapperSleep(1000); } } } /** * Function that will recursively attempt to match two strings where the * pattern can contain '?' or '*' wildcard characters. * * @param text Text to be searched. * @param pattern Pattern to search for. * @param patternLen Length of the pattern. * @param minTextLen Minimum number of characters that the text needs to possibly match the pattern. * * @return TRUE if found, FALSE otherwise. */ int wrapperWildcardMatch(const TCHAR *text, const TCHAR *pattern, size_t minTextLen) { size_t textLen; size_t patternLen; size_t textIndex; /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("wrapperWildcardMatch(\"%s\", \"%s\", %d)"), text, pattern, minTextLen);*/ textLen = _tcslen(text); if (textLen < minTextLen) { return FALSE; } patternLen = _tcslen(pattern); /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT(" textLen=%d, patternLen=%d"), textLen, patternLen);*/ textIndex = 0; while (textIndex <= textLen - minTextLen) { if (wildcardMatchInner(&(text[textIndex]), textLen - textIndex, pattern, patternLen, minTextLen)) { return TRUE; } textIndex++; } return FALSE; } /** * Calculates the minimum text length which could be matched by the specified pattern. * Patterns can contain '*' or '?' wildcards. * '*' matches 0 or more characters. * '?' matches exactly one character. * * @param pattern Pattern to calculate. * * @return The minimum text length of the pattern. */ size_t wrapperGetMinimumTextLengthForPattern(const TCHAR *pattern) { size_t patternLen; size_t patternIndex; size_t minLen; /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("wrapperGetMinimumTextLengthForPattern(%s)"), pattern);*/ patternLen = _tcslen(pattern); minLen = 0; for (patternIndex = 0; patternIndex < patternLen; patternIndex++) { if (pattern[patternIndex] == TEXT('*')) { /* Matches 0 or more characters, so don't increment the minLen */ } else { minLen++; } } /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("wrapperGetMinimumTextLengthForPattern(%s) -> %d"), pattern, minLen);*/ return minLen; } /** * Trims any whitespace from the beginning and end of the in string * and places the results in the out buffer. Assumes that the out * buffer is at least as large as the in buffer. */ void trim(const TCHAR *in, TCHAR *out) { size_t len; size_t first; size_t last; len = _tcslen(in); if (len > 0) { first = 0; last = len - 1; /* Left Trim */ while (((in[first] == ' ') || (in[first] == '\t')) && (first < last)) { first++; } /* Right Trim */ while ((last > first) && ((in[last] == ' ') || (in[last] == '\t'))) { last--; } /* Copy over what is left. */ len = last - first + 1; if (len > 0) { _tcsncpy(out, in + first, len); } } out[len] = TEXT('\0'); } /** * Comfirm that the Java version is in the range in which the Wrapper is allowed to run. * * @return TRUE if the Java version is ok, FALSE otherwise. */ int wrapperConfirmJavaVersion() { int result = TRUE; JavaVersion *minVersion1; JavaVersion *minVersion2; JavaVersion *maxVersion; const TCHAR *minVersionName = NULL; /* wrapper.java.version.min & wrapper.java.version.min can't be less than the minimum version of Java supported by the Wrapper. */ minVersion1 = getMinRequiredJavaVersion(); minVersion2 = minVersion1; if (wrapperData->javaVersionMin) { disposeJavaVersion(wrapperData->javaVersionMin); } wrapperData->javaVersionMin = getJavaVersionProperty(TEXT("wrapper.java.version.min"), minVersion1->displayName, minVersion1, NULL, 0); if (!wrapperData->javaVersionMin) { /* Invalid configuration. A FATAL error has been logged. */ result = FALSE; } else if (compareJavaVersion(wrapperData->javaVersionMin, minVersion2) > 0) { /* wrapper.java.version.max can't be less than wrapper.java.version.min. */ minVersion2 = wrapperData->javaVersionMin; minVersionName = TEXT("wrapper.java.version.min"); } if (wrapperData->javaVersionMax) { disposeJavaVersion(wrapperData->javaVersionMax); } wrapperData->javaVersionMax = getJavaVersionProperty(TEXT("wrapper.java.version.max"), TEXT("UNLIMITED"), minVersion2, minVersionName, UINT_MAX); if (!wrapperData->javaVersionMax) { /* Invalid configuration. A FATAL error has been logged. */ result = FALSE; } if (result) { if (!wrapperData->javaVersion) { /* This should never happen. */ result = FALSE; } else if (wrapperData->javaVersion->isUnknown) { maxVersion = getMaxRequiredJavaVersion(); if ((compareJavaVersion(wrapperData->javaVersionMin, minVersion1) != 0) || (compareJavaVersion(wrapperData->javaVersionMax, maxVersion) != 0)) { /* We previously failed to parse the Java version currently used, so if wrapper.java.version.min or wrapper.java.version.max * are not set to their defaults, we should stop. Otherwise continue to not risk blocking the Wrapper for certain JVMs. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Cannot confirm the version of Java. Usage of %s\n and %s will prevent the Wrapper from continuing."), TEXT("wrapper.java.version.min"), TEXT("wrapper.java.version.max")); result = FALSE; } disposeJavaVersion(maxVersion); } else { /* If the configuration is correct, confirm that the version Java is between the min and the max. */ if (compareJavaVersion(wrapperData->javaVersion, wrapperData->javaVersionMin) < 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("The version of Java specified by wrapper.java.command (%s)\n is lower than the minimum required (%s)."), wrapperData->javaVersion->displayName, wrapperData->javaVersionMin->displayName); result = FALSE; } else if (compareJavaVersion(wrapperData->javaVersion, wrapperData->javaVersionMax) > 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("The version of Java specified by wrapper.java.command (%s)\n is greater than the maximum allowed (%s)."), wrapperData->javaVersion->displayName, wrapperData->javaVersionMax->displayName); result = FALSE; } } } disposeJavaVersion(minVersion1); return result; } /** * Set the Java version that the Wrapper will use. * * @param javaVersion The Java version to set or NULL if a default value should be set. */ void wrapperSetJavaVersion(JavaVersion* javaVersion) { JavaVersion *fallbackVersion; JavaVersion *minRequiredVersion; if (wrapperData->javaVersion) { disposeJavaVersion(wrapperData->javaVersion); wrapperData->javaVersion = NULL; } if (javaVersion) { wrapperData->javaVersion = javaVersion; } else { minRequiredVersion = getMinRequiredJavaVersion(); fallbackVersion = getJavaVersionProperty(TEXT("wrapper.java.version.fallback"), NULL, minRequiredVersion, NULL, 0); if (fallbackVersion) { wrapperData->javaVersion = fallbackVersion; disposeJavaVersion(minRequiredVersion); log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to parse the version of Java. Resolving to the value of wrapper.java.version.fallback (%s)."), fallbackVersion->displayName); } else { /* We are in a void function. We will handle the error in the main eventloop if wrapperData->javaVersion is NULL. */ if (getNotEmptyStringProperty(properties, TEXT("wrapper.java.version.fallback"), NULL)) { /* 'wrapper.java.version.fallback' was specified in the configuration but * getJavaVersionProperty() returned NULL. That means we failed to parse the value. */ wrapperData->javaVersion = NULL; disposeJavaVersion(minRequiredVersion); } else { /* Resolve to the minimum required version. NULL is unlikely to happen but * would be handled in the main eventloop. */ wrapperData->javaVersion = minRequiredVersion; if (wrapperData->javaVersion) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to parse the version of Java. Resolving to the lowest supported version (%s)."), wrapperData->javaVersion->displayName); wrapperData->javaVersion->isUnknown = TRUE; } } } if (!wrapperData->printJVMVersion && (getLogfileLevelInt() > wrapperData->jvmDefaultLogLevel)) { /* Note: In theory we should also check that the logfile loglevel is INFO or less, but then we would need a different advice for each case... */ log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" Please set wrapper.java.version.output to TRUE, relaunch the Wrapper to print out the Java version output, and send the log file to support@tanukisoftware.com.")); } } if (wrapperData->javaVersion) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Java version: %d.%d.%d"), wrapperData->javaVersion->major, wrapperData->javaVersion->minor, wrapperData->javaVersion->revision); } } static int javaVersionCurrentParseLine; static int javaVersionParseLine; /* The line at which the version of Java can be found in the 'java -version' output. */ static int javaVendorParseLine; /* The line at which the JVM maker can be found in the 'java -version' output. */ static int javaBitsParseLine; /* The line at which the JVM bits can be found in the 'java -version' output. */ void logParseJavaVersionOutput(TCHAR* log) { JavaVersion *javaVersion; javaVersionCurrentParseLine++; /* Queue the messages to avoid logging it in the middle of the JVM output. */ if (javaVersionCurrentParseLine == javaVersionParseLine) { if (!_tcsstr(log, TEXT("version \""))) { /* This is not the line containing the version. This can happen when a system message is inserted before the Java output. * NOTE: we are parsing the output of a process that is already terminated, so we know that the output is not infinite. */ javaVersionParseLine++; javaVendorParseLine++; javaBitsParseLine++; return; } else { /* Parse the Java version. */ javaVersion = parseOutputJavaVersion(log); wrapperSetJavaVersion(javaVersion); } } if (javaVersionCurrentParseLine == javaVendorParseLine) { /* Parse the JVM maker (JVM implementation). */ wrapperData->jvmVendor = parseOutputJvmVendor(log); log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Java vendor: %s"), getJvmVendorName(wrapperData->jvmVendor)); } if (javaVersionCurrentParseLine == javaBitsParseLine) { /* Parse the JVM bits. */ wrapperData->jvmBits = parseOutputJvmBits(log, wrapperData->javaVersion); log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Java bits: %s"), getJvmBitsName(wrapperData->jvmBits)); } } void logApplyFilters(const TCHAR *log) { int i; const TCHAR *filter; const TCHAR *filterMessage; int matched; /* Look for output filters in the output. Only match the first. */ for (i = 0; i < wrapperData->outputFilterCount; i++) { if (_tcslen(wrapperData->outputFilters[i]) > 0) { /* The filter is defined. */ matched = FALSE; filter = wrapperData->outputFilters[i]; if (wrapperData->outputFilterAllowWildFlags[i]) { if (wrapperWildcardMatch(log, filter, wrapperData->outputFilterMinLens[i])) { matched = TRUE; } } else { /* Do a simple check to see if the pattern is found exactly as is. */ if (_tcsstr(log, filter)) { /* Found an exact match for the pattern. */ /* Any wildcards in the pattern can be matched exactly if they exist in the output. This is by design. */ matched = TRUE; } } if (matched) { filterMessage = wrapperData->outputFilterMessages[i]; if ((!filterMessage) || (_tcslen(filterMessage) <= 0)) { filterMessage = TEXT("Filter trigger matched."); } wrapperProcessActionList(wrapperData->outputFilterActionLists[i], filterMessage, WRAPPER_ACTION_SOURCE_CODE_FILTER, i, FALSE, wrapperData->errorExitCode); /* break out of the loop */ break; } } } } #ifdef _DEBUG void printBytes(const char * s) { TCHAR buffer[MAX_LOG_SIZE]; TCHAR *pBuffer; size_t len = __min(strlen(s), MAX_LOG_SIZE/3); size_t i; buffer[0] = 0; pBuffer = buffer; for (i = 0; i < len; i++) { _sntprintf(pBuffer, 4, TEXT("%02x "), s[i] & 0xff); pBuffer +=3; } buffer[MAX_LOG_SIZE-1] = 0; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, buffer); } #endif /** * Logs a single line of child output allowing any filtering * to be done in a common location. */ void logChildOutput(const char* log) { TCHAR* tlog = NULL; #ifdef UNICODE #ifdef WIN32 int size; UINT cp; #endif #endif #ifdef _DEBUG printBytes(log); #endif #ifdef UNICODE #ifdef WIN32 cp = getJvmOutputCodePage(); size = MultiByteToWideChar(cp, 0, log, -1 , NULL, 0); if (size <= 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Invalid multibyte sequence in %s: %s"), TEXT("JVM console output"), getLastErrorText()); return; } tlog = (TCHAR*)malloc((size + 1) * sizeof(TCHAR)); if (!tlog) { outOfMemory(TEXT("WLCO"), 1); return; } MultiByteToWideChar(cp, 0, log, -1, tlog, size + 1); #else if (converterMBToWide(log, getJvmOutputEncodingMB(), &tlog, TRUE)) { if (tlog) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("%s"), tlog); free(tlog); } else { outOfMemory(TEXT("WLCO"), 1); } return; } #endif #else tlog = (TCHAR*)log; #endif /* NOTE: Don't use 'TEXT("%s")' here! log_printf operates in a different (& faster) mode when the source is WRAPPER_SOURCE_JVM * or WRAPPER_SOURCE_JVM_VERSION. It knows that there is no message format and prints the output as a direct message. */ if (wrapperData->jvmSource == WRAPPER_SOURCE_JVM_VERSION) { /* Print the java version at the level set in wrapperReadJavaVersionOutput */ log_printf(WRAPPER_SOURCE_JVM_VERSION, wrapperData->jvmDefaultLogLevel, tlog); if (!wrapperData->jvmVersionFailed) { /* tlog will be modified by this call. Make sure it will not be used after that. */ logParseJavaVersionOutput(tlog); } } else { /* Normal JVM output. */ log_printf(wrapperData->jvmRestarts, wrapperData->jvmDefaultLogLevel, tlog); /* Look for output filters in the output. Only match the first. */ logApplyFilters(tlog); } #ifdef UNICODE free(tlog); #endif } /** * This function is for moving a buffer inside itself. * * This implementation exists because the standard memcpy is not reliable on * some platforms when the source and target buffer are the same. Most likely the * problem was caused by internal optimizations, but it was leading to crashes. */ void safeMemCpy(char *buffer, size_t target, size_t src, size_t nbyte) { size_t i; for (i = 0; i < nbyte; i++) { buffer[target + i] = buffer[src + i]; } } #define CHAR_LF 0x0a /** * Read and process any output from the child JVM Process. * * When maxTimeMS is non-zero this function will only be allowed to run for that maximum * amount of time. This is done to make sure the calling function is allowed CPU for * other activities. When timing out for this reason when there is more data in the * pipe, this function will return TRUE to let the calling code know that it should * not to any unnecessary sleeps. Otherwise FALSE will be returned. * * @param maxTimeMS The maximum number of milliseconds that this function will be allowed * to run without returning. In reality no new reads will happen after * this time, but actual processing may take longer. * * @return TRUE if the calling code should call this function again as soon as possible. */ int wrapperReadChildOutput(int maxTimeMS) { struct timeb timeBuffer; time_t startTime; int startTimeMillis; time_t now; int nowMillis; time_t durr; char *tempBuffer; char *cLF; int currentBlockRead; size_t loggedOffset; int defer = FALSE; int readThisPass = FALSE; size_t i; if (!wrapperChildWorkBuffer) { /* Initialize the wrapperChildWorkBuffer. Set its initial size to the block size + 1. * This is so that we can always add a \0 to the end of it. */ wrapperChildWorkBuffer = malloc(sizeof(char) * ((READ_BUFFER_BLOCK_SIZE * 2) + 1)); if (!wrapperChildWorkBuffer) { outOfMemory(TEXT("WRCO"), 1); return FALSE; } wrapperChildWorkBufferSize = READ_BUFFER_BLOCK_SIZE * 2; wrapperChildWorkBufferLen = 0; } wrapperGetCurrentTime(&timeBuffer); startTime = now = timeBuffer.time; startTimeMillis = nowMillis = timeBuffer.millitm; #ifdef DEBUG_CHILD_OUTPUT log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("wrapperReadChildOutput() BEGIN")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("now=%ld, nowMillis=%d"), now, nowMillis); #endif /* Loop and read in CHILD_BLOCK_SIZE characters at a time. * * To keep a JVM outputting lots of content from freezing the Wrapper, we force a return every 250ms. */ while ((maxTimeMS <= 0) || ((durr = (now - startTime) * 1000 + (nowMillis - startTimeMillis)) < maxTimeMS)) { #ifdef DEBUG_CHILD_OUTPUT log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("durr=%ld"), durr); #endif /* If there is not enough space in the work buffer to read in a full block then it needs to be extended. */ if (wrapperChildWorkBufferLen + READ_BUFFER_BLOCK_SIZE > wrapperChildWorkBufferSize) { #ifdef DEBUG_CHILD_OUTPUT log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Expand buffer.")); #endif /* Increase the buffer quickly, but try not to get too big. Increase to a size that is the * greater of size + 1024 or size * 1.1. * Also make sure the new buffer is larger than the buffer len. This should not be necessary * but is safer. */ wrapperChildWorkBufferSize = __max(wrapperChildWorkBufferLen + 1, __max(wrapperChildWorkBufferSize + READ_BUFFER_BLOCK_SIZE, wrapperChildWorkBufferSize + wrapperChildWorkBufferSize / 10)); tempBuffer = malloc(wrapperChildWorkBufferSize + 1); if (!tempBuffer) { outOfMemory(TEXT("WRCO"), 2); return FALSE; } memcpy(tempBuffer, wrapperChildWorkBuffer, wrapperChildWorkBufferLen); tempBuffer[wrapperChildWorkBufferLen] = '\0'; free(wrapperChildWorkBuffer); wrapperChildWorkBuffer = tempBuffer; #ifdef DEBUG_CHILD_OUTPUT log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("buffer now %d bytes"), wrapperChildWorkBufferSize); #endif } #ifdef DEBUG_CHILD_OUTPUT log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Try reading from pipe. totalBuffLen=%d, buffSize=%d"), wrapperChildWorkBufferLen, wrapperChildWorkBufferSize); #endif if (wrapperReadChildOutputBlock(wrapperChildWorkBuffer + (wrapperChildWorkBufferLen), (int)(wrapperChildWorkBufferSize - wrapperChildWorkBufferLen), ¤tBlockRead)) { /* Error already reported. */ return FALSE; } if (currentBlockRead > 0) { /* We read in a block, so increase the length. */ wrapperChildWorkBufferLen += currentBlockRead; if (wrapperChildWorkIsNewLine) { wrapperChildWorkLastDataTime = now; wrapperChildWorkLastDataTimeMillis = nowMillis; wrapperChildWorkIsNewLine = FALSE; } readThisPass = TRUE; #ifdef DEBUG_CHILD_OUTPUT log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT(" Read %d bytes of new output. totalBuffLen=%d, buffSize=%d"), currentBlockRead, wrapperChildWorkBufferLen, wrapperChildWorkBufferSize); #endif } /* Terminate the string just to avoid errors. The buffer has an extra character to handle this. */ wrapperChildWorkBuffer[wrapperChildWorkBufferLen] = '\0'; /* Loop over the contents of the buffer and try and extract as many lines as possible. * Keep track of where we are to avoid unnecessary memory copies. * At this point, the entire buffer will always be unlogged. */ loggedOffset = 0; defer = FALSE; while ((wrapperChildWorkBufferLen > loggedOffset) && (!defer)) { #ifdef DEBUG_CHILD_OUTPUT log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Inner loop. totalBuffLen=%d, loggedOffset=%d, unloggedBuffLen=%d, buffSize=%d"), wrapperChildWorkBufferLen, loggedOffset, wrapperChildWorkBufferLen - loggedOffset, wrapperChildWorkBufferSize); #endif /* We have something in the buffer. Loop and see if we have a complete line to log. * We will always find a LF at the end of the line. On Windows there may be a CR immediately before it. */ cLF = NULL; for (i = loggedOffset; i < wrapperChildWorkBufferLen; i++) { /* If there is a null character, replace it with a question mark (\0 is not a termination character in Java). */ if (wrapperChildWorkBuffer[i] == 0) { wrapperChildWorkBuffer[i] = '?'; } else if (wrapperChildWorkBuffer[i] == (char)CHAR_LF) { cLF = &wrapperChildWorkBuffer[i]; break; } } if (cLF != NULL) { /* We found a valid LF so we know that a full line is ready to be logged. */ #ifdef WIN32 if ((cLF > wrapperChildWorkBuffer) && ((cLF - sizeof(char))[0] == 0x0d)) { #ifdef DEBUG_CHILD_OUTPUT log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Found CR+LF")); #endif /* Replace the CR with a NULL */ (cLF - sizeof(char))[0] = 0; } else { #endif #ifdef DEBUG_CHILD_OUTPUT log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Found LF")); #endif #ifdef WIN32 } #endif /* Replace the LF with a NULL */ cLF[0] = '\0'; /* We have a string to log. */ #ifdef DEBUG_CHILD_OUTPUT #ifdef UNICODE /* It is not easy to log the string as is because they are not wide chars. Send it only to stdout. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Log: (see stdout)")); #ifdef WIN32 wprintf(TEXT("Log: [%S]\n"), wrapperChildWorkBuffer + loggedOffset); #else wprintf(TEXT("Log: [%s]\n"), wrapperChildWorkBuffer + loggedOffset); #endif #else log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Log: [%s]"), wrapperChildWorkBuffer + loggedOffset); #endif #endif /* Actually log the individual line of output. */ logChildOutput(wrapperChildWorkBuffer + loggedOffset); /* Update the offset so we know how far we've logged. */ loggedOffset = cLF - wrapperChildWorkBuffer + 1; wrapperChildWorkIsNewLine = TRUE; #ifdef DEBUG_CHILD_OUTPUT log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("loggedOffset: %d"), loggedOffset); #endif } else { /* If we read this pass or if the last character is a CR on Windows then we always want to defer. */ if (readThisPass #ifdef WIN32 || (wrapperChildWorkBuffer[wrapperChildWorkBufferLen - 1] == 0x0d) #endif /* Avoid dumping partial lines because we call this funtion too quickly more than once. * Never let the line be partial unless more than the LF-Delay threshold has expired. */ || (wrapperData->logLFDelayThreshold == 0) || (((now - wrapperChildWorkLastDataTime) * 1000 + (nowMillis - wrapperChildWorkLastDataTimeMillis)) < wrapperData->logLFDelayThreshold) ) { #ifdef DEBUG_CHILD_OUTPUT #ifdef UNICODE /* It is not easy to log the string as is because they are not wide chars. Send it only to stdout. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Incomplete line. Defer: (see stdout) Age: %d"), (now - wrapperChildWorkLastDataTime) * 1000 + (nowMillis - wrapperChildWorkLastDataTimeMillis)); #ifdef WIN32 wprintf(TEXT("Defer Log: [%S]\n"), wrapperChildWorkBuffer + loggedOffset); #else wprintf(TEXT("Defer Log: [%s]\n"), wrapperChildWorkBuffer + loggedOffset); #endif #else log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Incomplete line. Defer: [%s] Age: %d"), wrapperChildWorkBuffer, (now - wrapperChildWorkLastDataTime) * 1000 + (nowMillis - wrapperChildWorkLastDataTimeMillis)); #endif #endif defer = TRUE; } else { /* We have an incomplete line, but it was from a previous pass and is old enough, so we want to log it as it may be a prompt. * This will always be the complete buffer. */ #ifdef DEBUG_CHILD_OUTPUT #ifdef UNICODE /* It is not easy to log the string as is because they are not wide chars. Send it only to stdout. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Incomplete line, but log now: (see stdout) Age: %d"), (now - wrapperChildWorkLastDataTime) * 1000 + (nowMillis - wrapperChildWorkLastDataTimeMillis)); #ifdef WIN32 wprintf(TEXT("Log: [%S]\n"), wrapperChildWorkBuffer + loggedOffset); #else wprintf(TEXT("Log: [%s]\n"), wrapperChildWorkBuffer + loggedOffset); #endif #else log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Incomplete line, but log now: [%s] Age: %d"), wrapperChildWorkBuffer, (now - wrapperChildWorkLastDataTime) * 1000 + (nowMillis - wrapperChildWorkLastDataTimeMillis)); #endif #endif logChildOutput(wrapperChildWorkBuffer + loggedOffset); /* We know we read everything so we can safely reset the loggedOffset and clear the buffer. */ wrapperChildWorkBuffer[0] = '\0'; wrapperChildWorkBufferLen = 0; loggedOffset = 0; wrapperChildWorkIsNewLine = TRUE; } } } /* We have read as many lines from the buffered output as possible. * If we still have any partial lines, then we need to make sure they are moved to the beginning of the buffer so we can read in another block. */ if (loggedOffset > 0) { if (loggedOffset >= wrapperChildWorkBufferLen) { /* We know we have read everything in. So we can efficiently clear the buffer. */ #ifdef DEBUG_CHILD_OUTPUT log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Cleared Buffer as everything was logged.")); #endif wrapperChildWorkBuffer[0] = '\0'; wrapperChildWorkBufferLen = 0; /* loggedOffset = 0; Not needed. */ } else { /* We have logged one or more lines from the buffer, but unlogged content still exists. It needs to be moved to the head of the buffer. */ #ifdef DEBUG_CHILD_OUTPUT log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Moving %d bytes in buffer for next cycle."), wrapperChildWorkBufferLen - loggedOffset); #endif /* NOTE - This line intentionally does the copy within the same memory space. It is safe the way it is working however. */ wrapperChildWorkBufferLen = wrapperChildWorkBufferLen - loggedOffset; safeMemCpy(wrapperChildWorkBuffer, 0, loggedOffset, wrapperChildWorkBufferLen); /* Shouldn't be needed, but just to make sure the buffer has been ended properly */ wrapperChildWorkBuffer[wrapperChildWorkBufferLen] = 0; /* loggedOffset = 0; Not needed. */ } } else { } if (currentBlockRead <= 0) { /* All done for now. */ if (wrapperChildWorkBufferLen > 0) { #ifdef DEBUG_CHILD_OUTPUT log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("wrapperReadChildOutput() END (Incomplete)")); #endif } else { #ifdef DEBUG_CHILD_OUTPUT log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("wrapperReadChildOutput() END")); #endif } return FALSE; } /* Get the time again */ wrapperGetCurrentTime(&timeBuffer); now = timeBuffer.time; nowMillis = timeBuffer.millitm; } /* If we got here then we timed out. */ #ifdef DEBUG_CHILD_OUTPUT log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("wrapperReadChildOutput() END TIMEOUT")); #endif return TRUE; } /** * Read the output returned by the 'java -version' command. * * @param noVersionSetDefault TRUE if the Java version should be set to the default value * when the "version" token was not found in the output. */ void wrapperReadJavaVersionOutput(int noVersionSetDefault) { wrapperData->jvmDefaultLogLevel = wrapperData->printJVMVersion ? LEVEL_INFO : LEVEL_DEBUG; wrapperData->jvmSource = WRAPPER_SOURCE_JVM_VERSION; wrapperData->jvmVersionFailed = FALSE; javaVersionCurrentParseLine = 0; /* reset the default lines at which the version, maker and bits are assumed to appear. */ javaVersionParseLine = 1; javaVendorParseLine = 3; javaBitsParseLine = 3; /* Make sure to always read everything until the pipe is empty. * At this point the child process is gone so we know that the pipe won't get filled more. */ while (wrapperReadChildOutput(250)) {}; /* Some errors may have been queued when parsing the Java output. Print them now. */ maintainLogger(); if (javaVersionCurrentParseLine < javaVersionParseLine) { /* This means that the output did not have any line containing "version \"". */ if (noVersionSetDefault) { wrapperSetJavaVersion(NULL); } } else { /* If any error occured, it should already be logged and the Java version would be resolved * to its default value. */ } wrapperData->jvmDefaultLogLevel = LEVEL_INFO; wrapperData->jvmSource = WRAPPER_SOURCE_JVM; } /** * Immediately after a JVM is launched and whenever the log file name changes, * the log file name is sent to the JVM where it can be referenced by applications. */ void sendLogFileName() { TCHAR *currentLogFilePath; currentLogFilePath = getCurrentLogfilePath(); if (currentLogFilePath) { wrapperProtocolFunction(WRAPPER_MSG_LOGFILE, currentLogFilePath); free(currentLogFilePath); } } /** * Immediately after a JVM is launched, the wrapper configuration is sent to the * JVM where it can be used as a properties object. */ void sendProperties() { TCHAR *buffer; buffer = linearizeProperties(properties, TEXT('\t')); if (buffer) { wrapperProtocolFunction(WRAPPER_MSG_PROPERTIES, buffer); free(buffer); } } /** * Common cleanup code which should get called when we first decide that the JVM was down. */ void wrapperJVMDownCleanup(int setState) { /* Only set the state to DOWN_CHECK if we are not already in a state which reflects this. */ if (setState) { if (wrapperData->jvmCleanupTimeout > 0) { wrapperSetJavaState(WRAPPER_JSTATE_DOWN_CHECK, wrapperGetTicks(), wrapperData->jvmCleanupTimeout); } else { wrapperSetJavaState(WRAPPER_JSTATE_DOWN_CHECK, wrapperGetTicks(), -1); } } /* Remove java pid file if it was registered and created by this process. */ if (wrapperData->javaPidFilename) { _tunlink(wrapperData->javaPidFilename); } /* Reset the Java PID. * Also do it in wrapperJVMProcessExited(), whichever is called first. */ wrapperData->javaPID = 0; #ifdef WIN32 if (!CloseHandle(wrapperData->javaProcess)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to close the Java process handle: %s"), getLastErrorText()); } wrapperData->javaProcess = NULL; #endif /* Close any open socket to the JVM */ if (wrapperData->stoppedPacketReceived) { wrapperProtocolClose(); } else { /* Leave the socket open so the Wrapper has the chance to read any outstanding packets. */ } } /** * Immediately kill the JVM process and set the JVM state to * WRAPPER_JSTATE_DOWN_CHECK. */ int wrapperKillProcessNow() { #ifdef WIN32 int ret; #endif TCHAR errorMessage[512]; /* Check to make sure that the JVM process is still running */ #ifdef WIN32 ret = WaitForSingleObject(wrapperData->javaProcess, 0); if (ret == WAIT_TIMEOUT) { #else if (waitpid(wrapperData->javaPID, NULL, WNOHANG) == 0) { #endif /* JVM is still up when it should have already stopped itself. */ /* The JVM process is not responding so the only choice we have is to * kill it. */ #ifdef WIN32 /* The TerminateProcess funtion will kill the process, but it * does not correctly notify the process's DLLs that it is shutting * down. Ideally, we would call ExitProcess, but that can only be * called from within the process being killed. */ if (TerminateProcess(wrapperData->javaProcess, 0)) { #else if (kill(wrapperData->javaPID, SIGKILL) == 0) { #endif if (!wrapperData->jvmSilentKill) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("JVM did not exit on request, termination requested.")); } return FALSE; } else { if (!wrapperData->jvmSilentKill) { _sntprintf(errorMessage, 512, TEXT(" Attempt to terminate process failed: %s"), getLastErrorText()); errorMessage[511] = 0; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("JVM did not exit on request.")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, errorMessage); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Attempt to terminate the JVM failed: %s"), getLastErrorText()); } /* Terminating the current JVM failed. Cancel pending restart requests */ wrapperJVMDownCleanup(TRUE); wrapperData->exitCode = wrapperData->errorExitCode; return TRUE; } } wrapperJVMDownCleanup(TRUE); return FALSE; } /** * Puts the Wrapper into a state where the JVM will be killed at the soonest * possible opportunity. It is necessary to wait a moment if a final thread * dump is to be requested. This call will always set the JVM state to * WRAPPER_JSTATE_KILLING. * * @param silent TRUE to skip messages saying that the JVM did not exit on request. * This is useful in certain cases where we kill the JVM without trying * to shut it down cleanly. */ void wrapperKillProcess(int silent) { #ifdef WIN32 int ret; #endif int delay = 0; if ((wrapperData->jState == WRAPPER_JSTATE_DOWN_CLEAN) || (wrapperData->jState == WRAPPER_JSTATE_LAUNCH_DELAY) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_CHECK) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH_STDIN) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH)) { /* Already down. */ if (wrapperData->jState == WRAPPER_JSTATE_LAUNCH_DELAY) { wrapperSetJavaState(WRAPPER_JSTATE_DOWN_CLEAN, wrapperGetTicks(), 0); } return; } /* Check to make sure that the JVM process is still running */ #ifdef WIN32 ret = WaitForSingleObject(wrapperData->javaProcess, 0); if (ret == WAIT_TIMEOUT) { #else if (waitpid(wrapperData->javaPID, NULL, WNOHANG) == 0) { #endif /* JVM is still up when it should have already stopped itself. */ if (wrapperData->requestThreadDumpOnFailedJVMExit) { wrapperRequestDumpJVMState(); delay = wrapperData->requestThreadDumpOnFailedJVMExitDelay; } } wrapperSetJavaState(WRAPPER_JSTATE_KILLING, wrapperGetTicks(), delay); wrapperData->jvmSilentKill = silent; } /** * Add some checks of the properties to try to catch the case where the user is making use of TestWrapper scripts. * * @return TRUE if there is such a missconfiguration. FALSE if all is Ok. */ int checkForTestWrapperScripts() { const TCHAR* prop; prop = getStringProperty(properties, TEXT("wrapper.java.mainclass"), NULL); if (prop) { if (_tcscmp(prop, TEXT("org.tanukisoftware.wrapper.test.Main")) == 0) { /* This is the TestWrapper app. So don't check. */ } else { /* This is a user application, so make sure that they are not using the TestWrapper scripts. */ prop = getStringProperty(properties, TEXT("wrapper.app.parameter.2"), NULL); if (prop) { if (_tcscmp(prop, TEXT("{{TestWrapperBat}}")) == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "--------------------------------------------------------------------")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "We have detected that you are making use of the sample batch files\nthat are designed for the TestWrapper Example Application. When\nsetting up your own application, please copy fresh files over from\nthe Wrapper's src\\bin directory.")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "Shutting down as this will likely cause problems with your\napplication startup.")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "Please see the integration section of the documentation for more\ninformation.")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( " http://wrapper.tanukisoftware.com/integrate")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "--------------------------------------------------------------------")); return TRUE; } else if (_tcscmp(prop, TEXT("{{TestWrapperSh}}")) == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "--------------------------------------------------------------------")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "We have detected that you are making use of the sample shell scripts\nthat are designed for the TestWrapper Example Application. When\nsetting up your own application, please copy fresh files over from\nthe Wrapper's src/bin directory.")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "Shutting down as this will likely cause problems with your\napplication startup.")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "Please see the integration section of the documentation for more\ninformation.")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( " http://wrapper.tanukisoftware.com/integrate")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "--------------------------------------------------------------------")); return TRUE; } } } } return FALSE; } #ifdef WIN32 /** * Creates a human readable representation of the Windows OS the Wrapper is run on. * * @param pszOS the buffer the information gets stored to * @return FALSE if error or no information could be retrieved. TRUE otherwise. */ BOOL GetOSDisplayString(TCHAR** pszOS) { OSVERSIONINFOEX osvi; SYSTEM_INFO si; FARPROC pGNSI; FARPROC pGPI; DWORD dwType; TCHAR buf[80]; ZeroMemory(&si, sizeof(SYSTEM_INFO)); ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); #pragma warning(push) #pragma warning(disable : 4996) /* Visual Studio 2013 deprecates GetVersionEx but we still want to use it. */ if (!GetVersionEx((OSVERSIONINFO*) &osvi)) { return FALSE; } #pragma warning(pop) /* Call GetNativeSystemInfo if supported or GetSystemInfo otherwise.*/ pGNSI = GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetNativeSystemInfo"); if (NULL != pGNSI) { pGNSI(&si); } else { GetSystemInfo(&si); } if ((VER_PLATFORM_WIN32_NT == osvi.dwPlatformId) && (osvi.dwMajorVersion > 4)) { _tcsncpy(*pszOS, TEXT("Microsoft "), OSBUFSIZE); /* Test for the specific product. */ if (osvi.dwMajorVersion == 10) { if (osvi.dwMinorVersion == 0 ) { if (osvi.wProductType == VER_NT_WORKSTATION) { _tcsncat(*pszOS, TEXT("Windows 10 "), OSBUFSIZE); } else { _tcsncat(*pszOS, TEXT("Windows Server 2016 "), OSBUFSIZE); } } } else if (osvi.dwMajorVersion == 6) { if (osvi.dwMinorVersion == 0 ) { if (osvi.wProductType == VER_NT_WORKSTATION) { _tcsncat(*pszOS, TEXT("Windows Vista "), OSBUFSIZE); } else { _tcsncat(*pszOS, TEXT("Windows Server 2008 "), OSBUFSIZE); } } else if (osvi.dwMinorVersion == 1) { if (osvi.wProductType == VER_NT_WORKSTATION) { _tcsncat(*pszOS, TEXT("Windows 7 "), OSBUFSIZE); } else { _tcsncat(*pszOS, TEXT("Windows Server 2008 R2 "), OSBUFSIZE); } } else if ( osvi.dwMinorVersion == 2 ) { if( osvi.wProductType == VER_NT_WORKSTATION ) { _tcsncat(*pszOS, TEXT("Windows 8 "), OSBUFSIZE); } else { _tcsncat(*pszOS, TEXT("Windows Server 2012 "), OSBUFSIZE); } } else if ( osvi.dwMinorVersion == 3 ) { if( osvi.wProductType == VER_NT_WORKSTATION ) { _tcsncat(*pszOS, TEXT("Windows 8.1 "), OSBUFSIZE); } else { _tcsncat(*pszOS, TEXT("Windows Server 2012 R2 "), OSBUFSIZE); } } } else if ((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 2)) { if (GetSystemMetrics(89)) { _tcsncat(*pszOS, TEXT("Windows Server 2003 R2, "), OSBUFSIZE); } else if (osvi.wSuiteMask & 8192) { _tcsncat(*pszOS, TEXT("Windows Storage Server 2003"), OSBUFSIZE); } else if (osvi.wSuiteMask & 32768) { _tcsncat(*pszOS, TEXT("Windows Home Server"), OSBUFSIZE); } else if (osvi.wProductType == VER_NT_WORKSTATION && si.wProcessorArchitecture==PROCESSOR_ARCHITECTURE_AMD64) { _tcsncat(*pszOS, TEXT("Windows XP Professional x64 Edition"), OSBUFSIZE); } else { _tcsncat(*pszOS, TEXT("Windows Server 2003, "), OSBUFSIZE); } /* Test for the server type. */ if (osvi.wProductType != VER_NT_WORKSTATION) { if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_IA64) { if (osvi.wSuiteMask & VER_SUITE_DATACENTER) { _tcsncat(*pszOS, TEXT("Datacenter Edition for Itanium-based Systems"), OSBUFSIZE); } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) { _tcsncat(*pszOS, TEXT("Enterprise Edition for Itanium-based Systems"), OSBUFSIZE); } } else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) { if (osvi.wSuiteMask & VER_SUITE_DATACENTER) { _tcsncat(*pszOS, TEXT("Datacenter x64 Edition"), OSBUFSIZE); } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) { _tcsncat(*pszOS, TEXT("Enterprise x64 Edition"), OSBUFSIZE); } else { _tcsncat(*pszOS, TEXT("Standard x64 Edition"), OSBUFSIZE); } } else { if (osvi.wSuiteMask & VER_SUITE_COMPUTE_SERVER) { _tcsncat(*pszOS, TEXT("Compute Cluster Edition"), OSBUFSIZE); } else if (osvi.wSuiteMask & VER_SUITE_DATACENTER) { _tcsncat(*pszOS, TEXT("Datacenter Edition"), OSBUFSIZE); } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) { _tcsncat(*pszOS, TEXT("Enterprise Edition"), OSBUFSIZE); } else if (osvi.wSuiteMask & VER_SUITE_BLADE) { _tcsncat(*pszOS, TEXT("Web Edition" ), OSBUFSIZE); } else { _tcsncat(*pszOS, TEXT("Standard Edition"), OSBUFSIZE); } } } } else if ((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 1)) { _tcsncat(*pszOS, TEXT("Windows XP "), OSBUFSIZE); if (osvi.wSuiteMask & VER_SUITE_PERSONAL) { _tcsncat(*pszOS, TEXT("Home Edition"), OSBUFSIZE); } else { _tcsncat(*pszOS, TEXT("Professional"), OSBUFSIZE); } } else if ((osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion == 0)) { _tcsncat(*pszOS, TEXT("Windows 2000 "), OSBUFSIZE); if (osvi.wProductType == VER_NT_WORKSTATION) { _tcsncat(*pszOS, TEXT("Professional"), OSBUFSIZE); } else { if (osvi.wSuiteMask & VER_SUITE_DATACENTER) { _tcsncat(*pszOS, TEXT("Datacenter Server"), OSBUFSIZE); } else if (osvi.wSuiteMask & VER_SUITE_ENTERPRISE) { _tcsncat(*pszOS, TEXT("Advanced Server"), OSBUFSIZE); } else { _tcsncat(*pszOS, TEXT("Server"), OSBUFSIZE); } } } if (osvi.dwMajorVersion >= 6) { pGPI = GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetProductInfo"); pGPI(osvi.dwMajorVersion, osvi.dwMinorVersion, 0, 0, &dwType); switch (dwType) { case 1: _tcsncat(*pszOS, TEXT("Ultimate Edition" ), OSBUFSIZE); break; case 48: _tcsncat(*pszOS, TEXT("Professional"), OSBUFSIZE); break; case 3: _tcsncat(*pszOS, TEXT("Home Premium Edition"), OSBUFSIZE); break; case 67: _tcsncat(*pszOS, TEXT("Home Basic Edition"), OSBUFSIZE); break; case 4: _tcsncat(*pszOS, TEXT("Enterprise Edition"), OSBUFSIZE); break; case 6: _tcsncat(*pszOS, TEXT("Business Edition"), OSBUFSIZE); break; case 11: _tcsncat(*pszOS, TEXT("Starter Edition"), OSBUFSIZE); break; case 18: _tcsncat(*pszOS, TEXT("Cluster Server Edition"), OSBUFSIZE); break; case 8: _tcsncat(*pszOS, TEXT("Datacenter Edition"), OSBUFSIZE); break; case 12: _tcsncat(*pszOS, TEXT("Datacenter Edition (core installation)"), OSBUFSIZE); break; case 10: _tcsncat(*pszOS, TEXT("Enterprise Edition"), OSBUFSIZE); break; case 14: _tcsncat(*pszOS, TEXT("Enterprise Edition (core installation)"), OSBUFSIZE); break; case 15: _tcsncat(*pszOS, TEXT("Enterprise Edition for Itanium-based Systems"), OSBUFSIZE); break; case 9: _tcsncat(*pszOS, TEXT("Small Business Server"), OSBUFSIZE); break; case 25: _tcsncat(*pszOS, TEXT("Small Business Server Premium Edition"), OSBUFSIZE); break; case 7: _tcsncat(*pszOS, TEXT("Standard Edition"), OSBUFSIZE); break; case 13: _tcsncat(*pszOS, TEXT("Standard Edition (core installation)"), OSBUFSIZE); break; case 17: _tcsncat(*pszOS, TEXT("Web Server Edition"), OSBUFSIZE); break; case 101: _tcsncat(*pszOS, TEXT("Home"), OSBUFSIZE); break; case 98: _tcsncat(*pszOS, TEXT("Home N"), OSBUFSIZE); break; case 99: _tcsncat(*pszOS, TEXT("Home China"), OSBUFSIZE); break; case 100: _tcsncat(*pszOS, TEXT("Home Single Language"), OSBUFSIZE); break; case 104: _tcsncat(*pszOS, TEXT("Mobile"), OSBUFSIZE); break; case 133: _tcsncat(*pszOS, TEXT("Mobile Enterprise"), OSBUFSIZE); break; case 121: _tcsncat(*pszOS, TEXT("Education"), OSBUFSIZE); break; case 122: _tcsncat(*pszOS, TEXT("Education N"), OSBUFSIZE); break; case 70: _tcsncat(*pszOS, TEXT("Enterprise E"), OSBUFSIZE); break; case 84: _tcsncat(*pszOS, TEXT("Enterprise N (evaluation installation)"), OSBUFSIZE); break; case 27: _tcsncat(*pszOS, TEXT("Enterprise N"), OSBUFSIZE); break; case 72: _tcsncat(*pszOS, TEXT("Enterprise (evaluation installation)"), OSBUFSIZE); break; } } /* Include service pack (if any) and build number. */ if (_tcslen(osvi.szCSDVersion) > 0) { _tcsncat(*pszOS, TEXT(" "), OSBUFSIZE); _tcsncat(*pszOS, osvi.szCSDVersion, OSBUFSIZE); } _sntprintf(buf, 80, TEXT(" (build %d)"), osvi.dwBuildNumber); _tcsncat(*pszOS, buf, OSBUFSIZE); if (osvi.dwMajorVersion >= 6) { if ((si.wProcessorArchitecture & PROCESSOR_ARCHITECTURE_IA64) || (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64)) { _tcsncat(*pszOS, TEXT(", 64-bit"), OSBUFSIZE); } else if (si.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) { _tcsncat(*pszOS, TEXT(", 32-bit"), OSBUFSIZE); } } return TRUE; } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Unknown Windows Version")); return FALSE; } } #endif #ifndef ENABLE_QUICK_EDIT_MODE #define ENABLE_QUICK_EDIT_MODE 0x0040 #endif /** * Launch common setup code. */ int wrapperRunCommonInner() { const TCHAR *prop; #ifdef WIN32 TCHAR* szOS; DWORD consoleMode; HANDLE consoleHandle; int quickEditStatus; #endif struct tm timeTM; TCHAR* tz1; TCHAR* tz2; #if defined(UNICODE) size_t req; #endif /* Make sure the tick timer is working correctly. */ if (wrapperTickAssertions()) { return 1; } /* Log a startup banner. */ wrapperVersionBanner(); /* The following code will display a licensed to block if a license key is found * in the Wrapper configuration. This piece of code is required as is for * Development License owners to be in complience with their development license. * This code does not do any validation of the license keys and works differently * from the license code found in the Standard and Professional Editions of the * Wrapper. */ prop = getStringProperty(properties, TEXT("wrapper.license.type"), TEXT("")); if (strcmpIgnoreCase(prop, TEXT("DEV")) == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Licensed to %s for %s"), getStringProperty(properties, TEXT("wrapper.license.licensee"), TEXT("(LICENSE INVALID)")), getStringProperty(properties, TEXT("wrapper.license.dev_application"), TEXT("(LICENSE INVALID)"))); } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("")); if (checkForTestWrapperScripts()) { return 1; } #ifdef WIN32 if (initializeStartup()) { return 1; } if (wrapperProcessHasVisibleConsole()) { consoleHandle = GetStdHandle(STD_INPUT_HANDLE); if (consoleHandle == NULL) { /* Requested handle does not exist. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Standard input does not exist for the process.")); } else if (consoleHandle == INVALID_HANDLE_VALUE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Faild to retrieve the standard input handle: %s"), getLastErrorText()); } else { if (!GetConsoleMode(consoleHandle, &consoleMode)) { if (GetLastError() == ERROR_INVALID_HANDLE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The standard input appears to be a pipe. Not checking console mode.")); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to retrieve the current input mode of the console: %s"), getLastErrorText()); } } else { quickEditStatus = getStatusProperty(properties, TEXT("wrapper.console.quickedit"), STATUS_DISABLED); if (consoleMode & ENABLE_QUICK_EDIT_MODE) { /* Quick mode was enabled. */ if (quickEditStatus == STATUS_DISABLED) { /* Disable quick edit mode. */ if (!SetConsoleMode(consoleHandle, consoleMode & ~ENABLE_QUICK_EDIT_MODE)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to disable QuickEdit Mode for the console: %s"), getLastErrorText()); quickEditStatus = STATUS_ENABLED; } } else if (quickEditStatus == STATUS_UNCHANGED) { quickEditStatus = STATUS_ENABLED; } } else { /* Quick mode was disabled. */ if (quickEditStatus == STATUS_ENABLED) { /* Enable quick edit mode. */ if (!SetConsoleMode(consoleHandle, consoleMode | ENABLE_QUICK_EDIT_MODE)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to enable QuickEdit Mode for the console: %s"), getLastErrorText()); quickEditStatus = STATUS_DISABLED; } } } if (quickEditStatus == STATUS_ENABLED) { log_printf(WRAPPER_SOURCE_WRAPPER, getLogLevelForName(getStringProperty(properties, TEXT("wrapper.console.quickedit.loglevel"), TEXT("WARN"))), TEXT("Running in a console with QuickEdit Mode. Be careful when selecting text in the\n console as this may cause to block the Java Application.\n")); } } } } #endif if (wrapperData->isDebugging) { timeTM = wrapperGetReleaseTime(); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Release time: %04d/%02d/%02d %02d:%02d:%02d"), timeTM.tm_year + 1900, timeTM.tm_mon + 1, timeTM.tm_mday, timeTM.tm_hour, timeTM.tm_min, timeTM.tm_sec ); timeTM = wrapperGetBuildTime(); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Build time: %04d/%02d/%02d %02d:%02d:%02d"), timeTM.tm_year + 1900, timeTM.tm_mon + 1, timeTM.tm_mday, timeTM.tm_hour, timeTM.tm_min, timeTM.tm_sec ); /* Display timezone information. */ tzset(); #if defined(UNICODE) #if !defined(WIN32) req = mbstowcs(NULL, tzname[0], MBSTOWCS_QUERY_LENGTH); if (req == (size_t)-1) { return 1; } tz1 = malloc(sizeof(TCHAR) * (req + 1)); if (!tz1) { outOfMemory(TEXT("LHN"), 1); } else { mbstowcs(tz1, tzname[0], req + 1); tz1[req] = TEXT('\0'); /* Avoid bufferflows caused by badly encoded characters. */ req = mbstowcs(NULL, tzname[1], MBSTOWCS_QUERY_LENGTH); if (req == (size_t)-1) { free(tz1); return 1; } tz2 = malloc(sizeof(TCHAR) * (req + 1)); if (!tz2) { outOfMemory(TEXT("LHN"), 2); free(tz1); } else { mbstowcs(tz2, tzname[1], req + 1); tz2[req] = TEXT('\0'); /* Avoid bufferflows caused by badly encoded characters. */ #else req = MultiByteToWideChar(CP_OEMCP, 0, tzname[0], -1, NULL, 0); if (req <= 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Invalid multibyte sequence in port address \"%s\" : %s"), tzname[0], getLastErrorText()); return 1; } tz1 = malloc((req + 1) * sizeof(TCHAR)); if (!tz1) { outOfMemory(TEXT("LHN"), 1); } else { MultiByteToWideChar(CP_OEMCP,0, tzname[0], -1, tz1, (int)req + 1); req = MultiByteToWideChar(CP_OEMCP, 0, tzname[1], -1, NULL, 0); if (req <= 0) { free(tz1); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Invalid multibyte sequence in port address \"%s\" : %s"), tzname[1], getLastErrorText()); return 1; } tz2 = malloc((req + 1) * sizeof(TCHAR)); if (!tz2) { free(tz1); outOfMemory(TEXT("LHN"), 2); } else { MultiByteToWideChar(CP_OEMCP,0, tzname[1], -1, tz2, (int)req + 1); #endif #else tz1 = tzname[0]; tz2 = tzname[1]; #endif #ifndef FREEBSD log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Timezone: %s (%s) Offset: %ld, hasDaylight: %d"), tz1, tz2, timezone, daylight); #else log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Timezone: %s (%s) Offset: %ld"), tz1, tz2, timezone); #endif if (wrapperData->useSystemTime) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Using system timer.")); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Using tick timer.")); } #ifdef UNICODE free(tz1); free(tz2); } } #endif /* Log the Wrapper's PID. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("PID: %d"), wrapperData->wrapperPID); } #ifdef WIN32 if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Current User: %s Domain: %s"), (wrapperData->userName ? wrapperData->userName : TEXT("N/A")), (wrapperData->domainName ? wrapperData->domainName : TEXT("N/A"))); szOS = calloc(OSBUFSIZE, sizeof(TCHAR)); if (szOS) { if (GetOSDisplayString(&szOS)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Operating System ID: %s"), szOS); } free(szOS); } if (isCygwin()) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Cygwin detected")); } if (_tcscmp(wrapperBits, TEXT("32")) == 0) { if (wrapperData->DEPStatus) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("DEP status: Enabled")); } else if (!wrapperData->DEPApiAvailable) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("DEP status: Not supported")); } else if (wrapperData->DEPError == 5) { /* If the operating system is configured to always use DEP for all processes, * then SetProcessDEPPolicy() will return an Access Denied Error (5), but * that doesn't mean DEP is Disabled.*/ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("DEP status: Unchanged (set by the OS)")); } else if (wrapperData->DEPError) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("DEP status: Disabled (0x%x)"), wrapperData->DEPError); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("DEP status: Disabled")); } } } #endif #ifdef FREEBSD /* log the iconv library in use. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Iconv library: %s"), getIconvLibName()); } #endif /* Dump the configured properties */ dumpProperties(properties); /* Dump the environment variables */ dumpEnvironment(); #ifndef WIN32 showResourceslimits(); #endif #ifdef _DEBUG /* Multi-line logging tests. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("----- Should be 5 lines -----")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("\nLINE2:\n\nLINE4:\n")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("----- Next is one line ------")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("----- Next is two lines -----")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("\n")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("----- Next is two lines -----")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("ABC\nDEF")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("-----------------------------")); #endif #ifdef WRAPPER_FILE_DEBUG wrapperFileTests(); #endif return 0; } int wrapperRunCommon(const TCHAR *runMode) { int exitCode; /* Setup the wrapperData structure. */ wrapperSetWrapperState(WRAPPER_WSTATE_STARTING); wrapperSetJavaState(WRAPPER_JSTATE_DOWN_CLEAN, 0, -1); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("--> Wrapper Started as %s"), runMode); wrapperData->runCommonStarted = TRUE; /* Initialize the wrapper */ exitCode = wrapperInitializeRun(); if (exitCode == 0) { if (!wrapperRunCommonInner()) { /* Enter main event loop */ wrapperEventLoop(); /* Clean up any open sockets. */ wrapperProtocolClose(); protocolStopServer(); exitCode = wrapperData->exitCode; } else { exitCode = wrapperData->errorExitCode; } } else { exitCode = wrapperData->errorExitCode; } return exitCode; } /** * Launch the wrapper as a console application. */ int wrapperRunConsole() { return wrapperRunCommon(TEXT("Console")); } /** * Launch the wrapper as a service application. */ int wrapperRunService() { return wrapperRunCommon( #ifdef WIN32 TEXT("Service") #else TEXT("Daemon") #endif ); } /** * Used to ask the state engine to shut down the JVM and Wrapper. * * @param exitCode Exit code to use when shutting down. * @param force True to force the Wrapper to shutdown even if some configuration * had previously asked that the JVM be restarted. This will reset * any existing restart requests, but it will still be possible for * later actions to request a restart. */ void wrapperStopProcess(int exitCode, int force) { /* If we are pausing or paused, cancel it. */ if ((wrapperData->wState == WRAPPER_WSTATE_PAUSING) || (wrapperData->wState == WRAPPER_WSTATE_PAUSED)) { wrapperSetWrapperState(WRAPPER_WSTATE_STARTED); if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("wrapperStopProcess(%d, %s) called while pausing or being paused."), exitCode, (force ? TEXT("TRUE") : TEXT("FALSE"))); } } /* If we are are not aready shutting down, then do so. */ if ((wrapperData->wState == WRAPPER_WSTATE_STOPPING) || (wrapperData->wState == WRAPPER_WSTATE_STOPPED)) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("wrapperStopProcess(%d, %s) called while stopping. (IGNORED)"), exitCode, (force ? TEXT("TRUE") : TEXT("FALSE"))); } } else { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("wrapperStopProcess(%d, %s) called."), exitCode, (force ? TEXT("TRUE") : TEXT("FALSE"))); } /* If it has not already been set, set the exit request flag. */ if (wrapperData->exitRequested || (wrapperData->jState == WRAPPER_JSTATE_DOWN_CLEAN) || (wrapperData->jState == WRAPPER_JSTATE_STOP) || (wrapperData->jState == WRAPPER_JSTATE_STOPPING) || (wrapperData->jState == WRAPPER_JSTATE_STOPPED) || (wrapperData->jState == WRAPPER_JSTATE_KILLING) || (wrapperData->jState == WRAPPER_JSTATE_KILLED) || (wrapperData->jState == WRAPPER_JSTATE_KILL) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_CHECK) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH_STDIN) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH)) { /* JVM is already down or going down. */ } else { wrapperData->exitRequested = TRUE; } wrapperData->exitCode = exitCode; if (force) { /* Make sure that further restarts are disabled. */ wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_NO; /* Do not call wrapperSetWrapperState(WRAPPER_WSTATE_STOPPING) here. * It will be called by the wrappereventloop.c.jStateDown once the * the JVM is completely down. Calling it here will make it * impossible to trap and restart based on exit codes or other * Wrapper configurations. */ if (wrapperData->isDebugging) { if ((wrapperData->restartRequested == WRAPPER_RESTART_REQUESTED_AUTOMATIC) || (wrapperData->restartRequested == WRAPPER_RESTART_REQUESTED_CONFIGURED)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" Overriding request to restart JVM.")); } } } else { /* Do not call wrapperSetWrapperState(WRAPPER_WSTATE_STOPPING) here. * It will be called by the wrappereventloop.c.jStateDown once the * the JVM is completely down. Calling it here will make it * impossible to trap and restart based on exit codes. */ if (wrapperData->isDebugging) { if ((wrapperData->restartRequested == WRAPPER_RESTART_REQUESTED_AUTOMATIC) || (wrapperData->restartRequested == WRAPPER_RESTART_REQUESTED_CONFIGURED)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" Stop ignored. Continuing to restart JVM.")); } } } } } /** * Depending on the current state, we want to change the exact message displayed when restarting the JVM. * * The logic here needs to match that in wrapperRestartProcess. */ const TCHAR *wrapperGetRestartProcessMessage() { if ((wrapperData->jState == WRAPPER_JSTATE_DOWN_CLEAN) || (wrapperData->jState == WRAPPER_JSTATE_STOP) || (wrapperData->jState == WRAPPER_JSTATE_STOPPING) || (wrapperData->jState == WRAPPER_JSTATE_STOPPED) || (wrapperData->jState == WRAPPER_JSTATE_KILLING) || (wrapperData->jState == WRAPPER_JSTATE_KILL) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_CHECK) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH_STDIN) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH) || (wrapperData->jState == WRAPPER_JSTATE_LAUNCH_DELAY)) { if (wrapperData->restartRequested || (wrapperData->jState == WRAPPER_JSTATE_LAUNCH_DELAY)) { return TEXT("Restart JVM (Ignoring, already restarting)."); } else { return TEXT("Restart JVM (Ignoring, already shutting down)."); } } else if (wrapperData->exitRequested || wrapperData->restartRequested) { return TEXT("Restart JVM (Ignoring, already restarting)."); } else { return TEXT("Restarting JVM."); } } /** * Depending on the current state, we want to change the exact message displayed when pausing the Wrapper. * * The logic here needs to match that in wrapperPauseProcess. */ const TCHAR *wrapperGetPauseProcessMessage() { if (!wrapperData->pausable) { return TEXT("Pause (Ignoring, the Wrapper was not set pausable)."); } else if ((wrapperData->wState == WRAPPER_WSTATE_STOPPING) || (wrapperData->wState == WRAPPER_WSTATE_STOPPED)) { return TEXT("Pause (Ignoring, already stopping)."); } else if (wrapperData->wState == WRAPPER_WSTATE_PAUSING) { return TEXT("Pause (Ignoring, already pausing)."); } else if (wrapperData->wState == WRAPPER_WSTATE_PAUSED) { return TEXT("Pause (Ignoring, already paused)."); } else { return TEXT("Pausing..."); } } /** * Depending on the current state, we want to change the exact message displayed when resuming the Wrapper. * * The logic here needs to match that in wrapperResumeProcess. */ const TCHAR *wrapperGetResumeProcessMessage() { if ((wrapperData->wState == WRAPPER_WSTATE_STOPPING) || (wrapperData->wState == WRAPPER_WSTATE_STOPPED)) { return TEXT("Resume (Ignoring, already stopping)."); } else if (wrapperData->wState == WRAPPER_WSTATE_STARTING) { return TEXT("Resume (Ignoring, already starting)."); } else if (wrapperData->wState == WRAPPER_WSTATE_STARTED) { return TEXT("Resume (Ignoring, already started)."); } else if (wrapperData->wState == WRAPPER_WSTATE_RESUMING) { return TEXT("Resume (Ignoring, already resuming)."); } else { return TEXT("Resuming..."); } } /** * Used to ask the state engine to shut down the JVM. This are always intentional restart requests. */ void wrapperRestartProcess() { /* If it has not already been set, set the restart request flag in the wrapper data. */ if (wrapperData->exitRequested || wrapperData->restartRequested || (wrapperData->jState == WRAPPER_JSTATE_DOWN_CLEAN) || (wrapperData->jState == WRAPPER_JSTATE_STOP) || (wrapperData->jState == WRAPPER_JSTATE_STOPPING) || (wrapperData->jState == WRAPPER_JSTATE_STOPPED) || (wrapperData->jState == WRAPPER_JSTATE_KILLING) || (wrapperData->jState == WRAPPER_JSTATE_KILL) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_CHECK) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH_STDIN) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH) || (wrapperData->jState == WRAPPER_JSTATE_LAUNCH_DELAY)) { /* Down but not yet restarted. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("wrapperRestartProcess() called. (IGNORED)")); } } else { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("wrapperRestartProcess() called.")); } wrapperData->exitRequested = TRUE; wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_CONFIGURED; } } /** * Used to ask the state engine to pause the JVM. * * @param actionSourceCode Tracks where the action originated. */ void wrapperPauseProcess(int actionSourceCode) { TCHAR msgBuffer[10]; if (!wrapperData->pausable) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT( "wrapperPauseProcess() called but wrapper.pausable is FALSE. (IGNORED)")); } return; } if ((wrapperData->wState == WRAPPER_WSTATE_STOPPING) || (wrapperData->wState == WRAPPER_WSTATE_STOPPED)) { /* If we are already shutting down, then ignore and continue to do so. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT( "wrapperPauseProcess() called while stopping. (IGNORED)")); } } else if (wrapperData->wState == WRAPPER_WSTATE_PAUSING) { /* If we are currently being paused, then ignore and continue to do so. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT( "wrapperPauseProcess() called while pausing. (IGNORED)")); } } else if (wrapperData->wState == WRAPPER_WSTATE_PAUSED) { /* If we are currently paused, then ignore and continue to do so. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT( "wrapperPauseProcess() called while paused. (IGNORED)")); } } else { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT( "wrapperPauseProcess() called.")); } wrapperSetWrapperState(WRAPPER_WSTATE_PAUSING); if (!wrapperData->pausableStopJVM) { /* Notify the Java process. */ _sntprintf(msgBuffer, 10, TEXT("%d"), actionSourceCode); wrapperProtocolFunction(WRAPPER_MSG_PAUSE, msgBuffer); } } } /** * Used to ask the state engine to resume a paused the JVM. * * @param actionSourceCode Tracks where the action originated. */ void wrapperResumeProcess(int actionSourceCode) { TCHAR msgBuffer[10]; if ((wrapperData->wState == WRAPPER_WSTATE_STOPPING) || (wrapperData->wState == WRAPPER_WSTATE_STOPPED)) { /* If we are already shutting down, then ignore and continue to do so. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT( "wrapperResumeProcess() called while stopping. (IGNORED)")); } } else if (wrapperData->wState == WRAPPER_WSTATE_STARTING) { /* If we are currently being started, then ignore and continue to do so. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT( "wrapperResumeProcess() called while starting. (IGNORED)")); } } else if (wrapperData->wState == WRAPPER_WSTATE_STARTED) { /* If we are currently started, then ignore and continue to do so. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT( "wrapperResumeProcess() called while started. (IGNORED)")); } } else if (wrapperData->wState == WRAPPER_WSTATE_RESUMING) { /* If we are currently being continued, then ignore and continue to do so. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT( "wrapperResumeProcess() called while resuming. (IGNORED)")); } } else { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT( "wrapperResumeProcess() called.")); } /* If we were configured to stop the JVM then we want to reset its failed * invocation count as the current stoppage was expected. */ if (wrapperData->pausableStopJVM) { wrapperData->failedInvocationCount = 0; } wrapperSetWrapperState(WRAPPER_WSTATE_RESUMING); if (!wrapperData->pausableStopJVM) { /* Notify the Java process. */ _sntprintf(msgBuffer, 10, TEXT("%d"), actionSourceCode); wrapperProtocolFunction(WRAPPER_MSG_RESUME, msgBuffer); } } } /** * Sends a command off to the JVM asking it to perform a garbage collection sweep. * * @param actionSourceCode Tracks where the action originated. */ void wrapperRequestJVMGC(int actionSourceCode) { TCHAR msgBuffer[10]; /* Notify the Java process. */ _sntprintf(msgBuffer, 10, TEXT("%d"), actionSourceCode); wrapperProtocolFunction(WRAPPER_MSG_GC, msgBuffer); } /** * Loops over and strips all double quotes from prop and places the * stripped version into propStripped. * * The exception is double quotes that are preceeded by a backslash * in this case the backslash is stripped. * * If two backslashes are found in a row, then the first escapes the * second and the second is removed. */ static size_t wrapperStripQuotesInner(const TCHAR *prop, size_t propLen, TCHAR *propStripped) { size_t len; int i, j; len = propLen; j = 0; for (i = 0; i < (int)len; i++) { if ((prop[i] == TEXT('\\')) && (i < (int)len - 1)) { if (prop[i + 1] == TEXT('\\')) { /* Double backslash. Keep the first, and skip the second. */ propStripped[j] = prop[i]; j++; i++; } else if (prop[i + 1] == TEXT('\"')) { /* Escaped quote. Keep the quote. */ propStripped[j] = prop[i + 1]; j++; i++; } else { /* Include the backslash as normal. */ propStripped[j] = prop[i]; j++; } } else if (prop[i] == TEXT('\"')) { /* Quote. Skip it. */ } else { propStripped[j] = prop[i]; j++; } } return j; } /** * Stripped quotes out of the prop argument. * The resulting value in propStripped will always be equal or shorter in length * so the propStripped buffer should always be equal to the prop buffer in length. */ void wrapperStripQuotes(const TCHAR *prop, TCHAR *propStripped) { size_t len; len = wrapperStripQuotesInner(prop, _tcslen(prop), propStripped); propStripped[len] = TEXT('\0'); } /** * Adds quotes around the specified string in such a way that everything is * escaped correctly. If the bufferSize is not large enough then the * required size will be returned. 0 is returned if successful. */ size_t wrapperQuoteValue(const TCHAR* value, TCHAR *buffer, size_t bufferSize) { size_t len = _tcslen(value); size_t in = 0; size_t out = 0; size_t in2; int escape; /* Initial quote. */ if (out < bufferSize) { buffer[out] = TEXT('"'); } out++; /* Copy over characters of value. */ while ((in < len) && (value[in] != TEXT('\0'))) { escape = FALSE; if (value[in] == TEXT('\\')) { /* All '\' characters in a row prior to a '"' or the end of the string need to be * escaped */ in2 = in + 1; while ((in2 < len) && (value[in2] == TEXT('\\'))) { in2++; } escape = ((in2 >= len) || (value[in2] == TEXT('"'))); } else if (value[in] == TEXT('"')) { escape = TRUE; } if (escape) { /* Needs to be escaped. */ if (out < bufferSize) { buffer[out] = TEXT('\\'); } out++; } if (out < bufferSize) { buffer[out] = value[in]; } out++; in++; } /* Trailing quote. */ if (out < bufferSize) { buffer[out] = TEXT('"'); } out++; /* Null terminate. */ if (out < bufferSize) { buffer[out] = TEXT('\0'); } out++; if (out <= bufferSize) { return 0; } else { return out; } } /** * Checks the quotes in the value and displays an error if there are any problems. * This can be useful to help users debug quote problems. */ int wrapperCheckQuotes(const TCHAR *value, const TCHAR *propName) { size_t len = _tcslen(value); size_t in = 0; size_t in2 = 0; int inQuote = FALSE; int escaped; while (in < len) { if (value[in] == TEXT('"')) { /* Decide whether or not this '"' is escaped. */ escaped = FALSE; if (in > 0) { in2 = in - 1; while (value[in2] == TEXT('\\')) { escaped = !escaped; if (in2 > 0) { in2--; } else { break; } } } if (!escaped) { inQuote = !inQuote; } } else if (inQuote) { /* Quoted text. */ } else { /* Unquoted. white space is bad. */ if (value[in] == TEXT(' ')) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The value of property '%s', '%s' contains unquoted spaces and will most likely result in an invalid java command line."), propName, value); return 1; } } in++; } if (inQuote) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The value of property '%s', '%s' contains an unterminated quote and will most likely result in an invalid java command line."), propName, value); return 1; } return 0; } #ifndef WIN32 int checkIfExecutable(const TCHAR *filename) { int result; #if defined(WIN32) && !defined(WIN64) struct _stat64i32 statInfo; #else struct stat statInfo; #endif result = _tstat(filename, &statInfo); if (result < 0) { return 0; } if (!S_ISREG(statInfo.st_mode)) { return 0; } if (statInfo.st_uid == geteuid()) { return statInfo.st_mode & S_IXUSR; } if (statInfo.st_gid == getegid()) { return statInfo.st_mode & S_IXGRP; } return statInfo.st_mode & S_IXOTH; } #endif int checkIfBinary(const TCHAR *filename) { FILE* f; unsigned char head[5]; int r; f = _tfopen(filename, TEXT("rb")); if (!f) { /*couldnt find the java command... wrapper will moan later*/ return 1; } else { r = (int)fread( head,1, 4, f); if (r != 4) { fclose(f); return 0; } fclose(f); head[4] = '\0'; if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Magic number for file %s: 0x%02x%02x%02x%02x"), filename, head[0], head[1], head[2], head[3]); } #if defined(LINUX) || defined(FREEBSD) || defined(SOLARIS) if (head[1] == 'E' && head[2] == 'L' && head[3] == 'F') { return 1; /*ELF */ #elif defined(AIX) /* http://en.wikipedia.org/wiki/XCOFF */ if (head[0] == 0x01 && head[1] == 0xf7 && head[2] == 0x00) { /* 0x01f700NN */ return 1; /*xcoff 64*/ } else if (head[0] == 0x01 && head[1] == 0xdf && head[2] == 0x00) { /* 0x01df00NN */ return 1; /*xcoff 32*/ #elif defined(MACOSX) if (head[0] == 0xca && head[1] == 0xfe && head[2] == 0xba && head[3] == 0xbe) { /* 0xcafebabe */ return 1; /*MACOS Universal binary*/ } else if (head[0] == 0xcf && head[1] == 0xfa && head[2] == 0xed && head[3] == 0xfe) { /* 0xcffaedfe */ return 1; /*MACOS x86_64 binary*/ } else if (head[0] == 0xce && head[1] == 0xfa && head[2] == 0xed && head[3] == 0xfe) { /* 0xcefaedfe */ return 1; /*MACOS i386 binary*/ } else if (head[0] == 0xfe && head[1] == 0xed && head[2] == 0xfa && head[3] == 0xce) { /* 0xfeedface */ return 1; /*MACOS ppc, ppc64 binary*/ #elif defined(HPUX) if (head[0] == 0x02 && head[1] == 0x10 && head[2] == 0x01 && head[3] == 0x08) { /* 0x02100108 PA-RISC 1.1 */ return 1; /*HP UX PA RISC 32*/ } else if (head[0] == 0x02 && head[1] == 0x14 && head[2] == 0x01 && head[3] == 0x07) { /* 0x02140107 PA-RISC 2.0 */ return 1; /*HP UX PA RISC 32*/ } else if (head[1] == 'E' && head[2] == 'L' && head[3] == 'F') { return 1; /*ELF */ #elif defined(WIN32) if (head[0] == 'M' && head[1] == 'Z') { return 1; /* MS */ #else if (FALSE) { #error I dont know what to do for this host type. (in checkIfBinary()) #endif } else { return 0; } } } /** * Indicates if the Java command is jdb * * @param path to the Java command. Assumed not NULL. * @param whether the path is surrounded with quotes or not. * * @return TRUE if this is jdb, FALSE otherwise. */ int isCommandJdb(const TCHAR* path, int hasQuotes) { const TCHAR* fileName; if (hasQuotes) { fileName = getFileName(path + 1); } else { fileName = getFileName(path); } if ((_tcscmp(fileName, hasQuotes ? TEXT("jdb\"") : TEXT("jdb")) == 0) #ifdef WIN32 || (_tcscmp(fileName, hasQuotes ? TEXT("jdb.exe\"") : TEXT("jdb.exe")) == 0) #endif ) { return TRUE; } return FALSE; } #ifdef WIN32 /** * Indicates if the Java command is javaw * * @param path to the Java command. Assumed not NULL. * @param whether the path is surrounded with quotes or not. * * @return TRUE if this is javaw, FALSE otherwise. */ int isCommandJavaw(const TCHAR* path, int hasQuotes) { const TCHAR* fileName; if (hasQuotes) { fileName = getFileName(path + 1); } else { fileName = getFileName(path); } if ((_tcscmp(fileName, hasQuotes ? TEXT("javaw.exe\"") : TEXT("javaw.exe")) == 0) || (_tcscmp(fileName, hasQuotes ? TEXT("javaw\"") : TEXT("javaw")) == 0)) { return TRUE; } return FALSE; } #else /** * Searches for an executable file and returns its absolute path if it is found. * NOTE: This function behaves like Windows it receives a file name without relative path components * (it first searches in the current directory, then in PATH), and like UNIX otherwise (UNIX terminals * require to append './' before the executable name when launching it, so there is no fallback to the PATH). * We may change this in the future. * * @param exe Path to the binary to search. * @param name Label to use when printing debug output. * @param compat TRUE for compatibility mode which searches the file name in the current directory, * FALSE to require './' for the current directory. * * @return The absolute path to the file if it is found and executable, * otherwise NULL and errno is set to indicate the error. */ TCHAR* findPathOf(const TCHAR *exe, const TCHAR *name, int compat) { TCHAR *searchPath; TCHAR *beg, *end; int stop, found; TCHAR pth[PATH_MAX + 1]; TCHAR *ret; TCHAR resolvedPath[PATH_MAX + 1]; int err = ENOENT; if (exe[0] == TEXT('/')) { /* This is an absolute reference. */ if (_trealpathN(exe, resolvedPath, PATH_MAX + 1)) { _tcsncpy(pth, resolvedPath, PATH_MAX + 1); if (checkIfExecutable(pth)) { ret = malloc((_tcslen(pth) + 1) * sizeof(TCHAR)); if (!ret) { outOfMemory(TEXT("FPO"), 1); errno = ENOMEM; return NULL; } _tcsncpy(ret, pth, _tcslen(pth) + 1); if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Resolved the real path of %s as an absolute reference: %s"), name, ret); } errno = 0; return ret; } else { err = EACCES; } } else { err = errno; } if (wrapperData->isDebugging) { if (_tcslen(resolvedPath)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Unable to resolve the real path of %s as an absolute reference: %s. %s (Problem at: %s)"), name, exe, getLastErrorText(), resolvedPath); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Unable to resolve the real path of %s as an absolute reference: %s. %s"), name, exe, getLastErrorText()); } } errno = err; return NULL; } if (compat || (_tcschr(exe, TEXT('/')) != NULL)) { /* This is a non-absolute reference. See if it is a relative reference. */ if (_trealpathN(exe, resolvedPath, PATH_MAX + 1)) { /* Resolved. See if the file exists. */ _tcsncpy(pth, resolvedPath, PATH_MAX + 1); if (checkIfExecutable(pth)) { ret = malloc((_tcslen(pth) + 1) * sizeof(TCHAR)); if (!ret) { outOfMemory(TEXT("FPO"), 2); errno = ENOMEM; return NULL; } _tcsncpy(ret, pth, _tcslen(pth) + 1); if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Resolved the real path of %s as a relative reference: %s"), name, ret); } errno = 0; return ret; } else { /* Set err, but we may clear it if searching in the PATH. */ err = EACCES; } } else { err = errno; } if (wrapperData->isDebugging) { if (_tcslen(resolvedPath)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Unable to resolve the real path of %s as a relative reference: %s. %s (Problem at: %s)"), name, exe, getLastErrorText(), resolvedPath); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Unable to resolve the real path of %s as a relative reference: %s. %s"), name, exe, getLastErrorText()); } } } /* The file was not a direct relative reference. If and only if it does not contain any relative path components, we can search the PATH. */ if (_tcschr(exe, TEXT('/')) == NULL) { /* On UNIX, if a file is referenced twice in the PATH, the first location where the file has the correct permission will be chosen. * Any file with insufficient permissions will be considered as not found. Set err to "No such file or directory". */ err = ENOENT; searchPath = _tgetenv(TEXT("PATH")); if (searchPath && (_tcslen(searchPath) <= 0)) { #if defined(UNICODE) free(searchPath); #endif searchPath = NULL; } if (searchPath) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Attempt to locate %s on system PATH: %s"), name, exe); } beg = searchPath; stop = 0; found = 0; do { end = _tcschr(beg, TEXT(':')); if (end == NULL) { /* This is the last element in the PATH, so we want the whole thing. */ stop = 1; _tcsncpy(pth, beg, PATH_MAX + 1); } else { /* Copy the single path entry. */ _tcsncpy(pth, beg, end - beg); pth[end - beg] = TEXT('\0'); } if (pth[_tcslen(pth) - 1] != TEXT('/')) { _tcsncat(pth, TEXT("/"), PATH_MAX + 1); } _tcsncat(pth, exe, PATH_MAX + 1); /* The file can exist on the path, but via a symbolic link, so we need to expand it. Ignore errors here. */ #ifdef _DEBUG if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" Check PATH entry: %s"), pth); } #endif if (_trealpathN(pth, resolvedPath, PATH_MAX + 1) != NULL) { /* Copy over the result. */ _tcsncpy(pth, resolvedPath, PATH_MAX + 1); found = checkIfExecutable(pth); } else if (wrapperData->isDebugging) { if (_tcslen(resolvedPath)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Unable to resolve the path %s: %s (Problem at: %s)"), pth, getLastErrorText(), resolvedPath); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Unable to resolve the path %s: %s"), pth, getLastErrorText()); } stop = TRUE; } if (!stop) { beg = end + 1; } } while (!stop && !found); #if defined(UNICODE) free(searchPath); #endif if (found) { ret = malloc((_tcslen(pth) + 1) * sizeof(TCHAR)); if (!ret) { outOfMemory(TEXT("FPO"), 3); errno = ENOMEM; return NULL; } _tcsncpy(ret, pth, _tcslen(pth) + 1); if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Resolved the real path of %s from system PATH: %s"), name, ret); } errno = 0; return ret; } else if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Unable to resolve the real path of %s on the system PATH: %s"), name, exe); } } } /* Still did not find the file. So it must not exist. */ errno = err; return NULL; } #endif /** * Checks to see if the specified executable is a regular binary. * NOTE: On UNIX, this function will also resolve the java command if wrapper.java.command.resolve is set to TRUE. * * @param para The binary to check. On UNIX, the para memory may be freed and reallocated by this call. */ void checkIfRegularExe(TCHAR** para) { TCHAR* path; #ifdef WIN32 int len, start; if (_tcschr(*para, TEXT('\"')) != NULL){ start = 1; len = (int)_tcslen(*para) - 2; } else { start = 0; len = (int)_tcslen(*para); } path = malloc(sizeof(TCHAR) * (len + 1)); if (!path){ outOfMemory(TEXT("CIRE"), 1); } else { _tcsncpy(path, (*para) + start, len); path[len] = TEXT('\0'); #else int replacePath; path = findPathOf(*para, TEXT("wrapper.java.command"), TRUE); if (!path) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("The configured wrapper.java.command could not be found, attempting to launch anyway: %s"), *para); } else { replacePath = getBooleanProperty(properties, TEXT("wrapper.java.command.resolve"), TRUE); if (replacePath == TRUE) { free(*para); *para = malloc((_tcslen(path) + 1) * sizeof(TCHAR)); if (!(*para)) { outOfMemory(TEXT("CIRE"), 2); free(path); return; } _tcsncpy(*para, path, _tcslen(path) + 1); } #endif if (!checkIfBinary(path)) { /* Set a flag to later show a warning when receiving the Java PID from the backend. */ wrapperData->javaCommandNotBinary = TRUE; } free(path); } } #ifdef WIN32 void wrapperCheckMonitoredProcess(DWORD javaPID) { HANDLE hProc; TCHAR realJavaPath[_MAX_PATH]; int adviseRealPath = FALSE; /* Check if the PID of the Java process at the moment of the fork is the same as the one returned through backend. */ if (wrapperData->javaPID != javaPID) { if (wrapperData->javaCommandNotBinary == FALSE) { /* Java was launched via a redirector binary (such as C:\Program Files\Common Files\Oracle\Java\javapath\java.exe) * which PID differs from the real Java process. Unlike scripts (which are often user-made, editable or able to * log their own warnings), redirector binaries make it pretty difficult for the user to figure out the target path. * To help the user, get a handle to the Java process and print its executable path. * (Note: for Oracle binaries, a way to confirm the real path is to use the 'java -verbose' command) */ if (hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, javaPID)) { if (GetModuleFileNameEx(hProc, NULL, realJavaPath, _MAX_PATH)) { adviseRealPath = TRUE; } CloseHandle(hProc); } } if (adviseRealPath) { log_printf(WRAPPER_SOURCE_WRAPPER, wrapperData->monitorRedirectLogLevel, TEXT("Detected that the Java process launched by the Wrapper (PID %d) was redirected to %s (PID %d)."), wrapperData->javaPID, realJavaPath, javaPID); } else { log_printf(WRAPPER_SOURCE_WRAPPER, wrapperData->monitorRedirectLogLevel, TEXT("The PID (%d) of the running Java process differs from the PID (%d) of the process launched by the Wrapper."), javaPID, wrapperData->javaPID); } if (wrapperData->javaCommandNotBinary) { log_printf(WRAPPER_SOURCE_WRAPPER, wrapperData->monitorRedirectLogLevel, TEXT(" The value of wrapper.java.command does not appear to be a Java binary.")); } if (!wrapperData->monitorLaunched) { /* We monitor the Java process, so update its PID and handle. */ if (hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, javaPID)) { /* We got the handle. Print a message before the PID gets updated. */ log_printf(WRAPPER_SOURCE_WRAPPER, wrapperData->monitorRedirectLogLevel, TEXT(" Switch to monitoring the Java process.")); CloseHandle(wrapperData->javaProcess); wrapperData->javaProcess = hProc; wrapperData->javaPID = javaPID; /* Update the Java pid file. */ if (wrapperData->javaPidFilename) { if (writePidFile(wrapperData->javaPidFilename, wrapperData->javaPID, wrapperData->javaPidFileUmask)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to write the Java PID file: %s"), wrapperData->javaPidFilename); } } } else { /* Should not happen... */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" Failed to switch to monitoring the Java process - %s."), getLastErrorText()); wrapperStopProcess(wrapperData->errorExitCode, TRUE); } } else { log_printf(WRAPPER_SOURCE_WRAPPER, wrapperData->monitorRedirectLogLevel, TEXT(" Trying to continue, but some features such as thread dump requests or signal handling may not work correctly.")); } } } #else void wrapperCheckMonitoredProcess(pid_t javaPID) { /* Check if the PID of the Java process at the moment of the fork is the same as the one returned through backend. */ if (wrapperData->javaPID != javaPID) { log_printf(WRAPPER_SOURCE_WRAPPER, wrapperData->monitorRedirectLogLevel, TEXT("The PID (%d) of the running Java process differs from the PID (%d) of the process launched by the Wrapper."), javaPID, wrapperData->javaPID); if (wrapperData->javaCommandNotBinary) { /* Should be rare (non-standard). In a Shell Script, this can happen if java is called without 'exec'. */ log_printf(WRAPPER_SOURCE_WRAPPER, wrapperData->monitorRedirectLogLevel, TEXT(" The value of wrapper.java.command does not appear to be a Java binary.")); } log_printf(WRAPPER_SOURCE_WRAPPER, wrapperData->monitorRedirectLogLevel, TEXT(" Trying to continue, but some features such as thread dump requests or signal handling may not work correctly.")); } } #endif /** * Builds up the java command section of the Java command line. * * @param shallow This parameter is used when only the command name is needed, regardless its location, nature, etc. * * @return The final index into the strings array, or -1 if there were any problems. */ int wrapperBuildJavaCommandArrayJavaCommand(TCHAR **strings, int addQuotes, int index, int shallow) { const TCHAR *prop; #ifdef WIN32 TCHAR cpPath[512]; int found; #endif if (strings) { prop = getStringProperty(properties, TEXT("wrapper.java.command"), TEXT("java")); #ifdef WIN32 found = 0; if (_tcscmp(prop, TEXT("")) == 0) { /* If the java command is an empty string, we want to look for the * the java command in the windows registry. */ if (wrapperGetJavaHomeFromWindowsRegistry(cpPath)) { if (wrapperData->isDebugging && !shallow) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Loaded java home from registry: %s"), cpPath); } updateStringValue(&wrapperData->registry_java_home, cpPath); _tcsncat(cpPath, TEXT("\\bin\\java.exe"), 512); if (wrapperData->isDebugging && !shallow) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Found Java Runtime Environment home directory in system registry.")); } found = 1; } else { if (!shallow) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("The Java Runtime Environment home directory could not be located in the system registry.")); } found = 0; return -1; } } else { updateStringValue(&wrapperData->registry_java_home, NULL); /* To avoid problems on Windows XP systems, the '/' characters must * be replaced by '\' characters in the specified path. * prop is supposed to be constant, but allow this change as it is * the actual value that we want. */ wrapperCorrectWindowsPath((TCHAR *)prop); /* If the full path to the java command was not specified, then we * need to try and resolve it here to avoid problems later when * calling CreateProcess. CreateProcess will look in the windows * system directory before searching the PATH. This can lead to * the wrong JVM being run. */ _sntprintf(cpPath, 512, TEXT("%s"), prop); if ((PathFindOnPath((TCHAR*)cpPath, (TCHAR**)wrapperGetSystemPath())) && (!PathIsDirectory(cpPath))) { /*printf("Found %s on path.\n", cpPath); */ found = 1; } else { /*printf("Could not find %s on path.\n", cpPath); */ /* Try adding .exe to the end */ _sntprintf(cpPath, 512, TEXT("%s.exe"), prop); if ((PathFindOnPath(cpPath, wrapperGetSystemPath())) && (!PathIsDirectory(cpPath))) { /*printf("Found %s on path.\n", cpPath); */ found = 1; } else { /*printf("Could not find %s on path.\n", cpPath); */ } } } setInternalVarProperty(properties, TEXT("WRAPPER_JAVA_HOME"), wrapperData->registry_java_home, FALSE, FALSE); if (found) { strings[index] = malloc(sizeof(TCHAR) * (_tcslen(cpPath) + 2 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAJC"), 1); return -1; } if (addQuotes) { _sntprintf(strings[index], _tcslen(cpPath) + 2 + 1, TEXT("\"%s\""), cpPath); } else { _sntprintf(strings[index], _tcslen(cpPath) + 2 + 1, TEXT("%s"), cpPath); } } else { strings[index] = malloc(sizeof(TCHAR) * (_tcslen(prop) + 2 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAJC"), 2); return -1; } if (addQuotes) { _sntprintf(strings[index], _tcslen(prop) + 2 + 1, TEXT("\"%s\""), prop); } else { _sntprintf(strings[index], _tcslen(prop) + 2 + 1, TEXT("%s"), prop); } } if (addQuotes) { wrapperCheckQuotes(strings[index], TEXT("wrapper.java.command")); } #else /* UNIX */ strings[index] = malloc(sizeof(TCHAR) * (_tcslen(prop) + 2 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAJC"), 3); return -1; } if (addQuotes) { _sntprintf(strings[index], _tcslen(prop) + 2 + 1, TEXT("\"%s\""), prop); } else { _sntprintf(strings[index], _tcslen(prop) + 2 + 1, TEXT("%s"), prop); } #endif if (!shallow) { checkIfRegularExe(&strings[index]); } } index++; return index; } /** * The Java version will not parse correctly if the Java command is set to 'jdb' or 'javaw'. * - jdb has a different output when the '-version' parameters is specified. * - javaw has no std output. * This function tries to resolve the command used to request the Java version by searching * for the 'java' command located in the same directory, if it exists. If it doesn't, or if * the directory can't be resolved, a warning will be display and execution will continue to * let the Java version be resolved to its default value. * * @param pCommand pointer to the Java command (the value may be modified). * @param addQuotes TRUE if the resolved command should be surrounded by quotes, FALSE otherwise. * * @return TRUE if we ran out of memory, FALSE otherwise. */ int wrapperResolveJavaVersionCommand(TCHAR **pCommand, int addQuotes) { #ifdef WIN32 const TCHAR* const ext = TEXT(".exe"); #else const TCHAR* const ext = TEXT(""); #endif #if defined(WIN32) && !defined(WIN64) struct _stat64i32 fileStat; #else struct stat fileStat; #endif TCHAR* resolvedCommand; TCHAR* commandNoQuotes; TCHAR* path1 = NULL; TCHAR* path2 = NULL; TCHAR* ptr = NULL; TCHAR c; int resolved = FALSE; size_t len; int error = 0; int result = FALSE; if (isCommandJdb(*pCommand, addQuotes) #ifdef WIN32 || isCommandJavaw(*pCommand, addQuotes) #endif ) { /* First create a copy of the command without quotes. 'addQuotes' means that the received * command contains quotes, and quotes should be added after resolving/updating it. */ if (addQuotes) { len = _tcslen(*pCommand); commandNoQuotes = malloc(sizeof(TCHAR) * (len - 1)); if (!commandNoQuotes) { outOfMemory(TEXT("WRJVC"), 1); return TRUE; } _sntprintf(commandNoQuotes, len - 2, *pCommand + 1); commandNoQuotes[len - 2] = 0; } else { commandNoQuotes = *pCommand; } /* This function returns a pointer to the file name. */ ptr = (TCHAR*)getFileName(commandNoQuotes); if (!ptr) { /* Should never happen if the implementation of the function doesn't change. */ error = 10; } else if (commandNoQuotes == ptr) { /* The command is specified without path. */ #ifdef WIN32 /* On Windows this should never happen because wrapperBuildJavaCommandArrayJavaCommand always * resolves the path to the command using the system PATH. Still log a warning just in case. */ error = 1; #else /* On Unix the Java command may not be resolved, but we need to make sure that we are targeting * the same installation directory when calling 'java' without path. */ if (getBooleanProperty(properties, TEXT("wrapper.java.command.resolve"), TRUE)) { /* The only possible reason is that findPathOf already failed in checkIfRegularExe. * An error has already been logged but we also want to show a warning for the Java version. */ error = 1; } else { /* Get the full path to the Jdb/Javaw command. 3d param is FALSE to only resolve using system PATH (like the OS). */ path1 = findPathOf(commandNoQuotes, commandNoQuotes, FALSE); if (!path1) { error = 2; } else { ptr = (TCHAR*)getFileName(path1); if (!ptr) { /* Should never happen */ error = 11; } else { /* Truncate path1 to remove the file name. */ *ptr = 0; /* Get the full path to the Java command. 3d param is FALSE to only resolve using system PATH (like the OS). */ path2 = findPathOf(TEXT("java"), TEXT("java"), FALSE); if (!path2) { error = 3; } else { ptr = (TCHAR*)getFileName(path2); if (!ptr) { /* Should never happen */ error = 12; } else { /* Truncate path2 to remove the file name. */ *ptr = 0; if (_tcscmp(path1, path2) != 0) { error = 4; } else { updateStringValue(pCommand, TEXT("java")); if (*pCommand) { resolved = TRUE; } } } free(path2); } } free(path1); } } #endif } else { /* The command is specified with a path. */ /* Truncate the commandNoQuotes after the last separator and replace the command with 'java(.exe)'. */ c = *ptr; *ptr = 0; len = _tcslen(commandNoQuotes) + 4 + _tcslen(ext) + 1; if (addQuotes) { len += 2; } resolvedCommand = malloc(sizeof(TCHAR) * len); if (!resolvedCommand) { outOfMemory(TEXT("WRJVC"), 2); if (addQuotes) { free(commandNoQuotes); } return TRUE; } else { _sntprintf(resolvedCommand, len, TEXT("%sjava%s"), commandNoQuotes, ext); if (_tstat(resolvedCommand, &fileStat) != 0) { /* The 'java' command did not exist in the same directory */ error = 5; } else { if (addQuotes) { /* Quotes where removed, add them (there is room for 2 additional chars). */ _sntprintf(resolvedCommand, len, TEXT("\"%sjava%s\""), commandNoQuotes, ext); } updateStringValue(pCommand, resolvedCommand); if (*pCommand) { resolved = TRUE; } } free(resolvedCommand); } /* restore commandNoQuotes because we want to use it for logging. */ *ptr = c; } if (resolved) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Command used to query the Java version resolved to %s."), *pCommand); } else if (*pCommand == NULL) { /* OOM already reported */ result = TRUE; } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Could not find the 'java%s' command in the same directory as '%s' (Internal error %d).\n The Java version may not be resolved correctly."), ext, commandNoQuotes, error); if (wrapperData->isAdviserEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" You may use the wrapper.java.version.fallback property to set the version manually.")); } } if (addQuotes) { free(commandNoQuotes); } } return result; } /** * Builds up the additional section of the Java command line. * * @return The final index into the strings array, or -1 if there were any problems. */ int wrapperBuildJavaCommandArrayJavaAdditional(TCHAR **strings, int addQuotes, int detectDebugJVM, int index) { const TCHAR *prop; int i; size_t len; TCHAR paramBuffer2[128]; int quotable; int defaultStripQuote; int stripQuote; TCHAR *propStripped; TCHAR **propertyNames; TCHAR **propertyValues; long unsigned int *propertyIndices; if (getStringProperties(properties, TEXT("wrapper.java.additional."), TEXT(""), wrapperData->ignoreSequenceGaps, FALSE, &propertyNames, &propertyValues, &propertyIndices)) { /* Failed */ return -1; } defaultStripQuote = getBooleanProperty(properties, TEXT("wrapper.java.additional.default.stripquotes"), FALSE); i = 0; while (propertyNames[i]) { prop = propertyValues[i]; if (prop) { if (_tcslen(prop) > 0) { /* All additional parameters must begin with a - or they will be interpretted * as the being the main class name by Java. */ if (!((_tcsstr(prop, TEXT("-")) == prop) || (_tcsstr(prop, TEXT("\"-")) == prop))) { /* Only log the message on the second pass. */ if (strings) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("The value of property '%s', '%s' is not a valid argument to the JVM. Skipping."), propertyNames[i], prop); } } else { if (strings) { /* is quotable also changes the value of the property! therefore prop can potentially point to free'd memory*/ quotable = isQuotableProperty(properties, propertyNames[i]); prop = getStringProperty(properties, propertyNames[i], NULL); propertyValues[i] = (TCHAR*) prop; if (prop == NULL) { index = -1; break; } if (addQuotes) { stripQuote = FALSE; } else { _sntprintf(paramBuffer2, 128, TEXT("wrapper.java.additional.%lu.stripquotes"), propertyIndices[i]); stripQuote = getBooleanProperty(properties, paramBuffer2, defaultStripQuote); } if (stripQuote) { propStripped = malloc(sizeof(TCHAR) * (_tcslen(prop) + 1)); if (!propStripped) { outOfMemory(TEXT("WBJCAJA"), 2); index = -1; break; } wrapperStripQuotes(prop, propStripped); } else { propStripped = (TCHAR *)prop; } if (addQuotes && quotable && _tcschr(propStripped, TEXT(' '))) { len = wrapperQuoteValue(propStripped, NULL, 0); strings[index] = malloc(sizeof(TCHAR) * len); if (!strings[index]) { outOfMemory(TEXT("WBJCAJA"), 3); if (stripQuote) { free(propStripped); } index = -1; break; } wrapperQuoteValue(propStripped, strings[index], len); } else { strings[index] = malloc(sizeof(TCHAR) * (_tcslen(propStripped) + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAJA"), 4); if (stripQuote) { free(propStripped); } index = -1; break; } _sntprintf(strings[index], _tcslen(propStripped) + 1, TEXT("%s"), propStripped); } if (addQuotes) { wrapperCheckQuotes(strings[index], propertyNames[i]); } if (stripQuote) { free(propStripped); propStripped = NULL; } /* Set if this paremeter enables debugging. */ if (detectDebugJVM) { if (_tcsstr(strings[index], TEXT("-Xdebug")) == strings[index]) { wrapperData->debugJVM = TRUE; } } } index++; } } i++; } } freeStringProperties(propertyNames, propertyValues, propertyIndices); return index; } /** * Java command line callback. * * @return FALSE if there were any problems. */ static int loadParameterFileCallbackParam_AddArg(LoadParameterFileCallbackParam *param, TCHAR *arg, size_t argLen) { TCHAR *argTerm; TCHAR *argStripped; TCHAR argExpanded[MAX_PROPERTY_VALUE_LENGTH]; size_t len; /* The incoming arg can not be considered to be null terminated so we need a local copy. */ if (!(argTerm = malloc(sizeof(TCHAR) * (argLen + 1)))) { outOfMemory(TEXT("LPFCPAA"), 1); return FALSE; } memcpy(argTerm, arg, sizeof(TCHAR) * argLen); argTerm[argLen] = TEXT('\0'); #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_NOTICE, TEXT(" :> %s"), argTerm); #endif if (param->isJVMParam == TRUE) { /* As in wrapperBuildJavaCommandArrayJavaAdditional(), skip an argument which does not begin with '-'. */ if ((argTerm[0] != TEXT('-')) && !((argTerm[0] == TEXT('"')) && (argTerm[1] == TEXT('-')))) { if (param->strings) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("The value '%s' is not a valid argument to the JVM. Skipping."), argTerm); } free(argTerm); return TRUE; } } if (param->strings) { /* Create a buffer to hold the stripped copy of argTerm. */ len = _tcslen(argTerm); if (!(argStripped = malloc(sizeof(TCHAR) * (len + 1)))) { outOfMemory(TEXT("LPFCPAA"), 2); free(argTerm); return FALSE; } if (!param->stripQuote) { /* Nothing to strip, simply copy the string. */ _tcsncpy(argStripped, argTerm, len + 1); } else { /* Strip the quotes. */ wrapperStripQuotes(argTerm, argStripped); } /* No longer needed. */ free(argTerm); /* Just in case the string contains and environment variable references, make sure they are all evaluated. * argExpanded needs to be static because there is no way to know how long it will be in advance. */ evaluateEnvironmentVariables(argStripped, argExpanded, MAX_PROPERTY_VALUE_LENGTH, properties->logWarnings, properties->warnedVarMap, properties->logWarningLogLevel, properties->ignoreVarMap, NULL); /* No longer needed. */ free(argStripped); len = _tcslen(argExpanded); param->strings[param->index] = malloc(sizeof(TCHAR) * (len + 1)); if (!param->strings[param->index]) { return FALSE; } _tcsncpy(param->strings[param->index], argExpanded, len + 1); } else { free(argTerm); } param->index++; return TRUE; } /*exitOnOverwrite and logLevelOnOverwrite are not used here but the function must implement the signature of ConfigFileReader_Callback. */ static int loadParameterFileCallback(void *callbackParam, const TCHAR *fileName, int lineNumber, int depth, TCHAR *config, int exitOnOverwrite, int logLevelOnOverwrite) { LoadParameterFileCallbackParam *param = (LoadParameterFileCallbackParam *)callbackParam; TCHAR *tail_bound; TCHAR *arg; TCHAR *s; int InDelim = FALSE; int InQuotes = FALSE; int Escaped = FALSE; /* special case where the callback should do nothing */ if ((fileName == NULL) && (lineNumber == -1) && (config == NULL)) { return TRUE; } #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_NOTICE, TEXT(" : %s"), config); #endif /* Assume that the line `config' has no white spaces at its beginning and end. */ assert(config && _tcslen(config) > 0); assert(config[0] != TEXT(' ') && config[_tcslen(config) - 1] != TEXT(' ')); tail_bound = config + _tcslen(config) + 1; for (arg = s = config; s < tail_bound; s++) { switch (*s) { case TEXT('\0'): if (!loadParameterFileCallbackParam_AddArg(param, arg, s - arg)) { outOfMemory(TEXT("LJAC"), 1); return FALSE; } break; case TEXT(' '): Escaped = FALSE; if (!InDelim && !InQuotes) { InDelim = TRUE; if (!loadParameterFileCallbackParam_AddArg(param, arg, s - arg)) { outOfMemory(TEXT("LJAC"), 2); return FALSE; } } break; case TEXT('"'): if (!Escaped) { InQuotes = !InQuotes; } Escaped = FALSE; if (InDelim) { InDelim = FALSE; arg = s; } break; case TEXT('\\'): Escaped = !Escaped; if (InDelim) { InDelim = FALSE; arg = s; } break; default: Escaped = FALSE; if (InDelim) { InDelim = FALSE; arg = s; } break; } } return TRUE; } /** * Builds up the additional section of the Java command line. * * @return The final index into the strings array, or -1 if there were any problems. */ int wrapperLoadParameterFile(TCHAR **strings, int addQuotes, int detectDebugJVM, int index, TCHAR *parameterName, int isJVMParameter) { const TCHAR *parameterFilePath; int parameterFileRequired; LoadParameterFileCallbackParam callbackParam; int readResult; TCHAR prop[256]; parameterFilePath = getFileSafeStringProperty(properties, parameterName, TEXT("")); _sntprintf(prop, 256, TEXT("%s.required"), parameterName); parameterFileRequired = getBooleanProperty(properties, prop, TRUE); #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_NOTICE, TEXT("%s=%s"), parameterName, parameterFilePath ? parameterFilePath : TEXT("")); #endif if (_tcslen(parameterFilePath) == 0) { return index; } if (addQuotes) { callbackParam.stripQuote = FALSE; } else { _sntprintf(prop, 256, TEXT("%s.stripquotes"), parameterName); callbackParam.stripQuote = getBooleanProperty(properties, prop, FALSE); } callbackParam.strings = strings; callbackParam.index = index; callbackParam.isJVMParam = isJVMParameter; readResult = configFileReader(parameterFilePath, parameterFileRequired, loadParameterFileCallback, &callbackParam, NULL, FALSE, FALSE, wrapperData->originalWorkingDir, properties->warnedVarMap, properties->ignoreVarMap, properties->logWarnings, properties->logWarningLogLevel); switch (readResult) { case CONFIG_FILE_READER_OPEN_FAIL: return parameterFileRequired ? -1 : index; case CONFIG_FILE_READER_SUCCESS: return callbackParam.index; case CONFIG_FILE_READER_FAIL: case CONFIG_FILE_READER_HARD_FAIL: return -1; default: _tprintf(TEXT("Unexpected read error %d\n"), readResult); return index; }; } /** * Builds up the library path section of the Java command line. * * @return The final index into the strings array, or -1 if there were any problems. */ int wrapperBuildJavaCommandArrayLibraryPath(TCHAR **strings, int addQuotes, int index) { const TCHAR *prop; int i, j; size_t len2; size_t cpLen, cpLenAlloc; TCHAR *tmpString; TCHAR *systemPath; TCHAR **propertyNames; TCHAR **propertyValues; long unsigned int *propertyIndices; if (strings) { if (wrapperData->libraryPathAppendPath) { /* We are going to want to append the full system path to * whatever library path is generated. */ #ifdef WIN32 systemPath = _tgetenv(TEXT("PATH")); #else systemPath = _tgetenv(TEXT("LD_LIBRARY_PATH")); #endif if (systemPath) { /* If we are going to add our own quotes then we need to make sure that the system * PATH doesn't contain any of its own. Windows allows users to do this... */ if (addQuotes) { i = 0; j = 0; do { if (systemPath[i] != TEXT('"') ) { systemPath[j] = systemPath[i]; j++; } i++; } while (systemPath[j] != TEXT('\0')); } } } else { systemPath = NULL; } prop = getStringProperty(properties, TEXT("wrapper.java.library.path"), NULL); if (prop) { /* An old style library path was specified. * If quotes are being added, check the last character before the * closing quote. If it is a backslash then Windows will use it to * escape the quote. To make things work correctly, we need to add * another backslash first so it will result in a single backslash * before the quote. */ if (systemPath) { strings[index] = malloc(sizeof(TCHAR) * (22 + _tcslen(prop) + 1 + _tcslen(systemPath) + 1 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCALP"), 1); #if !defined(WIN32) && defined(UNICODE) free(systemPath); #endif return -1; } if (addQuotes) { if ((_tcslen(systemPath) > 1) && (systemPath[_tcslen(systemPath) - 1] == TEXT('\\'))) { _sntprintf(strings[index], 22 + _tcslen(prop) + 1 + _tcslen(systemPath) + 1 + 1, TEXT("-Djava.library.path=\"%s%c%s\\\""), prop, wrapperClasspathSeparator, systemPath); } else { _sntprintf(strings[index], 22 + _tcslen(prop) + 1 + _tcslen(systemPath) + 1 + 1, TEXT("-Djava.library.path=\"%s%c%s\""), prop, wrapperClasspathSeparator, systemPath); } } else { _sntprintf(strings[index], 22 + _tcslen(prop) + 1 + _tcslen(systemPath) + 1 + 1, TEXT("-Djava.library.path=%s%c%s"), prop, wrapperClasspathSeparator, systemPath); } } else { strings[index] = malloc(sizeof(TCHAR) * (22 + _tcslen(prop) + 1 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCALP"), 2); return -1; } if (addQuotes) { if ((_tcslen(prop) > 1) && (prop[_tcslen(prop) - 1] == TEXT('\\'))) { _sntprintf(strings[index], 22 + _tcslen(prop) + 1 + 1, TEXT("-Djava.library.path=\"%s\\\""), prop); } else { _sntprintf(strings[index], 22 + _tcslen(prop) + 1 + 1, TEXT("-Djava.library.path=\"%s\""), prop); } } else { _sntprintf(strings[index], 22 + _tcslen(prop) + 1 + 1, TEXT("-Djava.library.path=%s"), prop); } } if (addQuotes) { wrapperCheckQuotes(strings[index], TEXT("wrapper.java.library.path")); } } else { /* Look for a multiline library path. */ cpLen = 0; cpLenAlloc = 1024; strings[index] = malloc(sizeof(TCHAR) * cpLenAlloc); if (!strings[index]) { outOfMemory(TEXT("WBJCALP"), 3); #if !defined(WIN32) && defined(UNICODE) if (systemPath) { free(systemPath); } #endif return -1; } /* Start with the property value. */ _sntprintf(&(strings[index][cpLen]), cpLenAlloc - cpLen, TEXT("-Djava.library.path=")); cpLen += 20; /* Add an open quote to the library path */ if (addQuotes) { _sntprintf(&(strings[index][cpLen]), cpLenAlloc - cpLen, TEXT("\"")); cpLen++; } /* Loop over the library path entries adding each one */ if (getStringProperties(properties, TEXT("wrapper.java.library.path."), TEXT(""), wrapperData->ignoreSequenceGaps, FALSE, &propertyNames, &propertyValues, &propertyIndices)) { /* Failed */ #if !defined(WIN32) && defined(UNICODE) if (systemPath) { free(systemPath); } #endif return -1; } i = 0; j = 0; while (propertyNames[i]) { prop = propertyValues[i]; if (prop) { len2 = _tcslen(prop); if (len2 > 0) { /* Is there room for the entry? */ while (cpLen + len2 + 3 > cpLenAlloc) { /* Resize the buffer */ tmpString = strings[index]; cpLenAlloc += 1024; strings[index] = malloc(sizeof(TCHAR) * cpLenAlloc); if (!strings[index]) { outOfMemory(TEXT("WBJCALP"), 4); #if !defined(WIN32) && defined(UNICODE) if (systemPath) { free(systemPath); } #endif return -1; } _sntprintf(strings[index], cpLenAlloc, TEXT("%s"), tmpString); free(tmpString); tmpString = NULL; } if (j > 0) { strings[index][cpLen++] = wrapperClasspathSeparator; /* separator */ } _sntprintf(&(strings[index][cpLen]), cpLenAlloc - cpLen, TEXT("%s"), prop); cpLen += len2; j++; } i++; } } freeStringProperties(propertyNames, propertyValues, propertyIndices); if (systemPath) { /* We need to append the system path. */ len2 = _tcslen(systemPath); if (len2 > 0) { /* Is there room for the entry? */ while (cpLen + len2 + 3 > cpLenAlloc) { /* Resize the buffer */ tmpString = strings[index]; cpLenAlloc += 1024; strings[index] = malloc(sizeof(TCHAR) * cpLenAlloc); if (!strings[index]) { outOfMemory(TEXT("WBJCALP"), 5); #if !defined(WIN32) && defined(UNICODE) free(systemPath); #endif return -1; } _sntprintf(strings[index], cpLenAlloc, TEXT("%s"), tmpString); free(tmpString); tmpString = NULL; } if (j > 0) { strings[index][cpLen++] = wrapperClasspathSeparator; /* separator */ } _sntprintf(&(strings[index][cpLen]), cpLenAlloc - cpLen, TEXT("%s"), systemPath); cpLen += len2; j++; } } if (j == 0) { /* No library path, use default. always room */ _sntprintf(&(strings[index][cpLen]), cpLenAlloc - cpLen, TEXT("./")); cpLen++; } /* Add ending quote. If the previous character is a backslash then * Windows will use it to escape the quote. To make things work * correctly, we need to add another backslash first so it will * result in a single backslash before the quote. */ if (addQuotes) { if (strings[index][cpLen - 1] == TEXT('\\')) { _sntprintf(&(strings[index][cpLen]), cpLenAlloc - cpLen, TEXT("\\")); cpLen++; } _sntprintf(&(strings[index][cpLen]), cpLenAlloc - cpLen, TEXT("\"")); cpLen++; } if (addQuotes) { wrapperCheckQuotes(strings[index], TEXT("wrapper.java.library.path.")); } } #if !defined(WIN32) && defined(UNICODE) if (systemPath) { free(systemPath); } #endif } index++; return index; } /** * Builds up the java classpath. * * @return 0 if successful, or -1 if there were any problems. */ int wrapperBuildJavaClasspath(TCHAR **classpath) { const TCHAR *prop; TCHAR *propStripped; TCHAR *propBaseDir; int i, j; size_t cpLen, cpLenAlloc; size_t len2; TCHAR *tmpString; #if defined(WIN32) && !defined(WIN64) struct _stat64i32 statBuffer; #else struct stat statBuffer; #endif TCHAR **propertyNames; TCHAR **propertyValues; long unsigned int *propertyIndices; TCHAR **files; int cnt; int missingLogLevel; /* Build a classpath */ cpLen = 0; cpLenAlloc = 1024; *classpath = malloc(sizeof(TCHAR) * cpLenAlloc); if (!*classpath) { outOfMemory(TEXT("WBJCP"), 1); return -1; } /* Loop over the classpath entries adding each one. */ if (getStringProperties(properties, TEXT("wrapper.java.classpath."), TEXT(""), wrapperData->ignoreSequenceGaps, FALSE, &propertyNames, &propertyValues, &propertyIndices)) { /* Failed */ return -1; } /* Get the loglevel to display warnings about missing classpath elements. */ missingLogLevel = getLogLevelForName(getStringProperty(properties, TEXT("wrapper.java.classpath.missing.loglevel"), TEXT("DEBUG"))); i = 0; j = 0; while (propertyNames[i]) { prop = propertyValues[i]; /* Does this contain any quotes? */ if (_tcschr(prop, TEXT('"'))) { propStripped = malloc(sizeof(TCHAR) * (_tcslen(prop) + 1)); if (!propStripped) { outOfMemory(TEXT("WBJCP"), 2); freeStringProperties(propertyNames, propertyValues, propertyIndices); return -1; } wrapperStripQuotes(prop, propStripped); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT( "Classpath element, %s, should not contain quotes: %s, stripping and continuing: %s"), propertyNames[i], prop, propStripped); } else { propStripped = (TCHAR *)prop; } len2 = _tcslen(propStripped); if (len2 > 0) { /* Does this contain wildcards? */ if ((_tcsrchr(propStripped, TEXT('*')) != NULL) || (_tcschr(propStripped, TEXT('?')) != NULL)) { /* Need to do a wildcard search */ files = loggerFileGetFiles(propStripped, LOGGER_FILE_SORT_MODE_NAMES_ASC); if (!files) { /* Failed */ if (propStripped != prop) { free(propStripped); } freeStringProperties(propertyNames, propertyValues, propertyIndices); return -1; } /* Loop over the files. */ cnt = 0; while (files[cnt]) { len2 = _tcslen(files[cnt]); /* Is there room for the entry? */ if (cpLen + len2 + 3 > cpLenAlloc) { /* Resize the buffer */ tmpString = *classpath; cpLenAlloc += len2 + 3; *classpath = malloc(sizeof(TCHAR) * cpLenAlloc); if (!*classpath) { if (propStripped != prop) { free(propStripped); } loggerFileFreeFiles(files); freeStringProperties(propertyNames, propertyValues, propertyIndices); outOfMemory(TEXT("WBJCP"), 2); return -1; } if (j > 0) { _sntprintf(*classpath, cpLenAlloc, TEXT("%s"), tmpString); } free(tmpString); tmpString = NULL; } if (j > 0) { (*classpath)[cpLen++] = wrapperClasspathSeparator; /* separator */ } _sntprintf(&((*classpath)[cpLen]), cpLenAlloc - cpLen, TEXT("%s"), files[cnt]); cpLen += len2; j++; cnt++; } loggerFileFreeFiles(files); if (cnt <= 0) { log_printf(WRAPPER_SOURCE_WRAPPER, missingLogLevel, TEXT( "Classpath element, %s, did not match any files: %s"), propertyNames[i], propStripped); } } else { /* This classpath entry does not contain any wildcards. */ /* If the path element is a directory then we want to strip the trailing slash if it exists. */ propBaseDir = (TCHAR*)propStripped; if ((propStripped[_tcslen(propStripped) - 1] == TEXT('/')) || (propStripped[_tcslen(propStripped) - 1] == TEXT('\\'))) { propBaseDir = malloc(sizeof(TCHAR) * _tcslen(propStripped)); if (!propBaseDir) { outOfMemory(TEXT("WBJCP"), 3); if (propStripped != prop) { free(propStripped); } freeStringProperties(propertyNames, propertyValues, propertyIndices); return -1; } _tcsncpy(propBaseDir, propStripped, _tcslen(propStripped) - 1); propBaseDir[_tcslen(propStripped) - 1] = TEXT('\0'); } /* See if it exists so we can display a debug warning if it does not. */ if (_tstat(propBaseDir, &statBuffer)) { /* Encountered an error of some kind. */ if ((errno == ENOENT) || (errno == 3)) { log_printf(WRAPPER_SOURCE_WRAPPER, missingLogLevel, TEXT( "Classpath element, %s, does not exist: %s"), propertyNames[i], propStripped); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "Unable to get information of classpath element: %s (%s)"), propStripped, getLastErrorText()); } } else { /* Got the stat info. */ } /* If we allocated the propBaseDir buffer then free it up. */ if (propBaseDir != propStripped) { free(propBaseDir); } propBaseDir = NULL; /* Is there room for the entry? */ if (cpLen + len2 + 3 > cpLenAlloc) { /* Resize the buffer */ tmpString = *classpath; cpLenAlloc += len2 + 3; *classpath = malloc(sizeof(TCHAR) * cpLenAlloc); if (!*classpath) { outOfMemory(TEXT("WBJCP"), 4); if (propStripped != prop) { free(propStripped); } freeStringProperties(propertyNames, propertyValues, propertyIndices); return -1; } if (j > 0) { _sntprintf(*classpath, cpLenAlloc, TEXT("%s"), tmpString); } free(tmpString); tmpString = NULL; } if (j > 0) { (*classpath)[cpLen++] = wrapperClasspathSeparator; /* separator */ } _sntprintf(&((*classpath)[cpLen]), cpLenAlloc - cpLen, TEXT("%s"), propStripped); cpLen += len2; j++; } } /* If we allocated the propStripped buffer then free it up. */ if (propStripped != prop) { free(propStripped); } propStripped = NULL; i++; } freeStringProperties(propertyNames, propertyValues, propertyIndices); if (j == 0) { /* No classpath, use default. always room */ _sntprintf(&(*classpath[cpLen]), cpLenAlloc - cpLen, TEXT("./")); cpLen++; } return 0; } /** * Builds up the java classpath section of the Java command line. * * @return The final index into the strings array, or -1 if there were any problems. */ int wrapperBuildJavaCommandArrayClasspath(TCHAR **strings, int addQuotes, int index, const TCHAR *classpath) { size_t len; size_t cpLen; /* Store the classpath */ if (strings) { strings[index] = malloc(sizeof(TCHAR) * (10 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAC"), 1); return -1; } _sntprintf(strings[index], 10 + 1, TEXT("-classpath")); } index++; if (strings) { cpLen = 0; len = _tcslen(classpath); strings[index] = malloc(sizeof(TCHAR) * (len + 4)); if (!strings[index]) { outOfMemory(TEXT("WBJCAC"), 2); return -1; } /* Add an open quote the classpath */ if (addQuotes) { _sntprintf(&(strings[index][cpLen]), len + 4 - cpLen, TEXT("\"")); cpLen++; } _sntprintf(&(strings[index][cpLen]), len + 4 - cpLen, TEXT("%s"), classpath); cpLen += len; /* Add ending quote. If the previous character is a backslash then * Windows will use it to escape the quote. To make things work * correctly, we need to add another backslash first so it will * result in a single backslash before the quote. */ if (addQuotes) { if (strings[index][cpLen - 1] == TEXT('\\')) { _sntprintf(&(strings[index][cpLen]), len + 4 - cpLen, TEXT("\\")); cpLen++; } _sntprintf(&(strings[index][cpLen]), len + 4 - cpLen, TEXT("\"")); cpLen++; } if (addQuotes) { wrapperCheckQuotes(strings[index], TEXT("wrapper.java.classpath.")); } } index++; return index; } /** * Builds up the app parameters section of the Java command line. * * @return The final index into the strings array, or -1 if there were any problems. */ int wrapperBuildJavaCommandArrayAppParameters(TCHAR **strings, int addQuotes, int index, int thisIsTestWrapper) { const TCHAR *prop; int i; int quotable; TCHAR *propStripped; int defaultStripQuote; int stripQuote; TCHAR paramBuffer2[128]; size_t len; TCHAR **propertyNames; TCHAR **propertyValues; long unsigned int *propertyIndices; if (getStringProperties(properties, TEXT("wrapper.app.parameter."), TEXT(""), wrapperData->ignoreSequenceGaps, FALSE, &propertyNames, &propertyValues, &propertyIndices)) { /* Failed */ return -1; } defaultStripQuote = getBooleanProperty(properties, TEXT("wrapper.app.parameter.default.stripquotes"), FALSE); i = 0; while (propertyNames[i]) { prop = propertyValues[i]; if (_tcslen(prop) > 0) { if (thisIsTestWrapper && (i == 1) && ((_tcscmp(prop, TEXT("{{TestWrapperBat}}")) == 0) || (_tcscmp(prop, TEXT("{{TestWrapperSh}}")) == 0))) { /* This is the TestWrapper dummy parameter. Simply skip over it so it doesn't get put into the command line. */ } else { if (strings) { quotable = isQuotableProperty(properties, propertyNames[i]); _sntprintf(paramBuffer2, 128, TEXT("wrapper.app.parameter.%lu.stripquotes"), propertyIndices[i]); if (addQuotes) { stripQuote = FALSE; } else { stripQuote = getBooleanProperty(properties, paramBuffer2, defaultStripQuote); } if (stripQuote) { propStripped = malloc(sizeof(TCHAR) * (_tcslen(prop) + 1)); if (!propStripped) { outOfMemory(TEXT("WBJCAAP"), 1); freeStringProperties(propertyNames, propertyValues, propertyIndices); return -1; } wrapperStripQuotes(prop, propStripped); } else { propStripped = (TCHAR *)prop; } if (addQuotes && quotable && _tcschr(propStripped, TEXT(' '))) { len = wrapperQuoteValue(propStripped, NULL, 0); strings[index] = malloc(sizeof(TCHAR) * len); if (!strings[index]) { outOfMemory(TEXT("WBJCAAP"), 2); if (stripQuote) { free(propStripped); } freeStringProperties(propertyNames, propertyValues, propertyIndices); return -1; } wrapperQuoteValue(propStripped, strings[index], len); } else { strings[index] = malloc(sizeof(TCHAR) * (_tcslen(propStripped) + 1)); if (!strings[index]) { if (stripQuote) { free(propStripped); } freeStringProperties(propertyNames, propertyValues, propertyIndices); outOfMemory(TEXT("WBJCAAP"), 3); return -1; } _sntprintf(strings[index], _tcslen(propStripped) + 1, TEXT("%s"), propStripped); } if (addQuotes) { wrapperCheckQuotes(strings[index], propertyNames[i]); } if (stripQuote) { free(propStripped); propStripped = NULL; } } index++; } } i++; } freeStringProperties(propertyNames, propertyValues, propertyIndices); /* precede command line parameters */ if (wrapperData->javaArgValueCount > 0) { for (i = 0; i < wrapperData->javaArgValueCount; i++) { if (strings) { if (addQuotes && _tcschr(wrapperData->javaArgValues[i], TEXT(' '))) { len = wrapperQuoteValue(wrapperData->javaArgValues[i], NULL, 0); strings[index] = malloc(sizeof(TCHAR) * len); if (!strings[index]) { outOfMemory(TEXT("WBJCAAP"), 4); return -1; } wrapperQuoteValue(wrapperData->javaArgValues[i], strings[index], len); } else { strings[index] = malloc(sizeof(TCHAR) * (_tcslen(wrapperData->javaArgValues[i]) + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAAP"), 5); return -1; } _sntprintf(strings[index], _tcslen(wrapperData->javaArgValues[i]) + 1, TEXT("%s"), wrapperData->javaArgValues[i]); } } index++; } } return index; } /** * Checks the additional java parameters to see if the user has specified a specific system property. * Returns TRUE if already set or there was an error, FALSE if it is safe to set again. * * @param propName the name of the system property to search (the length should be less than 32 chars) * * @return TRUE if a JVM option was already specified by the user, FALSE otherwise */ int isSysPropInJavaArgs(const TCHAR* propName) { TCHAR** propNames; TCHAR** propValues; long unsigned int* propIndices; int i; int ret = FALSE; #ifdef WIN32 int j; TCHAR buffer[32]; TCHAR* pBuffer; #endif TCHAR* propValue; TCHAR c; if (getStringProperties(properties, TEXT("wrapper.java.additional."), TEXT(""), wrapperData->ignoreSequenceGaps, FALSE, &propNames, &propValues, &propIndices)) { /* Failed */ return TRUE; } for (i = 0; propNames[i] && !ret; i++) { propValue = propValues[i]; #ifdef WIN32 /* Clear any double quotes as they are ignored on Windows. * On UNIX this is not needed because it is not valid to surround a * system property with double quotes unless stripquotes has been set * to TRUE (in which case the double quotes are already stipped). */ j = 0; pBuffer = buffer; while (*propValue && (j < 31)) { if (*propValue != TEXT('\"')) { *pBuffer = *propValue; pBuffer++; j++; } propValue++; } *pBuffer = TEXT('\0'); propValue = buffer; #endif if (_tcsstr(propValue, propName) == propValue) { /* Make sure that this is the exact property name, not a substring. */ c = propValue[_tcslen(propName)]; if ((c == TEXT('\0')) || (c == TEXT('='))) { ret = TRUE; } } } freeStringProperties(propNames, propValues, propIndices); return ret; } /** * Loops over and stores all necessary commands into an array which * can be used to launch a process. * This method will only count the elements if stringsPtr is NULL. * * Note - Next Out Of Memory is #47 * * @return The final index into the strings array, or -1 if there were any problems. */ int wrapperBuildJavaCommandArrayInner(TCHAR **strings, int addQuotes, const TCHAR *classpath) { int index; int detectDebugJVM; const TCHAR *prop; int initMemory = 0; int maxMemory; int thisIsTestWrapper; TCHAR encodingBuff[ENCODING_BUFFER_SIZE]; TCHAR* fileEncodingPtr = NULL; int passEncoding = FALSE; #ifndef WIN32 TCHAR localeEncodingBuff[ENCODING_BUFFER_SIZE]; #endif #ifdef HPUX const TCHAR* fix_iconv_hpux; #endif setLogPropertyWarnings(properties, strings != NULL); index = 0; detectDebugJVM = getBooleanProperty(properties, TEXT("wrapper.java.detect_debug_jvm"), TRUE); /* Java command */ if ((index = wrapperBuildJavaCommandArrayJavaCommand(strings, addQuotes, index, FALSE)) < 0) { return -1; } if (strings && detectDebugJVM) { if (isCommandJdb(strings[0], addQuotes)) { /* The Java command is the Jdb debugger. go into debug JVM mode. */ wrapperData->debugJVM = TRUE; } } /* See if the auto bits parameter is set. Ignored by all but the following platforms. */ #if /*defined(WIN32) || defined(LINUX) ||*/ defined(HPUX) || defined(MACOSX) || defined(SOLARIS) || defined(FREEBSD) if (wrapperData->javaVersion->major < 9) { if (getBooleanProperty(properties, /*#ifdef WIN32 TEXT("wrapper.java.additional.auto_bits.windows"), #elif defined(LINUX) TEXT("wrapper.java.additional.auto_bits.linux"), */ #if defined(HPUX) TEXT("wrapper.java.additional.auto_bits.hpux"), #elif defined(SOLARIS) TEXT("wrapper.java.additional.auto_bits.solaris"), #elif defined(FREEBSD) TEXT("wrapper.java.additional.auto_bits.freebsd"), #elif defined(MACOSX) TEXT("wrapper.java.additional.auto_bits.macosx"), #endif getBooleanProperty(properties, TEXT("wrapper.java.additional.auto_bits"), FALSE))) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * 5); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 1); return -1; } _sntprintf(strings[index], 5, TEXT("-d%s"), wrapperBits); } index++; } } #endif /* Store additional java parameters */ if ((index = wrapperBuildJavaCommandArrayJavaAdditional(strings, addQuotes, detectDebugJVM, index)) < 0) { return -1; } /* Store additional java parameters specified in the parameter file */ if ((index = wrapperLoadParameterFile(strings, addQuotes, detectDebugJVM, index, TEXT("wrapper.java.additional_file"), TRUE)) < 0) { return -1; } #if defined(WIN32) || defined(MACOSX) /* On these platforms, the encoding used by the JVM can vary depending on the Java version. * In order to ensure that we read the JVM output correctly and to be consistent across JVMs, we can force the encoding * by setting 'file.encoding'. However, only do this if it was not specified in the Java additional properties. */ if (!passEncoding && !isSysPropInJavaArgs(TEXT("-Dfile.encoding"))) { #ifdef WIN32 if (getBooleanProperty(properties, TEXT("wrapper.lang.pass_encoding"), TRUE)) { passEncoding = TRUE; } #endif if (!passEncoding && (wrapperData->javaVersion->major < 7)) { /* On MacOSX, Java 6 ignores the encoding of the current locale and set file.encoding to "MacRoman". * On Windows, Java 6 does not always use the default ANSI Windows code page (for example when the UI language is English but the System language is Japanese). */ passEncoding = TRUE; } } #endif if (passEncoding) { if (strings) { encodingBuff[0] = 0; #ifdef WIN32 /* On Windows, convert the code page to the corresponding encoding expressed in the Java syntax: */ if (!getJvmIoEncodingFromCodePage(wrapperData->jvm_stdout_codepage, wrapperData->javaVersion->major, encodingBuff)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Could not infer the JVM encoding from the code page '%d'."), wrapperData->jvm_stdout_codepage); #else /* On Unix the JVM automatically maps system encodings to its own encodings if nothing is specified in the command line (see note above). * However, when adding system properties we need to convert the locale encoding to the Java syntax: */ if (!getJvmIoEncoding(getCurrentLocaleEncoding(localeEncodingBuff), wrapperData->javaVersion->major, encodingBuff)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Could not infer the JVM encoding from the locale encoding '%s'."), localeEncodingBuff); #endif log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" Please add the 'file.encoding' system property to the JVM arguments\n and set it with an appropriate encoding for the current platform.")); /* Stop the Wrapper. The user must fix the configuration. */ return -1; } if (!fileEncodingPtr) { fileEncodingPtr = encodingBuff; } /* NOTE: On Linux and jre 1.4.2_19, file.encoding is not used for stdout, but it is used with jre 1.5! * On Solaris and jre 1.4.1_07, file.encoding is used for stdout. * => file.encoding is not a standard specification. Its implementation may differ on old JVMs. * Currently the Wrapper will assume file.encoding is used to encode the JVM output as this is the case for recent JVMs. */ strings[index] = malloc(sizeof(TCHAR) * (16 + _tcslen(fileEncodingPtr) + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 4); return -1; } _sntprintf(strings[index], 16 + _tcslen(fileEncodingPtr) + 1, TEXT("-Dfile.encoding=%s"), fileEncodingPtr); } index++; } if (wrapperData->use_sun_encoding) { /* On the Java side, we need to know if sun.stdout.encoding (and sun.stderr.encoding) will be used to read JVM outputs. * There is indeed a chance that these properties are passed to the JVM although they are not supported, and we don't * want to remove any system property that the user may have added. We could duplicate the logic on the Java side to * check whether or not these properties are implemented, but there is a risk of not being in sync. Instead we will * pass a system property to inform the JVM when sun.stdout.encoding is used. This assumes resolveJvmEncoding() was * called earlier. */ if (strings) { strings[index] = malloc(sizeof(TCHAR) * (31 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 7); return -1; } _sntprintf(strings[index], 31 + 1, TEXT("-Dwrapper.use_sun_encoding=true")); } index++; } #ifdef HPUX fix_iconv_hpux = getStringProperty(properties, TEXT("wrapper.fix_iconv_hpux"), NULL); if (fix_iconv_hpux) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * (25 + _tcslen(fix_iconv_hpux) + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 9); return -1; } _sntprintf(strings[index], 25 + _tcslen(fix_iconv_hpux) + 1, TEXT("-Dwrapper.fix_iconv_hpux=%s"), fix_iconv_hpux); } index++; } #endif /* Initial JVM memory */ initMemory = getIntProperty(properties, TEXT("wrapper.java.initmemory"), 0); if (initMemory > 0) { if (strings) { initMemory = __max(initMemory, 1); /* 1 <= n */ strings[index] = malloc(sizeof(TCHAR) * (5 + 10 + 1)); /* Allow up to 10 digits. */ if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 10); return -1; } _sntprintf(strings[index], 5 + 10 + 1, TEXT("-Xms%dm"), initMemory); } index++; } else { /* Set the initMemory so the checks in the maxMemory section below will work correctly. */ initMemory = 3; } /* Maximum JVM memory */ maxMemory = getIntProperty(properties, TEXT("wrapper.java.maxmemory"), 0); if (maxMemory > 0) { if (strings) { maxMemory = __max(maxMemory, initMemory); /* initMemory <= n */ strings[index] = malloc(sizeof(TCHAR) * (5 + 10 + 1)); /* Allow up to 10 digits. */ if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 12); return -1; } _sntprintf(strings[index], 5 + 10 + 1, TEXT("-Xmx%dm"), maxMemory); } index++; } /* Library Path */ if ((index = wrapperBuildJavaCommandArrayLibraryPath(strings, addQuotes, index)) < 0) { return -1; } /* Classpath */ if (!wrapperData->environmentClasspath) { if ((index = wrapperBuildJavaCommandArrayClasspath(strings, addQuotes, index, classpath)) < 0) { return -1; } } /* Store the Wrapper key */ if (strings) { strings[index] = malloc(sizeof(TCHAR) * (16 + _tcslen(wrapperData->key) + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 24); return -1; } if (addQuotes) { _sntprintf(strings[index], 16 + _tcslen(wrapperData->key) + 1, TEXT("-Dwrapper.key=\"%s\""), wrapperData->key); } else { _sntprintf(strings[index], 16 + _tcslen(wrapperData->key) + 1, TEXT("-Dwrapper.key=%s"), wrapperData->key); } } index++; /* Store the backend connection information. */ if (wrapperData->backendType == WRAPPER_BACKEND_TYPE_PIPE) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * (22 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 25); return -1; } _sntprintf(strings[index], 22 + 1, TEXT("-Dwrapper.backend=pipe")); } index++; } else { /* default is socket ipv4, so we have to specify ipv6 if it's the case */ if (wrapperData->backendType == WRAPPER_BACKEND_TYPE_SOCKET_V6) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * (29 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 261); return -1; } _sntprintf(strings[index], 29 + 1, TEXT("-Dwrapper.backend=socket_ipv6")); } index++; /* specify to the JVM to change the preference and use IPv6 addresses over IPv4 ones where possible. */ if (strings) { strings[index] = malloc(sizeof(TCHAR) * (35 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 262); return -1; } _sntprintf(strings[index], 35 + 1, TEXT("-Djava.net.preferIPv6Addresses=TRUE")); } index++; } /* Store the Wrapper server port */ if (strings) { strings[index] = malloc(sizeof(TCHAR) * (15 + 5 + 1)); /* Port up to 5 characters */ if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 26); return -1; } _sntprintf(strings[index], 15 + 5 + 1, TEXT("-Dwrapper.port=%d"), (int)wrapperData->actualPort); } index++; } /* Store the Wrapper jvm min and max ports. */ if ((wrapperData->backendType == WRAPPER_BACKEND_TYPE_SOCKET_V4) || (wrapperData->backendType == WRAPPER_BACKEND_TYPE_SOCKET_V6)) { if (wrapperData->portAddress != NULL) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * (_tcslen(TEXT("-Dwrapper.port.address=")) + _tcslen(wrapperData->portAddress) + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 27); return -1; } _sntprintf(strings[index], _tcslen(TEXT("-Dwrapper.port.address=")) + _tcslen(wrapperData->portAddress) + 1, TEXT("-Dwrapper.port.address=%s"), wrapperData->portAddress); } index++; } if (wrapperData->jvmPort >= 0) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * (19 + 5 + 1)); /* Port up to 5 characters */ if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 28); return -1; } _sntprintf(strings[index], 19 + 5 + 1, TEXT("-Dwrapper.jvm.port=%d"), wrapperData->jvmPort); } index++; } if (strings) { strings[index] = malloc(sizeof(TCHAR) * (23 + 5 + 1)); /* Port up to 5 characters */ if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 29); return -1; } _sntprintf(strings[index], 23 + 5 + 1, TEXT("-Dwrapper.jvm.port.min=%d"), wrapperData->jvmPortMin); } index++; if (strings) { strings[index] = malloc(sizeof(TCHAR) * (23 + 5 + 1)); /* Port up to 5 characters */ if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 30); return -1; } _sntprintf(strings[index], 23 + 5 + 1, TEXT("-Dwrapper.jvm.port.max=%d"), wrapperData->jvmPortMax); } index++; } /* Store the Wrapper debug flag */ if (wrapperData->isDebugging) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * (22 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 31); return -1; } if (addQuotes) { _sntprintf(strings[index], 22 + 1, TEXT("-Dwrapper.debug=\"TRUE\"")); } else { _sntprintf(strings[index], 22 + 1, TEXT("-Dwrapper.debug=TRUE")); } } index++; } /* Store the Wrapper disable console input flag. */ if (wrapperData->disableConsoleInput) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * (38 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 32); return -1; } if (addQuotes) { _sntprintf(strings[index], 38 + 1, TEXT("-Dwrapper.disable_console_input=\"TRUE\"")); } else { _sntprintf(strings[index], 38 + 1, TEXT("-Dwrapper.disable_console_input=TRUE")); } } index++; } /* Store the Wrapper listener force stop flag. */ if (getBooleanProperty(properties, TEXT("wrapper.listener.force_stop"), FALSE)) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * (38 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 33); return -1; } if (addQuotes) { _sntprintf(strings[index], 38 + 1, TEXT("-Dwrapper.listener.force_stop=\"TRUE\"")); } else { _sntprintf(strings[index], 38 + 1, TEXT("-Dwrapper.listener.force_stop=TRUE")); } } index++; } /* Store the Wrapper PID */ if (strings) { strings[index] = malloc(sizeof(TCHAR) * (24 + 1)); /* Pid up to 10 characters */ if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 34); return -1; } #if defined(SOLARIS) && (!defined(_LP64)) _sntprintf(strings[index], 24 + 1, TEXT("-Dwrapper.pid=%ld"), wrapperData->wrapperPID); #else _sntprintf(strings[index], 24 + 1, TEXT("-Dwrapper.pid=%d"), wrapperData->wrapperPID); #endif } index++; /* Store a flag telling the JVM to use the system clock. */ if (wrapperData->useSystemTime) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * (32 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 35); return -1; } if (addQuotes) { _sntprintf(strings[index], 32 + 1, TEXT("-Dwrapper.use_system_time=\"TRUE\"")); } else { _sntprintf(strings[index], 32 + 1, TEXT("-Dwrapper.use_system_time=TRUE")); } } index++; } else { /* Only pass the timer fast and slow thresholds to the JVM if they are not default. * These are only used if the system time is not being used. */ if (wrapperData->timerFastThreshold != WRAPPER_TIMER_FAST_THRESHOLD) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * (43 + 1)); /* Allow for 10 digits */ if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 36); return -1; } if (addQuotes) { _sntprintf(strings[index], 43 + 1, TEXT("-Dwrapper.timer_fast_threshold=\"%d\""), wrapperData->timerFastThreshold * WRAPPER_TICK_MS / 1000); } else { _sntprintf(strings[index], 43 + 1, TEXT("-Dwrapper.timer_fast_threshold=%d"), wrapperData->timerFastThreshold * WRAPPER_TICK_MS / 1000); } } index++; } if (wrapperData->timerSlowThreshold != WRAPPER_TIMER_SLOW_THRESHOLD) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * (43 + 1)); /* Allow for 10 digits */ if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 37); return -1; } if (addQuotes) { _sntprintf(strings[index], 43 + 1, TEXT("-Dwrapper.timer_slow_threshold=\"%d\""), wrapperData->timerSlowThreshold * WRAPPER_TICK_MS / 1000); } else { _sntprintf(strings[index], 43 + 1, TEXT("-Dwrapper.timer_slow_threshold=%d"), wrapperData->timerSlowThreshold * WRAPPER_TICK_MS / 1000); } } index++; } } /* Always write the version of the wrapper binary as a property. The * WrapperManager class uses it to verify that the version matches. */ if (strings) { strings[index] = malloc(sizeof(TCHAR) * (20 + _tcslen(wrapperVersion) + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 38); return -1; } if (addQuotes) { _sntprintf(strings[index], 20 + _tcslen(wrapperVersion) + 1, TEXT("-Dwrapper.version=\"%s\""), wrapperVersion); } else { _sntprintf(strings[index], 20 + _tcslen(wrapperVersion) + 1, TEXT("-Dwrapper.version=%s"), wrapperVersion); } } index++; /* Store the base name of the native library. */ if (strings) { strings[index] = malloc(sizeof(TCHAR) * (27 + _tcslen(wrapperData->nativeLibrary) + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 39); return -1; } if (addQuotes) { _sntprintf(strings[index], 27 + _tcslen(wrapperData->nativeLibrary) + 1, TEXT("-Dwrapper.native_library=\"%s\""), wrapperData->nativeLibrary); } else { _sntprintf(strings[index], 27 + _tcslen(wrapperData->nativeLibrary) + 1, TEXT("-Dwrapper.native_library=%s"), wrapperData->nativeLibrary); } } index++; /* Store the arch name of the wrapper. */ if (strings) { strings[index] = malloc(sizeof(TCHAR) * (17 + _tcslen(wrapperArch) + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 40); return -1; } if (addQuotes) { _sntprintf(strings[index], 17 + _tcslen(wrapperArch) + 1, TEXT("-Dwrapper.arch=\"%s\""), wrapperArch); } else { _sntprintf(strings[index], 17 + _tcslen(wrapperArch) + 1, TEXT("-Dwrapper.arch=%s"), wrapperArch); } } index++; /* Store the ignore signals flag if configured to do so */ if (wrapperData->ignoreSignals & WRAPPER_IGNORE_SIGNALS_JAVA) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * (31 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 41); return -1; } if (addQuotes) { _sntprintf(strings[index], 31 + 1, TEXT("-Dwrapper.ignore_signals=\"TRUE\"")); } else { _sntprintf(strings[index], 31 + 1, TEXT("-Dwrapper.ignore_signals=TRUE")); } } index++; } /* If this is being run as a service, add a service flag. */ if (!wrapperData->isConsole) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * (24 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 42); return -1; } if (addQuotes) { _sntprintf(strings[index], 24 + 1, TEXT("-Dwrapper.service=\"TRUE\"")); } else { _sntprintf(strings[index], 24 + 1, TEXT("-Dwrapper.service=TRUE")); } } index++; } /* Store the Disable Tests flag */ if (wrapperData->isTestsDisabled) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * (30 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 43); return -1; } if (addQuotes) { _sntprintf(strings[index], 30 + 1, TEXT("-Dwrapper.disable_tests=\"TRUE\"")); } else { _sntprintf(strings[index], 30 + 1, TEXT("-Dwrapper.disable_tests=TRUE")); } } index++; } /* Store the Disable Shutdown Hook flag */ if (wrapperData->isShutdownHookDisabled) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * (38 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 44); return -1; } if (addQuotes) { _sntprintf(strings[index], 38 + 1, TEXT("-Dwrapper.disable_shutdown_hook=\"TRUE\"")); } else { _sntprintf(strings[index], 38 + 1, TEXT("-Dwrapper.disable_shutdown_hook=TRUE")); } } index++; } /* Store the CPU Timeout value */ if (strings) { /* Just to be safe, allow 20 characters for the timeout value */ strings[index] = malloc(sizeof(TCHAR) * (24 + 20 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 45); return -1; } if (addQuotes) { _sntprintf(strings[index], 24 + 20 + 1, TEXT("-Dwrapper.cpu.timeout=\"%d\""), wrapperData->cpuTimeout); } else { _sntprintf(strings[index], 24 + 20 + 1, TEXT("-Dwrapper.cpu.timeout=%d"), wrapperData->cpuTimeout); } } index++; if ((prop = getStringProperty(properties, TEXT("wrapper.java.outfile"), NULL))) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * (25 + _tcslen(prop) + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 47); return -1; } if (addQuotes) { _sntprintf(strings[index], 25 + _tcslen(prop) + 1, TEXT("-Dwrapper.java.outfile=\"%s\""), prop); } else { _sntprintf(strings[index], 25 + _tcslen(prop) + 1, TEXT("-Dwrapper.java.outfile=%s"), prop); } } index++; } if ((prop = getStringProperty(properties, TEXT("wrapper.java.errfile"), NULL))) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * (25 + _tcslen(prop) + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 48); return -1; } if (addQuotes) { _sntprintf(strings[index], 25 + _tcslen(prop) + 1, TEXT("-Dwrapper.java.errfile=\"%s\""), prop); } else { _sntprintf(strings[index], 25 + _tcslen(prop) + 1, TEXT("-Dwrapper.java.errfile=%s"), prop); } } index++; } /* Store the Wrapper JVM ID. (Get here before incremented) */ if (strings) { strings[index] = malloc(sizeof(TCHAR) * (16 + 5 + 1)); /* jvmid up to 5 characters */ if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 49); return -1; } _sntprintf(strings[index], 16 + 5 + 1, TEXT("-Dwrapper.jvmid=%d"), (wrapperData->jvmRestarts + 1)); } index++; /* If this JVM will be detached after startup, it needs to know that. */ if (wrapperData->detachStarted) { if (strings) { strings[index] = malloc(sizeof(TCHAR) * (30 + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 52); return -1; } if (addQuotes) { _sntprintf(strings[index], 30 + 1, TEXT("-Dwrapper.detachStarted=\"TRUE\"")); } else { _sntprintf(strings[index], 30 + 1, TEXT("-Dwrapper.detachStarted=TRUE")); } } index++; } /* Store the main class */ prop = getStringProperty(properties, TEXT("wrapper.java.mainclass"), TEXT("Main")); if (_tcscmp(prop, TEXT("org.tanukisoftware.wrapper.test.Main")) == 0) { thisIsTestWrapper = TRUE; } else { thisIsTestWrapper = FALSE; } if (strings) { strings[index] = malloc(sizeof(TCHAR) * (_tcslen(prop) + 1)); if (!strings[index]) { outOfMemory(TEXT("WBJCAI"), 53); return -1; } _sntprintf(strings[index], _tcslen(prop) + 1, TEXT("%s"), prop); } index++; /* Store any application parameters */ if ((index = wrapperBuildJavaCommandArrayAppParameters(strings, addQuotes, index, thisIsTestWrapper)) < 0) { return -1; } if ((index = wrapperLoadParameterFile(strings, addQuotes, detectDebugJVM, index, TEXT("wrapper.app.parameter_file"), FALSE)) < 0) { return -1; } return index; } /** * command is a pointer to a pointer of an array of character strings. * length is the number of strings in the above array. * * @return TRUE if there were any problems. */ int wrapperBuildJavaCommandArray(TCHAR ***stringsPtr, int *length, int addQuotes, const TCHAR *classpath) { int reqLen; /* Reset the flag stating that the JVM is a debug JVM. */ wrapperData->debugJVM = FALSE; wrapperData->debugJVMTimeoutNotified = FALSE; /* Find out how long the array needs to be first. */ reqLen = wrapperBuildJavaCommandArrayInner(NULL, addQuotes, classpath); if (reqLen < 0) { return TRUE; } *length = reqLen; /* Allocate the correct amount of memory */ *stringsPtr = malloc((*length) * sizeof **stringsPtr); if (!(*stringsPtr)) { outOfMemory(TEXT("WBJCA"), 1); return TRUE; } memset(*stringsPtr, 0, (*length) * sizeof **stringsPtr); /* Now actually fill in the strings */ reqLen = wrapperBuildJavaCommandArrayInner(*stringsPtr, addQuotes, classpath); if (reqLen < 0) { return TRUE; } if (wrapperData->debugJVM) { if ((wrapperData->startupTimeout > 0) || (wrapperData->pingTimeout > 0) || (wrapperData->shutdownTimeout > 0) || (wrapperData->jvmExitTimeout > 0)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("---------------------------------------------------------------------") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT( "The JVM is being launched with a debugger enabled and could possibly\nbe suspended. To avoid unwanted shutdowns, timeouts will be\ndisabled, removing the ability to detect and restart frozen JVMs.")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("---------------------------------------------------------------------") ); } } return FALSE; } void wrapperFreeStringArray(TCHAR **strings, int length) { int i; if (strings != NULL) { /* Loop over and free each of the strings in the array */ for (i = 0; i < length; i++) { if (strings[i] != NULL) { free(strings[i]); strings[i] = NULL; } } free(strings); strings = NULL; } } #ifdef WIN32 static void wrapperPrintExitCodeDescription(int exitCode) { const TCHAR* message = NULL; TCHAR* ptr = NULL; int firstLine = TRUE; int marginLen; HMODULE handle; handle = LoadLibrary(TEXT("ntdll.dll")); if (handle) { /* Try get a message description from ntdll. It contains most common system errors thrown when a program crashes. */ SetLastError(0); message = getErrorText(exitCode, handle); if ((GetLastError() == ERROR_MR_MID_NOT_FOUND) || /* A message description does not exist in the system for the exit code. */ (message[0] == TEXT('\0')) || /* Empty message. */ (_tcschr(message, TEXT('%')) != NULL)) { /* Message contains parameters which we can't print... */ message = NULL; } FreeLibrary(handle); } if (!message) { /* Try get a constant from our internal function. */ message = getExceptionName(exitCode, TRUE); } if (message) { /* The message may contain several lines. Print line by line. */ do { ptr = _tcschr(message, TEXT('\n')); if (ptr) { *ptr = 0; ptr++; } if (firstLine) { /* first line: print the error code */ firstLine = FALSE; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" %d: %s"), exitCode, message); } else { /* line > 1: print a margin to align with the first line. */ marginLen = (int)floor (log10 ((double)abs (exitCode))) + 1; if (exitCode < 0) { marginLen++; } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" %*s %s"), marginLen, TEXT(""), message); } message = ptr; } while (message); } } #endif /** * Called when the Wrapper detects that the JVM process has exited. * Contains code common to all platforms. */ void wrapperJVMProcessExited(TICKS nowTicks, int exitCode) { int setState = TRUE; int logLevel = LEVEL_DEBUG; #ifdef WIN32 int printCrashStatusDescription = FALSE; #endif if (!wrapperData->useJavaIOThread) { /* Make sure there is no JVM output left in the pipe. This must be done before * resetting the java PID for the 'J' format of these messages to be correct. */ while (wrapperReadChildOutput(250)) {}; } /* Reset the Java PID. * Also do it in wrapperJVMDownCleanup(), whichever is called first. */ wrapperData->javaPID = 0; if ((wrapperData->jState == WRAPPER_JSTATE_LAUNCHED) || (wrapperData->jState == WRAPPER_JSTATE_STARTING) || (wrapperData->jState == WRAPPER_JSTATE_STARTED) || (wrapperData->jState == WRAPPER_JSTATE_STOP) || (wrapperData->jState == WRAPPER_JSTATE_STOPPING)) { /* The JVM crashed (it terminated before the STOPPED state was confirmed). Always make sure to print its status code. */ logLevel = LEVEL_STATUS; #ifdef WIN32 /* If the Java application crashed and the JVM couldn't handle the exception, an exit code in the 0xC0... range will be returned. * In such cases, the JVM most likely did not have a chance to print a report in its exception handler. */ if ((NTSTATUS)exitCode > (unsigned long)0xc0000001) { /* NOTE: There is no upper limit for the range as more errors may be added in the future. There is also the * possibility for the exit code to simply be user defined (such as negative values which will be wrapped). * At the very least the print function below will only print messages that exist as standard errors. */ printCrashStatusDescription = TRUE; } #endif } /* The error codes are printed as signed integers and may be wrapped. This is done because the Wrapper exit * code, which is a signed integer, may be set to the JVM exit code. Backward compatibility needs to be kept. */ if (exitCode == 0) { /* The JVM exit code was 0, so leave any current exit code as is. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("JVM process exited with a code of %d, leaving the Wrapper exit code set to %d."), exitCode, wrapperData->exitCode); } else if (wrapperData->exitCode == 0) { /* Update the Wrapper exitCode. */ wrapperData->exitCode = exitCode; log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("JVM process exited with a code of %d, setting the Wrapper exit code to %d."), exitCode, wrapperData->exitCode); } else { /* The Wrapper exit code was already non-zero, so leave it as is. */ log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("JVM process exited with a code of %d, however the Wrapper exit code was already %d."), exitCode, wrapperData->exitCode); } #ifdef WIN32 if (printCrashStatusDescription) { wrapperPrintExitCodeDescription(exitCode); } #endif switch(wrapperData->jState) { case WRAPPER_JSTATE_DOWN_CLEAN: case WRAPPER_JSTATE_DOWN_CHECK: case WRAPPER_JSTATE_DOWN_FLUSH_STDIN: case WRAPPER_JSTATE_DOWN_FLUSH: /* Shouldn't be called in this state. But just in case. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("JVM already down.")); } setState = FALSE; break; case WRAPPER_JSTATE_LAUNCH_DELAY: /* We got a message that the JVM process died when we already thought is was down. * Most likely this was caused by a SIGCHLD signal. We are already in the expected * state so go ahead and ignore it. Do NOT go back to DOWN or the restart flag * and all restart counts will have be lost */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Received a message that the JVM is down when in the LAUNCH(DELAY) state.")); } setState = FALSE; break; case WRAPPER_JSTATE_RESTART: /* We got a message that the JVM process died when we already thought is was down. * Most likely this was caused by a SIGCHLD signal. We are already in the expected * state so go ahead and ignore it. Do NOT go back to DOWN or the restart flag * and all restart counts will have be lost */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Received a message that the JVM is down when in the RESTART state.")); } setState = FALSE; break; case WRAPPER_JSTATE_LAUNCH: /* We got a message that the JVM process died when we already thought is was down. * Most likely this was caused by a SIGCHLD signal. We are already in the expected * state so go ahead and ignore it. Do NOT go back to DOWN or the restart flag * and all restart counts will have be lost. * This can happen if the Java process dies Immediately after it is launched. It * is very rare if Java is launched, but can happen if the configuration is set to * launch something else. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Received a message that the JVM is down when in the LAUNCH state.")); } setState = FALSE; break; case WRAPPER_JSTATE_LAUNCHING: wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_AUTOMATIC; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("JVM exited while loading the application.")); break; case WRAPPER_JSTATE_LAUNCHED: /* Shouldn't be called in this state, but just in case. */ wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_AUTOMATIC; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("JVM exited before starting the application.")); break; case WRAPPER_JSTATE_STARTING: wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_AUTOMATIC; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("JVM exited while starting the application.")); break; case WRAPPER_JSTATE_STARTED: wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_AUTOMATIC; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("JVM exited unexpectedly.")); break; case WRAPPER_JSTATE_STOP: case WRAPPER_JSTATE_STOPPING: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("JVM exited unexpectedly while stopping the application.")); break; case WRAPPER_JSTATE_STOPPED: if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("JVM exited normally.")); } break; case WRAPPER_JSTATE_KILLING: case WRAPPER_JSTATE_KILL: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("JVM exited on its own while waiting to kill the application.")); break; case WRAPPER_JSTATE_KILLED: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("JVM exited after being requested to terminate.")); break; default: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unexpected jState=%d in wrapperJVMProcessExited."), wrapperData->jState); break; } wrapperJVMDownCleanup(setState); } void wrapperBuildKey() { int i; size_t kcNum; size_t num; static int seeded = FALSE; /* Seed the randomizer */ if (!seeded) { srand((unsigned)time(NULL)); seeded = TRUE; } /* Start by generating a key */ num = _tcslen(keyChars); for (i = 0; i < 16; i++) { /* The way rand works, this will sometimes equal num, which is too big. * This is rare so just round those cases down. */ /* Some platforms use very large RAND_MAX values that cause overflow problems in our math */ if (RAND_MAX > 0x10000) { kcNum = (size_t)((rand() >> 8) * num / (RAND_MAX >> 8)); } else { kcNum = (size_t)(rand() * num / RAND_MAX); } if (kcNum >= num) { kcNum = num - 1; } wrapperData->key[i] = keyChars[kcNum]; } wrapperData->key[16] = TEXT('\0'); /* printf(" Key=%s Len=%lu\n", wrapperData->key, _tcslen(wrapperData->key)); */ } #ifdef WIN32 /* The ABOVE and BELOW normal priority class constants are not defined in MFVC 6.0 headers. */ #ifndef ABOVE_NORMAL_PRIORITY_CLASS #define ABOVE_NORMAL_PRIORITY_CLASS 0x00008000 #endif #ifndef BELOW_NORMAL_PRIORITY_CLASS #define BELOW_NORMAL_PRIORITY_CLASS 0x00004000 #endif /** * Return FALSE if successful, TRUE if there were problems. */ int wrapperBuildNTServiceInfo() { TCHAR *work; const TCHAR *priority; size_t len; size_t valLen; size_t workLen; int i; TCHAR **propertyNames; TCHAR **propertyValues; long unsigned int *propertyIndices; if (!wrapperData->configured) { /* Load the service load order group */ updateStringValue(&wrapperData->ntServiceLoadOrderGroup, getStringProperty(properties, TEXT("wrapper.ntservice.load_order_group"), TEXT(""))); if (getStringProperties(properties, TEXT("wrapper.ntservice.dependency."), TEXT(""), wrapperData->ignoreSequenceGaps, FALSE, &propertyNames, &propertyValues, &propertyIndices)) { /* Failed */ return TRUE; } /* Build the dependency list. Decide how large the list needs to be */ len = 0; i = 0; while (propertyNames[i]) { valLen = _tcslen(propertyValues[i]); if (valLen > 0) { len += valLen + 1; } i++; } /* List must end with a double '\0'. If the list is not empty then it will end with 3. But that is fine. */ len += 2; /* Actually build the buffer */ if (wrapperData->ntServiceDependencies) { /** This is a reload, so free up the old data. */ free(wrapperData->ntServiceDependencies); wrapperData->ntServiceDependencies = NULL; } work = wrapperData->ntServiceDependencies = malloc(sizeof(TCHAR) * len); if (!work) { outOfMemory(TEXT("WBNTSI"), 1); return TRUE; } workLen = len; /* Now actually build up the list. Each value is separated with a '\0'. */ i = 0; while (propertyNames[i]) { valLen = _tcslen(propertyValues[i]); if (valLen > 0) { _tcsncpy(work, propertyValues[i], workLen); work += valLen + 1; workLen -= valLen + 1; } i++; } /* Add two more nulls to the end of the list. */ work[0] = TEXT('\0'); work[1] = TEXT('\0'); /* Memory allocated in work is stored in wrapperData. The memory should not be released here. */ work = NULL; freeStringProperties(propertyNames, propertyValues, propertyIndices); /* Set the service start type */ if (strcmpIgnoreCase(getStringProperty(properties, TEXT("wrapper.ntservice.starttype"), TEXT("DEMAND_START")), TEXT("AUTO_START")) == 0) { wrapperData->ntServiceStartType = SERVICE_AUTO_START; } else { wrapperData->ntServiceStartType = SERVICE_DEMAND_START; } /* Set the service priority class */ priority = getStringProperty(properties, TEXT("wrapper.ntservice.process_priority"), TEXT("NORMAL")); if ( (strcmpIgnoreCase(priority, TEXT("LOW")) == 0) || (strcmpIgnoreCase(priority, TEXT("IDLE")) == 0) ) { wrapperData->ntServicePriorityClass = IDLE_PRIORITY_CLASS; } else if (strcmpIgnoreCase(priority, TEXT("HIGH")) == 0) { wrapperData->ntServicePriorityClass = HIGH_PRIORITY_CLASS; } else if (strcmpIgnoreCase(priority, TEXT("REALTIME")) == 0) { wrapperData->ntServicePriorityClass = REALTIME_PRIORITY_CLASS; } else if (strcmpIgnoreCase(priority, TEXT("ABOVE_NORMAL")) == 0) { wrapperData->ntServicePriorityClass = ABOVE_NORMAL_PRIORITY_CLASS; } else if (strcmpIgnoreCase(priority, TEXT("BELOW_NORMAL")) == 0) { wrapperData->ntServicePriorityClass = BELOW_NORMAL_PRIORITY_CLASS; } else { wrapperData->ntServicePriorityClass = NORMAL_PRIORITY_CLASS; } /* Account name */ updateStringValue(&wrapperData->ntServiceAccount, getStringProperty(properties, TEXT("wrapper.ntservice.account"), NULL)); if (wrapperData->ntServiceAccount && (_tcslen(wrapperData->ntServiceAccount) <= 0)) { wrapperData->ntServiceAccount = NULL; } if (wrapperData->ntServiceAccount) { if (strcmpIgnoreCase(getStringProperty(properties, TEXT("wrapper.ntservice.account.logon_as_service"), TEXT("ALLOW")), TEXT("UNCHANGED")) != 0) { wrapperData->ntServiceAddLogonAsService = TRUE; } } /* Account password */ wrapperData->ntServicePrompt = getBooleanProperty(properties, TEXT("wrapper.ntservice.account.prompt"), FALSE); if (wrapperData->ntServicePrompt == TRUE) { wrapperData->ntServicePasswordPrompt = TRUE; } else { wrapperData->ntServicePasswordPrompt = getBooleanProperty(properties, TEXT("wrapper.ntservice.password.prompt"), FALSE); } wrapperData->ntServicePasswordPromptMask = getBooleanProperty(properties, TEXT("wrapper.ntservice.password.prompt.mask"), TRUE); updateStringValue(&wrapperData->ntServicePassword, getStringProperty(properties, TEXT("wrapper.ntservice.password"), NULL)); if ( wrapperData->ntServicePassword && ( _tcslen( wrapperData->ntServicePassword ) <= 0 ) ) { wrapperData->ntServicePassword = NULL; } if (!wrapperData->ntServiceAccount) { /* If there is not account name, then the password must not be set. */ wrapperData->ntServicePassword = NULL; } /* Interactive */ wrapperData->ntServiceInteractive = getBooleanProperty(properties, TEXT("wrapper.ntservice.interactive"), FALSE); /* The interactive flag can not be set if an account is also set. */ if (wrapperData->ntServiceAccount && wrapperData->ntServiceInteractive) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Ignoring the wrapper.ntservice.interactive property because it can not be set when wrapper.ntservice.account is also set.")); wrapperData->ntServiceInteractive = FALSE; } /* Display a Console Window. */ wrapperData->ntAllocConsole = getBooleanProperty(properties, TEXT("wrapper.ntservice.console"), FALSE); /* Set the default show wrapper console flag to the value of the alloc console flag. */ wrapperData->ntShowWrapperConsole = wrapperData->ntAllocConsole; /* Hide the JVM Console Window. */ wrapperData->ntHideJVMConsole = getBooleanProperty(properties, TEXT("wrapper.ntservice.hide_console"), TRUE); /* Make sure that a console is always generated to support thread dumps */ wrapperData->generateConsole = getBooleanProperty(properties, TEXT("wrapper.ntservice.generate_console"), TRUE); /* Wait hint used when reporting STARTING, RESUMING, PAUSING or STOPPING status to the service controller. */ wrapperData->ntStartupWaitHint = getIntProperty(properties, TEXT("wrapper.ntservice.startup.waithint"), 30); wrapperData->ntShutdownWaitHint = getIntProperty(properties, TEXT("wrapper.ntservice.shutdown.waithint"), 30); /* Set the range of valid values for the wait hints to [2secs, 1min] */ if (wrapperData->ntStartupWaitHint > 60) { wrapperData->ntStartupWaitHint = 60; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The value of %s must be at most %d second(s). Changing to %d."), TEXT("wrapper.ntservice.startup.waithint"), 60, wrapperData->ntStartupWaitHint); } else if (wrapperData->ntStartupWaitHint < 2) { wrapperData->ntStartupWaitHint = 2; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The value of %s must be at least %d second(s). Changing to %d."), TEXT("wrapper.ntservice.startup.waithint"), 2, wrapperData->ntStartupWaitHint); } if (wrapperData->ntShutdownWaitHint > 60) { wrapperData->ntShutdownWaitHint = 60; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The value of %s must be at most %d second(s). Changing to %d."), TEXT("wrapper.ntservice.shutdown.waithint"), 60, wrapperData->ntStartupWaitHint); } else if (wrapperData->ntShutdownWaitHint < 2) { wrapperData->ntShutdownWaitHint = 2; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The value of %s must be at least %d second(s). Changing to %d."), TEXT("wrapper.ntservice.shutdown.waithint"), 2, wrapperData->ntStartupWaitHint); } if (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("i")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-install")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("it")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-installstart"))) { #ifdef SUPPORT_PRESHUTDOWN /* Accept preshutdown control code? */ wrapperData->ntPreshutdown = getBooleanProperty(properties, TEXT("wrapper.ntservice.preshutdown"), FALSE); if (wrapperData->ntPreshutdown) { if (!isVista()) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Property %s is ignored on this system."), TEXT("wrapper.ntservice.preshutdown")); removeProperty(properties, TEXT("wrapper.ntservice.preshutdown")); wrapperData->ntPreshutdown = FALSE; } else { wrapperData->ntPreshutdownTimeout = getIntProperty(properties, TEXT("wrapper.ntservice.preshutdown.timeout"), 180); if (wrapperData->ntPreshutdownTimeout > 3600) { wrapperData->ntPreshutdownTimeout = 3600; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The value of %s must be at most %d hour (%d second(s)). Changing to %d."), TEXT("wrapper.ntservice.preshutdown.timeout"), 1, 3600, wrapperData->ntPreshutdownTimeout); } else if (wrapperData->ntPreshutdownTimeout < 1) { wrapperData->ntPreshutdownTimeout = 1; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The value of %s must be at least %d second(s). Changing to %d."), TEXT("wrapper.ntservice.preshutdown.timeout"), 1, wrapperData->ntPreshutdownTimeout); } } } #else if (getBooleanProperty(properties, TEXT("wrapper.ntservice.preshutdown"), FALSE)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Property %s is not supported on Windows Itanium."), TEXT("wrapper.ntservice.preshutdown")); removeProperty(properties, TEXT("wrapper.ntservice.preshutdown")); } #endif } } /* Set the single invocation flag. */ wrapperData->isSingleInvocation = getBooleanProperty(properties, TEXT("wrapper.single_invocation"), FALSE); wrapperData->threadDumpControlCode = getIntProperty(properties, TEXT("wrapper.thread_dump_control_code"), 255); if (wrapperData->threadDumpControlCode <= 0) { /* Disabled */ } else if ((wrapperData->threadDumpControlCode < 128) || (wrapperData->threadDumpControlCode > 255)) { wrapperData->threadDumpControlCode = 255; log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("Ignoring the wrapper.thread_dump_control_code property because it must be in the range 128-255 or 0.")); } return FALSE; } #endif int validateTimeout(const TCHAR* propertyName, int value) { int okValue; if (value <= 0) { okValue = 0; } else if (value > WRAPPER_TIMEOUT_MAX) { okValue = WRAPPER_TIMEOUT_MAX; } else { okValue = value; } if (okValue != value) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("The value of %s must be in the range 1 to %d seconds (%d days), or 0 to disable. Changing to %d."), propertyName, WRAPPER_TIMEOUT_MAX, WRAPPER_TIMEOUT_MAX / 86400, okValue); } return okValue; } void wrapperLoadHostName() { char hostName[80]; TCHAR* hostName2; #ifdef UNICODE int len; #endif if (gethostname(hostName, sizeof(hostName))) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to obtain host name. %s"), getLastErrorText()); } else { #ifdef UNICODE #ifdef WIN32 len = MultiByteToWideChar(CP_OEMCP, 0, hostName, -1, NULL, 0); if (len <= 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Invalid multibyte sequence in port address \"%s\" : %s"), hostName, getLastErrorText()); return; } hostName2 = malloc(sizeof(LPWSTR) * (len + 1)); if (!hostName2) { outOfMemory(TEXT("LHN"), 1); return; } MultiByteToWideChar(CP_OEMCP,0, hostName, -1, hostName2, len + 1); #else len = mbstowcs(NULL, hostName, MBSTOWCS_QUERY_LENGTH); if (len == (size_t)-1) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Invalid multibyte sequence in port address \"%s\" : %s"), hostName, getLastErrorText()); return; } hostName2 = malloc(sizeof(TCHAR) * (len + 1)); if (!hostName2) { outOfMemory(TEXT("LHN"), 2); return; } mbstowcs(hostName2, hostName, len + 1); hostName2[len] = TEXT('\0'); /* Avoid bufferflows caused by badly encoded characters. */ #endif #else /* No conversion needed. Do an extra malloc here to keep the code simple below. */ len = _tcslen(hostName); hostName2 = malloc(sizeof(TCHAR) * (len + 1)); if (!hostName2) { outOfMemory(TEXT("LHN"), 3); return; } _tcsncpy(hostName2, hostName, len + 1); #endif wrapperData->hostName = malloc(sizeof(TCHAR) * (_tcslen(hostName2) + 1)); if (!wrapperData->hostName) { outOfMemory(TEXT("LHN"), 4); free(hostName2); return; } _tcsncpy(wrapperData->hostName, hostName2, _tcslen(hostName2) + 1); free(hostName2); } } /** * Resolves an action name into an actionId. * * @param actionName Action to be resolved. (Contents of buffer will be converted to upper case.) * @param propertyName The name of the property where the action name originated. * @param logErrors TRUE if errors should be logged. * * @return The action Id, or 0 if it was unknown. */ int getActionForName(TCHAR *actionName, const TCHAR *propertyName, int logErrors) { size_t len; size_t i; int action; /* We need the actionName in upper case. */ len = _tcslen(actionName); for (i = 0; i < len; i++) { actionName[i] = _totupper(actionName[i]); } if (_tcscmp(actionName, TEXT("RESTART")) == 0) { action = ACTION_RESTART; } else if (_tcscmp(actionName, TEXT("SHUTDOWN")) == 0) { action = ACTION_SHUTDOWN; } else if (_tcscmp(actionName, TEXT("DUMP")) == 0) { action = ACTION_DUMP; } else if (_tcscmp(actionName, TEXT("NONE")) == 0) { action = ACTION_NONE; } else if (_tcscmp(actionName, TEXT("DEBUG")) == 0) { action = ACTION_DEBUG; } else if (_tcscmp(actionName, TEXT("SUCCESS")) == 0) { action = ACTION_SUCCESS; } else if (_tcscmp(actionName, TEXT("GC")) == 0) { action = ACTION_GC; } else if (_tcscmp(actionName, TEXT("PAUSE")) == 0) { if (logErrors) { log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("Pause actions require the Standard Edition. Ignoring action '%s' in the %s property."), actionName, propertyName); } action = 0; } else if (_tcscmp(actionName, TEXT("RESUME")) == 0) { if (logErrors) { log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("Resume actions require the Standard Edition. Ignoring action '%s' in the %s property."), actionName, propertyName); } action = 0; } else if (_tcsstr(actionName, TEXT("USER_")) == actionName) { if (logErrors) { log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("User actions require the Professional Edition. Ignoring action '%s' in the %s property."), actionName, propertyName); } action = 0; } else if (_tcsstr(actionName, TEXT("SUSPEND_TIMEOUTS_")) == actionName) { if (logErrors) { log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("Suspend timeouts action requires the Standard Edition. Ignoring action '%s' in the %s property."), actionName, propertyName); } action = 0; } else if (_tcscmp(actionName, TEXT("RESUME_TIMEOUTS")) == 0) { if (logErrors) { log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("Resume timeouts actions require the Standard Edition. Ignoring action '%s' in the %s property."), actionName, propertyName); } action = 0; } else { if (logErrors) { log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("Encountered an unknown action '%s' in the %s property. Skipping."), actionName, propertyName); } action = 0; } return action; } /** * Parses a list of actions for an action property. * * @param actionNameList A space separated list of action names. * @param propertyName The name of the property where the action name originated. * * @return an array of integer action ids, or NULL if there were any problems. */ int *wrapperGetActionListForNames(const TCHAR *actionNameList, const TCHAR *propertyName) { size_t len; TCHAR *workBuffer; TCHAR *token; int actionCount; int action; int *actionList = NULL; #if defined(UNICODE) && !defined(WIN32) TCHAR *state; #endif #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("wrapperGetActionListForNames(%s, %s)"), actionNameList, propertyName); #endif /* First get a count of the number of valid actions. */ len = _tcslen(actionNameList); workBuffer = malloc(sizeof(TCHAR) * (len + 1)); if (!workBuffer) { outOfMemory(TEXT("GALFN"), 1); } else { actionCount = 0; _tcsncpy(workBuffer, actionNameList, len + 1); token = _tcstok(workBuffer, TEXT(" ,") #if defined(UNICODE) && !defined(WIN32) , &state #endif ); while (token != NULL) { action = getActionForName(token, propertyName, TRUE); if (action == 0) { /* Unknown action */ } else { actionCount++; } #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT(" action='%s' -> %d"), token, action); #endif token = _tcstok(NULL, TEXT(" ,") #if defined(UNICODE) && !defined(WIN32) , &state #endif ); } /* Add ACTION_LIST_END */ actionCount++; /* Create the action list to return. */ actionList = malloc(sizeof(int) * actionCount); if (!actionList) { outOfMemory(TEXT("GALFN"), 2); } else { /* Now actually pull out the actions */ actionCount = 0; _tcsncpy(workBuffer, actionNameList, len + 1); token = _tcstok(workBuffer, TEXT(" ,") #if defined(UNICODE) && !defined(WIN32) , &state #endif ); while (token != NULL) { action = getActionForName(token, propertyName, FALSE); if (action == 0) { /* Unknown action */ } else { actionList[actionCount] = action; actionCount++; } token = _tcstok(NULL, TEXT(" ,") #if defined(UNICODE) && !defined(WIN32) , &state #endif ); } /* Add ACTION_LIST_END */ actionList[actionCount] = ACTION_LIST_END; actionCount++; /* actionList returned, so don't free it. */ } free(workBuffer); } return actionList; } /** * Loads in the configuration triggers. * * @return Returns FALSE if successful, TRUE if there were any problems. */ int loadConfigurationTriggers() { const TCHAR *prop; TCHAR propName[256]; int i; TCHAR **propertyNames; TCHAR **propertyValues; long unsigned int *propertyIndices; #ifdef _DEBUG int j; #endif /* To support reloading, we need to free up any previously loaded filters. */ if (wrapperData->outputFilterCount > 0) { for (i = 0; i < wrapperData->outputFilterCount; i++) { free(wrapperData->outputFilters[i]); wrapperData->outputFilters[i] = NULL; } free(wrapperData->outputFilters); wrapperData->outputFilters = NULL; if (wrapperData->outputFilterActionLists) { for (i = 0; i < wrapperData->outputFilterCount; i++) { free(wrapperData->outputFilterActionLists[i]); wrapperData->outputFilterActionLists[i] = NULL; } free(wrapperData->outputFilterActionLists); wrapperData->outputFilterActionLists = NULL; } /* Individual messages are references to property values and are not malloced. */ free(wrapperData->outputFilterMessages); wrapperData->outputFilterMessages = NULL; free(wrapperData->outputFilterAllowWildFlags); wrapperData->outputFilterAllowWildFlags = NULL; free(wrapperData->outputFilterMinLens); wrapperData->outputFilterMinLens = NULL; } wrapperData->outputFilterCount = 0; if (getStringProperties(properties, TEXT("wrapper.filter.trigger."), TEXT(""), wrapperData->ignoreSequenceGaps, FALSE, &propertyNames, &propertyValues, &propertyIndices)) { /* Failed */ return TRUE; } /* Loop over the properties and count how many triggers there are. */ i = 0; while (propertyNames[i]) { wrapperData->outputFilterCount++; i++; } #if defined(MACOSX) wrapperData->outputFilterCount++; i++; #endif /* Now that a count is known, allocate memory to hold the filters and actions and load them in. */ if (wrapperData->outputFilterCount > 0) { wrapperData->outputFilters = malloc(sizeof(TCHAR *) * wrapperData->outputFilterCount); if (!wrapperData->outputFilters) { outOfMemory(TEXT("LC"), 1); return TRUE; } memset(wrapperData->outputFilters, 0, sizeof(TCHAR *) * wrapperData->outputFilterCount); wrapperData->outputFilterActionLists = malloc(sizeof(int*) * wrapperData->outputFilterCount); if (!wrapperData->outputFilterActionLists) { outOfMemory(TEXT("LC"), 2); return TRUE; } memset(wrapperData->outputFilterActionLists, 0, sizeof(int*) * wrapperData->outputFilterCount); wrapperData->outputFilterMessages = malloc(sizeof(TCHAR *) * wrapperData->outputFilterCount); if (!wrapperData->outputFilterMessages) { outOfMemory(TEXT("LC"), 3); return TRUE; } wrapperData->outputFilterAllowWildFlags = malloc(sizeof(int) * wrapperData->outputFilterCount); if (!wrapperData->outputFilterAllowWildFlags) { outOfMemory(TEXT("LC"), 4); return TRUE; } memset(wrapperData->outputFilterAllowWildFlags, 0, sizeof(int) * wrapperData->outputFilterCount); wrapperData->outputFilterMinLens = malloc(sizeof(size_t) * wrapperData->outputFilterCount); if (!wrapperData->outputFilterMinLens) { outOfMemory(TEXT("LC"), 5); return TRUE; } memset(wrapperData->outputFilterMinLens, 0, sizeof(size_t) * wrapperData->outputFilterCount); i = 0; while (propertyNames[i]) { prop = propertyValues[i]; wrapperData->outputFilters[i] = malloc(sizeof(TCHAR) * (_tcslen(prop) + 1)); if (!wrapperData->outputFilters[i]) { outOfMemory(TEXT("LC"), 3); return TRUE; } _tcsncpy(wrapperData->outputFilters[i], prop, _tcslen(prop) + 1); /* Get the action */ _sntprintf(propName, 256, TEXT("wrapper.filter.action.%lu"), propertyIndices[i]); prop = getStringProperty(properties, propName, TEXT("RESTART")); wrapperData->outputFilterActionLists[i] = wrapperGetActionListForNames(prop, propName); /* Get the message */ _sntprintf(propName, 256, TEXT("wrapper.filter.message.%lu"), propertyIndices[i]); prop = getStringProperty(properties, propName, NULL); wrapperData->outputFilterMessages[i] = (TCHAR *)prop; /* Get the wildcard flags. */ _sntprintf(propName, 256, TEXT("wrapper.filter.allow_wildcards.%lu"), propertyIndices[i]); wrapperData->outputFilterAllowWildFlags[i] = getBooleanProperty(properties, propName, FALSE); if (wrapperData->outputFilterAllowWildFlags[i]) { /* Calculate the minimum text length. */ wrapperData->outputFilterMinLens[i] = wrapperGetMinimumTextLengthForPattern(wrapperData->outputFilters[i]); } #ifdef _DEBUG _tprintf(TEXT("filter #%lu, actions=("), propertyIndices[i]); if (wrapperData->outputFilterActionLists[i]) { j = 0; while (wrapperData->outputFilterActionLists[i][j]) { if (j > 0) { _tprintf(TEXT(",")); } _tprintf(TEXT("%d"), wrapperData->outputFilterActionLists[i][j]); j++; } } _tprintf(TEXT("), filter='%s'\n"), wrapperData->outputFilters[i]); #endif i++; } #if defined(MACOSX) wrapperData->outputFilters[i] = malloc(sizeof(TCHAR) * (_tcslen(TRIGGER_ADVICE_NIL_SERVER) + 1)); if (!wrapperData->outputFilters[i]) { outOfMemory(TEXT("LC"), 4); return TRUE; } _tcsncpy(wrapperData->outputFilters[i], TRIGGER_ADVICE_NIL_SERVER, _tcslen(TRIGGER_ADVICE_NIL_SERVER) + 1); wrapperData->outputFilterActionLists[i] = malloc(sizeof(int) * 2); if (!wrapperData->outputFilters[i]) { outOfMemory(TEXT("LC"), 5); return TRUE; } wrapperData->outputFilterActionLists[i][0] = ACTION_ADVICE_NIL_SERVER; wrapperData->outputFilterActionLists[i][1] = ACTION_LIST_END; wrapperData->outputFilterMessages[i] = NULL; wrapperData->outputFilterAllowWildFlags[i] = FALSE; wrapperData->outputFilterMinLens[i] = 0; i++; #endif } freeStringProperties(propertyNames, propertyValues, propertyIndices); return FALSE; } int getBackendTypeForName(const TCHAR *typeName) { if (strcmpIgnoreCase(typeName, TEXT("SOCKET")) == 0) { return WRAPPER_BACKEND_TYPE_SOCKET; } else if (strcmpIgnoreCase(typeName, TEXT("SOCKET_IPv4")) == 0) { return WRAPPER_BACKEND_TYPE_SOCKET_V4; } else if (strcmpIgnoreCase(typeName, TEXT("SOCKET_IPv6")) == 0) { return WRAPPER_BACKEND_TYPE_SOCKET_V6; } else if (strcmpIgnoreCase(typeName, TEXT("PIPE")) == 0) { return WRAPPER_BACKEND_TYPE_PIPE; } else if (strcmpIgnoreCase(typeName, TEXT("AUTO")) == 0) { return WRAPPER_BACKEND_TYPE_AUTO; } else { return WRAPPER_BACKEND_TYPE_UNKNOWN; } } /** * Return FALSE if successful, TRUE if there were problems. */ int loadConfiguration() { TCHAR propName[256]; const TCHAR* val; int startupDelay; if (wrapperLoadLoggingProperties(FALSE)) { return TRUE; } /* Decide on the error exit code */ getConfiguredErrorExitCode(FALSE); /* Decide on the backend type to use. */ val = getStringProperty(properties, TEXT("wrapper.backend.type"), TEXT("AUTO")); wrapperData->backendType = getBackendTypeForName(val); if (wrapperData->backendType == WRAPPER_BACKEND_TYPE_UNKNOWN) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Unknown value for wrapper.backend.type: %s. Setting it to AUTO."), val); wrapperData->backendType = WRAPPER_BACKEND_TYPE_AUTO; } /* Decide whether the classpath should be passed via the environment. */ wrapperData->environmentClasspath = getBooleanProperty(properties, TEXT("wrapper.java.classpath.use_environment"), FALSE); /* Decide how sequence gaps should be handled before any other properties are loaded. */ wrapperData->ignoreSequenceGaps = getBooleanProperty(properties, TEXT("wrapper.ignore_sequence_gaps"), FALSE); /* To make configuration reloading work correctly with changes to the log file, * it needs to be closed here. */ closeLogfile(); /* Maintain the logger just in case we wrote any queued errors. */ maintainLogger(); /* Because the first call could cause errors as well, do it again to clear them out. * This is only a one-time thing on startup as we test the new logfile configuration. */ maintainLogger(); /* Initialize some values not loaded */ wrapperData->exitCode = 0; updateStringValue(&wrapperData->portAddress, getStringProperty(properties, TEXT("wrapper.port.address"), NULL)); /* Get the port. The int will wrap within the 0-65535 valid range, so no need to test the value. */ wrapperData->port = getIntProperty(properties, TEXT("wrapper.port"), 0); wrapperData->portMin = getIntProperty(properties, TEXT("wrapper.port.min"), 32000); if ((wrapperData->portMin < 1) || (wrapperData->portMin > 65535)) { wrapperData->portMin = 32000; log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("%s must be in the range %d to %d. Changing to %d."), TEXT("wrapper.port.min"), 1, 65535, wrapperData->portMin); } wrapperData->portMax = getIntProperty(properties, TEXT("wrapper.port.max"), 32999); if ((wrapperData->portMax < 1) || (wrapperData->portMax > 65535)) { wrapperData->portMax = __min(wrapperData->portMin + 999, 65535); log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("%s must be in the range %d to %d. Changing to %d."), TEXT("wrapper.port.max"), 1, 65535, wrapperData->portMax); } else if (wrapperData->portMax < wrapperData->portMin) { wrapperData->portMax = __min(wrapperData->portMin + 999, 65535); log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("%s must be greater than or equal to %s. Changing to %d."), TEXT("wrapper.port.max"), TEXT("wrapper.port.min"), wrapperData->portMax); } /* Get the port for the JVM side of the socket. */ wrapperData->jvmPort = getIntProperty(properties, TEXT("wrapper.jvm.port"), -1); if (wrapperData->jvmPort > 0) { if (wrapperData->jvmPort == wrapperData->port) { wrapperData->jvmPort = -1; log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("wrapper.jvm.port must not equal wrapper.port. Changing to the default.")); } } wrapperData->jvmPortMin = getIntProperty(properties, TEXT("wrapper.jvm.port.min"), 31000); if ((wrapperData->jvmPortMin < 1) || (wrapperData->jvmPortMin > 65535)) { wrapperData->jvmPortMin = 31000; log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("%s must be in the range %d to %d. Changing to %d."), TEXT("wrapper.jvm.port.min"), 1, 65535, wrapperData->jvmPortMin); } wrapperData->jvmPortMax = getIntProperty(properties, TEXT("wrapper.jvm.port.max"), 31999); if ((wrapperData->jvmPortMax < 1) || (wrapperData->jvmPortMax > 65535)) { wrapperData->jvmPortMax = __min(wrapperData->jvmPortMin + 999, 65535); log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("%s must be in the range %d to %d. Changing to %d."), TEXT("wrapper.jvm.port.max"), 1, 65535, wrapperData->jvmPortMax); } else if (wrapperData->jvmPortMax < wrapperData->jvmPortMin) { wrapperData->jvmPortMax = __min(wrapperData->jvmPortMin + 999, 65535); log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("%s must be greater than or equal to %s. Changing to %d."), TEXT("wrapper.jvm.port.max"), TEXT("wrapper.jvm.port.min"), wrapperData->jvmPortMax); } wrapperData->printJVMVersion = getBooleanProperty(properties, TEXT("wrapper.java.version.output"), FALSE); /* Get the wrapper command log level. */ wrapperData->commandLogLevel = getLogLevelForName( getStringProperty(properties, TEXT("wrapper.java.command.loglevel"), TEXT("DEBUG"))); if ((wrapperData->commandLogLevel >= LEVEL_NONE) || (wrapperData->commandLogLevel == LEVEL_UNKNOWN)) { /* Should never be possible to completely disable the java command as this would make it very difficult to support. */ wrapperData->commandLogLevel = LEVEL_DEBUG; } #ifdef WIN32 /* Unless wrapper.java.monitor=LAUNCHED is specified, always manage the Java process if there is a redirection. */ val = getStringProperty(properties, TEXT("wrapper.java.monitor"), TEXT("JAVA")); if (strcmpIgnoreCase(val, TEXT("LAUNCHED")) == 0) { wrapperData->monitorLaunched = TRUE; } else { if (strcmpIgnoreCase(val, TEXT("JAVA")) != 0) { log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("Encountered an invalid value for configuration property %s=%s. Resolving to %s."), TEXT("wrapper.java.monitor"), val, TEXT("JAVA")); } wrapperData->monitorLaunched = FALSE; } #endif wrapperData->monitorRedirectLogLevel = getLogLevelForName(getStringProperty(properties, TEXT("wrapper.java.monitor.redirect.loglevel"), TEXT("WARN"))); if ((wrapperData->monitorRedirectLogLevel >= LEVEL_NONE) || (wrapperData->monitorRedirectLogLevel == LEVEL_UNKNOWN)) { /* Always at least show a debug message for support. */ wrapperData->monitorRedirectLogLevel = LEVEL_DEBUG; } /* Should we detach the JVM on startup. */ if (wrapperData->isConsole) { wrapperData->detachStarted = getBooleanProperty(properties, TEXT("wrapper.jvm_detach_started"), FALSE); } /* Get the use system time flag. */ if (!wrapperData->configured) { wrapperData->useSystemTime = getBooleanProperty(properties, TEXT("wrapper.use_system_time"), FALSE); } if (!wrapperData->configured) { wrapperData->logBufferGrowth = getBooleanProperty(properties, TEXT("wrapper.log_buffer_growth"), FALSE); setLogBufferGrowth(wrapperData->logBufferGrowth); } #ifdef WIN32 /* Get the use javaio buffer size. */ if (!wrapperData->configured) { wrapperData->javaIOBufferSize = getIntProperty(properties, TEXT("wrapper.javaio.buffer_size"), WRAPPER_JAVAIO_BUFFER_SIZE_DEFAULT); if (wrapperData->javaIOBufferSize == WRAPPER_JAVAIO_BUFFER_SIZE_SYSTEM_DEFAULT) { /* Ok. System default buffer size. */ } else if (wrapperData->javaIOBufferSize < WRAPPER_JAVAIO_BUFFER_SIZE_MIN) { wrapperData->javaIOBufferSize = WRAPPER_JAVAIO_BUFFER_SIZE_MIN; log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("%s must be in the range %d to %d or %d. Changing to %d."), TEXT("wrapper.javaio.buffer_size"), WRAPPER_JAVAIO_BUFFER_SIZE_MIN, WRAPPER_JAVAIO_BUFFER_SIZE_MAX, WRAPPER_JAVAIO_BUFFER_SIZE_SYSTEM_DEFAULT, wrapperData->javaIOBufferSize); } else if (wrapperData->javaIOBufferSize > WRAPPER_JAVAIO_BUFFER_SIZE_MAX) { wrapperData->javaIOBufferSize = WRAPPER_JAVAIO_BUFFER_SIZE_MAX; log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("%s must be in the range %d to %d or %d. Changing to %d."), TEXT("wrapper.javaio.buffer_size"), WRAPPER_JAVAIO_BUFFER_SIZE_MIN, WRAPPER_JAVAIO_BUFFER_SIZE_MAX, WRAPPER_JAVAIO_BUFFER_SIZE_SYSTEM_DEFAULT, wrapperData->javaIOBufferSize); } } #endif /* Get the use javaio thread flag. */ if (!wrapperData->configured) { wrapperData->useJavaIOThread = getBooleanProperty(properties, TEXT("wrapper.javaio.use_thread"), getBooleanProperty(properties, TEXT("wrapper.use_javaio_thread"), FALSE)); } /* Decide whether or not a mutex should be used to protect the tick timer. */ if (!wrapperData->configured) { wrapperData->useTickMutex = getBooleanProperty(properties, TEXT("wrapper.use_tick_mutex"), FALSE); } /* Get the timer thresholds. Properties are in seconds, but internally we use ticks. */ wrapperData->timerFastThreshold = getIntProperty(properties, TEXT("wrapper.timer_fast_threshold"), WRAPPER_TIMER_FAST_THRESHOLD * WRAPPER_TICK_MS / 1000) * 1000 / WRAPPER_TICK_MS; wrapperData->timerSlowThreshold = getIntProperty(properties, TEXT("wrapper.timer_slow_threshold"), WRAPPER_TIMER_SLOW_THRESHOLD * WRAPPER_TICK_MS / 1000) * 1000 / WRAPPER_TICK_MS; /* Load the name of the native library to be loaded. */ wrapperData->nativeLibrary = getStringProperty(properties, TEXT("wrapper.native_library"), TEXT("wrapper")); /* Get the append PATH to library path flag. */ wrapperData->libraryPathAppendPath = getBooleanProperty(properties, TEXT("wrapper.java.library.path.append_system_path"), FALSE); /* Get the state output status. */ wrapperData->isStateOutputEnabled = getBooleanProperty(properties, TEXT("wrapper.state_output"), FALSE); /* Get the mode used to print the state output. */ wrapperData->stateOutputMode = getStateOutputModeForName(getStringProperty(properties, TEXT("wrapper.state_output.mode"), TEXT("DEFAULT"))); #ifdef WIN32 /* Get the message output status. */ wrapperData->isMessageOutputEnabled = getBooleanProperty(properties, TEXT("wrapper.message_output"), FALSE); #endif /* Get the tick output status. */ wrapperData->isTickOutputEnabled = getBooleanProperty(properties, TEXT("wrapper.tick_output"), FALSE); /* Get the loop debug output status. */ wrapperData->isLoopOutputEnabled = getBooleanProperty(properties, TEXT("wrapper.loop_output"), FALSE); /* Get the sleep debug output status. */ wrapperData->isSleepOutputEnabled = getBooleanProperty(properties, TEXT("wrapper.sleep_output"), FALSE); /* Get the memory output status. */ wrapperData->isMemoryOutputEnabled = getBooleanProperty(properties, TEXT("wrapper.memory_output"), FALSE); wrapperData->memoryOutputInterval = getIntProperty(properties, TEXT("wrapper.memory_output.interval"), 1); /* Get the cpu output status. */ wrapperData->isCPUOutputEnabled = getBooleanProperty(properties, TEXT("wrapper.cpu_output"), FALSE); wrapperData->cpuOutputInterval = getIntProperty(properties, TEXT("wrapper.cpu_output.interval"), 1); /* Get the pageFault output status. */ if (!wrapperData->configured) { wrapperData->isPageFaultOutputEnabled = getBooleanProperty(properties, TEXT("wrapper.pagefault_output"), FALSE); wrapperData->pageFaultOutputInterval = getIntProperty(properties, TEXT("wrapper.pagefault_output.interval"), 1); } /* Get the disable tests flag. */ wrapperData->isTestsDisabled = getBooleanProperty(properties, TEXT("wrapper.disable_tests"), FALSE); /* Get the shutdown hook status */ wrapperData->isShutdownHookDisabled = getBooleanProperty(properties, TEXT("wrapper.disable_shutdown_hook"), FALSE); /* Get the forced shutdown flag status. */ wrapperData->isForcedShutdownDisabled = getBooleanProperty(properties, TEXT("wrapper.disable_forced_shutdown"), FALSE); wrapperData->forcedShutdownDelay = getIntProperty(properties, TEXT("wrapper.forced_shutdown.delay"), 2); /* Get the startup delay. */ startupDelay = getIntProperty(properties, TEXT("wrapper.startup.delay"), 0); wrapperData->startupDelayConsole = getIntProperty(properties, TEXT("wrapper.startup.delay.console"), startupDelay); if (wrapperData->startupDelayConsole < 0) { wrapperData->startupDelayConsole = 0; } wrapperData->startupDelayService = getIntProperty(properties, TEXT("wrapper.startup.delay.service"), startupDelay); if (wrapperData->startupDelayService < 0) { wrapperData->startupDelayService = 0; } /* Get the restart delay. */ wrapperData->restartDelay = getIntProperty(properties, TEXT("wrapper.restart.delay"), 5); if (wrapperData->restartDelay < 0) { wrapperData->restartDelay = 0; } /* Get the flag which decides whether or not configuration should be reloaded on JVM restart. */ wrapperData->restartReloadConf = getBooleanProperty(properties, TEXT("wrapper.restart.reload_configuration"), FALSE); /* Get the disable restart flag */ wrapperData->isRestartDisabled = getBooleanProperty(properties, TEXT("wrapper.disable_restarts"), FALSE); wrapperData->isAutoRestartDisabled = getBooleanProperty(properties, TEXT("wrapper.disable_restarts.automatic"), wrapperData->isRestartDisabled); /* Get the flag which decides whether or not a JVM is allowed to be launched. */ wrapperData->runWithoutJVM = getBooleanProperty(properties, TEXT("wrapper.test.no_jvm"), FALSE); wrapperData->pidLogLevel = getLogLevelForName(getStringProperty(properties, TEXT("wrapper.java.pid.loglevel"), TEXT("DEBUG"))); /* Get the timeout settings */ wrapperData->cpuTimeout = getIntProperty(properties, TEXT("wrapper.cpu.timeout"), 10); wrapperData->startupTimeout = getIntProperty(properties, TEXT("wrapper.startup.timeout"), 30); wrapperData->pingTimeout = getIntProperty(properties, TEXT("wrapper.ping.timeout"), 30); if (wrapperData->pingActionList) { free(wrapperData->pingActionList); } wrapperData->pingActionList = wrapperGetActionListForNames(getStringProperty(properties, TEXT("wrapper.ping.timeout.action"), TEXT("RESTART")), TEXT("wrapper.ping.timeout.action")); wrapperData->pingAlertThreshold = getIntProperty(properties, TEXT("wrapper.ping.alert.threshold"), __max(1, wrapperData->pingTimeout / 4)); wrapperData->pingAlertLogLevel = getLogLevelForName(getStringProperty(properties, TEXT("wrapper.ping.alert.loglevel"), TEXT("STATUS"))); wrapperData->pingInterval = getIntProperty(properties, TEXT("wrapper.ping.interval"), 5); wrapperData->pingIntervalLogged = getIntProperty(properties, TEXT("wrapper.ping.interval.logged"), 1); wrapperData->shutdownTimeout = getIntProperty(properties, TEXT("wrapper.shutdown.timeout"), 30); wrapperData->jvmExitTimeout = getIntProperty(properties, TEXT("wrapper.jvm_exit.timeout"), 15); wrapperData->jvmCleanupTimeout = getIntProperty(properties, TEXT("wrapper.jvm_cleanup.timeout"), 10); wrapperData->jvmTerminateTimeout = getIntProperty(properties, TEXT("wrapper.jvm_terminate.timeout"), 10); wrapperData->cpuTimeout = validateTimeout(TEXT("wrapper.cpu.timeout"), wrapperData->cpuTimeout); wrapperData->startupTimeout = validateTimeout(TEXT("wrapper.startup.timeout"), wrapperData->startupTimeout); wrapperData->pingTimeout = validateTimeout(TEXT("wrapper.ping.timeout"), wrapperData->pingTimeout); wrapperData->shutdownTimeout = validateTimeout(TEXT("wrapper.shutdown.timeout"), wrapperData->shutdownTimeout); wrapperData->jvmExitTimeout = validateTimeout(TEXT("wrapper.jvm_exit.timeout"), wrapperData->jvmExitTimeout); wrapperData->jvmTerminateTimeout = validateTimeout(TEXT("wrapper.jvm_terminate.timeout"), wrapperData->jvmTerminateTimeout); wrapperData->jvmCleanupTimeout = validateTimeout(TEXT("wrapper.jvm_cleanup.timeout"), wrapperData->jvmCleanupTimeout); if (wrapperData->pingInterval < 1) { wrapperData->pingInterval = 1; log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("The value of %s must be at least %d second(s). Changing to %d."), TEXT("wrapper.ping.interval"), 1, wrapperData->pingInterval); } else if (wrapperData->pingInterval > 3600) { wrapperData->pingInterval = 3600; log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("wrapper.ping.interval must be less than or equal to 1 hour (3600 seconds). Changing to 3600.")); } if (wrapperData->pingIntervalLogged < 1) { wrapperData->pingIntervalLogged = 1; log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("The value of %s must be at least %d second(s). Changing to %d."), TEXT("wrapper.ping.interval.logged"), 1, wrapperData->pingIntervalLogged); } else if (wrapperData->pingIntervalLogged > 86400) { wrapperData->pingIntervalLogged = 86400; log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("wrapper.ping.interval.logged must be less than or equal to 1 day (86400 seconds). Changing to 86400.")); } if ((wrapperData->pingTimeout > 0) && (wrapperData->pingTimeout < wrapperData->pingInterval + 5)) { wrapperData->pingTimeout = wrapperData->pingInterval + 5; log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("wrapper.ping.timeout must be at least 5 seconds longer than wrapper.ping.interval. Changing to %d."), wrapperData->pingTimeout); } if (wrapperData->pingAlertThreshold <= 0) { /* Ping Alerts disabled. */ wrapperData->pingAlertThreshold = 0; } else if ((wrapperData->pingTimeout > 0) && (wrapperData->pingAlertThreshold > wrapperData->pingTimeout)) { wrapperData->pingAlertThreshold = wrapperData->pingTimeout; log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("wrapper.ping.alert.threshold must be less than or equal to the value of wrapper.ping.timeout (%d seconds). Changing to %d."), wrapperData->pingInterval, wrapperData->pingTimeout); } if (wrapperData->cpuTimeout > 0) { /* Make sure that the timeouts are all longer than the cpu timeout. */ if ((wrapperData->startupTimeout > 0) && (wrapperData->startupTimeout < wrapperData->cpuTimeout)) { log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("CPU timeout detection may not operate correctly during startup because wrapper.cpu.timeout is not smaller than wrapper.startup.timeout.")); } if ((wrapperData->pingTimeout > 0) && (wrapperData->pingTimeout < wrapperData->cpuTimeout)) { log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("CPU timeout detection may not operate correctly because wrapper.cpu.timeout is not smaller than wrapper.ping.timeout.")); } if ((wrapperData->shutdownTimeout > 0) && (wrapperData->shutdownTimeout < wrapperData->cpuTimeout)) { log_printf(WRAPPER_SOURCE_WRAPPER, properties->logWarningLogLevel, TEXT("CPU timeout detection may not operate correctly during shutdown because wrapper.cpu.timeout is not smaller than wrapper.shutdown.timeout.")); } /* jvmExit timeout can be shorter than the cpu timeout. */ } /* Load properties controlling the number times the JVM can be restarted. */ wrapperData->maxFailedInvocations = getIntProperty(properties, TEXT("wrapper.max_failed_invocations"), 5); if (wrapperData->maxFailedInvocations < 1) { wrapperData->maxFailedInvocations = 1; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The value of %s must be at least %d. Changing to %d."), TEXT("wrapper.max_failed_invocations"), 1, wrapperData->maxFailedInvocations); } wrapperData->successfulInvocationTime = getIntProperty(properties, TEXT("wrapper.successful_invocation_time"), 300); if (wrapperData->successfulInvocationTime < 1) { wrapperData->successfulInvocationTime = 1; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The value of %s must be at least %d second(s). Changing to %d."), TEXT("wrapper.successful_invocation_time"), 1, wrapperData->successfulInvocationTime); } /* TRUE if the JVM should be asked to dump its state when it fails to halt on request. */ wrapperData->requestThreadDumpOnFailedJVMExit = getBooleanProperty(properties, TEXT("wrapper.request_thread_dump_on_failed_jvm_exit"), FALSE); wrapperData->requestThreadDumpOnFailedJVMExitDelay = getIntProperty(properties, TEXT("wrapper.request_thread_dump_on_failed_jvm_exit.delay"), 5); if (wrapperData->requestThreadDumpOnFailedJVMExitDelay < 1) { wrapperData->requestThreadDumpOnFailedJVMExitDelay = 1; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The value of %s must be at least %d second(s). Changing to %d."), TEXT("wrapper.request_thread_dump_on_failed_jvm_exit.delay"), 1, wrapperData->requestThreadDumpOnFailedJVMExitDelay); } /* Load the output filters. */ if (loadConfigurationTriggers()) { return TRUE; } /** Get the pid files if any. May be NULL */ if (!wrapperData->configured) { updateStringValue(&wrapperData->pidFilename, getFileSafeStringProperty(properties, TEXT("wrapper.pidfile"), NULL)); wrapperCorrectWindowsPath(wrapperData->pidFilename); } wrapperData->pidFileStrict = getBooleanProperty(properties, TEXT("wrapper.pidfile.strict"), FALSE); updateStringValue(&wrapperData->javaPidFilename, getFileSafeStringProperty(properties, TEXT("wrapper.java.pidfile"), NULL)); wrapperCorrectWindowsPath(wrapperData->javaPidFilename); /** Get the lock file if any. May be NULL */ if (!wrapperData->configured) { updateStringValue(&wrapperData->lockFilename, getFileSafeStringProperty(properties, TEXT("wrapper.lockfile"), NULL)); wrapperCorrectWindowsPath(wrapperData->lockFilename); } /** Get the java id file. May be NULL */ updateStringValue(&wrapperData->javaIdFilename, getFileSafeStringProperty(properties, TEXT("wrapper.java.idfile"), NULL)); wrapperCorrectWindowsPath(wrapperData->javaIdFilename); /** Get the status files if any. May be NULL */ if (!wrapperData->configured) { updateStringValue(&wrapperData->statusFilename, getFileSafeStringProperty(properties, TEXT("wrapper.statusfile"), NULL)); wrapperCorrectWindowsPath(wrapperData->statusFilename); } updateStringValue(&wrapperData->javaStatusFilename, getFileSafeStringProperty(properties, TEXT("wrapper.java.statusfile"), NULL)); wrapperCorrectWindowsPath(wrapperData->javaStatusFilename); /** Get the command file if any. May be NULL */ updateStringValue(&wrapperData->commandFilename, getFileSafeStringProperty(properties, TEXT("wrapper.commandfile"), NULL)); wrapperCorrectWindowsPath(wrapperData->commandFilename); wrapperData->commandFileTests = getBooleanProperty(properties, TEXT("wrapper.commandfile.enable_tests"), FALSE); /** Get the interval at which the command file will be polled. */ wrapperData->commandPollInterval = propIntMin(propIntMax(getIntProperty(properties, TEXT("wrapper.commandfile.poll_interval"), getIntProperty(properties, TEXT("wrapper.command.poll_interval"), 5)), 1), 3600); /** Get the anchor file if any. May be NULL */ if (!wrapperData->configured) { updateStringValue(&wrapperData->anchorFilename, getFileSafeStringProperty(properties, TEXT("wrapper.anchorfile"), NULL)); wrapperCorrectWindowsPath(wrapperData->anchorFilename); } /** Get the interval at which the anchor file will be polled. */ wrapperData->anchorPollInterval = propIntMin(propIntMax(getIntProperty(properties, TEXT("wrapper.anchorfile.poll_interval"), getIntProperty(properties, TEXT("wrapper.anchor.poll_interval"), 5)), 1), 3600); /** Flag controlling whether or not system signals should be ignored. */ val = getStringProperty(properties, TEXT("wrapper.ignore_signals"), TEXT("FALSE")); if ( ( strcmpIgnoreCase( val, TEXT("TRUE") ) == 0 ) || ( strcmpIgnoreCase( val, TEXT("BOTH") ) == 0 ) ) { wrapperData->ignoreSignals = WRAPPER_IGNORE_SIGNALS_WRAPPER + WRAPPER_IGNORE_SIGNALS_JAVA; } else if ( strcmpIgnoreCase( val, TEXT("WRAPPER") ) == 0 ) { wrapperData->ignoreSignals = WRAPPER_IGNORE_SIGNALS_WRAPPER; } else if ( strcmpIgnoreCase( val, TEXT("JAVA") ) == 0 ) { wrapperData->ignoreSignals = WRAPPER_IGNORE_SIGNALS_JAVA; } else { wrapperData->ignoreSignals = 0; } /* Obtain the Console Title. */ _sntprintf(propName, 256, TEXT("wrapper.console.title.%s"), wrapperOS); updateStringValue(&wrapperData->consoleTitle, getStringProperty(properties, propName, getStringProperty(properties, TEXT("wrapper.console.title"), NULL))); /* Load the service name (Used to be windows specific so use those properties if set.) */ updateStringValue(&wrapperData->serviceName, getStringProperty(properties, TEXT("wrapper.name"), getStringProperty(properties, TEXT("wrapper.ntservice.name"), TEXT("wrapper")))); /* Load the service display name (Used to be windows specific so use those properties if set.) */ updateStringValue(&wrapperData->serviceDisplayName, getStringProperty(properties, TEXT("wrapper.displayname"), getStringProperty(properties, TEXT("wrapper.ntservice.displayname"), wrapperData->serviceName))); /* Load the service description, default to display name (Used to be windows specific so use those properties if set.) */ updateStringValue(&wrapperData->serviceDescription, getStringProperty(properties, TEXT("wrapper.description"), getStringProperty(properties, TEXT("wrapper.ntservice.description"), wrapperData->serviceDisplayName))); /* Pausable */ if (!wrapperData->configured) { wrapperData->pausable = getBooleanProperty(properties, TEXT("wrapper.pausable"), getBooleanProperty(properties, TEXT("wrapper.ntservice.pausable"), FALSE)); wrapperData->pausableStopJVM = getBooleanProperty(properties, TEXT("wrapper.pausable.stop_jvm"), getBooleanProperty(properties, TEXT("wrapper.ntservice.pausable.stop_jvm"), TRUE)); wrapperData->initiallyPaused = getBooleanProperty(properties, TEXT("wrapper.pause_on_startup"), FALSE); } #ifdef WIN32 wrapperData->ignoreUserLogoffs = getBooleanProperty(properties, TEXT("wrapper.ignore_user_logoffs"), FALSE); /* Configure the NT service information */ if (wrapperBuildNTServiceInfo()) { return TRUE; } if (wrapperData->generateConsole) { if (!wrapperData->ntAllocConsole) { /* We need to allocate a console in order for the thread dumps to work * when running as a service. But the user did not request that a * console be visible. */ wrapperData->ntAllocConsole = TRUE; wrapperData->ntShowWrapperConsole = FALSE; /* Unchanged actually */ } } #else /* UNIX */ /* Configure the Unix daemon information */ wrapperBuildUnixDaemonInfo(); if (loadResourcesLimitsConfiguration()) { return TRUE; } #endif #ifdef WIN32 if (!wrapperProcessHasVisibleConsole()) { #else if (!wrapperData->isConsole) { #endif /* The console is not visible, so we shouldn't waste time logging to it. */ setConsoleLogLevelInt(LEVEL_NONE); } /* stdin */ #ifdef WIN32 wrapperData->disableConsoleInput = getBooleanProperty(properties, TEXT("wrapper.disable_console_input"), FALSE); #else if (wrapperData->disableConsoleInputPermanent && wrapperData->javaNewProcessGroup) { wrapperData->disableConsoleInput = TRUE; } else { /* We want to disable console input by default when daemonized. */ wrapperData->disableConsoleInput = getBooleanProperty(properties, TEXT("wrapper.disable_console_input"), wrapperData->daemonize); } wrapperData->javaNewProcessGroup = getBooleanProperty(properties, TEXT("wrapper.java.new_process_group"), TRUE); wrapperData->javaINFlush = getBooleanProperty(properties, TEXT("wrapper.restart.flush_input"), TRUE); if (!wrapperData->configured) { /* Changing the buffer size on reload is not allowed as it would complicate things if we don't flush. */ wrapperData->javaINBufferSize = getIntProperty(properties, TEXT("wrapper.javain.buffer_size"), 8192); } #endif if (_tcscmp(wrapperVersionRoot, getStringProperty(properties, TEXT("wrapper.script.version"), wrapperVersionRoot)) != 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("The version of the script (%s) doesn't match the version of this Wrapper (%s). This might cause some problems"), getStringProperty(properties, TEXT("wrapper.script.version"), wrapperVersionRoot), wrapperVersionRoot); } wrapperData->configured = TRUE; return FALSE; } /** * Requests a lock on the tick mutex. * * @return TRUE if there were any problems, FALSE if successful. */ int wrapperLockTickMutex() { #ifdef WIN32 switch (WaitForSingleObject(tickMutexHandle, INFINITE)) { case WAIT_ABANDONED: _tprintf(TEXT("Tick was abandoned.\n")); return TRUE; case WAIT_FAILED: _tprintf(TEXT("Tick wait failed.\n")); return TRUE; case WAIT_TIMEOUT: _tprintf(TEXT("Tick wait timed out.\n")); return TRUE; default: /* Ok */ break; } #else if (pthread_mutex_lock(&tickMutex)) { _tprintf(TEXT("Failed to lock the Tick mutex. %s\n"), getLastErrorText()); return TRUE; } #endif return FALSE; } /** * Releases a lock on the tick mutex. * * @return TRUE if there were any problems, FALSE if successful. */ int wrapperReleaseTickMutex() { #ifdef WIN32 if (!ReleaseMutex(tickMutexHandle)) { _tprintf(TEXT("Failed to release tick mutex. %s\n"), getLastErrorText()); return TRUE; } #else if (pthread_mutex_unlock(&tickMutex)) { _tprintf(TEXT("Failed to unlock the tick mutex. %s\n"), getLastErrorText()); return TRUE; } #endif return FALSE; } /** * Calculates a tick count using the system time. * * We normally need 64 bits to do this calculation. Play some games to get * the correct values with 32 bit variables. */ TICKS wrapperGetSystemTicks() { static int firstCall = TRUE; static TICKS initialTicks = 0; struct timeb timeBuffer; DWORD high, low; TICKS sum; #ifdef _DEBUG TICKS assertSum; #endif wrapperGetCurrentTime(&timeBuffer); /* Break in half. */ high = (DWORD)(timeBuffer.time >> 16) & 0xffff; low = (DWORD)(timeBuffer.time & 0xffff); /* Work on each half. */ high = high * 1000 / WRAPPER_TICK_MS; low = (low * 1000 + timeBuffer.millitm) / WRAPPER_TICK_MS; /* Now combine them in such a way that the correct bits are truncated. */ high = high + ((low >> 16) & 0xffff); sum = (TICKS)(((high & 0xffff) << 16) + (low & 0xffff)); /* Check the result. */ #ifdef _DEBUG #ifdef WIN32 assertSum = (TICKS)((timeBuffer.time * 1000UI64 + timeBuffer.millitm) / WRAPPER_TICK_MS); #else /* This will produce the following warning on some compilers: * warning: ANSI C forbids long long integer constants * Is there another way to do this? */ assertSum = (TICKS)((timeBuffer.time * 1000ULL + timeBuffer.millitm) / WRAPPER_TICK_MS); #endif if (assertSum != sum) { _tprintf(TEXT("wrapperGetSystemTicks() resulted in %08x rather than %08x\n"), sum, assertSum); } #endif if (firstCall) { initialTicks = sum - WRAPPER_TICK_INITIAL; firstCall = FALSE; } return (sum - initialTicks); } /** * Returns difference in seconds between the start and end ticks. This function * handles cases where the tick counter has wrapped between when the start * and end tick counts were taken. See the wrapperGetTicks() function. * * This can be done safely in 32 bits */ int wrapperGetTickAgeSeconds(TICKS start, TICKS end) { /* log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" wrapperGetTickAgeSeconds(%08x, %08x) -> %08x"), start, end, (int)((end - start) * WRAPPER_TICK_MS) / 1000); */ /* Simply subtracting the values will always work even if end has wrapped due to overflow. * This is only true if end has wrapped by less than half of the range of TICKS/WRAPPER_TICK_MS! * 0x00000001 - 0xffffffff = 0x00000002 = 2 * 0xffffffff - 0x00000001 = 0xfffffffe = -2 */ return (int)((end - start) * WRAPPER_TICK_MS) / 1000; } /** * Returns difference in ticks between the start and end ticks. This function * handles cases where the tick counter has wrapped between when the start * and end tick counts were taken. See the wrapperGetTicks() function. * * This can be done safely in 32 bits */ int wrapperGetTickAgeTicks(TICKS start, TICKS end) { /* log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" wrapperGetTickAgeSeconds(%08x, %08x) -> %08x"), start, end, (int)(end - start)); */ /* Simply subtracting the values will always work even if end has wrapped due to overflow. * This is only true if end has wrapped by less than half of the range of TICKS/WRAPPER_TICK_MS! * 0x00000001 - 0xffffffff = 0x00000002 = 2 * 0xffffffff - 0x00000001 = 0xfffffffe = -2 */ return (int)(end - start); } /** * Returns TRUE if the specified tick timeout has expired relative to the * specified tick count. */ int wrapperTickExpired(TICKS nowTicks, TICKS timeoutTicks) { /* Convert to a signed value. */ int age = nowTicks - timeoutTicks; if (age >= 0) { return TRUE; } else { return FALSE; } } /** * Returns a tick count that is the specified number of seconds later than * the base tick count. * * This calculation will work as long as the number of seconds is not large * enough to require more than 32 bits when multiplied by 1000. */ TICKS wrapperAddToTicks(TICKS start, int seconds) { /* log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" wrapperAddToTicks(%08x, %08x) -> %08x"), start, seconds, start + (seconds * 1000 / WRAPPER_TICK_MS)); */ return start + (seconds * 1000 / WRAPPER_TICK_MS); } /** * Do some sanity checks on the tick timer math. */ int wrapperTickAssertions() { int result = FALSE; TICKS ticks1, ticks2, ticksR, ticksE; int value1, valueR, valueE; /** wrapperGetTickAgeTicks test. */ ticks1 = 0xfffffffe; ticks2 = 0xffffffff; valueE = 1; valueR = wrapperGetTickAgeTicks(ticks1, ticks2); if (valueR != valueE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Assert Failed: wrapperGetTickAgeTicks(%08x, %08x) == %0d != %0d"), ticks1, ticks2, valueR, valueE); result = TRUE; } ticks1 = 0xffffffff; ticks2 = 0xfffffffe; valueE = -1; valueR = wrapperGetTickAgeTicks(ticks1, ticks2); if (valueR != valueE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Assert Failed: wrapperGetTickAgeTicks(%08x, %08x) == %0d != %0d"), ticks1, ticks2, valueR, valueE); result = TRUE; } ticks1 = 0xffffffff; ticks2 = 0x00000000; valueE = 1; valueR = wrapperGetTickAgeTicks(ticks1, ticks2); if (valueR != valueE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Assert Failed: wrapperGetTickAgeTicks(%08x, %08x) == %0d != %0d"), ticks1, ticks2, valueR, valueE); result = TRUE; } ticks1 = 0x00000000; ticks2 = 0xffffffff; valueE = -1; valueR = wrapperGetTickAgeTicks(ticks1, ticks2); if (valueR != valueE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Assert Failed: wrapperGetTickAgeTicks(%08x, %08x) == %0d != %0d"), ticks1, ticks2, valueR, valueE); result = TRUE; } /** wrapperGetTickAgeSeconds test. */ ticks1 = 0xfffffff0; ticks2 = 0xffffffff; valueE = 1; valueR = wrapperGetTickAgeSeconds(ticks1, ticks2); if (valueR != valueE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Assert Failed: wrapperGetTickAgeSeconds(%08x, %08x) == %0d != %0d"), ticks1, ticks2, valueR, valueE); result = TRUE; } ticks1 = 0xffffffff; ticks2 = 0x0000000f; valueE = 1; valueR = wrapperGetTickAgeSeconds(ticks1, ticks2); if (valueR != valueE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Assert Failed: wrapperGetTickAgeSeconds(%08x, %08x) == %0d != %0d"), ticks1, ticks2, valueR, valueE); result = TRUE; } ticks1 = 0x0000000f; ticks2 = 0xffffffff; valueE = -1; valueR = wrapperGetTickAgeSeconds(ticks1, ticks2); if (valueR != valueE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Assert Failed: wrapperGetTickAgeSeconds(%08x, %08x) == %0d != %0d"), ticks1, ticks2, valueR, valueE); result = TRUE; } /** wrapperTickExpired test. */ ticks1 = 0xfffffffe; ticks2 = 0xffffffff; valueE = FALSE; valueR = wrapperTickExpired(ticks1, ticks2); if (valueR != valueE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Assert Failed: wrapperTickExpired(%08x, %08x) == %0d != %0d"), ticks1, ticks2, valueR, valueE); result = TRUE; } ticks1 = 0xffffffff; ticks2 = 0xffffffff; valueE = TRUE; valueR = wrapperTickExpired(ticks1, ticks2); if (valueR != valueE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Assert Failed: wrapperTickExpired(%08x, %08x) == %0d != %0d"), ticks1, ticks2, valueR, valueE); result = TRUE; } ticks1 = 0xffffffff; ticks2 = 0x00000001; valueE = FALSE; valueR = wrapperTickExpired(ticks1, ticks2); if (valueR != valueE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Assert Failed: wrapperTickExpired(%08x, %08x) == %0d != %0d"), ticks1, ticks2, valueR, valueE); result = TRUE; } ticks1 = 0x00000001; ticks2 = 0xffffffff; valueE = TRUE; valueR = wrapperTickExpired(ticks1, ticks2); if (valueR != valueE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Assert Failed: wrapperTickExpired(%08x, %08x) == %0d != %0d"), ticks1, ticks2, valueR, valueE); result = TRUE; } /** wrapperAddToTicks test. */ ticks1 = 0xffffffff; value1 = 1; ticksE = 0x00000009; ticksR = wrapperAddToTicks(ticks1, value1); if (ticksR != ticksE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Assert Failed: wrapperAddToTicks(%08x, %d) == %08x != %08x"), ticks1, value1, ticksR, ticksE); result = TRUE; } return result; } /** * Sets the working directory of the Wrapper to the specified directory. * The directory can be relative or absolute. * If there are any problems then a non-zero value will be returned. * * @param dir Directory to change to. * * @return TRUE if the directory failed to be set, FALSE otherwise. */ int wrapperSetWorkingDir(const TCHAR* dir) { int showOutput = wrapperData->configured; if (_tchdir(dir)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to set working directory to: %s (%s)"), dir, getLastErrorText()); return TRUE; } /* This function is sometimes called before the configuration is loaded. */ #ifdef _DEBUG showOutput = TRUE; #endif if (showOutput) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Working directory set to: %s"), dir); } /* Set a variable to the location of the binary. */ setEnv(TEXT("WRAPPER_WORKING_DIR"), dir, ENV_SOURCE_APPLICATION); return FALSE; } /****************************************************************************** * Protocol callback functions *****************************************************************************/ void wrapperLogSignaled(int logLevel, TCHAR *msg) { /* */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Got a log message from JVM: %s"), msg); } /* */ log_printf(wrapperData->jvmRestarts, logLevel, msg); } void wrapperKeyRegistered(TCHAR *key) { /* Allow for a large integer + \0 */ TCHAR buffer[11]; if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Got key from JVM: %s"), key); } switch (wrapperData->jState) { case WRAPPER_JSTATE_LAUNCHING: /* We now know that the Java side wrapper code has started and * registered with a key. We still need to verify that it is * the correct key however. */ if (_tcscmp(key, wrapperData->key) == 0) { /* This is the correct key. */ /* We now know that the Java side wrapper code has started. */ wrapperSetJavaState(WRAPPER_JSTATE_LAUNCHED, 0, -1); /* Send the low log level to the JVM so that it can control output via the log method. */ _sntprintf(buffer, 11, TEXT("%d"), getLowLogLevel()); wrapperProtocolFunction(WRAPPER_MSG_LOW_LOG_LEVEL, buffer); /* Send the log file name. */ sendLogFileName(); /* Send the properties. */ sendProperties(); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Received a connection request with an incorrect key. Waiting for another connection.")); /* This was the wrong key. Send a response. */ wrapperProtocolFunction(WRAPPER_MSG_BADKEY, TEXT("Incorrect key. Connection rejected.")); /* Close the current connection. Assume that the real JVM * is still out there trying to connect. So don't change * the state. If for some reason, this was the correct * JVM, but the key was wrong. then this state will time * out and recover. */ wrapperProtocolClose(); } break; case WRAPPER_JSTATE_STOP: /* We got a key registration. This means that the JVM thinks it was * being launched but the Wrapper is trying to stop. This state * will clean up correctly. */ break; case WRAPPER_JSTATE_STOPPING: /* We got a key registration. This means that the JVM thinks it was * being launched but the Wrapper is trying to stop. Now that the * connection to the JVM has been opened, tell it to stop cleanly. */ wrapperSetJavaState(WRAPPER_JSTATE_STOP, 0, -1); break; default: /* We got a key registration that we were not expecting. Ignore it. */ break; } } /** * Called when a ping is first determined to be slower than the wrapper.ping.alert.threshold. * This will happen before it has actually been responded to. */ void wrapperPingSlow() { } /** * Called when a ping is responded to, but was slower than the wrapper.ping.alert.threshold. * * @param tickAge The number of seconds it took to respond. */ void wrapperPingRespondedSlow(int tickAge) { log_printf(WRAPPER_SOURCE_WRAPPER, wrapperData->pingAlertLogLevel, TEXT("Pinging the JVM took %d seconds to respond."), tickAge); } /** * Called when a ping response is received. * * @param pingSendTicks Time in ticks when the ping was originally sent. * @param queueWarnings TRUE if warnings about the queue should be logged, FALSE if the ping response did not contain a time. */ void wrapperPingResponded(TICKS pingSendTicks, int queueWarnings) { TICKS nowTicks; int tickAge; PPendingPing pendingPing; int pingSearchDone; #ifdef DEBUG_PING_QUEUE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" PING QUEUE Ping Response (tick %08x)"), pingSendTicks); #endif /* We want to purge the ping from the PendingPing list. */ do { pendingPing = wrapperData->firstPendingPing; if (pendingPing != NULL) { tickAge = wrapperGetTickAgeTicks(pingSendTicks, pendingPing->sentTicks); #ifdef DEBUG_PING_QUEUE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" PING QUEUE First Queued Ping (tick %08x, age %d)"), pendingPing->sentTicks, tickAge); #endif if (tickAge > 0) { /* pendingPing->sentTicks > pingSendTicks */ /* We received a ping response that is earlier than the one we were expecting. * If the pendingPingQueue has overflown then we will stop writing to it. Don't log warning messages when in this state as they would be confusing and are expected. * Leave this one in the queue for later. */ if (queueWarnings) { if ((!wrapperData->pendingPingQueueOverflow) && (!wrapperData->pendingPingQueueOverflowEmptied)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Received an unexpected ping response, sent at tick %08x. First expected ping was sent at tick %08x."), pingSendTicks, pendingPing->sentTicks); } else { #ifdef DEBUG_PING_QUEUE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" PING QUEUE Silently skipping unexpected ping response. (tick %08x) (First tick %08x)"), pingSendTicks, pendingPing->sentTicks); #endif } } pendingPing = NULL; pingSearchDone = TRUE; } else { if (tickAge < 0) { /* This PendingPing object was sent before the PING that we received. This means that we somehow lost a ping. */ if (queueWarnings) { if ((!wrapperData->pendingPingQueueOverflow) && (!wrapperData->pendingPingQueueOverflowEmptied)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Lost a ping response, sent at tick %08x."), pendingPing->sentTicks); } else { #ifdef DEBUG_PING_QUEUE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" PING QUEUE Silently skipping lost ping response. (tick %08x)"), pendingPing->sentTicks); #endif } } pingSearchDone = FALSE; } else { /* This PendingPing object is for this PING event. */ pingSearchDone = TRUE; #ifdef DEBUG_PING_QUEUE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" PING QUEUE Expected Ping Response. (tick %08x)"), pendingPing->sentTicks); #endif /* When the emptied flag is set, we know that we are recovering from an overflow. * That flag is reset on the first expected PendingPing found in the queue. */ if (wrapperData->pendingPingQueueOverflowEmptied) { wrapperData->pendingPingQueueOverflowEmptied = FALSE; #ifdef DEBUG_PING_QUEUE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" PING QUEUE Emptied Set flag reset.")); #endif } } /* Detach the PendingPing from the queue. */ if (pendingPing->nextPendingPing != NULL) { /* This was the first PendingPing of several in the queue. */ wrapperData->pendingPingCount--; if (wrapperData->firstUnwarnedPendingPing == wrapperData->firstPendingPing) { wrapperData->firstUnwarnedPendingPing = pendingPing->nextPendingPing; } wrapperData->firstPendingPing = pendingPing->nextPendingPing; pendingPing->nextPendingPing = NULL; #ifdef DEBUG_PING_QUEUE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("--- PING QUEUE Size: %d"), wrapperData->pendingPingCount); #endif } else { /* This was the only PendingPing in the queue. */ wrapperData->pendingPingCount = 0; wrapperData->firstUnwarnedPendingPing = NULL; wrapperData->firstPendingPing = NULL; wrapperData->lastPendingPing = NULL; #ifdef DEBUG_PING_QUEUE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("--- PING QUEUE Empty.") ); #endif if (wrapperData->pendingPingQueueOverflow) { wrapperData->pendingPingQueueOverflowEmptied = TRUE; wrapperData->pendingPingQueueOverflow = FALSE; #ifdef DEBUG_PING_QUEUE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" PING QUEUE Reset Overflow, Emptied Set.") ); #endif } } /* Free up the pendingPing object. */ if (pendingPing != NULL) { free(pendingPing); pendingPing = NULL; } } } else { /* Got a ping response when the queue was empty. */ if (queueWarnings) { if ((!wrapperData->pendingPingQueueOverflow) && (!wrapperData->pendingPingQueueOverflowEmptied)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Received an unexpected ping response, sent at tick %08x."), pingSendTicks); } else { #ifdef DEBUG_PING_QUEUE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" PING QUEUE Silently skipping unexpected ping response. (tick %08x) (Empty)"), pingSendTicks); #endif } } pingSearchDone = TRUE; } } while (!pingSearchDone); /* Depending on the current JVM state, do something. */ switch (wrapperData->jState) { case WRAPPER_JSTATE_STARTED: /* We got a response to a ping. */ nowTicks = wrapperGetTicks(); /* Figure out how long it took for us to get this ping response in seconds. */ tickAge = wrapperGetTickAgeSeconds(pingSendTicks, nowTicks); /* If we took longer than the threshold then we want to log a message. */ if ((wrapperData->pingAlertThreshold > 0) && (tickAge >= wrapperData->pingAlertThreshold)) { wrapperPingRespondedSlow(tickAge); } /* Allow 5 + more seconds before the JVM is considered to be dead. */ if (wrapperData->pingTimeout > 0) { wrapperUpdateJavaStateTimeout(nowTicks, 5 + wrapperData->pingTimeout); } else { wrapperUpdateJavaStateTimeout(nowTicks, -1); } break; default: /* We got a ping response that we were not expecting. Ignore it. */ break; } } void wrapperPingTimeoutResponded() { wrapperProcessActionList(wrapperData->pingActionList, TEXT("JVM appears hung: Timed out waiting for signal from JVM."), WRAPPER_ACTION_SOURCE_CODE_PING_TIMEOUT, 0, TRUE, wrapperData->errorExitCode); } void wrapperStopRequested(int exitCode) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("JVM requested a shutdown. (%d)"), exitCode); } /* Remember that a stop message was received so we don't try to send any other messages to the JVM. */ wrapperData->stopPacketReceived = TRUE; /* Get things stopping on this end. Ask the JVM to stop again in case the * user code on the Java side is not written correctly. */ wrapperStopProcess(exitCode, FALSE); } void wrapperRestartRequested() { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("JVM requested a restart.")); /* Make a note of the fact that we received this restart packet. */ wrapperData->restartPacketReceived = TRUE; wrapperRestartProcess(); } /** * If the current state of the JVM is STOPPING then this message is used to * extend the time that the wrapper will wait for a STOPPED message before * giving up on the JVM and killing it. */ void wrapperStopPendingSignaled(int waitHint) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("JVM signaled a stop pending with waitHint of %d millis."), waitHint); } if (wrapperData->jState == WRAPPER_JSTATE_STARTED) { /* Change the state to STOPPING */ wrapperSetJavaState(WRAPPER_JSTATE_STOPPING, 0, -1); /* Don't need to set the timeout here because it will be set below. */ } if (wrapperData->jStateTimeoutTicksSet) { if (wrapperData->jState == WRAPPER_JSTATE_STOPPING) { if (waitHint < 0) { waitHint = 0; } wrapperUpdateJavaStateTimeout(wrapperGetTicks(), (int)ceil(waitHint / 1000.0)); } } } /** * The wrapper received a signal from the JVM that it has completed the stop * process. If the state of the JVM is STOPPING, then change the state to * STOPPED. It is possible to get this request after the Wrapper has given up * waiting for the JVM. In this case, the message is ignored. */ void wrapperStoppedSignaled() { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("JVM signaled that it was stopped.")); } /* If the restart mode is already set and it is WRAPPER_RESTART_REQUESTED_AUTOMATIC but we * have not yet received a RESTART packet, this means that state engine got confused because * of an unexpected delay. The fact that the STOPPED packet arived but not the RESTART packet * means that the application did not intend for the restart to take place. * Reset the restart and let the Wrapper exit. */ if ((wrapperData->restartRequested == WRAPPER_RESTART_REQUESTED_AUTOMATIC) && (!wrapperData->restartPacketReceived)) { /* If we get here it is because the Wrapper previously decided to do a restart to recover. * That means that another message was already shown to the user. We want to show another * message here so there is a record of why we don't restart. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Received Stopped packet late. Cancel automatic restart.")); wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_NO; } /* Make a note of the fact that we received this stopped packet. */ wrapperData->stoppedPacketReceived = TRUE; if ((wrapperData->jState == WRAPPER_JSTATE_LAUNCHED) || (wrapperData->jState == WRAPPER_JSTATE_STARTING) || (wrapperData->jState == WRAPPER_JSTATE_STARTED) || (wrapperData->jState == WRAPPER_JSTATE_STOPPING)) { /* The Java side of the wrapper signaled that it stopped * allow 5 + jvmExitTimeout seconds for the JVM to exit. */ if (wrapperData->jvmExitTimeout > 0) { wrapperSetJavaState(WRAPPER_JSTATE_STOPPED, wrapperGetTicks(), 5 + wrapperData->jvmExitTimeout); } else { wrapperSetJavaState(WRAPPER_JSTATE_STOPPED, 0, -1); } } } /** * If the current state of the JVM is STARTING then this message is used to * extend the time that the wrapper will wait for a STARTED message before * giving up on the JVM and killing it. */ void wrapperStartPendingSignaled(int waitHint) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("JVM signaled a start pending with waitHint of %d millis."), waitHint); } /* Only process the start pending signal if the JVM state is starting or * stopping. Stopping are included because if the user hits CTRL-C while * the application is starting, then the stop request will not be noticed * until the application has completed its startup. */ if (wrapperData->jStateTimeoutTicksSet) { if ((wrapperData->jState == WRAPPER_JSTATE_STARTING) || (wrapperData->jState == WRAPPER_JSTATE_STOPPING)) { if (waitHint < 0) { waitHint = 0; } wrapperUpdateJavaStateTimeout(wrapperGetTicks(), (int)ceil(waitHint / 1000.0)); } } } /** * The wrapper received a signal from the JVM that it has completed the startup * process. If the state of the JVM is STARTING, then change the state to * STARTED. It is possible to get this request after the Wrapper has given up * waiting for the JVM. In this case, the message is ignored. */ void wrapperStartedSignaled() { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("JVM signaled that it was started.")); } if (wrapperData->jState == WRAPPER_JSTATE_STARTING) { /* We got the expected started packed. Now start pinging. Allow 5 + more seconds before the JVM * is considered to be dead. */ if (wrapperData->pingTimeout > 0) { wrapperSetJavaState(WRAPPER_JSTATE_STARTED, wrapperGetTicks(), 5 + wrapperData->pingTimeout); } else { wrapperSetJavaState(WRAPPER_JSTATE_STARTED, 0, -1); } /* Is the wrapper state STARTING? */ if (wrapperData->wState == WRAPPER_WSTATE_STARTING) { wrapperSetWrapperState(WRAPPER_WSTATE_STARTED); if (!wrapperData->isConsole) { /* Tell the service manager that we started */ wrapperReportStatus(FALSE, WRAPPER_WSTATE_STARTED, 0, 0); } } /* If we are configured to detach and shutdown when the JVM is started then start doing so. */ if (wrapperData->detachStarted) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("JVM launched and detached. Wrapper Shutting down...")); wrapperProtocolClose(); wrapperDetachJava(); wrapperStopProcess(0, FALSE); } } else if (wrapperData->jState == WRAPPER_JSTATE_STOP) { /* This will happen if the Wrapper was asked to stop as the JVM is being launched. */ } else if (wrapperData->jState == WRAPPER_JSTATE_STOPPING) { /* This will happen if the Wrapper was asked to stop as the JVM is being launched. */ wrapperSetJavaState(WRAPPER_JSTATE_STOP, 0, -1); } } /** * Get the parent process id * ATTENTION: in some rare cases CreateToolhelp32Snapshot can have infinite cycles when looping on the parent processes * It is the responsibility of the caller not to fall into an infinite loop. * * @param pid process id * @param found TRUE if the parent process could be found. * * @return the parent process id */ #ifdef WIN32 DWORD wrapperGetPPid(DWORD pid, int *found) { DWORD ppid = 0; HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); PROCESSENTRY32 pe = { 0 }; pe.dwSize = sizeof(PROCESSENTRY32); if (found != NULL) { *found = FALSE; } if( Process32First(h, &pe)) { do { if (pe.th32ProcessID == pid) { ppid = pe.th32ParentProcessID; if (found != NULL) { *found = TRUE; } break; } } while( Process32Next(h, &pe)); } CloseHandle(h); return ppid; } #endif #ifdef WIN32 #ifdef MAX_WAIT_PS #define MAX_WAIT_CHECKPPID MAX_WAIT_PS + 100 /* ms */ #else #define MAX_WAIT_CHECKPPID 2000 /* ms */ #endif /** * Check ancestry relationship between two processes. * NOTE: this function will give up if a matching parent can't be found after 100 iterations. * It keeps a list of all parents found in order to prevent infinite loop (see comment in wrapperGetPPid) * * @param pid Id of the child process. * @param ppid Id to be searched among pid's ancestors. * @param depth 0 if the research should include the pid itself, * 1 if it should start from its direct parent, * 2 from its grand-parent, * etc. * @param pError pointer that will be set TRUE if the function could not complete because of an error. * * @return TRUE if ppid is found among pid's ancestors. */ int wrapperCheckPPid(DWORD pid, DWORD ppid, int depth, int* pError) { DWORD pids[100] = {0}; int i = 0; int j = 0; int found = FALSE; TICKS start = wrapperGetTicks(); do { #ifdef DEBUG_PPID log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT(" pid:%d - ppid:%d"), pid, ppid); #endif if ((i >= depth) && (pid == ppid)) { return TRUE; } /* add pid to the list of the PIDs found */ pids[j] = pid; pid = wrapperGetPPid(pid, &found); if (!found) { if (pError) { *pError = TRUE; } return FALSE; } /* make sure that the parent PID was not already found before */ j = 0; while (pids[j] != 0) { if (pids[j] == pid) { /* circular ancestry */ if (pError) { *pError = TRUE; } return FALSE; } j++; } /* Make sure this function doesn't take too long */ if (wrapperGetTickAgeTicks(start, wrapperGetTicks()) * WRAPPER_TICK_MS > MAX_WAIT_CHECKPPID) { if (pError) { *pError = TRUE; } return FALSE; } } while ((pid != 0) && (i++ < 100)); return FALSE; } #endif /** * Check if the PID file should be blocking. * * return 1 if the file already exists and we are strict, 0 otherwise. */ int checkPidFile() { if (wrapperData->pidFileStrict && wrapperData->pidFilename && wrapperFileExists(wrapperData->pidFilename)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("%d pid file, %s, already exists."), wrapperData->wrapperPID, wrapperData->pidFilename); /* We should not clean up files of the other instance running. */ return 1; } return 0; } /** * Write the Wrapper PID file, anchor file and lockfile when the Wrapper starts up. * * return 1 if there is any error, 0 otherwise. */ int wrapperWriteStartupPidFiles() { if (wrapperData->pidFilename) { if (writePidFile(wrapperData->pidFilename, wrapperData->wrapperPID, wrapperData->pidFileUmask #ifndef WIN32 , wrapperData->pidFileGroup #endif )) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("ERROR: Could not write pid file %s: %s"), wrapperData->pidFilename, getLastErrorText()); return 1; } } if (wrapperData->anchorFilename) { if (writePidFile(wrapperData->anchorFilename, wrapperData->wrapperPID, wrapperData->anchorFileUmask #ifndef WIN32 , wrapperData->anchorFileGroup #endif )) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("ERROR: Could not write anchor file %s: %s"), wrapperData->anchorFilename, getLastErrorText()); return 1; } } if (wrapperData->lockFilename) { if (writePidFile(wrapperData->lockFilename, wrapperData->wrapperPID, wrapperData->lockFileUmask #ifndef WIN32 , wrapperData->lockFileGroup #endif )) { /* This will fail if the user is running as a user without full privileges. * To make things easier for user configuration, this is ignored if sufficient * privileges do not exist. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("WARNING: Could not write lock file %s: %s"), wrapperData->lockFilename, getLastErrorText()); wrapperData->lockFilename = NULL; } } return 0; } #ifdef CUNIT static void tsJAP_subTestJavaAdditionalParamSuite(int stripQuote, TCHAR *config, TCHAR **strings, int strings_len, int isJVMParam) { LoadParameterFileCallbackParam param; int ret; /*int i;*/ param.stripQuote = stripQuote; param.strings = NULL; param.index = 0; param.isJVMParam = isJVMParam; ret = loadParameterFileCallback((void *)(¶m), NULL, 0, 0, config, FALSE, LEVEL_NONE); CU_ASSERT_TRUE(ret); if (!ret) { return; } CU_ASSERT(strings_len == param.index); param.stripQuote = stripQuote; param.strings = (TCHAR **)malloc(sizeof(TCHAR *) * strings_len); if (!param.strings) { return; } param.index = 0; param.isJVMParam = isJVMParam; ret = loadParameterFileCallback((void *)(¶m), NULL, 0, 0, config, FALSE, LEVEL_NONE); CU_ASSERT_TRUE(ret); if (!ret) { return; } CU_ASSERT(strings_len == param.index); if (!param.strings) { return; } /*for (i = 0; i < strings_len; i++) { CU_ASSERT(_tcscmp(strings[i], param.strings[i]) == 0); }*/ /*for (i = 0; i < strings_len; i++) { free(param.strings[i]); }*/ free(param.strings); } #define TSJAP_ARRAY_LENGTH(a) (sizeof(a) / sizeof(a[0])) void tsJAP_testJavaAdditionalParamSuite(void) { int stripQuote; int i = 0; int isJVM = TRUE; for (i = 0; i < 2; i++) { _tprintf(TEXT("%d round\n"), i); if (i > 0) { isJVM = FALSE; } /* Test set #1 */ { /* Single parameter in 1 line. */ TCHAR *config = TEXT("-Dsomething=something"); TCHAR *strings[1]; strings[0] = TEXT("-Dsomething=something"); tsJAP_subTestJavaAdditionalParamSuite(FALSE, config, strings, TSJAP_ARRAY_LENGTH(strings), isJVM); } { /* Multiple parameters in 1 line. */ TCHAR *config = TEXT("-Dsomething=something -Dxxx=xxx"); TCHAR *strings[2]; strings[0] = TEXT("-Dsomething=something"); strings[1] = TEXT("-Dxxx=xxx"); tsJAP_subTestJavaAdditionalParamSuite(FALSE, config, strings, TSJAP_ARRAY_LENGTH(strings), isJVM); } { /* Horizontal Tab is not a delimiter. */ TCHAR *config = TEXT("-Dsomething1=something1\t-Dsomething2=something2 -Dxxx=xxx"); TCHAR *strings[2]; strings[0] = TEXT("-Dsomething1=something1\t-Dsomething2=something2"); strings[1] = TEXT("-Dxxx=xxx"); tsJAP_subTestJavaAdditionalParamSuite(FALSE, config, strings, TSJAP_ARRAY_LENGTH(strings), isJVM); } { /* Horizontal Tab is not a delimiter. */ TCHAR *config = TEXT("-Dsomething1=something1\t-Dsomething2=something2 -Dxxx=xxx"); TCHAR *strings[2]; strings[0] = TEXT("-Dsomething1=something1\t-Dsomething2=something2"); strings[1] = TEXT("-Dxxx=xxx"); tsJAP_subTestJavaAdditionalParamSuite(FALSE, config, strings, TSJAP_ARRAY_LENGTH(strings), isJVM); } if (isJVM == TRUE) { { /* A parameter without heading '-' will be skipped. */ TCHAR *config = TEXT("something=something -Dxxx=xxx"); TCHAR *strings[1]; strings[0] = TEXT("-Dxxx=xxx"); tsJAP_subTestJavaAdditionalParamSuite(FALSE, config, strings, TSJAP_ARRAY_LENGTH(strings), isJVM); } } else { { /* A parameter without heading '-' will not be skipped. */ TCHAR *config = TEXT("something=something -Dxxx=xxx"); TCHAR *strings[2]; strings[0] = TEXT("something=something"); strings[1] = TEXT("-Dxxx=xxx"); tsJAP_subTestJavaAdditionalParamSuite(FALSE, config, strings, TSJAP_ARRAY_LENGTH(strings), isJVM); } } /* Test set #2 : without stripping double quotations */ stripQuote = FALSE; { /* Quotations #1 */ TCHAR *config = TEXT("-DmyApp.x1=\"Hello World.\" -DmyApp.x2=x2"); TCHAR *strings[2]; strings[0] = TEXT("-DmyApp.x1=\"Hello World.\""); strings[1] = TEXT("-DmyApp.x2=x2"); tsJAP_subTestJavaAdditionalParamSuite(stripQuote, config, strings, TSJAP_ARRAY_LENGTH(strings), isJVM); } { /* Quotations #2 */ TCHAR *config = TEXT("\"-DmyApp.x1=Hello World.\" -DmyApp.x2=x2"); TCHAR *strings[2]; strings[0] = TEXT("\"-DmyApp.x1=Hello World.\""); strings[1] = TEXT("-DmyApp.x2=x2"); tsJAP_subTestJavaAdditionalParamSuite(stripQuote, config, strings, TSJAP_ARRAY_LENGTH(strings), isJVM); } { /* Escaped quotation */ TCHAR *config = TEXT("-DmyApp.x1=\"Hello \\\"World.\" -DmyApp.x2=x2"); TCHAR *strings[2]; strings[0] = TEXT("-DmyApp.x1=\"Hello \\\"World.\""); strings[1] = TEXT("-DmyApp.x2=x2"); tsJAP_subTestJavaAdditionalParamSuite(stripQuote, config, strings, TSJAP_ARRAY_LENGTH(strings), isJVM); } { /* Escaped backslash */ TCHAR *config = TEXT("-DmyApp.x1=\"Hello World.\\\\\" -DmyApp.x2=x2"); TCHAR *strings[2]; strings[0] = TEXT("-DmyApp.x1=\"Hello World.\\\\\""); strings[1] = TEXT("-DmyApp.x2=x2"); tsJAP_subTestJavaAdditionalParamSuite(stripQuote, config, strings, TSJAP_ARRAY_LENGTH(strings), isJVM); } /* Test set #3 : with stripping double quotations */ stripQuote = TRUE; { /* Quotations #1 */ TCHAR *config = TEXT("-DmyApp.x1=\"Hello World.\" -DmyApp.x2=x2"); TCHAR *strings[2]; strings[0] = TEXT("-DmyApp.x1=Hello World."); strings[1] = TEXT("-DmyApp.x2=x2"); tsJAP_subTestJavaAdditionalParamSuite(stripQuote, config, strings, TSJAP_ARRAY_LENGTH(strings), isJVM); } { /* Quotations #2 */ TCHAR *config = TEXT("\"-DmyApp.x1=Hello World.\" -DmyApp.x2=x2"); TCHAR *strings[2]; strings[0] = TEXT("-DmyApp.x1=Hello World."); strings[1] = TEXT("-DmyApp.x2=x2"); tsJAP_subTestJavaAdditionalParamSuite(stripQuote, config, strings, TSJAP_ARRAY_LENGTH(strings), isJVM); } { /* Escaped quotation */ TCHAR *config = TEXT("-DmyApp.x1=\"Hello \\\"World.\" -DmyApp.x2=x2"); TCHAR *strings[2]; strings[0] = TEXT("-DmyApp.x1=Hello \"World."); strings[1] = TEXT("-DmyApp.x2=x2"); tsJAP_subTestJavaAdditionalParamSuite(stripQuote, config, strings, TSJAP_ARRAY_LENGTH(strings), isJVM); } { /* Escaped backslash */ TCHAR *config = TEXT("-DmyApp.x1=\"Hello World.\\\\\" -DmyApp.x2=x2"); TCHAR *strings[2]; strings[0] = TEXT("-DmyApp.x1=Hello World.\\"); strings[1] = TEXT("-DmyApp.x2=x2"); tsJAP_subTestJavaAdditionalParamSuite(stripQuote, config, strings, TSJAP_ARRAY_LENGTH(strings), isJVM); } } } #endif /* CUNIT */ wrapper_3.5.51_src/src/c/wrapper.dll.manifest100644 0 0 3035 14333053647 16351 0ustar 0 0 wrapper.dll wrapper_3.5.51_src/src/c/wrapper.exe.manifest100644 0 0 3035 14333053647 16357 0ustar 0 0 wrapper.exe wrapper_3.5.51_src/src/c/wrapper.h100644 0 0 204541 14333053650 14257 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ #ifndef _WRAPPER_H #define _WRAPPER_H #ifdef WIN32 #include #endif #include #ifdef WIN32 #include #else /* UNIX */ #include #include #include #include #ifndef MACOSX #define u_short unsigned short #endif /* MACOSX */ #endif #ifndef DWORD #define DWORD unsigned long #endif #include #include "property.h" #include "wrapper_jvminfo.h" #ifndef WIN32 /* * Mac OSX 10.5 does not define the environ variable. This is work around for that. */ #ifdef MACOSX #include #define environ *_NSGetEnviron(); #endif extern char** environ; #else #if !defined(WINIA) && defined(SERVICE_ACCEPT_PRESHUTDOWN) #define SUPPORT_PRESHUTDOWN #endif #endif /* The following define will enable debug output of the code to parse the JVM output. */ /*#define DEBUG_CHILD_OUTPUT*/ /* Initialize the timerTicks to a very high value. This means that we will * always encounter the first rollover (512 * WRAPPER_MS / 1000) seconds * after the Wrapper the starts, which means the rollover will be well * tested. */ #define WRAPPER_TICK_INITIAL 0xfffffe00 #define WRAPPER_TICK_MS 100 /* The number of ms that are represented by a single * tick. Ticks are used as an alternative time * keeping method. See the wrapperGetTicks() and * wrapperGetTickAgeSeconds() functions for more information. * Some code assumes that this number can be evenly * divided into 1000. */ #define WRAPPER_MAX_UPTIME_SECONDS 365 * 24 * 3600 /* Maximum uptime count. 1 year. */ #define WRAPPER_MAX_UPTIME_TICKS (WRAPPER_MAX_UPTIME_SECONDS * (1000 / WRAPPER_TICK_MS)) /* The paranthesis are important to avoid overflow. */ #define WRAPPER_TIMER_FAST_THRESHOLD (2 * 24 * 3600 * 1000 / WRAPPER_TICK_MS) /* Default to 2 days. */ #define WRAPPER_TIMER_SLOW_THRESHOLD (2 * 24 * 3600 * 1000 / WRAPPER_TICK_MS) /* Default to 2 days. */ #define WRAPPER_BACKEND_ERROR_NEXT 99 /* value returned when there is an error trying to connect to a socket and we should try the next way to connect */ #define WRAPPER_BACKEND_TYPE_UNKNOWN 0 /* Unknown type. */ #define WRAPPER_BACKEND_TYPE_SOCKET_V4 0x01 #define WRAPPER_BACKEND_TYPE_SOCKET_V6 0x02 #define WRAPPER_BACKEND_TYPE_SOCKET (WRAPPER_BACKEND_TYPE_SOCKET_V4 | WRAPPER_BACKEND_TYPE_SOCKET_V6) #define WRAPPER_BACKEND_TYPE_PIPE 0x04 /* Use a pair of pipes to communicate. */ #define WRAPPER_BACKEND_TYPE_AUTO (WRAPPER_BACKEND_TYPE_SOCKET | WRAPPER_BACKEND_TYPE_PIPE) #define WRAPPER_WSTATE_STARTING 51 /* Wrapper is starting. Remains in this state * until the JVM enters the STARTED state or * the wrapper jumps into the STOPPING state * in response to the JVM application asking * to shut down. */ #define WRAPPER_WSTATE_STARTED 52 /* The JVM has entered the STARTED state. * The wrapper will remain in this state * until the wrapper decides to shut down. * This is true even when the JVM process * is being restarted. */ #define WRAPPER_WSTATE_PAUSING 53 /* The wrapper enters this state when asked to Pause. */ #define WRAPPER_WSTATE_PAUSED 54 /* The wrapper enters this state when the Wrapper * has actually paused. */ #define WRAPPER_WSTATE_RESUMING 55 /* The wrapper enters this state when asked to Resume. */ #define WRAPPER_WSTATE_STOPPING 56 /* The wrapper is shutting down. Will be in * this state until the JVM enters the DOWN * state. */ #define WRAPPER_WSTATE_STOPPED 57 /* The wrapper enters this state just before * it exits. */ #define WRAPPER_JSTATE_DOWN_CHECK 70 /* JVM is confirmed to be down, but we still need * to do our cleanup work. This is the state after * a JVM process has gone away. */ #define WRAPPER_JSTATE_DOWN_FLUSH_STDIN 71 /* JVM is confirmed to be down but we still need * flush all bytes in stdin. */ #define WRAPPER_JSTATE_DOWN_FLUSH 72 /* JVM is confirmed to be down but we still need * flush and process all of its output. */ #define WRAPPER_JSTATE_DOWN_CLEAN 73 /* JVM is confirmed to be down and we have cleaned * up and flushed all output. This is the initial * state and the state after the JVM process has * gone away and and cleanup has been done. */ #define WRAPPER_JSTATE_LAUNCH_DELAY 74 /* Set from the DOWN state to launch a JVM. The * timeout will be the time to actually launch * the JVM after any required delay. */ #define WRAPPER_JSTATE_RESTART 75 /* JVM is about to be restarted. No timeout. */ #define WRAPPER_JSTATE_LAUNCH 76 /* JVM is about to launch a JVM. No timeout. */ #define WRAPPER_JSTATE_LAUNCHING 77 /* JVM was launched, but has not yet responded. * Must enter the LAUNCHED state before * or the JVM will be killed. */ #define WRAPPER_JSTATE_LAUNCHED 78 /* JVM was launched, and responed to a ping. */ #define WRAPPER_JSTATE_STARTING 79 /* JVM has been asked to start. Must enter the * STARTED state before or the JVM will be * killed. */ #define WRAPPER_JSTATE_STARTED 80 /* JVM has responded that it is running. Must * respond to a ping by or the JVM will * be killed. */ #define WRAPPER_JSTATE_STOP 81 /* JVM is about to be sent a stop command to shutdown * cleanly. */ #define WRAPPER_JSTATE_STOPPING 82 /* JVM was sent a stop command, but has not yet * responded. Must enter the STOPPED state * and exit before or the JVM will be killed. */ #define WRAPPER_JSTATE_STOPPED 83 /* JVM has responed that it is stopped. */ #define WRAPPER_JSTATE_KILLING 84 /* The Wrapper is about ready to kill the JVM * process but it must wait a few moments before * actually doing so. After has expired, the * JVM will be killed and we will enter the STOPPED * state. */ #define WRAPPER_JSTATE_KILL 85 /* The Wrapper is about ready to kill the JVM process. */ #define WRAPPER_JSTATE_KILLED 86 /* The Wrapper has requested termination of the JVM and confirmed the JVM is actually killed */ /* Defined Action types. Registered actions are negative. Custom types are positive. */ #define ACTION_LIST_END 0 #define ACTION_NONE -1 #define ACTION_RESTART -2 #define ACTION_SHUTDOWN -3 #define ACTION_DUMP -4 #define ACTION_DEBUG -5 #define ACTION_PAUSE -6 #define ACTION_RESUME -7 #define ACTION_SUCCESS -8 #define ACTION_GC -9 #if defined(MACOSX) #define TRIGGER_ADVICE_NIL_SERVER TEXT("****** Returning nil _server **********") #define ACTION_ADVICE_NIL_SERVER -32 #endif /* The following codes are passed through and referenced within the WrapperServiceActionEvent class. * IMPORTANT: They can be added, but NOT changed without the possibility of affecting user code. */ #define WRAPPER_ACTION_SOURCE_CODE_FILTER 1 /* Action originated with a filter. */ #define WRAPPER_ACTION_SOURCE_CODE_COMMANDFILE 2 /* Action originated from a commandfile. */ #define WRAPPER_ACTION_SOURCE_CODE_WINDOWS_SERVICE_MANAGER 3 /* Action originated from the Windows Service Manager. */ #define WRAPPER_ACTION_SOURCE_CODE_ON_EXIT 4 /* Action originated from an on_exit configuration. */ #define WRAPPER_ACTION_SOURCE_CODE_SIGNAL 5 /* Action originated from a signal. */ #define WRAPPER_ACTION_SOURCE_CODE_PING_TIMEOUT 11 /* Action originated from a timeout. */ /* Required services access rights per action * ATTENTION: Any change in the definitions below should be reported to the functions & variables in wrapper_service_permission_win.c */ #ifdef WIN32 #define WRAPPER_SERVICE_QUERY_STATUS (SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG) #define WRAPPER_SERVICE_START (SERVICE_QUERY_STATUS | SERVICE_START) #define WRAPPER_SERVICE_STOP (SERVICE_QUERY_STATUS | SERVICE_STOP) #define WRAPPER_SERVICE_PAUSE_CONTINUE (SERVICE_QUERY_STATUS | SERVICE_PAUSE_CONTINUE) #define WRAPPER_SERVICE_CONTROL_CODE (SERVICE_QUERY_STATUS | SERVICE_USER_DEFINED_CONTROL) #endif /* Because of the way time is converted to ticks, the largest possible timeout that * can be specified without causing 32-bit overflows is (2^31 / 1000) - 5 = 2147478 * Which is a little over 24 days. To make the interface nice, round this down to * 20 days. Or 1728000. */ #define WRAPPER_TIMEOUT_MAX 1728000 #define WRAPPER_IGNORE_SIGNALS_WRAPPER 1 #define WRAPPER_IGNORE_SIGNALS_JAVA 2 #define WRAPPER_RESTART_REQUESTED_NO 0 #define WRAPPER_RESTART_REQUESTED_INITIAL 1 #define WRAPPER_RESTART_REQUESTED_AUTOMATIC 2 #define WRAPPER_RESTART_REQUESTED_CONFIGURED 4 #ifdef JSW64 typedef unsigned int TICKS; #else typedef unsigned long TICKS; #endif #define JAVA_VERSION_COMPLETED 0 /* 'java -version' executed successfully */ #define JAVA_VERSION_LAUNCH_FAILED 1 /* failed to launch */ #define JAVA_VERSION_WAIT_FAILED 2 /* launched but failed to wait for the process (timed out or another error) */ #define JAVA_VERSION_KILL_FAILED 3 /* failed to wait, and then failed to kill the process */ #define STATE_OUTPUT_MODE_DEFAULT 1 #define STATE_OUTPUT_MODE_SECONDS 2 #define STATE_OUTPUT_MODE_CHANGED 4 #define STATE_OUTPUT_MASK_ALL (STATE_OUTPUT_MODE_DEFAULT | STATE_OUTPUT_MODE_SECONDS | STATE_OUTPUT_MODE_CHANGED) #define STATE_OUTPUT_MASK_NOSECS (STATE_OUTPUT_MASK_ALL & (~STATE_OUTPUT_MODE_SECONDS)) #ifdef WIN32 /* Defines the maximum number of service manager control events that can be queued in a single loop. */ #define CTRL_CODE_QUEUE_SIZE 26 /* Can enqueue one less than this count at any time. */ #endif #define WRAPPER_JAVAIO_BUFFER_SIZE_SYSTEM_DEFAULT 0 #define WRAPPER_JAVAIO_BUFFER_SIZE_MIN 1024 #define WRAPPER_JAVAIO_BUFFER_SIZE_MAX (10 * 1024 * 1024) #define WRAPPER_JAVAIO_BUFFER_SIZE_DEFAULT (64 * 1024) /*#define DEBUG_PING_QUEUE*/ #define WRAPPER_MAX_PENDING_PINGS 10 typedef struct PendingPing PendingPing, *PPendingPing; struct PendingPing { TICKS sentTicks; TICKS slowTicks; PPendingPing nextPendingPing; }; /* Type definitions */ typedef struct WrapperConfig WrapperConfig; struct WrapperConfig { TCHAR *argBinary; /* The path to the wrapper binary. */ TCHAR *argCommand; /* The command used to launch the wrapper. */ TCHAR *argCommandArg; /* The argument to the command used to launch the wrapper. */ TCHAR *argConfFile; /* The name of the config file from the command line. */ int confFileOptional; /* Whether the conf file is required or not. */ TCHAR *confDir; int argConfFileDefault; /* True if the config file was not specified. */ int argConfFileFound; /* True if the config file was found. */ int argCount; /* The total argument count. */ TCHAR **argValues; /* Argument values. */ TCHAR **javaArgValues; /* Arguments getting passed over to the java application */ int javaArgValueCount; /* Number of the arguments getting passed over to the java application */ TCHAR *initialPath; /* What the working directory was when the Wrapper process was first launched. */ TCHAR *baseName; /* The name of the wrapper binary without the OS, arch and bits. */ #ifdef WIN32 UINT jvm_stdout_codepage; /* The code page used for JVM outputs. */ #endif int use_sun_encoding; /* TRUE if the Wrapper uses the value of sun.stdout.encoding to read JVM output. */ int backendType; /* The type of the backend that the Wrapper and Java use to communicate. */ int configured; /* TRUE if loadConfiguration has been called. */ int useSystemTime; /* TRUE if the wrapper should use the system clock for timing, FALSE if a tick counter should be used. */ int logBufferGrowth; /* TRUE if changes to internal buffer sizes should be logged. */ int timerFastThreshold; /* If the difference between the system time based tick count and the timer tick count ever falls by more than this value then a warning will be displayed. */ int timerSlowThreshold; /* If the difference between the system time based tick count and the timer tick count ever grows by more than this value then a warning will be displayed. */ int useTickMutex; /* TRUE if access to the tick count should be protected by a mutex. */ int uptimeFlipped; /* TRUE when the maximum uptime has been flipped. (Overflown) */ int runCommonStarted; /* TRUE once wrapperRunCommon() reported the 'started' status (used to know when to print the 'stop' status). */ int ignoreSequenceGaps; /* TRUE if all sequence properties should be used. */ int port; /* Port number which the Wrapper is configured to be listening on */ int portMin; /* Minimum port to use when looking for a port to bind to. */ int portMax; /* Maximum port to use when looking for a port to bind to. */ int actualPort; /* Port number which the Wrapper is actually listening on */ int jvmPort; /* The port which the JVM should bind to when connecting back to the wrapper. */ int jvmPortMin; /* Minimum port which the JVM should bind to when connecting back to the wrapper. */ int jvmPortMax; /* Maximum port which the JVM should bind to when connecting back to the wrapper. */ int sock; /* Socket number. if open. */ TCHAR *portAddress; TCHAR *originalWorkingDir; /* Original Wrapper working directory. */ TCHAR *workingDir; /* Configured working directory. */ TCHAR *configFile; /* Name of the configuration file */ int commandLogLevel; /* The log level to use when logging the java command. */ int printJVMVersion; /* tells the Wrapper to create a temp JVM to query the version, before starting the java application */ int jvmVersionFailed; /* Flag which is set to true when the forked process used to print the Java version exited with an error. */ JavaVersion *javaVersion; /* Java version of the current or next JVM instance to be launched. */ JavaVersion *javaVersionMin; /* The minimum version of Java required for the Wrapper to launch a JVM. */ JavaVersion *javaVersionMax; /* The maximum version of Java required for the Wrapper to launch a JVM. */ int jvmBits; /* JVM bits of the current or next JVM instance to be launched. */ int jvmVendor; /* JVM implementation (Oracle, IBM, etc.) of the current or next JVM instance to be launched. */ int jvmDefaultLogLevel; /* The default log level used for JVM outputs. */ int jvmSource; /* The source used for JVM outputs. */ #ifdef WIN32 TCHAR *registry_java_home; /* Path to the Java Home when the command is located to the registry, or NULL otherwise. */ TCHAR *jvmVersionCommand; /* Command used to launch the JVM and request its version */ TCHAR *jvmCommand; /* Command used to launch the JVM */ #else /* UNIX */ TCHAR **jvmVersionCommand; /* Command used to launch the JVM and request its version */ TCHAR **jvmCommand; /* Command used to launch the JVM */ #endif int javaCommandNotBinary; /* Flag used to warn when the Java command is not a binary. */ int detachStarted; /* TRUE if the JVM process should be detached once it has started. */ int environmentClasspath; /* TRUE if the classpath should be passed to the JVM in the environment. */ TCHAR *classpath; /* Classpath to pass to the JVM. */ int debugJVM; /* True if the JVM is being launched with a debugger enabled. */ int debugJVMTimeoutNotified;/* True if the JVM is being launched with a debugger enabled and the user has already been notified of a timeout. */ TCHAR key[17]; /* Key which the JVM uses to authorize connections. (16 digits + \0) */ int isConsole; /* TRUE if the wrapper was launched as a console. */ int cpuTimeout; /* Number of seconds without CPU before the JVM will issue a warning and extend timeouts */ int pidLogLevel; /* The log level to use when logging the PID of the JVM after it is launched. */ int startupTimeout; /* Number of seconds the wrapper will wait for a JVM to startup */ int pingTimeout; /* Number of seconds the wrapper will wait for a JVM to reply to a ping */ int pingAlertThreshold; /* Number of seconds without a ping response that the Wrapper will start to warn about a slow ping. */ int pingAlertLogLevel; /* Long level at which slow ping notices are logged. */ int pingInterval; /* Number of seconds between pinging the JVM */ int pingIntervalLogged; /* Number of seconds between pings which can be logged to debug output. */ int *pingActionList; /* The action list to take when a ping timeout is detected. */ int pingTimedOut; int shutdownTimeout; /* Number of seconds the wrapper will wait for a JVM to shutdown */ int jvmExitTimeout; /* Number of seconds the wrapper will wait for a JVM to process to terminate */ int jvmCleanupTimeout; /* Number of seconds the wrapper will allow for its post JVM shudown cleanup. */ int jvmTerminateTimeout; /* Number of seconds the wrapper will allow for the JVM to respond to TerminateProcess request. */ int jvmSilentKill; /* TRUE if the JVM should be silently killed at the next opportunity. */ #ifdef WIN32 int javaIOBufferSize; /* Size of the pipe buffer to use for java I/O. */ #else int javaINBufferSize; /* Size of the pipe buffer to use for java input. */ int javaINFlush; /* Flag to specify whether the JavaIN thread should flush stdin or not on a JVM restart. */ int javaINFlushed; /* Flag to inform the main event loop that the JavaIN thread finished to flush stdin. */ int javaINFlushing; /* Flag to inform the main event loop that the JavaIN thread is currently flushing. */ int disableConsoleInputPermanent; /* TRUE if console input should be permanantly disabled (will force disableConsoleInput to be TRUE on configuation reload). */ #endif int disableConsoleInput; /* TRUE if console input should be disabled. */ int useJavaIOThread; /* If TRUE then a dedicated thread will be used to process console output form the JVM. */ int pauseThreadMain; /* Number of seconds to pause the main thread on its next loop. Only used for testing. */ int pauseThreadTimer; /* Number of seconds to pause the timer thread on its next loop. Only used for testing. */ int pauseThreadJavaIO; /* Number of seconds to pause the javaio thread on its next loop. Only used for testing. */ #ifdef WIN32 int ignoreUserLogoffs; /* If TRUE, the Wrapper will ignore logoff events when run in the background as an in console mode. */ TCHAR *userName; /* The username (account) of the Wrapper process. */ TCHAR *domainName; /* The domain of the Wrapper process. */ DWORD wrapperPID; /* PID of the Wrapper process. */ DWORD javaPID; /* PID of the Java process. */ HANDLE wrapperProcess; /* Handle of the Wrapper process. */ HANDLE javaProcess; /* Handle of the Java process. */ int monitorLaunched; /* TRUE to monitor the launched process, FALSE to nonitor the Java process. */ #else pid_t wrapperPID; /* PID of the Wrapper process. */ pid_t javaPID; /* PID of the Java process. */ #endif int monitorRedirectLogLevel;/* The log level to use when warning about the monitored process redirection. */ int wState; /* The current state of the wrapper */ int jState; /* The current state of the jvm */ TICKS jStateTimeoutTicks; /* Tick count until which the current jState is valid */ int jStateTimeoutTicksSet; /* 1 if the current jStateTimeoutTicks is set. */ TICKS lastPingTicks; /* Time that the last ping was sent */ TICKS lastLoggedPingTicks; /* Time that the last logged ping was sent */ int environmentLogLevel; /* Log Level at which the environment variables should be logged. */ int isDebugging; /* TRUE if set in the configuration file */ int isAdviserEnabled; /* TRUE if advice messages should be output. */ const TCHAR *nativeLibrary; /* The base name of the native library loaded by the WrapperManager. */ int libraryPathAppendPath; /* TRUE if the PATH environment variable should be appended to the java library path. */ int isStateOutputEnabled; /* TRUE if set in the configuration file. Shows output on the state of the state engine. */ int stateOutputMode; /* Mode used to print the state output. */ #ifdef WIN32 int isMessageOutputEnabled; /* TRUE if detailed Message output should be included in debug output. */ #endif int isJavaIOOutputEnabled; /* TRUE if detailed javaIO output should be included in debug output. */ int isTickOutputEnabled; /* TRUE if detailed tick timer output should be included in debug output. */ int isLoopOutputEnabled; /* TRUE if very detailed output from the main loop should be output. */ int isSleepOutputEnabled; /* TRUE if detailed sleep output should be included in debug output. */ int isMemoryOutputEnabled; /* TRUE if detailed memory output should be included in status output. */ int memoryOutputInterval; /* Interval in seconds at which memory usage is logged. */ TICKS memoryOutputTimeoutTicks; /* Tick count at which memory will next be logged. */ int isCPUOutputEnabled; /* TRUE if detailed CPU output should be included in status output. */ int cpuOutputInterval; /* Interval in seconds at which CPU usage is logged. */ TICKS cpuOutputTimeoutTicks; /* Tick count at which CPU will next be logged. */ int isPageFaultOutputEnabled;/* TRUE if detailed PageFault output should be included in status output. */ int pageFaultOutputInterval;/* Interval in seconds at which PageFault usage is logged. */ TICKS pageFaultOutputTimeoutTicks; /* Tick count at which PageFault will next be logged. */ int logfileFlushTimeout; /* The number of seconds before the logfile will be flushed. */ TICKS logfileFlushTimeoutTicks; /* Tick count at which the logfile will be flushed. */ int logfileFlushTimeoutTicksSet; /* TRUE if logfileFlushTimeoutTicks is set. */ int logfileCloseTimeout; /* The number of seconds of inactivity before the logfile will be closed. */ TICKS logfileCloseTimeoutTicks; /* Tick count at which the logfile will be considered inactive and closed. */ int logfileCloseTimeoutTicksSet; /* TRUE if logfileCloseTimeoutTicks is set. */ int isTestsDisabled; /* TRUE if the use of tests in the WrapperManager class should be disabled. */ int isShutdownHookDisabled; /* TRUE if the use of a shutdown hook by the WrapperManager class should be disabled. */ int isForcedShutdownDisabled; /* TRUE if forced shutdowns are disabled. */ int forcedShutdownDelay; /* minimum amount of time required between two CTRL-C or TERM signals. */ int startupDelayConsole; /* Delay in seconds before starting the first JVM in console mode. */ int startupDelayService; /* Delay in seconds before starting the first JVM in service mode. */ int exitCode; /* Code which the wrapper will exit with */ int errorExitCode; /* Code which the wrapper will exit with when there is an error. */ int exitRequested; /* TRUE if the current JVM should be shutdown. */ int restartRequested; /* WRAPPER_RESTART_REQUESTED_NO, WRAPPER_RESTART_REQUESTED_AUTOMATIC, or WRAPPER_RESTART_REQUESTED_CONFIGURED if another JVM should be launched after the current JVM is shutdown. Only set if exitRequested is set. */ int restartJavaVersionFailed; /* TRUE if the restart was caused by a failure while waiting for the completion of the 'java -version' command. */ int shutdownActionTriggered; /* TRUE if any action causing a JVM exit was triggered by the Wrapper (see the WRAPPER_ACTION_SOURCE_CODE_* definitions above). * This flag indicates that the action specified by wrapper.on_exit should be ignored. It must be reset after the JVM is * restarted so that wrapper.on_exit can continue to catch JVM exit codes. This flag is only needed for actions being triggered after the JVM was started. */ TCHAR* shutdownActionPropertyName; /* Keep the name of the property that originated the shudown for logging. */ int stopPacketReceived; /* TRUE if a stop message was received, needed to be able to prevent sending other messages after that point. */ int stoppedPacketReceived; /* TRUE if the STOPPED packet was received before a restart. */ int restartPacketReceived; /* TRUE if the RESTART packet was received before a restart. */ int jvmRestarts; /* Number of times that a JVM has been launched since the wrapper was started. */ int restartDelay; /* Delay in seconds before restarting a new JVM. */ int restartReloadConf; /* TRUE if the configuration should be reloaded before a JVM restart. */ int isRestartDisabled; /* TRUE if restarts should be disabled. */ int isAutoRestartDisabled; /* TRUE if automatic restarts should be disabled. */ int runWithoutJVM; /* TRUE if the Wrapper should run without launching a JVM (used for testing). */ int requestThreadDumpOnFailedJVMExit; /* TRUE if the JVM should be asked to dump its state when it fails to halt on request. */ int requestThreadDumpOnFailedJVMExitDelay; /* Number of seconds to wait after the thread dump before killing the JVM. */ TICKS jvmLaunchTicks; /* The tick count at which the previous or current JVM was launched. */ int failedInvocationCount; /* The number of times that the JVM exited in less than successfulInvocationTime in a row. */ int successfulInvocationTime;/* Amount of time that a new JVM must be running so that the invocation will be considered to have been a success, leading to a reset of the restart count. */ int maxFailedInvocations; /* Maximum number of failed invocations in a row before the Wrapper will give up and exit. */ int outputFilterCount; /* Number of registered output filters. */ TCHAR **outputFilters; /* Array of output filters. */ int **outputFilterActionLists;/* Array of output filter action lists. */ TCHAR **outputFilterMessages; /* Array of output filter messages. */ int *outputFilterAllowWildFlags; /* Array of output filter flags that say whether or not wild cards in the filter can be processed. */ size_t *outputFilterMinLens; /* Array of the minimum text lengths that could possibly match the specified filter. Only used if it contains wildcards. */ TCHAR *pidFilename; /* Name of file to store wrapper pid in */ int pidFileStrict; /* TRUE if a preexisting pid file should cause an error. */ TCHAR *lockFilename; /* Name of file to store wrapper lock in */ TCHAR *javaPidFilename; /* Name of file to store jvm pid in */ TCHAR *javaIdFilename; /* Name of file to store jvm id in */ TCHAR *statusFilename; /* Name of file to store wrapper status in */ TCHAR *javaStatusFilename; /* Name of file to store jvm status in */ TCHAR *commandFilename; /* Name of a command file used to send commands to the Wrapper. */ int commandFileTests; /* True if test commands will be accepted via the command file. */ int commandPollInterval; /* Interval in seconds at which the existence of the command file is polled. */ TICKS commandTimeoutTicks; /* Tick count at which the command file will be checked next. */ TCHAR *anchorFilename; /* Name of an anchor file used to control when the Wrapper should quit. */ int anchorPollInterval; /* Interval in seconds at which the existence of the anchor file is polled. */ TICKS anchorTimeoutTicks; /* Tick count at which the anchor file will be checked next. */ int umask; /* Default umask for all files. */ int javaUmask; /* Default umask for the java process. */ int pidFileUmask; /* Umask to use when creating the pid file. */ int lockFileUmask; /* Umask to use when creating the lock file. */ int javaPidFileUmask; /* Umask to use when creating the java pid file. */ int javaIdFileUmask; /* Umask to use when creating the java id file. */ int statusFileUmask; /* Umask to use when creating the status file. */ int javaStatusFileUmask; /* Umask to use when creating the java status file. */ int anchorFileUmask; /* Umask to use when creating the anchor file. */ #ifndef WIN32 int groupStrict; /* Whether the Wrapper should stop or not when it detects invalid values for the wrapper.*.group properties while loading the configuration. */ gid_t group; /* Default group for all files (Unlike umask, I don't think there is a way to automatically specify a default group for each file created by the process because the underlying function needs the filepath. So this member only specifies the default value for the files below). */ /* gid_t javaGroup; */ /* Default group for the java process. *//* TODO: can this exist? */ gid_t pidFileGroup; /* Group to use when creating the pid file. */ gid_t lockFileGroup; /* Group to use when creating the lock file. */ gid_t javaPidFileGroup; /* Group to use when creating the java pid file. */ gid_t javaIdFileGroup; /* Group to use when creating the java id file. */ gid_t statusFileGroup; /* Group to use when creating the status file. */ gid_t javaStatusFileGroup; /* Group to use when creating the java status file. */ gid_t anchorFileGroup; /* Group to use when creating the anchor file. */ int javaNewProcessGroup; /* Indicates whether the Java process should be created in a new process group or not (Unix only). */ #endif int ignoreSignals; /* Mask that determines where the Wrapper should ignore any catchable system signals. Can be ingored in the Wrapper and/or JVM. */ TCHAR *consoleTitle; /* Text to set the console title to. */ TCHAR *serviceName; /* Name of the service. */ TCHAR *serviceDisplayName; /* Display name of the service. */ TCHAR *serviceDescription; /* Description for service. */ TCHAR *hostName; /* The name of the current host. */ int pausable; /* Should the service be allowed to be paused? */ int pausableStopJVM; /* Should the JVM be stopped when the service is paused? */ int initiallyPaused; /* Should the Wrapper come up initially in a paused state? */ int logLFDelayThreshold; /* The LF Delay threshold to use when logging java output. */ #ifdef WIN32 int isSingleInvocation; /* TRUE if only a single invocation of an application should be allowed to launch. */ TCHAR *ntServiceLoadOrderGroup; /* Load order group name. */ TCHAR *ntServiceDependencies; /* List of Dependencies */ int ntServiceStartType; /* Mode in which the Service is installed. * {SERVICE_AUTO_START | SERVICE_DEMAND_START} */ DWORD ntServicePriorityClass; /* Priority at which the Wrapper and its JVMS will run. * {HIGH_PRIORITY_CLASS | IDLE_PRIORITY_CLASS | NORMAL_PRIORITY_CLASS | REALTIME_PRIORITY_CLASS} */ TCHAR *ntServiceAccount; /* Account name to use when running as a service. NULL to use the LocalSystem account. */ int ntServiceAddLogonAsService; /* Automatically add the 'Log on as a service' privilege to the service account. */ TCHAR *ntServicePassword; /* Password to use when running as a service. NULL means no password. */ int ntServicePrompt; /* If true then the user will be prompted for a account name, domain, password when installing as a service. */ int ntServicePasswordPrompt; /* If true then the user will be prompted for a password when installing as a service. */ int ntServicePasswordPromptMask; /* If true then the password will be masked as it is input. */ int ntServiceInteractive; /* Should the service be allowed to interact with the desktop? */ int ntHideJVMConsole; /* Should the JVMs Console window be hidden when run as a service. True by default but GUIs will not be visible for JVMs prior to 1.4.0. */ int ntShowWrapperConsole; /* Should the Wrapper Console window be shown when run as a service/wrapperw. */ HINSTANCE wrapperHInstance; /* The HINSTANCE of the Wrapper process. */ int wrapperConsoleHide; /* True if the Wrapper Console window should be hidden. */ HWND wrapperConsoleHWND; /* The HWND of the Wrapper's console if it was located. */ int wrapperConsoleVisible; /* True if the Wrapper Console window is visible. */ HWND jvmConsoleHandle; /* Pointer to the JVM Console handle if it exists. */ int jvmConsoleVisible; /* True if the JVM Console window is visible. */ int ntAllocConsole; /* True if a console should be allocated for the Service. */ int generateConsole; /* Make sure that a console is always generated to support thread dumps */ #ifdef SUPPORT_PRESHUTDOWN int ntPreshutdown; /* True if the service accepts SERVICE_CONTROL_PRESHUTDOWN controle code, False if it accepts SERVICE_CONTROL_SHUTDOWN. */ int ntPreshutdownTimeout; /* Number of seconds that the service controler will wait after sending the SERVICE_CONTROL_PRESHUTDOWN notification. */ #endif int ntShutdownWaitHint; /* Delay in seconds requested by the Wrapper when it reports a STOPPING or PAUSING status to the service controler. */ int ntStartupWaitHint; /* Delay in seconds requested by the Wrapper when it reports a STARTING or RESUMING status to the service controler. */ int threadDumpControlCode; /* Control code which can be used to trigger a thread dump. */ BOOL DEPApiAvailable; /* TRUE if the DEP API is available in Kernel32, false otherwise. */ BOOL DEPStatus; /* TRUE if DEP has been enabled. */ DWORD DEPError; /* Error code returned when failing to enable DEP. */ #else /* UNIX */ int daemonize; /* TRUE if the process should be spawned as a daemon process on launch. */ int signalHUPMode; /* Controls what happens when the Wrapper receives a HUP signal. */ int signalUSR1Mode; /* Controls what happens when the Wrapper receives a USR1 signal. */ int signalUSR2Mode; /* Controls what happens when the Wrapper receives a USR2 signal. */ int jvmStopped; /* Flag which remembers the stopped state of the JVM process. */ #endif int pendingPingQueueOverflow; /* Flag which is set to true if the PendingPingQueue overflows the limit of WRAPPER_MAX_PENDING_PINGS. */ int pendingPingQueueOverflowEmptied; /* Flag which is set when the queue size is reduced to 0 after having overflowed. */ int pendingPingCount; /* Number of PendingPing events in the list. */ PPendingPing firstPendingPing; /* Pointer to the first PendingPing in the list. */ PPendingPing firstUnwarnedPendingPing; /* Pointer to the first PendingPing in the list for which a slow warning has not been logged. */ PPendingPing lastPendingPing; /* Pointer to the last PendingPing in the list. */ #ifdef WIN32 int ctrlEventCTRLCTrapped; /* CTRL_C_EVENT trapped. */ int ctrlEventCloseTrapped; /* CTRL_CLOSE_EVENT trapped. */ int ctrlEventLogoffTrapped; /* CTRL_LOGOFF_EVENT trapped. */ int ctrlEventShutdownTrapped;/* CTRL_SHUTDOWN_EVENT trapped. */ int *ctrlCodeQueue; /* Queue of control code ids trapped. */ int ctrlCodeQueueWriteIndex; int ctrlCodeQueueReadIndex; int ctrlCodeQueueWrapped; int ctrlCodePauseTrapped; /* SERVICE_CONTROL_PAUSE was trapped. */ int ctrlCodeContinueTrapped;/* SERVICE_CONTROL_CONTINUE was trapped. */ int ctrlCodeStopTrapped; /* SERVICE_CONTROL_STOP was trapped. */ int ctrlCodeShutdownTrapped;/* SERVICE_CONTROL_SHUTDOWN was trapped. */ #ifdef SUPPORT_PRESHUTDOWN int ctrlCodePreShutdownTrapped;/* SERVICE_CONTROL_PRESHUTDOWN was trapped. */ #endif int ctrlCodeDumpTrapped; /* The configured thread dump control code was trapped. */ #else volatile sig_atomic_t signalInterruptTrapped; /* SIGINT was trapped. */ volatile sig_atomic_t signalQuitTrapped; /* SIGQUIT was trapped. */ volatile sig_atomic_t signalQuitSkip; /* SIGQUIT should be ignored. */ volatile sig_atomic_t signalChildTrapped; /* SIGCHLD was trapped. */ volatile sig_atomic_t signalChildContinuedTrapped; /* SIGCHLD with CLD_CONTINUED was trapped. */ volatile sig_atomic_t signalTermTrapped; /* SIGTERM was trapped. */ volatile sig_atomic_t signalHUPTrapped; /* SIGHUP was trapped. */ volatile sig_atomic_t signalUSR1Trapped; /* SIGUSR1 was trapped. */ volatile sig_atomic_t signalUSR2Trapped; /* SIGUSR2 was trapped. */ #endif }; #define WRAPPER_SIGNAL_MODE_IGNORE (char)100 #define WRAPPER_SIGNAL_MODE_RESTART (char)101 #define WRAPPER_SIGNAL_MODE_SHUTDOWN (char)102 #define WRAPPER_SIGNAL_MODE_FORWARD (char)103 #define WRAPPER_SIGNAL_MODE_PAUSE (char)104 #define WRAPPER_SIGNAL_MODE_RESUME (char)105 #define WRAPPER_SIGNAL_MODE_CLOSE_LOGFILE (char)106 #define WRAPPER_MSG_START (char)100 #define WRAPPER_MSG_STOP (char)101 #define WRAPPER_MSG_RESTART (char)102 #define WRAPPER_MSG_PING (char)103 #define WRAPPER_MSG_STOP_PENDING (char)104 #define WRAPPER_MSG_START_PENDING (char)105 #define WRAPPER_MSG_STARTED (char)106 #define WRAPPER_MSG_STOPPED (char)107 #define WRAPPER_MSG_JAVA_PID (char)108 #define WRAPPER_MSG_KEY (char)110 #define WRAPPER_MSG_BADKEY (char)111 #define WRAPPER_MSG_LOW_LOG_LEVEL (char)112 #define WRAPPER_MSG_PING_TIMEOUT (char)113 /* No longer used. But keep reserved to avoid future problems. */ #define WRAPPER_MSG_SERVICE_CONTROL_CODE (char)114 #define WRAPPER_MSG_PROPERTIES (char)115 /** Log commands are actually 116 + the LOG LEVEL (LEVEL_UNKNOWN ~ LEVEL_NONE), (116 ~ 124). */ #define WRAPPER_MSG_LOG (char)116 #define WRAPPER_MSG_LOGFILE (char)134 #define WRAPPER_MSG_APPEAR_ORPHAN (char)137 /* No longer used. But keep reserved to avoid future problems. */ #define WRAPPER_MSG_PAUSE (char)138 #define WRAPPER_MSG_RESUME (char)139 #define WRAPPER_MSG_GC (char)140 #ifdef WIN32 #define WRAPPER_MSG_FIRE_CTRL_EVENT (char)143 #endif #define WRAPPER_PROCESS_DOWN 200 #define WRAPPER_PROCESS_UP 201 /* default timeout (in second) when executing child process to print java version (in 3.5.42 the value was increased from 10 to 30) */ #define DEFAULT_JAVA_VERSION_TIMEOUT 30 extern WrapperConfig *wrapperData; extern Properties *properties; extern int handleSignals; extern TCHAR wrapperClasspathSeparator; /** * Dumps the table of environment variables, and their sources. */ void dumpEnvironment(); /** * Return TRUE if the this is a prompt call made from the script (like --translate or --jvm_bits). * * @param argCommand the first arguement passed when launching the Wrapper */ int isPromptCallCommand(const TCHAR* argCommand); /** * Return TRUE if the this is a prompt call made from the script (like -translate or -jvm_bits). */ int isPromptCall(); /** * Attempt to set the console title if it exists and is accessible. */ extern void wrapperSetConsoleTitle(); /* Protocol Functions */ /** * Close the backend socket. */ extern void wrapperProtocolClose(); /** * Sends a command to the JVM process. * * @param function The command to send. (This is intentionally an 8-bit char.) * @param message Message to send along with the command. * * @return TRUE if there were any problems. */ extern int wrapperProtocolFunction(char function, const TCHAR *message); /** * Checks the status of the server backend. * * The backend will be initialized if the JVM is in a state where it should * be up, otherwise the backend will be left alone. * * If the forceOpen flag is set then an attempt will be made to initialize * the backend regardless of the JVM state. * * Returns TRUE if the backend is open and ready on return, FALSE if not. */ extern int wrapperCheckServerBackend(int forceOpen); /** * Read any data sent from the JVM. This function will loop and read as many * packets are available. The loop will only be allowed to go for 250ms to * ensure that other functions are handled correctly. * * Returns 0 if all available data has been read, 1 if more data is waiting. */ extern int wrapperProtocolRead(); /****************************************************************************** * Utility Functions *****************************************************************************/ /** * Does any necessary post processing on the command string. * This function assumes that command has been malloced. It will either return * the string as is, or return a modified string. When a modified string is * returned the orignal command buffer will always be freed. * * 1) Replace the first instance of the %WRAPPER_COMMAND_FILLER_N% environment * variable so that the total command length will be equal to or greater than * the length specified by N. The padding will be the length plus a series of * Xs terminated by a single Y. This is mainly for testing. * * @param command The original command. * * @return The modifed command. */ extern TCHAR *wrapperPostProcessCommandElement(TCHAR *command); /** * Test function to pause the current thread for the specified amount of time. * This is used to test how the rest of the Wrapper behaves when a particular * thread blocks for any reason. * * @param pauseTime Number of seconds to pause for. -1 will pause indefinitely. * @param threadName Name of the thread that will be logged prior to pausing. */ extern void wrapperPauseThread(int pauseTime, const TCHAR *threadName); /** * Function that will recursively attempt to match two strings where the * pattern can contain '?' or '*' wildcard characters. * * @param text Text to be searched. * @param pattern Pattern to search for. * @param patternLen Length of the pattern. * @param minTextLen Minimum number of characters that the text needs to possibly match the pattern. * * @return TRUE if found, FALSE otherwise. */ extern int wrapperWildcardMatch(const TCHAR *text, const TCHAR *pattern, size_t minTextLen); /** * Calculates the minimum text length which could be matched by the specified pattern. * Patterns can contain '*' or '?' wildcards. * '*' matches 0 or more characters. * '?' matches exactly one character. * * @param pattern Pattern to calculate. * * @return The minimum text length of the pattern. */ extern size_t wrapperGetMinimumTextLengthForPattern(const TCHAR *pattern); /** * Trims any whitespace from the beginning and end of the in string * and places the results in the out buffer. Assumes that the out * buffer is at least as large as the in buffer. */ extern void trim(const TCHAR *in, TCHAR *out); /** * Returns a constant text representation of the specified Wrapper State. * * @param wState The Wrapper State whose name is being requested. * * @return The requested Wrapper State. */ extern const TCHAR *wrapperGetWState(int wState); /** * Returns a constant text representation of the specified Java State. * * @param jState The Java State whose name is being requested. * * @return The requested Java State. */ extern const TCHAR *wrapperGetJState(int jState); extern struct tm wrapperGetReleaseTime(); extern struct tm wrapperGetBuildTime(); #ifdef WIN32 extern int initializeStartup(); extern void disposeStartup(); #else extern void closeStdinPipe(); extern void disposeJavaIN(); #endif extern void disposeJavaIO(); extern int initializeTimer(); extern void disposeTimer(); extern int showHostIds(int logLevel, int notUsed); #ifdef WIN32 /** * Returns TRUE if the Wrapper process is associated with a console or if one * will be allocated a later stage. This function should be called after the * configuration file has been read. */ int wrapperProcessHasVisibleConsole(); #endif extern void wrapperLoadHostName(); /** * Parses a list of actions for an action property. * * @param actionNameList A space separated list of action names. * @param propertyName The name of the property where the action name originated. * * @return an array of integer action ids, or NULL if there were any problems. */ extern int *wrapperGetActionListForNames(const TCHAR *actionNameList, const TCHAR *propertyName); /** * Performs the specified action, * * @param actionList An array of action Ids ending with a value ACTION_LIST_END. * Negative values are standard actions, positive are user * custom events. * @param triggerMsg The reason the actions are being fired. * @param actionSourceCode Tracks where the action originated. * @param actionPropertyIndex Index of the property where the action was configured. Ignored if the type of action is not configured with a -component property. * @param logForActionNone Flag stating whether or not a message should be logged * for the NONE action. * @param exitCode Error code to use in case the action results in a shutdown. */ extern void wrapperProcessActionList(int *actionList, const TCHAR *triggerMsg, int actionSourceCode, int actionPropertyIndex, int logForActionNone, int exitCode); extern void wrapperAddDefaultProperties(Properties *props); extern int getOriginalWorkingDir(); extern int wrapperLoadConfigurationProperties(int preload); extern void updateStringValue(TCHAR **ptr, const TCHAR *value); #ifdef WIN32 int confReadFilterCallbackMB(const char *bufferMB); extern void wrapperInitializeProfileCounters(); extern void wrapperDumpPageFaultUsage(); extern void disposeSystemPath(); extern TCHAR** wrapperGetSystemPath(); extern int wrapperGetJavaHomeFromWindowsRegistry(TCHAR *javaHome); #endif extern int wrapperCheckRestartTimeOK(); extern int wrapperBuildJavaClasspath(TCHAR **classpath); /** * command is a pointer to a pointer of an array of character strings. * length is the number of strings in the above array. */ extern int wrapperBuildJavaCommandArray(TCHAR ***strings, int *length, int addQuotes, const TCHAR *classpath); extern void wrapperFreeStringArray(TCHAR **strings, int length); extern int wrapperInitialize(); extern void wrapperDispose(int exitCode); /** * Output the version. */ extern void wrapperVersionBanner(); /** * Output the application usage. */ extern void wrapperUsage(TCHAR *appName); /** * Parse the main arguments. * * Returns FALSE if the application should exit with an error. A message will * already have been logged. */ extern int wrapperParseArguments(int argc, TCHAR **argv); /** * Called when the Wrapper detects that the JVM process has exited. * Contains code common to all platforms. */ extern void wrapperJVMProcessExited(TICKS nowTicks, int exitCode); /** * Comfirm that the Java version is in the range in which the Wrapper is allowed to run. * * @return TRUE if the Java version is ok, FALSE otherwise. */ extern int wrapperConfirmJavaVersion(); /** * Set the Java version that the Wrapper will use. * * @param javaVersion The Java version to set or NULL if a default value should be set. */ void wrapperSetJavaVersion(JavaVersion* javaVersion); /** * Read and process any output from the child JVM Process. * * When maxTimeMS is non-zero this function will only be allowed to run for that maximum * amount of time. This is done to make sure the calling function is allowed CPU for * other activities. When timing out for this reason when there is more data in the * pipe, this function will return TRUE to let the calling code know that it should * not to any unnecessary sleeps. Otherwise FALSE will be returned. * * @param maxTimeMS The maximum number of milliseconds that this function will be allowed * to run without returning. In reality no new reads will happen after * this time, but actual processing may take longer. * * @return TRUE if the calling code should call this function again as soon as possible. */ extern int wrapperReadChildOutput(int maxTimeMS); /** * Read the output returned by the 'java -version' command. * * @param noVersionSetDefault TRUE if the Java version should be set to the default value * when the "version" token was not found in the output. */ extern void wrapperReadJavaVersionOutput(int noVersionSetDefault); /** * Changes the current Wrapper state. * * wState - The new Wrapper state. */ extern void wrapperSetWrapperState(int wState); /** * Updates the current state time out. * * nowTicks - The current tick count at the time of the call, may be -1 if * delay is negative. * delay - The delay in seconds, added to the nowTicks after which the state * will time out, if negative will never time out. */ extern void wrapperUpdateJavaStateTimeout(TICKS nowTicks, int delay); /** * Changes the current Java state. * * jState - The new Java state. * nowTicks - The current tick count at the time of the call, may be -1 if * delay is negative. * delay - The delay in seconds, added to the nowTicks after which the state * will time out, if negative will never time out. */ extern void wrapperSetJavaState(int jState, TICKS nowTicks, int delay); /****************************************************************************** * Platform specific methods *****************************************************************************/ #ifdef WIN32 extern void wrapperCheckConsoleWindows(); /** * checks the digital Signature of the binary and reports the result. */ extern void verifyEmbeddedSignature(); extern TCHAR* getExceptionName(DWORD exCode, int nullOnUnknown); extern int exceptionFilterFunction(PEXCEPTION_POINTERS exceptionPointers); BOOL isSecondary(); BOOL extern elevateThis(int argc, TCHAR **argv); BOOL extern duplicateSTD(); BOOL extern myShellExec(HWND hwnd, LPCTSTR pszVerb, LPCTSTR pszPath, LPCTSTR pszParameters, LPCTSTR pszDirectory, TCHAR* namedPipeName); BOOL extern runElevated( __in LPCTSTR pszPath, __in_opt LPCTSTR pszParameters, __in_opt LPCTSTR pszDirectory, TCHAR* namedPipeName); BOOL extern isElevated(); BOOL extern isWin10OrHigher(); BOOL extern isVista(); BOOL extern isWinXP(); extern void wrapperMaintainControlCodes(); void getTrusteeSidFromName(const TCHAR* trusteeName, PSID *ppSid); int wrapperServiceStatus(const TCHAR* serviceName, const TCHAR* serviceDisplayName, int consoleOutput); extern int wrapperRemove(); #define OSBUFSIZE 256 BOOL GetOSDisplayString(TCHAR** pszOS); void wrapperCheckMonitoredProcess(DWORD javaPID); #else void wrapperCheckMonitoredProcess(pid_t javaPID); extern void changePidFileGroup(const TCHAR* filename, gid_t newGroup); extern void wrapperMaintainSignals(); extern TCHAR* findPathOf(const TCHAR *exe, const TCHAR* name, int compat); #endif /** * Execute initialization code to get the wrapper set up. */ extern int wrapperInitializeRun(); extern void wrapperSleep(int ms); /** * Reports the status of the wrapper to the service manager * Possible status values: * WRAPPER_WSTATE_STARTING * WRAPPER_WSTATE_STARTED * WRAPPER_WSTATE_STOPPING * WRAPPER_WSTATE_STOPPED */ extern void wrapperReportStatus(int useLoggerQueue, int status, int errorCode, int waitHint); /** * Reads a single block of data from the child pipe. * * @param blockBuffer Pointer to the buffer where the block will be read. * @param blockSize Maximum number of bytes to read. * @param readCount Pointer to an int which will hold the number of bytes * actually read by the call. * * Returns TRUE if there were any problems, FALSE otherwise. */ extern int wrapperReadChildOutputBlock(char *blockBuffer, int blockSize, int *readCount); /** * Checks on the status of the JVM Process. * Returns WRAPPER_PROCESS_UP or WRAPPER_PROCESS_DOWN */ extern int wrapperGetProcessStatus(TICKS nowTicks, int childContinued); /** * Pauses before launching a new JVM if necessary. */ extern void wrapperPauseBeforeExecute(); /** * Create a child process to print the Java version running the command: * /path/to/java -version * After printing the java version, the process is terminated. * * In case the JVM is slow to start, it will time out after * the number of seconds set in "wrapper.java.version.timeout". * * Note: before the timeout is reached, the user can ctrl+c to stop the Wrapper. */ extern int wrapperLaunchJavaVersion(); /** * Launches a JVM process and store it internally * * @return TRUE if there were any problems. When this happens the Wrapper will not try to restart. */ extern int wrapperLaunchJavaApp(); /** * Returns a tick count that can be used in combination with the * wrapperGetTickAgeSeconds() function to perform time keeping. */ extern TICKS wrapperGetTicks(); /** * Runs some assertion checks on the tick timer logic. */ extern int wrapperTickAssertions(); /** * Outputs a a log entry describing what the memory dump columns are. */ extern void wrapperDumpMemoryBanner(); /** * Outputs a log entry at regular intervals to track the memory usage of the * Wrapper and its JVM. */ extern void wrapperDumpMemory(); /** * Outputs a log entry at regular intervals to track the CPU usage over each * interval for the Wrapper and its JVM. */ extern void wrapperDumpCPUUsage(); /****************************************************************************** * Wrapper inner methods. *****************************************************************************/ /** * Immediately kill the JVM process and set the JVM state to * WRAPPER_JSTATE_DOWN. */ extern int wrapperKillProcessNow(); /** * Puts the Wrapper into a state where the JVM will be killed at the soonest * possible opportunity. It is necessary to wait a moment if a final thread * dump is to be requested. This call will always set the JVM state to * WRAPPER_JSTATE_KILLING. * * @param silent TRUE to skip messages saying that the JVM did not exit on request. * This is useful in certain cases where we kill the JVM without trying * to shut it down cleanly. */ extern void wrapperKillProcess(int silent); /** * Launch the wrapper as a console application. */ extern int wrapperRunConsole(); /** * Launch the wrapper as a service application. */ extern int wrapperRunService(); /** * Used to ask the state engine to pause the JVM and Wrapper * * @param actionSourceCode Tracks where the action originated. */ extern void wrapperPauseProcess(int actionSourceCode); /** * Used to ask the state engine to resume the JVM and Wrapper * * @param actionSourceCode Tracks where the action originated. */ extern void wrapperResumeProcess(int actionSourceCode); /** * Detaches the Java process so the Wrapper will if effect forget about it. */ extern void wrapperDetachJava(); /** * Used to ask the state engine to shut down the JVM and Wrapper. * * @param exitCode Exit code to use when shutting down. * @param force True to force the Wrapper to shutdown even if some configuration * had previously asked that the JVM be restarted. This will reset * any existing restart requests, but it will still be possible for * later actions to request a restart. */ extern void wrapperStopProcess(int exitCode, int force); /** * Depending on the current state, we want to change the exact message displayed when restarting the JVM. * * The logic here needs to match that in wrapperRestartProcess. */ extern const TCHAR *wrapperGetRestartProcessMessage(); /** * Depending on the current state, we want to change the exact message displayed when pausing the Wrapper. * * The logic here needs to match that in wrapperPauseProcess. */ extern const TCHAR *wrapperGetPauseProcessMessage(); /** * Depending on the current state, we want to change the exact message displayed when resuming the Wrapper. * * The logic here needs to match that in wrapperResumeProcess. */ extern const TCHAR *wrapperGetResumeProcessMessage(); /** * Used to ask the state engine to shut down the JVM. */ extern void wrapperRestartProcess(); /** * Sends a command off to the JVM asking it to perform a garbage collection sweep. * * @param actionSourceCode Tracks where the action originated. */ extern void wrapperRequestJVMGC(int actionSourceCode); /** * Loops over and strips all double quotes from prop and places the * stripped version into propStripped. * * The exception is double quotes that are preceeded by a backslash * in this case the backslash is stripped. * * If two backslashes are found in a row, then the first escapes the * second and the second is removed. */ extern void wrapperStripQuotes(const TCHAR *prop, TCHAR *propStripped); /** * Adds quotes around the specified string in such a way that everything is * escaped correctly. If the bufferSize is not large enough then the * required size will be returned. 0 is returned if successful. */ extern size_t wrapperQuoteValue(const TCHAR* value, TCHAR *buffer, size_t bufferSize); /** * Checks the quotes in the value and displays an error if there are any problems. * This can be useful to help users debug quote problems. */ extern int wrapperCheckQuotes(const TCHAR *value, const TCHAR *propName); extern int getStateOutputModeForName(const TCHAR *name); /** * The main event loop for the wrapper. Handles all state changes and events. */ extern void wrapperEventLoop(); extern void wrapperBuildKey(); /** * Send a signal to the JVM process asking it to dump its JVM state. */ extern void wrapperRequestDumpJVMState(); /** * Build the command line used to get the Java version. * * @return TRUE if there were any problems. */ extern int wrapperBuildJavaVersionCommand(); /** * Builds up the java command section of the Java command line. * * @return The final index into the strings array, or -1 if there were any problems. */ extern int wrapperBuildJavaCommandArrayJavaCommand(TCHAR **strings, int addQuotes, int index, int shallow); /** * The Java version will not parse correctly if the Java command is set to 'jdb' or 'javaw'. * - jdb has a different output when the '-version' parameters is specified. * - javaw has no std output. * This function tries to resolve the command used to request the Java version by searching * for the 'java' command located in the same directory, if it exists. If it doesn't, or if * the directory can't be resolved, a warning will be display and execution will continue to * let the Java version be resolved to its default value. * * @param pCommand pointer to the Java command (the value may be modified). * @param addQuotes TRUE if the resolved command should be surrounded by quotes, FALSE otherwise. * * @return TRUE if we ran out of memory, FALSE otherwise. */ extern int wrapperResolveJavaVersionCommand(TCHAR **pCommand, int addQuotes); /** * Build the java command line. * * @return TRUE if there were any problems. */ extern int wrapperBuildJavaCommand(); /** * Requests a lock on the tick mutex. */ extern int wrapperLockTickMutex(); /** * Releases a lock on the tick mutex. */ extern int wrapperReleaseTickMutex(); /** * Calculates a tick count using the system time. */ extern TICKS wrapperGetSystemTicks(); /** * Returns difference in seconds between the start and end ticks. This function * handles cases where the tick counter has wrapped between when the start * and end tick counts were taken. See the wrapperGetTicks() function. */ extern int wrapperGetTickAgeSeconds(TICKS start, TICKS end); /** * Returns difference in ticks between the start and end ticks. This function * handles cases where the tick counter has wrapped between when the start * and end tick counts were taken. See the wrapperGetTicks() function. * * This can be done safely in 32 bits */ extern int wrapperGetTickAgeTicks(TICKS start, TICKS end); /** * Returns TRUE if the specified tick timeout has expired relative to the * specified tick count. */ extern int wrapperTickExpired(TICKS nowTicks, TICKS timeoutTicks); /** * Returns a tick count that is the specified number of seconds later than * the base tick count. */ extern TICKS wrapperAddToTicks(TICKS start, int seconds); /** * Sets the working directory of the Wrapper to the specified directory. * The directory can be relative or absolute. * If there are any problems then a non-zero value will be returned. * * @param dir Directory to change to. * * @return TRUE if the directory failed to be set, FALSE otherwise. */ extern int wrapperSetWorkingDir(const TCHAR* dir); /****************************************************************************** * Protocol callback functions *****************************************************************************/ extern void wrapperLogSignaled(int logLevel, TCHAR *msg); extern void wrapperKeyRegistered(TCHAR *key); /** * Called when a ping is first determined to be slower than the wrapper.ping.alert.threshold. * This will happen before it has actually been responded to. */ extern void wrapperPingSlow(); /** * Called when a ping is responded to, but was slower than the wrapper.ping.alert.threshold. * * @param tickAge The number of seconds it took to respond. */ extern void wrapperPingRespondedSlow(int tickAge); /** * Called when a ping response is received. * * @param pingSendTicks Time in ticks when the ping was originally sent. * @param queueWarnings TRUE if warnings about the queue should be logged, FALSE if the ping response did not contain a time. */ extern void wrapperPingResponded(TICKS pingSendTicks, int queueWarnings); extern void wrapperPingTimeoutResponded(); extern void wrapperStopRequested(int exitCode); extern void wrapperRestartRequested(); extern void wrapperStopPendingSignaled(int waitHint); extern void wrapperStoppedSignaled(); extern void wrapperStartPendingSignaled(int waitHint); extern void wrapperStartedSignaled(); #ifdef WIN32 int wrapperCheckPPid(DWORD pid, DWORD ppid, int depth, int* pError); #endif int checkPidFile(); int writePidFile(const TCHAR *filename, DWORD pid, int newUmask #ifndef WIN32 , gid_t newGroup #endif ); int wrapperWriteStartupPidFiles(); /****************************************************************************** * Inner types and methods for loading Wrapper configuration. *****************************************************************************/ /* Callback parameter for loading the file specified by wrapper.java.additional_file configuration property. */ typedef struct LoadParameterFileCallbackParam LoadParameterFileCallbackParam; struct LoadParameterFileCallbackParam { int stripQuote; /* Value of wrapper.java.additional_file.stripquotes property */ TCHAR **strings; /* Array of character strings to which configurations are loaded */ int index; /* Index of a string in `strings' to which a configuration are copied next */ int isJVMParam; /* Indicates whether the loaded parameters will be JVM parameters */ }; #ifdef CUNIT extern void tsJAP_testJavaAdditionalParamSuite(void); #endif /* CUNIT */ #endif wrapper_3.5.51_src/src/c/wrapper.ico100644 0 0 61176 14333053647 14575 0ustar 0 0  (h h&  v  00h'00..00 %<( %&)(:?JFDN[zokl|}Bb2~K@\_-v\P掸?}8n:ʸț_kpU0D(  "),4-*@40"7;9C999(:=->@EFE KW P]Q\T`aSN\l\[Ydddofbzhabkjqiersqssavywvuxpyr\{-}{yt}};]}l|M}]jf\ťJ1CƬ 7!ͳ!1:ε,͸"Ѻr""ؾǻ O_SLJK>D+ GM\!0:?#8]U&7E9/5-CZF$TN;*652c( YW6 1ARñʶ̷))!"'"!,7TYrĹǾǻȽȼȻȺȻȹȹȹɻɸɷɷ̾ͿWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW22222WWWWWWWWWWWWWWWWWWWWWWWWWW22222222WWWWWWWJJJJJWWWWWWWWWWWWW22WWWW225WWWJJJJJJJVWWWWWWWWWWWW5"   )WWWWWWWWWW  WWWP@4WWWWW WWWWWW W'BWWWWJLWW22WWWVUWW WWW WWW;-WOJWWWWW2.WRJWWWW WW WWWWW7DMWWWWWWW8=HWWWWWW WW WWWWQA3WWWWWWKE<.WWWWW WWW WWUSW/2WWWWJKWW.:WWW WWWWWW (CWW90WIJWWWW,&  WWWWWWWWWWT*6G% +!WWWWWWWWWWWWW#UJJJJJNW1$WWWWWW?>WWWWWWWWWWWWWWJJFJWWWWW222WWWWW22WWWWWWWWWWWWWWWWWWWWWWW222WWWW22WWWWWWWWWWWWWWWWWWWWWWWWW22222222WWWWWWWWWWWWWWWWWWWWWWWWWW22222WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW??|yÇ'Ꮟ'2x~???( @ "_"""""""""""""""ȻȻȻȻȻȻȻRy"""T"v""!ɼKȻȻȻȻȻȻȻ[!N~Ր㍎􍎍s򒒐찥ʶdЍ|􊏏ɻr",byѬЍdJ퍎񂓕k>idʽdȻȻ#b""7Ϳ翮t򍎍퍎J%ȹȻȻF"5"N,ɷȻͿK,ȹȻ%7YȽKͿ>ɸ'ȺǾTR󍎍򄈈p:ͿɷYC"Ȼ*ȻȺǾTD |򍎍󍎍Rb׌ñĹ\:zKȼȻȻz z)A׍b̷̾񋌌q)Q퉐<ͿȻȻȻȻȻȹ3k!I뎍XmȻ*ȻȻȻȻȻ7 !"""a"""""""v"T"""y""""""""""""""""_??|yÇ'Ꮟ'2x~???(0`q[LBϺ2 ˿~ȸ$D3w?"wkoY ~i_w~?c㏎??q>d~(0`xrl_XSQJIFCŲƳ61˶˷к";<GOZp÷ǾǾȽȻ666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666!!!!!!66666666666666666666666666666666666666666!!!!!!!!!666666666666666666666666666666666666666!!!!!!!!!!!66666666666555555556666666666666666666!!!66666!!!!666666655555555556666666666666666666!!!66666666655556666666666666666666! 66666666666666666666666/#%*66666666666666666666666666556!!!66666665)6666666666!!!66666555666!!!666665556666666666666!!6666556666666!!6665556666666666666666!!6555666666666!!6556666666666666666666!05566666666666!$16666666666666666666624&6666666666663'!666666666666666666655-!!6666666665556!!666666666666666655566!!6666666556666!!6666666666666556666!!6666555666666!!666666666666655 66!!6555666666 !66666666666666666.55+4( !666666666666666666666555566 ,55"!666666!!6666666666666666666665555555556666!!6666666666!!!6666666666666666666665555566666666!!!666666666!!!66666666666666666666666666666666666!!!6666666!!!666666666666666666666666666666666666!!!!66666!!!66666666666666666666666666666666666666!!!!!!!!!!!666666666666666666666666666666666666666!!!!!!!!!66666666666666666666666666666666666666666!!!!!!666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666666?c㏎??q>d~(0` """"""""<""""""""""WȻȻpȻpȻ<""""""""""""<ȻȻȻȻȻȻȻȻ"p"""""W"""""Ȼ<ȻȻȻȻȻȻȻȻȻȻ"""d~wrapper_3.5.51_src/src/c/wrapper_encoding.c100644 0 0 203032 14333053650 16112 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ #include #include #include #include #include "wrapper.h" #include "wrapper_hashmap.h" #include "property.h" #include "logger.h" #include "wrapper_jvminfo.h" #include "wrapper_encoding.h" #ifdef WIN32 #define WIN_FILL(a, i, v) a[i] = v #define NIX_FILL(a, i, v) /* nothing */ #else #define WIN_FILL(a, i, v) /* nothing */ #define NIX_FILL(a, i, v) a[i] = v #endif #define K_ENCODING_V_ENCODING 1 /* map any encoding (io or nio) to its corresponding encoding (On Unix: io if key is nio, and nio if key is io, On Windows: code page). */ #define K_ENCODING_V_JVERSION 2 /* map any encoding (io or nio) to the Java version in which it was introduced. */ #ifndef WIN32 #define K_ENCODING_V_ALIAS 3 /* map any encoding (io or nio) to its corresponding alias. */ #define K_ALIAS_V_IOENCODING 4 /* map any iconv alias encoding (if it exists) to its corresponding io encoding. */ #define K_ENCODING_V_IOENCODING 5 /* map any encoding (io or nio) to its corresponding io encoding (this also allows to normalize the case when the key is an io encoding). */ #define K_SHORTENCODING_V_IOENCODING 6 /* map the short notation of any encoding (io or nio) to its corresponding io encoding (this also allows to normalize the case when the key is an io encoding). */ #else #define K_CODEPAGE_V_IOENCODING 7 /* map a code page to its corresponding io encoding. */ #endif #define ENCODINGS_TABLE_SIZE 172 /** * Build a hashMap containing the encodings supported by Java. * - On Windows, the keys are the canonical names for all APIs * and the values are the corresponding code pages. * If there there are no corresponding code page, 0 is set. * - On UNIX, the keys are the canonical names for for java.io API and * java.lang API and the values are the canonical names for java.nio API. * * @return The created hashmap or NULL on failure. */ PHashMap buildJvmEncodingsHashMap(int mode) { PHashMap hashMap; int i = 0; int jv[ENCODINGS_TABLE_SIZE]; /* Java versions in which the encodings were introduced. */ const TCHAR* e1[ENCODINGS_TABLE_SIZE]; /* Canonical Names for java.io API and java.lang API */ const TCHAR* e2[ENCODINGS_TABLE_SIZE]; /* Canonical Names for java.nio API */ #ifdef WIN32 int cp[ENCODINGS_TABLE_SIZE]; /* Windows Code Pages */ int id[ENCODINGS_TABLE_SIZE]; /* Whether the code page is an ID to retrieve the encoding */ #else const TCHAR* al[ENCODINGS_TABLE_SIZE]; /* Alias used by iconv. */ TCHAR key1Buff[ENCODING_BUFFER_SIZE]; TCHAR key2Buff[ENCODING_BUFFER_SIZE]; #endif TCHAR* key1; TCHAR* key2; jv[i] = 5; e1[i] = TEXT("Cp858"); e2[i] = TEXT("IBM00858"); NIX_FILL(al, i, TEXT("IBM-858")); WIN_FILL(cp, i, 858); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp437"); e2[i] = TEXT("IBM437"); NIX_FILL(al, i, TEXT("IBM-437")); WIN_FILL(cp, i, 437); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp775"); e2[i] = TEXT("IBM775"); NIX_FILL(al, i, TEXT("IBM-775")); WIN_FILL(cp, i, 775); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp850"); e2[i] = TEXT("IBM850"); NIX_FILL(al, i, TEXT("IBM-850")); WIN_FILL(cp, i, 850); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp852"); e2[i] = TEXT("IBM852"); NIX_FILL(al, i, TEXT("IBM-852")); WIN_FILL(cp, i, 852); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp855"); e2[i] = TEXT("IBM855"); NIX_FILL(al, i, TEXT("IBM-855")); WIN_FILL(cp, i, 855); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp857"); e2[i] = TEXT("IBM857"); NIX_FILL(al, i, TEXT("IBM-857")); WIN_FILL(cp, i, 857); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp862"); e2[i] = TEXT("IBM862"); NIX_FILL(al, i, TEXT("IBM-862")); WIN_FILL(cp, i, 862); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp866"); e2[i] = TEXT("IBM866"); NIX_FILL(al, i, TEXT("IBM-866")); WIN_FILL(cp, i, 866); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("ISO8859_1"); e2[i] = TEXT("ISO-8859-1"); NIX_FILL(al, i, TEXT("ISO8859-1")); WIN_FILL(cp, i, 28591); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("ISO8859_2"); e2[i] = TEXT("ISO-8859-2"); NIX_FILL(al, i, TEXT("ISO8859-2")); WIN_FILL(cp, i, 28592); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("ISO8859_4"); e2[i] = TEXT("ISO-8859-4"); NIX_FILL(al, i, TEXT("ISO8859-4")); WIN_FILL(cp, i, 28594); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("ISO8859_5"); e2[i] = TEXT("ISO-8859-5"); NIX_FILL(al, i, TEXT("ISO8859-5")); WIN_FILL(cp, i, 28595); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("ISO8859_7"); e2[i] = TEXT("ISO-8859-7"); NIX_FILL(al, i, TEXT("ISO8859-7")); WIN_FILL(cp, i, 28597); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("ISO8859_9"); e2[i] = TEXT("ISO-8859-9"); NIX_FILL(al, i, TEXT("ISO8859-9")); WIN_FILL(cp, i, 28599); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("ISO8859_13"); e2[i] = TEXT("ISO-8859-13"); NIX_FILL(al, i, TEXT("ISO8859-13")); WIN_FILL(cp, i, 28603); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("ISO8859_15"); e2[i] = TEXT("ISO-8859-15"); NIX_FILL(al, i, TEXT("ISO8859-15")); WIN_FILL(cp, i, 28605); WIN_FILL(id, i, TRUE); i++; jv[i] = 11; e1[i] = TEXT("ISO8859_16"); e2[i] = TEXT("ISO-8859-16"); NIX_FILL(al, i, TEXT("ISO_8859-16")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); i++; jv[i] = 5; e1[i] = TEXT("KOI8_R"); e2[i] = TEXT("KOI8-R"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 20866); WIN_FILL(id, i, TRUE); i++; jv[i] = 6; e1[i] = TEXT("KOI8_U"); e2[i] = TEXT("KOI8-U"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 21866); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("ASCII"); e2[i] = TEXT("US-ASCII"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 20127); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("UTF8"); e2[i] = TEXT("UTF-8"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 65001); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("UTF-16"); e2[i] = TEXT("UTF-16"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 7 */ i++; jv[i] = 5; e1[i] = TEXT("UnicodeBigUnmarked"); e2[i] = TEXT("UTF-16BE"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 1201); WIN_FILL(id, i, TRUE); /* NOTE 7 */ i++; jv[i] = 5; e1[i] = TEXT("UnicodeLittleUnmarked"); e2[i] = TEXT("UTF-16LE"); NIX_FILL(al, i, TEXT("UTF-16le")); WIN_FILL(cp, i, 1200); WIN_FILL(id, i, TRUE); /* NOTE 7 */ i++; jv[i] = 6; e1[i] = TEXT("UTF_32"); e2[i] = TEXT("UTF-32"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined */ i++; jv[i] = 6; e1[i] = TEXT("UTF_32BE"); e2[i] = TEXT("UTF-32BE"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 12001); WIN_FILL(id, i, TRUE); /* NOTE 7 */ i++; jv[i] = 6; e1[i] = TEXT("UTF_32LE"); e2[i] = TEXT("UTF-32LE"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 12000); WIN_FILL(id, i, TRUE); /* NOTE 7 */ i++; jv[i] = 6; e1[i] = TEXT("UTF_32BE_BOM"); e2[i] = TEXT("x-UTF-32BE-BOM"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 12001); WIN_FILL(id, i, FALSE); /* NOTE 7, NOTE 19 */ i++; jv[i] = 6; e1[i] = TEXT("UTF_32LE_BOM"); e2[i] = TEXT("x-UTF-32LE-BOM"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 12000); WIN_FILL(id, i, FALSE); /* NOTE 7, NOTE 19 */ i++; jv[i] = 0; e1[i] = TEXT("Cp1250"); e2[i] = TEXT("windows-1250"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 1250); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp1251"); e2[i] = TEXT("windows-1251"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 1251); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp1252"); e2[i] = TEXT("windows-1252"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 1252); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp1253"); e2[i] = TEXT("windows-1253"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 1253); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp1254"); e2[i] = TEXT("windows-1254"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 1254); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp1257"); e2[i] = TEXT("windows-1257"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 1257); WIN_FILL(id, i, TRUE); i++; jv[i] = 6; e1[i] = TEXT("UnicodeBig"); e2[i] = TEXT("Not available"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined */ i++; jv[i] = 5; e1[i] = TEXT("Cp737"); e2[i] = TEXT("x-IBM737"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 737); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp874"); e2[i] = TEXT("x-IBM874"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 874); WIN_FILL(id, i, FALSE); i++; jv[i] = 0; e1[i] = TEXT("UnicodeLittle"); e2[i] = TEXT("x-UTF-16LE-BOM"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 1200); WIN_FILL(id, i, FALSE); /* NOTE 7, NOTE 13 */ i++; jv[i] = 0; e1[i] = TEXT("Big5"); e2[i] = TEXT("Big5"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 950); WIN_FILL(id, i, FALSE); i++; jv[i] = 0; e1[i] = TEXT("Big5_HKSCS"); e2[i] = TEXT("Big5-HKSCS"); NIX_FILL(al, i, TEXT("BIG5HKSCS")); WIN_FILL(cp, i, 951); WIN_FILL(id, i, FALSE); /* NOTE 1 */ i++; jv[i] = 11; e1[i] = TEXT("Big5_HKSCS_2001"); e2[i] = TEXT("x-Big5-HKSCS-2001"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 951); WIN_FILL(id, i, FALSE); i++; jv[i] = 0; e1[i] = TEXT("EUC_JP"); e2[i] = TEXT("EUC-JP"); NIX_FILL(al, i, TEXT("eucJP")); WIN_FILL(cp, i, 20932); WIN_FILL(id, i, TRUE); /* NOTE 2, NOTE 16 */ i++; jv[i] = 0; e1[i] = TEXT("EUC_KR"); e2[i] = TEXT("EUC-KR"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 51949); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("GB18030"); e2[i] = TEXT("GB18030"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 54936); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("EUC_CN"); e2[i] = TEXT("GB2312"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 51936); WIN_FILL(id, i, TRUE); /* NOTE 3 */ i++; jv[i] = 0; e1[i] = TEXT("GBK"); e2[i] = TEXT("GBK"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined */ i++; jv[i] = 5; e1[i] = TEXT("Cp838"); e2[i] = TEXT("IBM-Thai"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 20838); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp1140"); e2[i] = TEXT("IBM01140"); NIX_FILL(al, i, TEXT("IBM-1140")); WIN_FILL(cp, i, 1140); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp1141"); e2[i] = TEXT("IBM01141"); NIX_FILL(al, i, TEXT("IBM-1141")); WIN_FILL(cp, i, 1141); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp1142"); e2[i] = TEXT("IBM01142"); NIX_FILL(al, i, TEXT("IBM-1142")); WIN_FILL(cp, i, 1142); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp1143"); e2[i] = TEXT("IBM01143"); NIX_FILL(al, i, TEXT("IBM-1143")); WIN_FILL(cp, i, 1143); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp1144"); e2[i] = TEXT("IBM01144"); NIX_FILL(al, i, TEXT("IBM-1144")); WIN_FILL(cp, i, 1144); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp1145"); e2[i] = TEXT("IBM01145"); NIX_FILL(al, i, TEXT("IBM-1145")); WIN_FILL(cp, i, 1145); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp1146"); e2[i] = TEXT("IBM01146"); NIX_FILL(al, i, TEXT("IBM-1146")); WIN_FILL(cp, i, 1146); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp1147"); e2[i] = TEXT("IBM01147"); NIX_FILL(al, i, TEXT("IBM-1147")); WIN_FILL(cp, i, 1147); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp1148"); e2[i] = TEXT("IBM01148"); NIX_FILL(al, i, TEXT("IBM-1148")); WIN_FILL(cp, i, 1148); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp1149"); e2[i] = TEXT("IBM01149"); NIX_FILL(al, i, TEXT("IBM-1149")); WIN_FILL(cp, i, 1149); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp037"); e2[i] = TEXT("IBM037"); NIX_FILL(al, i, TEXT("IBM-037")); WIN_FILL(cp, i, 037); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp1026"); e2[i] = TEXT("IBM1026"); NIX_FILL(al, i, TEXT("IBM-1026")); WIN_FILL(cp, i, 1026); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp1047"); e2[i] = TEXT("IBM1047"); NIX_FILL(al, i, TEXT("IBM-1047")); WIN_FILL(cp, i, 1047); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp273"); e2[i] = TEXT("IBM273"); NIX_FILL(al, i, TEXT("IBM-273")); WIN_FILL(cp, i, 20273); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp277"); e2[i] = TEXT("IBM277"); NIX_FILL(al, i, TEXT("IBM-277")); WIN_FILL(cp, i, 20277); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp278"); e2[i] = TEXT("IBM278"); NIX_FILL(al, i, TEXT("IBM-278")); WIN_FILL(cp, i, 20278); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp280"); e2[i] = TEXT("IBM280"); NIX_FILL(al, i, TEXT("IBM-280")); WIN_FILL(cp, i, 20280); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp284"); e2[i] = TEXT("IBM284"); NIX_FILL(al, i, TEXT("IBM-284")); WIN_FILL(cp, i, 20284); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp285"); e2[i] = TEXT("IBM285"); NIX_FILL(al, i, TEXT("IBM-285")); WIN_FILL(cp, i, 20285); WIN_FILL(id, i, TRUE); i++; jv[i] = 9; e1[i] = TEXT("Cp290"); e2[i] = TEXT("IBM290"); NIX_FILL(al, i, TEXT("IBM-290")); WIN_FILL(cp, i, 20290); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp297"); e2[i] = TEXT("IBM297"); NIX_FILL(al, i, TEXT("IBM-297")); WIN_FILL(cp, i, 20297); WIN_FILL(id, i, TRUE); i++; jv[i] = 9; e1[i] = TEXT("Cp300"); e2[i] = TEXT("IBM300"); NIX_FILL(al, i, TEXT("IBM-300")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* NOTE 18 */ i++; jv[i] = 0; e1[i] = TEXT("Cp420"); e2[i] = TEXT("IBM420"); NIX_FILL(al, i, TEXT("IBM-420")); WIN_FILL(cp, i, 20420); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp424"); e2[i] = TEXT("IBM424"); NIX_FILL(al, i, TEXT("IBM-424")); WIN_FILL(cp, i, 20424); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp500"); e2[i] = TEXT("IBM500"); NIX_FILL(al, i, TEXT("IBM-500")); WIN_FILL(cp, i, 500); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp860"); e2[i] = TEXT("IBM860"); NIX_FILL(al, i, TEXT("IBM-860")); WIN_FILL(cp, i, 860); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp861"); e2[i] = TEXT("IBM861"); NIX_FILL(al, i, TEXT("IBM-861")); WIN_FILL(cp, i, 861); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp863"); e2[i] = TEXT("IBM863"); NIX_FILL(al, i, TEXT("IBM-863")); WIN_FILL(cp, i, 863); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp864"); e2[i] = TEXT("IBM864"); NIX_FILL(al, i, TEXT("IBM-864")); WIN_FILL(cp, i, 864); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp865"); e2[i] = TEXT("IBM865"); NIX_FILL(al, i, TEXT("IBM-865")); WIN_FILL(cp, i, 865); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp868"); e2[i] = TEXT("IBM868"); NIX_FILL(al, i, TEXT("IBM-868")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 0; e1[i] = TEXT("Cp869"); e2[i] = TEXT("IBM869"); NIX_FILL(al, i, TEXT("IBM-869")); WIN_FILL(cp, i, 869); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp870"); e2[i] = TEXT("IBM870"); NIX_FILL(al, i, TEXT("IBM-870")); WIN_FILL(cp, i, 870); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp871"); e2[i] = TEXT("IBM871"); NIX_FILL(al, i, TEXT("IBM-871")); WIN_FILL(cp, i, 20871); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp918"); e2[i] = TEXT("IBM918"); NIX_FILL(al, i, TEXT("IBM-918")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("ISO2022CN"); e2[i] = TEXT("ISO-2022-CN"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 5, NOTE 14 */ i++; jv[i] = 0; e1[i] = TEXT("ISO2022JP"); e2[i] = TEXT("ISO-2022-JP"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 50222); WIN_FILL(id, i, TRUE); /* NOTE 4 */ i++; jv[i] = 11; e1[i] = TEXT("ISO2022JP2"); e2[i] = TEXT("ISO-2022-JP-2"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* NOTE 4 */ i++; jv[i] = 0; e1[i] = TEXT("ISO2022KR"); e2[i] = TEXT("ISO-2022-KR"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 50225); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("ISO8859_3"); e2[i] = TEXT("ISO-8859-3"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 28593); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("ISO8859_6"); e2[i] = TEXT("ISO-8859-6"); NIX_FILL(al, i, TEXT("ISO8859-6")); WIN_FILL(cp, i, 28596); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("ISO8859_8"); e2[i] = TEXT("ISO-8859-8"); NIX_FILL(al, i, TEXT("ISO8859-8")); WIN_FILL(cp, i, 28598); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("JIS_X0201"); e2[i] = TEXT("JIS_X0201"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 50221); WIN_FILL(id, i, FALSE); /* NOTE 4 */ i++; jv[i] = 0; e1[i] = TEXT("JIS_X0212-1990"); e2[i] = TEXT("JIS_X0212-1990"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 20932); WIN_FILL(id, i, FALSE); /* NOTE 6 */ i++; jv[i] = 0; e1[i] = TEXT("SJIS"); e2[i] = TEXT("Shift_JIS"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 932); WIN_FILL(id, i, FALSE); i++; jv[i] = 0; e1[i] = TEXT("TIS620"); e2[i] = TEXT("TIS-620"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 28601); WIN_FILL(id, i, TRUE); /* NOTE 1 */ i++; jv[i] = 0; e1[i] = TEXT("Cp1255"); e2[i] = TEXT("windows-1255"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 1255); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp1256"); e2[i] = TEXT("windows-1256"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 1256); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp1258"); e2[i] = TEXT("windows-1258"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 1258); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("MS932"); e2[i] = TEXT("windows-31j"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 932); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Big5_Solaris"); e2[i] = TEXT("x-Big5-Solaris"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined */ i++; jv[i] = 0; e1[i] = TEXT("EUC_JP_LINUX"); e2[i] = TEXT("x-euc-jp-linux"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined */ i++; jv[i] = 0; e1[i] = TEXT("EUC_TW"); e2[i] = TEXT("x-EUC-TW"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined */ i++; jv[i] = 0; e1[i] = TEXT("EUC_JP_Solaris"); e2[i] = TEXT("x-eucJP-Open"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined */ i++; jv[i] = 5; e1[i] = TEXT("Cp1006"); e2[i] = TEXT("x-IBM1006"); NIX_FILL(al, i, TEXT("IBM-1006")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp1025"); e2[i] = TEXT("x-IBM1025"); NIX_FILL(al, i, TEXT("IBM-1025")); WIN_FILL(cp, i, 21025); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp1046"); e2[i] = TEXT("x-IBM1046"); NIX_FILL(al, i, TEXT("IBM-1046")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp1097"); e2[i] = TEXT("x-IBM1097"); NIX_FILL(al, i, TEXT("IBM-1097")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp1098"); e2[i] = TEXT("x-IBM1098"); NIX_FILL(al, i, TEXT("IBM-1098")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp1112"); e2[i] = TEXT("x-IBM1112"); NIX_FILL(al, i, TEXT("IBM-1112")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp1122"); e2[i] = TEXT("x-IBM1122"); NIX_FILL(al, i, TEXT("IBM-1122")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp1123"); e2[i] = TEXT("x-IBM1123"); NIX_FILL(al, i, TEXT("IBM-1123")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp1124"); e2[i] = TEXT("x-IBM1124"); NIX_FILL(al, i, TEXT("IBM-1124")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 11; e1[i] = TEXT("Cp1129"); e2[i] = TEXT("x-IBM1129"); NIX_FILL(al, i, TEXT("IBM-1129")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 11; e1[i] = TEXT("Cp1166"); e2[i] = TEXT("x-IBM1166"); NIX_FILL(al, i, TEXT("IBM-1166")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 11; e1[i] = TEXT("Cp1364"); e2[i] = TEXT("x-IBM1364"); NIX_FILL(al, i, TEXT("IBM-1364")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp1381"); e2[i] = TEXT("x-IBM1381"); NIX_FILL(al, i, TEXT("IBM-1381")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp1383"); e2[i] = TEXT("x-IBM1383"); NIX_FILL(al, i, TEXT("IBM-1383")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp33722"); e2[i] = TEXT("x-IBM33722"); NIX_FILL(al, i, TEXT("IBM-eucJP")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 11; e1[i] = TEXT("Cp833"); e2[i] = TEXT("x-IBM833"); NIX_FILL(al, i, TEXT("IBM-833")); WIN_FILL(cp, i, 20833); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp834"); e2[i] = TEXT("x-IBM834"); NIX_FILL(al, i, TEXT("IBM-834")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0, NOTE 14 */ i++; jv[i] = 5; e1[i] = TEXT("Cp856"); e2[i] = TEXT("x-IBM856"); NIX_FILL(al, i, TEXT("IBM-856")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp875"); e2[i] = TEXT("x-IBM875"); NIX_FILL(al, i, TEXT("IBM-875")); WIN_FILL(cp, i, 875); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp921"); e2[i] = TEXT("x-IBM921"); NIX_FILL(al, i, TEXT("IBM-921")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp922"); e2[i] = TEXT("x-IBM922"); NIX_FILL(al, i, TEXT("IBM-922")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp930"); e2[i] = TEXT("x-IBM930"); NIX_FILL(al, i, TEXT("IBM-930")); WIN_FILL(cp, i, 50930); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp933"); e2[i] = TEXT("x-IBM933"); NIX_FILL(al, i, TEXT("IBM-933")); WIN_FILL(cp, i, 50933); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp935"); e2[i] = TEXT("x-IBM935"); NIX_FILL(al, i, TEXT("IBM-935")); WIN_FILL(cp, i, 50935); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp937"); e2[i] = TEXT("x-IBM937"); NIX_FILL(al, i, TEXT("IBM-937")); WIN_FILL(cp, i, 50937); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp939"); e2[i] = TEXT("x-IBM939"); NIX_FILL(al, i, TEXT("IBM-939")); WIN_FILL(cp, i, 50939); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("Cp942"); e2[i] = TEXT("x-IBM942"); NIX_FILL(al, i, TEXT("IBM-942")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp942C"); e2[i] = TEXT("x-IBM942C"); NIX_FILL(al, i, TEXT("IBM-942")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp943"); e2[i] = TEXT("x-IBM943"); NIX_FILL(al, i, TEXT("IBM-943")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp943C"); e2[i] = TEXT("x-IBM943C"); NIX_FILL(al, i, TEXT("IBM-943")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp948"); e2[i] = TEXT("x-IBM948"); NIX_FILL(al, i, TEXT("IBM-948")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp949"); e2[i] = TEXT("x-IBM949"); NIX_FILL(al, i, TEXT("IBM-949")); WIN_FILL(cp, i, 949); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp949C"); e2[i] = TEXT("x-IBM949C"); NIX_FILL(al, i, TEXT("IBM-949")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp950"); e2[i] = TEXT("x-IBM950"); NIX_FILL(al, i, TEXT("IBM-950")); WIN_FILL(cp, i, 950); WIN_FILL(id, i, FALSE); i++; jv[i] = 5; e1[i] = TEXT("Cp964"); e2[i] = TEXT("x-IBM964"); NIX_FILL(al, i, TEXT("IBM-964")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 5; e1[i] = TEXT("Cp970"); e2[i] = TEXT("x-IBM970"); NIX_FILL(al, i, TEXT("IBM-970")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 0 */ i++; jv[i] = 0; e1[i] = TEXT("ISCII91"); e2[i] = TEXT("x-ISCII91"); NIX_FILL(al, i, TEXT("ISCII.1991")); WIN_FILL(cp, i, 57002); WIN_FILL(id, i, FALSE); /* undefined - NOTE 8 */ i++; jv[i] = 6; e1[i] = TEXT("ISO2022_CN_CNS"); e2[i] = TEXT("x-ISO2022-CN-CNS"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 50229); WIN_FILL(id, i, TRUE); /* NOTE 17 */ i++; jv[i] = 6; e1[i] = TEXT("ISO2022_CN_GB"); e2[i] = TEXT("x-ISO2022-CN-GB"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 50227); WIN_FILL(id, i, TRUE); /* NOTE 17 */ i++; jv[i] = 5; e1[i] = TEXT("x-iso-8859-11"); e2[i] = TEXT("x-iso-8859-11"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 874); WIN_FILL(id, i, FALSE); /* NOTE 9 */ i++; jv[i] = 0; e1[i] = TEXT("x-JIS0208"); e2[i] = TEXT("x-JIS0208"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 20932); WIN_FILL(id, i, FALSE); i++; jv[i] = 0; e1[i] = TEXT("JISAutoDetect"); e2[i] = TEXT("x-JISAutoDetect"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 12 */ i++; jv[i] = 0; e1[i] = TEXT("x-Johab"); e2[i] = TEXT("x-Johab"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 1361); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("MacArabic"); e2[i] = TEXT("x-MacArabic"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 10004); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("MacCentralEurope"); e2[i] = TEXT("x-MacCentralEurope"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 10029); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("MacCroatian"); e2[i] = TEXT("x-MacCroatian"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 10082); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("MacCyrillic"); e2[i] = TEXT("x-MacCyrillic"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 10007); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("MacDingbat"); e2[i] = TEXT("x-MacDingbat"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined */ i++; jv[i] = 5; e1[i] = TEXT("MacGreek"); e2[i] = TEXT("x-MacGreek"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 10006); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("MacHebrew"); e2[i] = TEXT("x-MacHebrew"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 10005); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("MacIceland"); e2[i] = TEXT("x-MacIceland"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 10079); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("MacRoman"); e2[i] = TEXT("x-MacRoman"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 10000); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("MacRomania"); e2[i] = TEXT("x-MacRomania"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 10010); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("MacSymbol"); e2[i] = TEXT("x-MacSymbol"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined */ i++; jv[i] = 5; e1[i] = TEXT("MacThai"); e2[i] = TEXT("x-MacThai"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 10021); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("MacTurkish"); e2[i] = TEXT("x-MacTurkish"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 10081); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("MacUkraine"); e2[i] = TEXT("x-MacUkraine"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 10017); WIN_FILL(id, i, TRUE); i++; jv[i] = 11; e1[i] = TEXT("MS932_0213"); e2[i] = TEXT("x-MS932_0213"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 20932); WIN_FILL(id, i, FALSE); /* NOTE 2 */ i++; jv[i] = 0; e1[i] = TEXT("MS950_HKSCS"); e2[i] = TEXT("x-MS950-HKSCS"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 951); WIN_FILL(id, i, TRUE); /* NOTE 1 */ i++; jv[i] = 11; e1[i] = TEXT("MS950_HKSCS_XP"); e2[i] = TEXT("x-MS950-HKSCS-XP"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 951); WIN_FILL(id, i, FALSE); /* NOTE 1 */ i++; jv[i] = 0; e1[i] = TEXT("MS936"); e2[i] = TEXT("x-mswin-936"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 936); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("PCK"); e2[i] = TEXT("x-PCK"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 932); WIN_FILL(id, i, FALSE); /* NOTE 10 */ i++; jv[i] = 5; e1[i] = TEXT("x-SJIS_0213"); e2[i] = TEXT("x-SJIS_0213"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, -1); WIN_FILL(id, i, FALSE); /* undefined - NOTE 11, NOTE 15 */ i++; jv[i] = 0; e1[i] = TEXT("Cp50220"); e2[i] = TEXT("x-windows-50220"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 50220); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("Cp50221"); e2[i] = TEXT("x-windows-50221"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 50221); WIN_FILL(id, i, TRUE); i++; jv[i] = 5; e1[i] = TEXT("MS874"); e2[i] = TEXT("x-windows-874"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 874); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("MS949"); e2[i] = TEXT("x-windows-949"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 949); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("MS950"); e2[i] = TEXT("x-windows-950"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 950); WIN_FILL(id, i, TRUE); i++; jv[i] = 0; e1[i] = TEXT("x-windows-iso2022jp"); e2[i] = TEXT("x-windows-iso2022jp"); NIX_FILL(al, i, TEXT("")); WIN_FILL(cp, i, 50220); WIN_FILL(id, i, FALSE); hashMap = newHashMap(16); if (!hashMap) { return NULL; } if (mode == K_ENCODING_V_ENCODING) { for (; i >= 0; i--) { key1 = toLower(e1[i]); if (!key1) { freeHashMap(hashMap); return NULL; } key2 = toLower(e2[i]); if (!key2) { free(key1); freeHashMap(hashMap); return NULL; } #ifdef WIN32 hashMapPutKWVI(hashMap, key1, cp[i]); #else hashMapPutKWVW(hashMap, key1, e2[i]); #endif if (_tcscmp(key1, key2) != 0) { #ifdef WIN32 hashMapPutKWVI(hashMap, key2, cp[i]); #else hashMapPutKWVW(hashMap, key2, e1[i]); #endif } free(key1); free(key2); } } else if (mode == K_ENCODING_V_JVERSION) { for (; i >= 0; i--) { key1 = toLower(e1[i]); if (!key1) { freeHashMap(hashMap); return NULL; } key2 = toLower(e2[i]); if (!key2) { free(key1); freeHashMap(hashMap); return NULL; } hashMapPutKWVI(hashMap, key1, jv[i]); if (_tcscmp(key1, key2) != 0) { hashMapPutKWVI(hashMap, key2, jv[i]); } free(key1); free(key2); } #ifndef WIN32 } else if ((mode == K_ENCODING_V_IOENCODING) || (mode == K_SHORTENCODING_V_IOENCODING) || (mode == K_ENCODING_V_ALIAS)) { for (; i >= 0; i--) { key1 = toLower(e1[i]); if (!key1) { freeHashMap(hashMap); return NULL; } if (mode == K_SHORTENCODING_V_IOENCODING) { clearNonAlphanumeric(key1, key1Buff); free(key1); key1 = key1Buff; } key2 = toLower(e2[i]); if (!key2) { free(key1); freeHashMap(hashMap); return NULL; } if (mode == K_SHORTENCODING_V_IOENCODING) { clearNonAlphanumeric(key2, key2Buff); free(key2); key2 = key2Buff; } if (mode == K_ENCODING_V_ALIAS) { hashMapPutKWVW(hashMap, key1, al[i]); } else { hashMapPutKWVW(hashMap, key1, e1[i]); } if (_tcscmp(key1, key2) != 0) { if (mode == K_ENCODING_V_ALIAS) { hashMapPutKWVW(hashMap, key2, al[i]); } else { hashMapPutKWVW(hashMap, key2, e1[i]); } } if (mode != K_SHORTENCODING_V_IOENCODING) { free(key1); free(key2); } } } else if (mode == K_ALIAS_V_IOENCODING) { for (; i >= 0; i--) { if (_tcslen(al[i]) > 0) { key1 = toLower(al[i]); if (!key1) { freeHashMap(hashMap); return NULL; } hashMapPutKWVW(hashMap, key1, e1[i]); } } #else } else if (mode == K_CODEPAGE_V_IOENCODING) { for (; i >= 0; i--) { if (id[i] && cp[i] > 0) { if (wrapperData->isDebugging) { if (hashMapGetKIVW(hashMap, cp[i])) { /* This should not happen if the above tables are well maintened. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Don't know if code page %d should be mapped to '%s' or '%s'."), cp[i], hashMapGetKIVW(hashMap, cp[i]), e1[i]); } } hashMapPutKIVW(hashMap, cp[i], e1[i]); } } #endif } return hashMap; } static PHashMap hashMapJvmEncoding = NULL; static PHashMap hashMapJavaVersions = NULL; /** * Should be called on exit to release the hashmap containing the JVM encodings. */ void disposeHashMapJvmEncoding() { if (hashMapJvmEncoding) { freeHashMap(hashMapJvmEncoding); hashMapJvmEncoding = NULL; } if (hashMapJavaVersions) { freeHashMap(hashMapJavaVersions); hashMapJavaVersions = NULL; } } /** * Check if the given encoding is supported for a specific version of Java. * * @encoding the encoding to search * @javaVersion current java version * * @return TRUE if the encoding exists, FALSE otherwise. */ int checkEncodingJavaVersion(const TCHAR* encoding, int javaVersion, int *pRequiredJavaVersion) { TCHAR* encLower; int outRequiredJavaVersion; int result = FALSE; if (encoding) { encLower = toLower(encoding); if (encLower) { if (!hashMapJavaVersions) { /* Create a hashmap containing the Java versions in which each encoding was introduced. Keep it as a static global variable. */ hashMapJavaVersions = buildJvmEncodingsHashMap(K_ENCODING_V_JVERSION); } if (hashMapJavaVersions) { /* Return TRUE if the Java version is greater than or equal to the required Java version for the encoding. */ outRequiredJavaVersion = hashMapGetKWVI(hashMapJavaVersions, encLower); result = (javaVersion >= outRequiredJavaVersion); if (pRequiredJavaVersion) { *pRequiredJavaVersion = outRequiredJavaVersion; } } free(encLower); } } return result; } #ifndef WIN32 /** * On most systems, Iconv uses uppercase notations for the encodings, sometimes being case-sensitive. * On HPUX, the lowercase notation is often used (e.g. 'utf8'). * This function will check Iconv support of the given encoding and its uppercase/lowercase notations * to offer more flexibility. It's not a problem to try many times, because we would stop anyway if * no supported encoding can be found. * * @encoding the encoding to check * @buffer buffer which will be filled with the supported encoding: * - encoding if it is supported by iconv without transformation. * - the uppercase/lowercase version of encoding, if supported. * - empty if none of the above are supported. * * @return the buffer or NULL if the encoding is not supported. */ TCHAR* getIconvEncodingVariousCases(const TCHAR* encoding, TCHAR* buffer) { TCHAR* altEncoding; buffer[0] = 0; if (encoding && (_tcslen(encoding) > 0)) { if (getIconvEncodingSupport(encoding) != ICONV_ENCODING_NOT_SUPPORTED) { _tcsncpy(buffer, encoding, ENCODING_BUFFER_SIZE); } else { altEncoding = toUpper(encoding); if (altEncoding) { if ((_tcscmp(altEncoding, encoding) != 0) && (getIconvEncodingSupport(altEncoding) != ICONV_ENCODING_NOT_SUPPORTED)) { _tcsncpy(buffer, altEncoding, ENCODING_BUFFER_SIZE); free(altEncoding); } else { free(altEncoding); altEncoding = toLower(encoding); if (altEncoding) { if ((_tcscmp(altEncoding, encoding) != 0) && (getIconvEncodingSupport(altEncoding) != ICONV_ENCODING_NOT_SUPPORTED)) { _tcsncpy(buffer, altEncoding, ENCODING_BUFFER_SIZE); } free(altEncoding); } } } } } if (_tcslen(buffer) == 0) { return NULL; } return buffer; } /** * Check if the encoding is supported by the JVM. * * @localeEncoding the locale encoding * @javaVersion current java version * @buffer buffer where the output encoding should be copied * * @return a string representation of the JVM io encoding, or NULL if no value could be found. */ TCHAR* getJvmIoEncoding(TCHAR* localeEncoding, int javaVersion, TCHAR* buffer) { TCHAR* localeEncLower; TCHAR localeEncShort[ENCODING_BUFFER_SIZE]; PHashMap hashMap; const TCHAR* encoding; TCHAR* result = NULL; buffer[0] = 0; if (localeEncoding && (_tcslen(localeEncoding) > 0)) { localeEncLower = toLower(localeEncoding); if (localeEncLower) { hashMap = buildJvmEncodingsHashMap(K_ENCODING_V_IOENCODING); if (hashMap) { encoding = hashMapGetKWVW(hashMap, localeEncLower); if (!encoding) { /* No match found - search among aliases. */ freeHashMap(hashMap); hashMap = buildJvmEncodingsHashMap(K_ALIAS_V_IOENCODING); if (hashMap) { encoding = hashMapGetKWVW(hashMap, localeEncLower); if (!encoding) { /* No match found - try without canonical dashes and punctuation (ex: EUC-JP -> eucjp). * No need to do this for aliases because they are already in the locale syntax. */ freeHashMap(hashMap); hashMap = buildJvmEncodingsHashMap(K_SHORTENCODING_V_IOENCODING); if (hashMap) { clearNonAlphanumeric(localeEncLower, localeEncShort); encoding = hashMapGetKWVW(hashMap, localeEncShort); } } } } if (checkEncodingJavaVersion(encoding, javaVersion, NULL)) { _tcsncpy(buffer, encoding, ENCODING_BUFFER_SIZE); result = buffer; } freeHashMap(hashMap); } free(localeEncLower); } } return result; } #else /** * Get the JVM encoding corresponding to a code page. * * @codePage the Windows code page * @javaVersion current java version * @buffer buffer where the output encoding should be copied. * * @return a string representation of the JVM io encoding, or NULL if no value could be found. */ TCHAR* getJvmIoEncodingFromCodePage(int codePage, int javaVersion, TCHAR* buffer) { PHashMap hashMap = buildJvmEncodingsHashMap(K_CODEPAGE_V_IOENCODING); const TCHAR* encoding; TCHAR* result = NULL; buffer[0] = 0; if (hashMap) { encoding = hashMapGetKIVW(hashMap, codePage); if (checkEncodingJavaVersion(encoding, javaVersion, NULL)) { _tcsncpy(buffer, encoding, ENCODING_BUFFER_SIZE); result = buffer; } freeHashMap(hashMap); } return result; } #endif int checkEquivalentEncodings(TCHAR* encoding1, TCHAR* encoding2) { #ifndef WIN32 const TCHAR* value; #endif int result = FALSE; TCHAR* enc1Lower; TCHAR* enc2Lower; enc1Lower = toLower(encoding1); if (!enc1Lower) { return FALSE; } enc2Lower = toLower(encoding2); if (!enc2Lower) { free(enc1Lower); return FALSE; } if (_tcscmp(enc1Lower, enc2Lower) == 0) { result = TRUE; } else { if (!hashMapJvmEncoding) { hashMapJvmEncoding = buildJvmEncodingsHashMap(K_ENCODING_V_ENCODING); } if (hashMapJvmEncoding && #ifdef WIN32 (hashMapGetKWVI(hashMapJvmEncoding, enc1Lower) == hashMapGetKWVI(hashMapJvmEncoding, enc2Lower)) #else (((value = hashMapGetKWVW(hashMapJvmEncoding, enc1Lower)) != NULL) && strcmpIgnoreCase(value, enc2Lower) == 0) #endif ) { result = TRUE; } } free(enc1Lower); free(enc2Lower); return result; } int getJvmSunEncodingSupport(int javaVersion, int jvmVendor) { int result = 0; if (jvmVendor == JVM_VENDOR_IBM) { result |= SUN_ENCODING_UNSUPPORTED_JVM_VENDOR; } else if ((jvmVendor != JVM_VENDOR_ORACLE) || (jvmVendor != JVM_VENDOR_OPENJDK)) { result |= SUN_ENCODING_SUPPORT_UNKNOWN; } if (javaVersion < 8) { result |= SUN_ENCODING_UNSUPPORTED_JAVA_VERSION; } if (result == 0) { result |= SUN_ENCODING_SUPPORTED; } return result; } void tryGetSunSystemProperty(const TCHAR* name, int javaVersion, int jvmVendor, TCHAR* propValue, TCHAR* buffer, int* result) { static int warnedPropsNotSupported[] = { FALSE, FALSE }; /* Remember if we logged warnings for each property, in the order: stdout, stderr */ int propIndex; TCHAR argBase[23]; int sunSupport; #ifdef UNIT_TESTS if (resetStaticVariables) { warnedPropsNotSupported[0] = FALSE; warnedPropsNotSupported[1] = FALSE; resetStaticVariables = FALSE; } #endif _sntprintf(argBase, 23, TEXT("-D%s="), name); if (_tcsstr(propValue, argBase) == propValue) { sunSupport = getJvmSunEncodingSupport(javaVersion, jvmVendor); if ((sunSupport & SUN_ENCODING_UNSUPPORTED_JVM_VENDOR) == SUN_ENCODING_UNSUPPORTED_JVM_VENDOR) { propIndex = (_tcscmp(name, TEXT("sun.stdout.encoding")) == 0) ? 0 : 1; if (!warnedPropsNotSupported[propIndex]) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Found %s among the JVM parameters but this system property is not\n supported on this implementation of Java."), name); warnedPropsNotSupported[propIndex] = TRUE; } } else if ((sunSupport & SUN_ENCODING_UNSUPPORTED_JAVA_VERSION) == SUN_ENCODING_UNSUPPORTED_JAVA_VERSION) { propIndex = (_tcscmp(name, TEXT("sun.stdout.encoding")) == 0) ? 0 : 1; if (!warnedPropsNotSupported[propIndex]) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Found %s among the JVM parameters but this system property is not\n supported by this version of Java.\n Requires Java 8 or above, using Java %d."), name, javaVersion); warnedPropsNotSupported[propIndex] = TRUE; } } else { if (buffer[0] == 0) { /* This is the first time we found this property. */ _tcsncpy(buffer, propValue + 22, ENCODING_BUFFER_SIZE); buffer[ENCODING_BUFFER_SIZE - 1] = 0; /* Set the result to SUN_ENCODING to avoid searching for file.encoding. We may change it later if the sun properties are not configured correctly. */ *result = SUN_ENCODING; } else if (!checkEquivalentEncodings(buffer, propValue + 22)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Found multiple occurrences of %s set with different values\n among the JVM parameters. Cannot resolve the JVM output encoding."), name); *result = UNRESOLVED_ENCODING; } /* The remaining case is when the value of the current system property is different than one of a previously defined, * but both values point to the same encoding. If this happen, leave the result unchanged and continue. */ } } } /** * Retrieved the value of file.encoding (or sun.std*.encoding) if defined in the java additional properties. * The buffer is set to an empty string if the value could not be found. * disposeHashMapJvmEncoding() should be called before calling this function. * * @buffer buffer in which the encoding should be copied * @javaVersion current java version * @jvmVendor current java implementation (Oracle, IBM, etc.) * * @return LOCALE_ENCODING if no encoding was specified in the JVM arguments * FILE_ENCODING if the encoding was resolved to the value of file.encoding * SUN_ENCODING if the encoding was resolved to the value of the sun.std*.encoding properties * UNRESOLVED_ENCODING if there was any error. A FATAL message will be printed before returning */ int getJvmArgumentsEncoding(TCHAR* buffer, int javaVersion, int jvmVendor) { TCHAR** propNames; TCHAR** propValues; TCHAR bufferSunOut[ENCODING_BUFFER_SIZE]; TCHAR bufferSunErr[ENCODING_BUFFER_SIZE]; long unsigned int* propIndices; TCHAR* propValue; int i; int result = LOCALE_ENCODING; /* We can move the result to a higher value but never decrease it. The order is LOCALE_ENCODING -> FILE_ENCODING -> SUN_ENCODING -> UNRESOLVED_ENCODING. */ int foundMultipleFileEncoding = FALSE; buffer[0] = 0; if (getStringProperties(properties, TEXT("wrapper.java.additional."), TEXT(""), wrapperData->ignoreSequenceGaps, FALSE, &propNames, &propValues, &propIndices)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Failed to retrieve the values of the wrapper.java.additional.* properties.")); return UNRESOLVED_ENCODING; } bufferSunOut[0] = 0; bufferSunErr[0] = 0; for (i = 0; propValues[i]; i++){ propValue = propValues[i]; tryGetSunSystemProperty(TEXT("sun.stdout.encoding"), javaVersion, jvmVendor, propValue, bufferSunOut, &result); if (result == UNRESOLVED_ENCODING) { break; } tryGetSunSystemProperty(TEXT("sun.stderr.encoding"), javaVersion, jvmVendor, propValue, bufferSunErr, &result); if (result == UNRESOLVED_ENCODING) { break; } if ((result != SUN_ENCODING) && (_tcsstr(propValue, TEXT("-Dfile.encoding=")) == propValue)) { if (buffer[0] == 0) { /* This is the first time we found this property. */ _tcsncpy(buffer, propValue + 16, ENCODING_BUFFER_SIZE); buffer[ENCODING_BUFFER_SIZE - 1] = 0; result = FILE_ENCODING; } else if (!checkEquivalentEncodings(buffer, propValue + 16)) { /* Keep a flag and log later. We will ignore this case if the sun properties are set. */ foundMultipleFileEncoding = TRUE; } /* The remaining case is when the value of the current system property is different than one of a previously defined, * but both values point to the same encoding. If this happen, leave the result unchanged and continue. */ } } if ((result == FILE_ENCODING) && foundMultipleFileEncoding) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Found multiple occurrences of %s set with different values\n among the JVM parameters. Cannot resolve the JVM output encoding."), TEXT("file.encoding")); result = UNRESOLVED_ENCODING; } else if (result == SUN_ENCODING) { /* For clarity, the sun.*.encoding propeties, when defined, should be both present. We don't even accept cases * where only one would be defined along with file.encoding also set to the same value (although this is valid). */ if (bufferSunOut[0] == 0) { if (bufferSunErr[0] != 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Found sun.stderr.encoding but sun.stdout.encoding was not specified.\n Please add sun.stdout.encoding to the list of JVM parameters and set its encoding to the same value as sun.stderr.encoding (%s)."), bufferSunErr); result = UNRESOLVED_ENCODING; } } else { /* bufferSunOut[0] != 0 */ if (bufferSunErr[0] == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Found sun.stdout.encoding but sun.stderr.encoding was not specified.\n Please add sun.stderr.encoding to the list of JVM parameters and set its encoding to the same value as sun.stdout.encoding (%s)."), bufferSunOut); result = UNRESOLVED_ENCODING; } else if (!checkEquivalentEncodings(bufferSunOut, bufferSunErr)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("The encodings of sun.stdout.encoding (%s) and sun.stderr.encoding (%s) don't match.\n Please set both system properties to the same value."), bufferSunOut, bufferSunErr); result = UNRESOLVED_ENCODING; } else { /* Both sun properties are defined and set to the same value (or equivalent values pointing to the same encoding). */ _tcsncpy(buffer, bufferSunOut, ENCODING_BUFFER_SIZE); } } } if (result == UNRESOLVED_ENCODING) { buffer[0] = 0; } freeStringProperties(propNames, propValues, propIndices); return result; } #ifdef WIN32 static UINT jvmOutputCodePage; /** * Get the code page used to encode the current JVM outputs. * resolveJvmEncoding() should be called before using this function. * * @return UINT value of the code page (the code page of the current locale is returned by default) */ UINT getJvmOutputCodePage() { return jvmOutputCodePage; } #else static TCHAR jvmOutputEncoding[ENCODING_BUFFER_SIZE]; static char jvmOutputEncodingMB[ENCODING_BUFFER_SIZE]; /** * Get the encoding used for the current JVM outputs. * resolveJvmEncoding() should be called before using this function. * * @return String representation of the encoding if the value found in file.encoding is supported by iconv, NULL otherwise. * The returned value doesn't need to be freed. */ const char* getJvmOutputEncodingMB() { if (jvmOutputEncodingMB[0] == 0) { return NULL; } return jvmOutputEncodingMB; } #endif /** * Clear the Jvm encoding previously cached. * This function can be called before getJvmOutputEncodingMB() to force using the encoding of the current locale. * A call to resolveJvmEncoding() may then be necessary to restore the encoding. * * @debug TRUE to print a debug message, FALSE otherwise. */ void resetJvmOutputEncoding(int debug) { TCHAR buffer[ENCODING_BUFFER_SIZE]; buffer[0] = 0; #ifdef WIN32 jvmOutputCodePage = wrapperData->jvm_stdout_codepage; _sntprintf(buffer, ENCODING_BUFFER_SIZE, TEXT("%d"), jvmOutputCodePage); #else jvmOutputEncoding[0] = 0; jvmOutputEncodingMB[0] = 0; if (debug) { getCurrentLocaleEncoding(buffer); } #endif if (debug) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Reading the JVM output using the encoding of the current locale (%s)."), buffer); } } #define GET_ENCODING_SYSPROP(o) o == FILE_ENCODING ? TEXT("file.encoding") : TEXT("sun.stdout.encoding and sun.stderr.encoding") /** * Resolve the Java output encoding using system properties and an internal hashMap containing the supported encoding. * This function should be called prior to using getJvmOutputCodePage() and getJvmOutputEncodingMB() * * @javaVersion current java version * @jvmVendor current java implementation (Oracle, IBM, etc.) * * @return TRUE if there is any error (misconfiguration of system properties or unsuported encoding), FALSE otherwise. */ int resolveJvmEncoding(int javaVersion, int jvmVendor) { TCHAR buffer[ENCODING_BUFFER_SIZE]; int jvmEncodingOrigin; #ifndef WIN32 PHashMap aliasHashMap; const TCHAR* altEncoding; #endif int requiredJavaVersion = 0; TCHAR* encLower; /* Initiate use_sun_encoding to FALSE. We will use this flag when building the Java command line. */ wrapperData->use_sun_encoding = FALSE; jvmEncodingOrigin = getJvmArgumentsEncoding(buffer, javaVersion, jvmVendor); if (jvmEncodingOrigin == UNRESOLVED_ENCODING) { /* Unresolved encoding - any error has already been logged */ return TRUE; } else if (jvmEncodingOrigin != LOCALE_ENCODING) { /* The encoding was specified in a system property passed to the Java command line. */ if (!hashMapJvmEncoding) { hashMapJvmEncoding = buildJvmEncodingsHashMap(K_ENCODING_V_ENCODING); if (!hashMapJvmEncoding) { return TRUE; } } encLower = toLower(buffer); if (!encLower) { return TRUE; } #ifdef WIN32 jvmOutputCodePage = (UINT)hashMapGetKWVI(hashMapJvmEncoding, encLower); free(encLower); if (jvmOutputCodePage == 0) { /* The value was not found in the hasmap. We have no way to know if the encoding is invalid or if it was * added after this version of the Wrapper was released, so log a message to indicate both possibilities. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("'%s' is not a valid value for %s\n or is not supported by this version of the Wrapper."), buffer, GET_ENCODING_SYSPROP(jvmEncodingOrigin)); return TRUE; } else if (jvmOutputCodePage == -1) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("The value '%s' of %s is not supported on Windows."), buffer, GET_ENCODING_SYSPROP(jvmEncodingOrigin)); jvmOutputCodePage = 0; return TRUE; } else if (!IsValidCodePage(jvmOutputCodePage)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("The value '%s' of %s is not a valid code page."), buffer, GET_ENCODING_SYSPROP(jvmEncodingOrigin)); jvmOutputCodePage = 0; return TRUE; } if (!checkEncodingJavaVersion(buffer, javaVersion, &requiredJavaVersion)) { /* The value exist for a more recent version of Java. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("The value '%s' of %s is supported from Java %d. The current version of Java is %d."), buffer, GET_ENCODING_SYSPROP(jvmEncodingOrigin), requiredJavaVersion, javaVersion); return TRUE; } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Reading the JVM output using the value of %s: %s (resolved to code page %d)."), GET_ENCODING_SYSPROP(jvmEncodingOrigin), buffer, jvmOutputCodePage); #else altEncoding = hashMapGetKWVW(hashMapJvmEncoding, encLower); if (!altEncoding || (_tcscmp(buffer, TEXT("Not available")) == 0)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("'%s' is not a valid value for %s\n or is not supported by this version of the Wrapper."), buffer, GET_ENCODING_SYSPROP(jvmEncodingOrigin)); free(encLower); return TRUE; } else if (!getIconvEncodingVariousCases(buffer, jvmOutputEncoding)) { /* Check if the alternative encoding is supported by Iconv. */ if ((_tcscmp(altEncoding, TEXT("Not available")) == 0) || (!getIconvEncodingVariousCases(altEncoding, jvmOutputEncoding))) { /* Check if an alias is supported by Iconv. */ altEncoding = NULL; aliasHashMap = buildJvmEncodingsHashMap(K_ENCODING_V_ALIAS); if (aliasHashMap) { altEncoding = hashMapGetKWVW(aliasHashMap, encLower); } if (!altEncoding || (_tcslen(altEncoding) == 0) || (!getIconvEncodingVariousCases(altEncoding, jvmOutputEncoding))) { /* Possible improvement: list all locale encodings, build a hashmap (key=, value=), and search a match in it. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("The value '%s' of %s is not supported by iconv."), buffer, GET_ENCODING_SYSPROP(jvmEncodingOrigin)); freeHashMap(aliasHashMap); free(encLower); return TRUE; } freeHashMap(aliasHashMap); } } free(encLower); if (!checkEncodingJavaVersion(buffer, javaVersion, &requiredJavaVersion)) { /* The value exists for a more recent version of Java. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("The value '%s' of %s is supported from Java %d. The current version of Java is %d."), buffer, GET_ENCODING_SYSPROP(jvmEncodingOrigin), requiredJavaVersion, javaVersion); return TRUE; } if (wcstombs(jvmOutputEncodingMB, jvmOutputEncoding, ENCODING_BUFFER_SIZE) == (size_t)-1) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Error when converting the JVM output encoding '%s'."), jvmOutputEncoding); return TRUE; } if (_tcscmp(buffer, jvmOutputEncoding) == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Reading the JVM output using the value of %s: %s."), GET_ENCODING_SYSPROP(jvmEncodingOrigin), buffer); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Reading the JVM output using the value of %s: %s (resolved to %s)."), GET_ENCODING_SYSPROP(jvmEncodingOrigin), buffer, jvmOutputEncoding); } #endif if (jvmEncodingOrigin == SUN_ENCODING) { wrapperData->use_sun_encoding = TRUE; } } else { /* The encoding of the current locale should be used. */ resetJvmOutputEncoding(wrapperData->isDebugging); } return FALSE; } wrapper_3.5.51_src/src/c/wrapper_encoding.h100644 0 0 12367 14333053650 16110 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ #ifndef _WRAPPER_ENCODING_H #define _WRAPPER_ENCODING_H #include "wrapper_i18n.h" /* Constants that define which system property was used to determine the JVM output encoding. */ #define LOCALE_ENCODING 1 #define FILE_ENCODING 2 #define SUN_ENCODING 3 #define UNRESOLVED_ENCODING 4 #define SUN_ENCODING_SUPPORTED 0x0001 /* sun.std*.encoding System Properties are supported */ #define SUN_ENCODING_SUPPORT_UNKNOWN 0x0002 /* support is unknown */ #define SUN_ENCODING_UNSUPPORTED 0x0004 /* not supported */ #define SUN_ENCODING_UNSUPPORTED_JAVA_VERSION (0x0008 | SUN_ENCODING_UNSUPPORTED) /* not supported by this version of Java */ #define SUN_ENCODING_UNSUPPORTED_JVM_VENDOR (0x0010 | SUN_ENCODING_UNSUPPORTED) /* not supported by the JVM implementation */ #ifdef WIN32 /** * Get the JVM encoding corresponding to a code page. * * @codePage the Windows code page * @javaVersion current java version * @buffer buffer where the output encoding should be copied. * * @return a string representation of the JVM io encoding, or NULL if no value could be found. */ TCHAR* getJvmIoEncodingFromCodePage(int codePage, int javaVersion, TCHAR* buffer); /** * Retrieved the value of file.encoding (or sun.std*.encoding) if defined in the java additional properties. * The buffer is set to an empty string if the value could not be found. * disposeHashMapJvmEncoding() should be called before calling this function. * * @buffer buffer in which the encoding should be copied * @javaVersion current java version * @jvmVendor current java implementation (Oracle, IBM, etc.) * * @return LOCALE_ENCODING if no encoding was specified in the JVM arguements * FILE_ENCODING if the encoding was resolved to the value of file.encoding * SUN_ENCODING if the encoding was resolved to the value of the sun.std*.encoding properties * UNRESOLVED_ENCODING if there was any error. A FATAL message will be printed before returning */ int getJvmArgumentsEncoding(TCHAR* buffer, int javaVersion, int jvmVendor); /** * Get the code page used to encode the current JVM outputs. * resolveJvmEncoding() should be called before using this function. * * @return UINT value of the code page (the code page of the current locale is returned by default) */ UINT getJvmOutputCodePage(); #else /** * Check if the encoding is supported by the JVM. * * @localeEncoding the locale encoding * @javaVersion current java version * @buffer buffer where the output encoding should be copied * * @return a string representation of the JVM io encoding, or NULL if no value could be found. */ TCHAR* getJvmIoEncoding(TCHAR* localeEncoding, int javaVersion, TCHAR* buffer); /** * Get the encoding used for the current JVM outputs. * resolveJvmEncoding() should be called before using this function. * * @return String representation of the encoding if the value found in file.encoding is supported by iconv, NULL otherwise. * The returned value doesn't need to be freed. */ const char* getJvmOutputEncodingMB(); #endif /** * Should be called on exit to release the hashmap containing the JVM encodings. */ void disposeHashMapJvmEncoding(); /** * Clear the Jvm encoding previously cached. * This function can be called before getJvmOutputEncodingMB() to force using the encoding of the current locale. * A call to resolveJvmEncoding() may then be necessary to restore the encoding. * * @debug TRUE to print a debug message, FALSE otherwise. */ void resetJvmOutputEncoding(int debug); /** * Resolve the Java output encoding using system properties and an internal hashMap containing the supported encoding. * This function should be called prior to using getJvmOutputCodePage() and getJvmOutputEncodingMB() * * @javaVersion current java version * @jvmVendor current java implementation (Oracle, IBM, etc.) * * @return TRUE if there is any error (misconfiguration of system properties or unsuported encoding), FALSE otherwise. */ int resolveJvmEncoding(int javaVersion, int jvmVendor); #endif wrapper_3.5.51_src/src/c/wrapper_file.c100644 0 0 101435 14333053650 15247 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * Author: * Tanuki Software Development Team */ #include #include #include #ifdef WIN32 #include #include #include #include #else #include #include #include #include #include #endif #include "wrapper_file.h" #include "logger.h" #ifndef TRUE #define TRUE -1 #endif #ifndef FALSE #define FALSE 0 #endif #define MAX_INCLUDE_DEPTH 10 /** * Tests whether a file exists. * * @return TRUE if exists, FALSE otherwise. */ int wrapperFileExists(const TCHAR * filename) { FILE * file; if ((file = _tfopen(filename, TEXT("r")))) { fclose(file); return TRUE; } return FALSE; } #ifdef WIN32 /** * @param path to check. * @param advice 0 if advice should be displayed. * * @return advice or advice + 1 if advice was logged. */ int wrapperGetUNCFilePath(const TCHAR *path, int advice) { TCHAR drive[4]; DWORD result; /* See if the path starts with a drive. Some users use forward slashes in the paths. */ if ((path != NULL) && (_tcslen(path) >= 3) && (path[1] == TEXT(':')) && ((path[2] == TEXT('\\')) || (path[2] == TEXT('/')))) { _tcsncpy(drive, path, 2); drive[2] = TEXT('\\'); drive[3] = TEXT('\0'); result = GetDriveType(drive); if (result == DRIVE_REMOTE) { if (advice == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("The following path in your Wrapper configuration file is to a mapped Network\n Drive. Using mapped network drives is not recommended as they will fail to\n be resolved correctly under certain circumstances. Please consider using\n UNC paths (\\\\\\\\path). Additional references will be ignored.\n Path: %s"), path); advice++; } } else if (result == DRIVE_NO_ROOT_DIR) { if (advice == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("The following path in your Wrapper configuration file could not be resolved.\n Please make sure the path exists. If the path is a network share, it may be\n that the current user is unable to resolve it. Please consider using UNC\n paths (\\\\\\\\path) or run the service as another user\n (see wrapper.ntservice.account). Additional references will be ignored.\n Path: %s"), path); advice++; } } } return advice; } #endif /** * This function searches for the last directory separator in the given string * and returns a pointer to its location + 1, or simply returns the string if * no separator is found. It does no malloc. * * NOTE: special cases to be supported: * - path/filename.ext\" => filename.ext\" (the final quote should be kept) * - filename.ext\" => filename.ext\" */ const TCHAR* getFileName(const TCHAR* path) { const TCHAR* ptr1; #ifdef WIN32 const TCHAR* ptr2; #endif if (path) { ptr1 = _tcsrchr(path, TEXT('/')); #ifdef WIN32 ptr2 = _tcsrchr(path, TEXT('\\')); if (!ptr1 && !ptr2) { return path; } else if (!ptr1) { return ptr2 + 1; } else if (!ptr2) { return ptr1 + 1; } else if (&ptr2 > &ptr1) { return ptr2 + 1; #else if (!ptr1) { return path; #endif } else { return ptr1 + 1; } } return NULL; } /** * Read configuration file. */ int configFileReader_Read(ConfigFileReader *reader, const TCHAR *filename, int fileRequired, int depth, const TCHAR *parentFilename, int parentLineNumber, const TCHAR *originalWorkingDir, PHashMap warnedVarMap, PHashMap ignoreVarMap, int logWarnings, int logWarningLogLevel) { FILE *stream; char bufferMB[MAX_PROPERTY_NAME_VALUE_LENGTH]; TCHAR expBuffer[MAX_PROPERTY_NAME_VALUE_LENGTH]; TCHAR *trimmedBuffer; size_t trimmedBufferLen; TCHAR *c; TCHAR *d; size_t i, j; size_t len; int quoted; TCHAR *absoluteBuffer; int hadBOM; int lineNumber; char *encodingMB; #ifdef WIN32 int encoding; #else char* encoding; char* interumEncoding; TCHAR* encodingW = NULL; TCHAR* interumEncodingW = NULL; size_t size; #endif int includeRequired; int readResult = CONFIG_FILE_READER_SUCCESS; int ret; TCHAR *bufferW; size_t bufferWSize; #ifdef WIN32 int size; #endif int logLevelOnOverwrite; #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("configFileReader_Read('%s', required %d, depth %d, parent '%s', number %d, debugIncludes %d, preload %d)"), filename, fileRequired, depth, (parentFilename ? parentFilename : TEXT("")), parentLineNumber, reader->debugIncludes, reader->preload ); #endif /* Look for the specified file. */ if ((stream = _tfopen(filename, TEXT("rb"))) == NULL) { /* Unable to open the file. */ if (reader->debugIncludes || fileRequired) { if (depth > 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%sIncluded configuration file not found: %s\n Referenced from: %s (line %d)\n Current working directory: %s"), (reader->debugIncludes ? TEXT(" ") : TEXT("")), filename, parentFilename, parentLineNumber, originalWorkingDir); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Configuration file not found: %s\n Current working directory: %s"), filename, originalWorkingDir); } } else { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Configuration file not found: %s"), filename); #endif } return CONFIG_FILE_READER_OPEN_FAIL; } if (reader->debugIncludes) { if (!reader->preload) { if (depth > 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Reading included configuration file, %s"), filename); } else { /* Will not actually get here because the debug includes can't be set until it is loaded. log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Reading configuration file, %s"), filename); */ } } } /* Load in the first row of configurations to check the encoding. */ if (fgets(bufferMB, MAX_PROPERTY_NAME_VALUE_LENGTH, stream)) { /* If the file starts with a BOM (Byte Order Marker) then we want to skip over it. */ if ((bufferMB[0] == (char)0xef) && (bufferMB[1] == (char)0xbb) && (bufferMB[2] == (char)0xbf)) { i = 3; hadBOM = TRUE; } else { i = 0; hadBOM = FALSE; } /* Does the file start with "#encoding="? */ if ((bufferMB[i++] == '#') && (bufferMB[i++] == 'e') && (bufferMB[i++] == 'n') && (bufferMB[i++] == 'c') && (bufferMB[i++] == 'o') && (bufferMB[i++] == 'd') && (bufferMB[i++] == 'i') && (bufferMB[i++] == 'n') && (bufferMB[i++] == 'g') && (bufferMB[i++] == '=')) { encodingMB = bufferMB + i; i = 0; while ((encodingMB[i] != ' ') && (encodingMB[i] != '\n') && (encodingMB[i] != '\r')) { i++; } encodingMB[i] = '\0'; if ((hadBOM) && (strIgnoreCaseCmp(encodingMB, "UTF-8") != 0)) { } if (getEncodingByName(encodingMB, &encoding) == TRUE) { fclose(stream); return CONFIG_FILE_READER_FAIL; } } else { #ifdef WIN32 encoding = GetACP(); #else encoding = nl_langinfo(CODESET); #ifdef MACOSX if (strlen(encoding) == 0) { encoding = "UTF-8"; } #endif #endif } } else { /* Failed to read the first line of the file. */ #ifdef WIN32 encoding = GetACP(); #else encoding = nl_langinfo(CODESET); #ifdef MACOSX if (strlen(encoding) == 0) { encoding = "UTF-8"; } #endif #endif } fclose(stream); if ((stream = _tfopen(filename, TEXT("rb"))) == NULL) { /* Unable to open the file. */ if (reader->debugIncludes || fileRequired) { if (depth > 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%sIncluded configuration file, %s, was not found."), (reader->debugIncludes ? TEXT(" ") : TEXT("")), filename); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Configuration file, %s, was not found."), filename); } } else { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Configuration file not found: %s"), filename); #endif } return CONFIG_FILE_READER_FAIL; } if (depth == 0) { /* Default to AUTO (= -1). */ reader->logLevelOnOverwrite = -1; } /* Read all of the configurations */ lineNumber = 1; do { c = (TCHAR*)fgets(bufferMB, MAX_PROPERTY_NAME_VALUE_LENGTH, stream); if (c != NULL) { /* First check if this line should be skipped (in some modes we know that passwords are not used, so we want to avoid store them in memory) */ if (reader->readFilterCallback && !reader->readFilterCallback(bufferMB)) { /* Fill the buffer with 0 to ensure nothing remains in memory. */ wrapperSecureZero(bufferMB, sizeof(bufferMB)); #ifdef _DEBUG printf("configFileReader_Read - address of bufferMB[0] (%c): %p\n", bufferMB[0], &(bufferMB[0])); printf("configFileReader_Read - address of bufferMB[1] (%c): %p\n", bufferMB[1], &(bufferMB[1])); #endif lineNumber++; continue; } #ifdef WIN32 ret = multiByteToWideChar(bufferMB, encoding, &bufferW, TRUE); #else interumEncoding = nl_langinfo(CODESET); #ifdef MACOSX if (strlen(interumEncoding) == 0) { interumEncoding = "UTF-8"; } #endif ret = multiByteToWideChar(bufferMB, encoding, interumEncoding, &bufferW, TRUE); #endif if (ret) { if (bufferW) { /* bufferW contains an error message. */ if (!reader->preload) { if (depth > 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("%sIncluded configuration file, %s, contains a problem on line #%d and could not be read. (%s)"), (reader->debugIncludes ? TEXT(" ") : TEXT("")), filename, lineNumber, bufferW); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Configuration file, %s, contains a problem on line #%d and could not be read. (%s)"), filename, lineNumber, bufferW); } #ifndef WIN32 /* On Windows there is no such problem because wide chars are always UTF-16 which should support most characters. */ size = mbstowcs(NULL, encoding, 0); if (size > (size_t)0) { encodingW = malloc(sizeof(TCHAR) * (size + 1)); if (encodingW) { mbstowcs(encodingW, encoding, size + 1); } } size = mbstowcs(NULL, interumEncoding, 0); if (size > (size_t)0) { interumEncodingW = malloc(sizeof(TCHAR) * (size + 1)); if (interumEncodingW) { mbstowcs(interumEncodingW, interumEncoding, size + 1); } } if (!compareEncodingsSysMode(encodingW, interumEncodingW)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("One possible cause of failure is when the encoding of the locale used by the\n Wrapper doesn't support certain characters of the configuration file.\n The encoding of the current locale is '%s'. It is advised to set it\n identical to the encoding of the configuration file (%s),\n either by changing the default system locale or by using the wrapper.lang and\n wrapper.lang..encoding properties."), interumEncodingW, encodingW); } free(encodingW); free(interumEncodingW); #endif } free(bufferW); } else { outOfMemory(TEXT("RCF"), 1); } if (reader->preload) { /* On preload, ignore the line and continue. We want to load as much as possible and hopefully load the appropriate locale to read the file on the second load. */ lineNumber++; continue; } fclose(stream); wrapperSecureZero(bufferMB, sizeof(bufferMB)); return CONFIG_FILE_READER_HARD_FAIL; } /* Store the size of the buffer because the callback will insert a 0 after any valid property name. */ bufferWSize = _tcslen(bufferW) * sizeof(TCHAR); #ifdef _DEBUG /* The line feeds are not yet stripped here. */ /* #ifdef WIN32 wprintf(TEXT("%s:%d (%d): [%s]\n"), filename, lineNumber, encoding, bufferW); #else wprintf(TEXT("%S:%d (%s to %s): [%S]\n"), filename, lineNumber, encoding, interumEncoding, bufferW); #endif */ #endif c = bufferW; /* Always strip both ^M and ^J off the end of the line, this is done rather * than simply checking for \n so that files will work on all platforms * even if their line feeds are incorrect. */ if ((d = _tcschr(bufferW, 0x0d /* ^M */)) != NULL) { d[0] = TEXT('\0'); } if ((d = _tcschr(bufferW, 0x0a /* ^J */)) != NULL) { d[0] = TEXT('\0'); } /* Strip any whitespace from the front of the line. */ trimmedBuffer = bufferW; while ((trimmedBuffer[0] == TEXT(' ')) || (trimmedBuffer[0] == TCHAR_TAB)) { trimmedBuffer++; } /* If the line does not start with a comment, make sure that * any comment at the end of line are stripped. If at any point, a * double hash, '##', is encountered it should be interpreted as a * hash in the actual property rather than the beginning of a comment. */ if (trimmedBuffer[0] != TEXT('#')) { len = _tcslen(trimmedBuffer); i = 0; quoted = 0; while (i < len) { if (trimmedBuffer[i] == TEXT('"')) { quoted = !quoted; } else if ((trimmedBuffer[i] == TEXT('#')) && (!quoted)) { /* Checking the next character will always be ok because it will be * '\0 at the end of the string. */ if (trimmedBuffer[i + 1] == TEXT('#')) { /* We found an escaped #. Shift the rest of the string * down by one character to remove the second '#'. * Include the shifting of the '\0'. */ for (j = i + 1; j <= len; j++) { trimmedBuffer[j - 1] = trimmedBuffer[j]; } len--; } else { /* We found a comment. So this is the end. */ trimmedBuffer[i] = TEXT('\0'); len = i; } } i++; } } /* Strip any whitespace from the end of the line. */ trimmedBufferLen = _tcslen(trimmedBuffer); while ((trimmedBufferLen > 0) && ((trimmedBuffer[trimmedBufferLen - 1] == TEXT(' ')) || (trimmedBuffer[trimmedBufferLen - 1] == TCHAR_TAB))) { trimmedBuffer[--trimmedBufferLen] = TEXT('\0'); } /* Only look at lines which contain data and do not start with a '#' * If the line starts with '#include' then recurse to the include file */ if (_tcslen(trimmedBuffer) > 0) { if (trimmedBuffer[0] != TEXT('#')) { /*_tprintf(TEXT("%s\n"), trimmedBuffer);*/ if (!(*reader->callback)(reader->callbackParam, filename, lineNumber, depth, trimmedBuffer, reader->exitOnOverwrite, reader->logLevelOnOverwrite)) { readResult = CONFIG_FILE_READER_HARD_FAIL; break; } } else { /* so the line starts with a # */ if (reader->enableIncludes && strcmpIgnoreCase(trimmedBuffer, TEXT("#include.debug")) == 0) { /* Enable include file debugging. */ if (reader->preload == FALSE) { reader->debugIncludes = TRUE; if (depth == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Base configuration file is %s"), filename); } } else { reader->debugIncludes = FALSE; } } else if (reader->enableIncludes && ((_tcsstr(trimmedBuffer, TEXT("#include ")) == trimmedBuffer) || (_tcsstr(trimmedBuffer, TEXT("#include.required ")) == trimmedBuffer))) { if (_tcsstr(trimmedBuffer, TEXT("#include.required ")) == trimmedBuffer) { /* The include file is required. */ includeRequired = TRUE; c = trimmedBuffer + 18; } else { /* Include file, if the file does not exist, then ignore it */ includeRequired = FALSE; c = trimmedBuffer + 9; } /* Strip any leading whitespace */ while ((c[0] != TEXT('\0')) && (c[0] == TEXT(' '))) { c++; } /* The filename may contain environment variables, so expand them. */ if (reader->debugIncludes) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Found #include file in %s: %s"), filename, c); } evaluateEnvironmentVariables(c, expBuffer, MAX_PROPERTY_NAME_VALUE_LENGTH, logWarnings, warnedVarMap, logWarningLogLevel, ignoreVarMap, NULL); if (reader->debugIncludes && (_tcscmp(c, expBuffer) != 0)) { /* Only show this log if there were any environment variables. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" After environment variable replacements: %s"), expBuffer); } /* Now obtain the real absolute path to the include file. */ #ifdef WIN32 /* Find out how big the absolute path will be */ size = GetFullPathName(expBuffer, 0, NULL, NULL); /* Size includes '\0' */ if (!size) { if (reader->debugIncludes || includeRequired) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Unable to resolve the full path of included configuration file: %s (%s)\n Referenced from: %s (line %d)\n Current working directory: %s"), expBuffer, getLastErrorText(), filename, lineNumber, originalWorkingDir); } absoluteBuffer = NULL; } else { absoluteBuffer = malloc(sizeof(TCHAR) * size); if (!absoluteBuffer) { outOfMemory(TEXT("RCF"), 2); } else { if (!GetFullPathName(expBuffer, size, absoluteBuffer, NULL)) { if (reader->debugIncludes || includeRequired) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Unable to resolve the full path of included configuration file: %s (%s)\n Referenced from: %s (line %d)\n Current working directory: %s"), expBuffer, getLastErrorText(), filename, lineNumber, originalWorkingDir); } free(absoluteBuffer); absoluteBuffer = NULL; } } } #else absoluteBuffer = malloc(sizeof(TCHAR) * (PATH_MAX + 1)); if (!absoluteBuffer) { outOfMemory(TEXT("RCF"), 3); } else { if (_trealpathN(expBuffer, absoluteBuffer, PATH_MAX + 1) == NULL) { if (reader->debugIncludes || includeRequired) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Unable to resolve the full path of included configuration file: %s (%s)\n Referenced from: %s (line %d)\n Current working directory: %s"), expBuffer, getLastErrorText(), filename, lineNumber, originalWorkingDir); } free(absoluteBuffer); absoluteBuffer = NULL; } } #endif if (absoluteBuffer) { if (depth < MAX_INCLUDE_DEPTH) { readResult = configFileReader_Read(reader, absoluteBuffer, includeRequired, depth + 1, filename, lineNumber, originalWorkingDir, warnedVarMap, ignoreVarMap, logWarnings, logWarningLogLevel); if (readResult == CONFIG_FILE_READER_SUCCESS) { /* Ok continue. */ } else if ((readResult == CONFIG_FILE_READER_FAIL) || (readResult == CONFIG_FILE_READER_HARD_FAIL) || (readResult == CONFIG_FILE_READER_OPEN_FAIL)) { /* Failed. */ if (includeRequired) { /* Include file was required, but we failed to read it. */ if (!reader->preload) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("%sThe required configuration file, %s, was not loaded.\n%s Referenced from: %s (line %d)"), (reader->debugIncludes ? TEXT(" ") : TEXT("")), absoluteBuffer, (reader->debugIncludes ? TEXT(" ") : TEXT("")), filename, lineNumber); } readResult = CONFIG_FILE_READER_HARD_FAIL; } if (readResult == CONFIG_FILE_READER_HARD_FAIL) { /* Can't continue. */ break; } else { /* Failed but continue. */ readResult = CONFIG_FILE_READER_SUCCESS; } } else { _tprintf(TEXT("Unexpected load error %d\n"), readResult); /* continue. */ readResult = CONFIG_FILE_READER_SUCCESS; } } else { if (reader->debugIncludes) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Unable to include configuration file, %s, because the max include depth was reached."), absoluteBuffer); } } free(absoluteBuffer); } else { if (includeRequired) { /* Include file was required, but we failed to read it. */ if (!reader->preload) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("%sThe required configuration file, %s, was not read.\n%s Referenced from: %s (line %d)"), (reader->debugIncludes ? TEXT(" ") : TEXT("")), expBuffer, (reader->debugIncludes ? TEXT(" ") : TEXT("")), filename, lineNumber); } readResult = CONFIG_FILE_READER_HARD_FAIL; break; } } } else if (_tcsstr(trimmedBuffer, TEXT("#properties.")) == trimmedBuffer) { if(_tcsstr(trimmedBuffer, TEXT("#properties.on_overwrite.exit=")) == trimmedBuffer) { trimmedBuffer += 30; if (_tcsicmp(trimmedBuffer, TEXT("TRUE")) == 0) { reader->exitOnOverwrite = TRUE; } else if (_tcsicmp(trimmedBuffer, TEXT("FALSE")) == 0) { reader->exitOnOverwrite = FALSE; } else if (!reader->preload) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Encountered an invalid boolean value for directive #properties.on_overwrite.exit=%s (line %d). Ignoring this directive."), trimmedBuffer, lineNumber); } } else if (_tcsstr(trimmedBuffer, TEXT("#properties.on_overwrite.loglevel=")) == trimmedBuffer) { trimmedBuffer += 34; if (_tcsicmp(trimmedBuffer, TEXT("AUTO")) == 0) { reader->logLevelOnOverwrite = -1; } else { logLevelOnOverwrite = getLogLevelForName(trimmedBuffer); if (logLevelOnOverwrite >= LEVEL_NONE ) { /* At least log with LEVEL_DEBUG to help support. */ reader->logLevelOnOverwrite = LEVEL_DEBUG; } else if (logLevelOnOverwrite != LEVEL_UNKNOWN) { reader->logLevelOnOverwrite = logLevelOnOverwrite; } else if (!reader->preload) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Encountered an invalid value for directive #properties.on_overwrite.loglevel=%s (line %d). Ignoring this directive."), trimmedBuffer, lineNumber); } } } else if (strcmpIgnoreCase(trimmedBuffer, TEXT("#properties.debug")) == 0) { reader->logLevelOnOverwrite = LEVEL_STATUS; } } else if (_tcsstr(trimmedBuffer, TEXT("#log_messages.buffer_size=")) == trimmedBuffer) { trimmedBuffer += 26; setThreadMessageBufferInitialSize(_ttoi(trimmedBuffer)); } } } /* Always free each line read. */ wrapperSecureFree(bufferW, bufferWSize); } lineNumber++; } while (c != NULL); /* Always fill the buffer with 0 when finished. */ wrapperSecureZero(bufferMB, sizeof(bufferMB)); /* Call the callback after reading the file completely in order to copy reader->exitOnOverwrite & reader->logLevelOnOverwrite into the properties structure. * (we want to keep these values after preload for logging potential problems on properties defined in the command line) * This is needed if directives are set at the end of the file with no properties after. */ if (reader->preload) { if (!(*reader->callback)(reader->callbackParam, NULL, -1, depth, NULL, reader->exitOnOverwrite, reader->logLevelOnOverwrite)) { readResult = CONFIG_FILE_READER_HARD_FAIL; } } /* Close the file */ fclose(stream); return readResult; } /** * Reads configuration lines from the file `filename' and calls `callback' with the line and * `callbackParam' specified to its arguments. * * @param filename Name of configuration file to read. * @param fileRequired TRUE if the file specified by filename is required, FALSE if a missing * file will silently fail. * @param callback Pointer to a callback function which will be called for each line read. * @param callbackParam Pointer to additional user data which will be passed to the callback. * @param readFilterCallback Pointer to a callback funtion which will be used to filter some * lines that should not be processed. * @param enableIncludes If TRUE then includes will be supported. * @param preload TRUE if this is being called in the preload step meaning that all errors * should be suppressed. * @param originalWorkingDir Working directory of the binary at the moment it was launched. * @param warnedVarMap Map of undefined environment variables for which the user was warned. * @param ignoreVarMap Map of environment variables that should not be expanded. * @param logWarnings Flag that controls whether or not warnings will be logged. * @param logWarningLogLevel Log level at which any log warnings will be logged. * * @return CONFIG_FILE_READER_SUCCESS if the file was read successfully, * CONFIG_FILE_READER_OPEN_FAIL if the file could not be found or opened. * CONFIG_FILE_READER_FAIL if there were any problems at all, or * CONFIG_FILE_READER_HARD_FAIL if the problem should cascaded all the way up. */ int configFileReader(const TCHAR *filename, int fileRequired, ConfigFileReader_Callback callback, void *callbackParam, ConfigFileReader_ReadFilterCallbackMB readFilterCallback, int enableIncludes, int preload, const TCHAR *originalWorkingDir, PHashMap warnedVarMap, PHashMap ignoreVarMap, int logWarnings, int logWarningLogLevel) { ConfigFileReader reader; /* Initialize the reader. */ reader.callback = callback; reader.callbackParam = callbackParam; reader.readFilterCallback = readFilterCallback; reader.enableIncludes = enableIncludes; reader.preload = preload; reader.debugIncludes = FALSE; reader.exitOnOverwrite = FALSE; reader.logLevelOnOverwrite = LEVEL_NONE; /* on preload, don't log anything. */ return configFileReader_Read(&reader, filename, fileRequired, 0, NULL, 0, originalWorkingDir, warnedVarMap, ignoreVarMap, logWarnings, logWarningLogLevel); } wrapper_3.5.51_src/src/c/wrapper_file.h100644 0 0 10403 14333053650 15226 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * Author: * Tanuki Software Development Team */ #ifndef _WRAPPER_FILE_H #define _WRAPPER_FILE_H #ifdef WIN32 #include #endif #include "property.h" #include "wrapper_i18n.h" #include "wrapper_hashmap.h" /*#define WRAPPER_FILE_DEBUG*/ /** * Callback declaration which can be passed to calls to configFileReader. */ typedef int (*ConfigFileReader_Callback)(void *param, const TCHAR *fileName, int lineNumber, int depth, TCHAR *config, int exitOnOverwrite, int logLevelOnOverwrite); /* Structure used by configFileReader to read files. */ typedef struct ConfigFileReader ConfigFileReader; struct ConfigFileReader { ConfigFileReader_Callback callback; ConfigFileReader_ReadFilterCallbackMB readFilterCallback; void *callbackParam; int enableIncludes; int preload; int debugIncludes; /* debugIncludes controls whether or not debug output is logged. It is set using directives in the file being read. */ int exitOnOverwrite; /* If TRUE, causes the wrapper to exit when any property is overwritten in the config files. */ int logLevelOnOverwrite; /* Defines the log level of the messages reported when properties are overwritten. */ }; /** * Tests whether a file exists. * * @return TRUE if exists, FALSE otherwise. */ extern int wrapperFileExists(const TCHAR * filename); #ifdef WIN32 extern int wrapperGetUNCFilePath(const TCHAR *path, int advice); #endif extern const TCHAR* getFileName(const TCHAR* path); #ifdef WRAPPER_FILE_DEBUG extern void wrapperFileTests(); #endif #define CONFIG_FILE_READER_SUCCESS 101 #define CONFIG_FILE_READER_FAIL 102 #define CONFIG_FILE_READER_HARD_FAIL 103 #define CONFIG_FILE_READER_OPEN_FAIL 104 /** * Reads configuration lines from the file `filename' and calls `callback' with the line and * `callbackParam' specified to its arguments. * * @param filename Name of configuration file to read. * @param fileRequired TRUE if the file specified by filename is required, FALSE if a missing * file will silently fail. * @param callback Pointer to a callback funtion which will be called for each line read. * @param callbackParam Pointer to additional user data which will be passed to the callback. * @param readFilterCallback Pointer to a callback funtion which will be used to filter some * lines that should not be processed. * @param enableIncludes If TRUE then includes will be supported. * @param preload TRUE if this is being called in the preload step meaning that all errors * should be suppressed. * @param originalWorkingDir Working directory of the binary at the moment it was launched. * @param warnedVarMap Map of undefined environment variables for which the user was warned. * @param ignoreVarMap Map of environment variables that should not be expanded. * @param logWarnings Flag that controls whether or not warnings will be logged. * @param logWarningLogLevel Log level at which any log warnings will be logged. * * @return CONFIG_FILE_READER_SUCCESS if the file was read successfully, * CONFIG_FILE_READER_FAIL if there were any problems at all, or * CONFIG_FILE_READER_HARD_FAIL if the problem should cascaded all the way up. */ extern int configFileReader(const TCHAR *filename, int fileRequired, ConfigFileReader_Callback callback, void *callbackParam, ConfigFileReader_ReadFilterCallbackMB readFilterCallback, int enableIncludes, int preload, const TCHAR *originalWorkingDir, PHashMap warnedVarMap, PHashMap ignoreVarMap, int logWarnings, int logWarningLogLevel); #endif wrapper_3.5.51_src/src/c/wrapper_hashmap.c100644 0 0 35340 14333053650 15732 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ #ifdef WIN32 #include #include #include #endif #include #include "wrapper_hashmap.h" #include "wrapper_i18n.h" #ifndef TRUE #define TRUE -1 #endif #ifndef FALSE #define FALSE 0 #endif /** * Compare two memory blocks. * * @param vA First memory block. * @param vALen Size of first memory block. * @param vB Second memory block. * @param vBLen Size of second memory block. * * @return -1 if vA is smaller, 0 if equal, 1 if vA is greater than vB. */ int memcmpHM(const void *vA, size_t vALen, const void *vB, size_t vBLen) { size_t i; unsigned char cA, cB; for (i = 0; (i < vALen) && (i < vBLen); i++) { cA = ((unsigned char *)vA)[i]; cB = ((unsigned char *)vB)[i]; if (cA < cB) { return -1; } else if (cA > cB) { return 1; } } /* Lengths are different. */ if (vALen < vBLen) { return -1; } else if (vALen > vBLen ) { return 1; } else { return 0; } } /** * Frees up any memory used by a HashMap. Any values returned from the HashMap will also * cease to be valid. * * @param hashMap HashMap to be freed. */ void freeHashMap(PHashMap hashMap) { int i; PHashBucket bucket; PHashEntry thisEntry; PHashEntry nextEntry; if (!hashMap) { return; } if (hashMap->buckets) { for (i = 0; i < hashMap->bucketCount; i++) { bucket = hashMap->buckets[i]; /* Free up the entries */ thisEntry = bucket->firstEntry; while (thisEntry) { nextEntry = thisEntry->nextEntry; /* Free up the entry. */ if (thisEntry->key) { free(thisEntry->key); } if (thisEntry->value) { free(thisEntry->value); } free(thisEntry); thisEntry = nextEntry; } /* Free up the bucket. */ free(bucket); } free(hashMap->buckets); } free(hashMap); } /** * Creates an empty HashMap with the specified number of buckets. * * @param bucketCount The number of buckets. The search within a bucket is * linear so it should be large enough to store all data * without too many items in each bucket. * * @return The new HashMap. */ PHashMap newHashMap(int bucketCount) { int i; PHashMap hashMap; PHashBucket bucket; /* Build up an empty HashMap. Be careful about clearing memory so a freeHashMap call will always work. */ hashMap = malloc(sizeof(HashMap)); if (!hashMap) { _tprintf(TEXT("Out of memory (%s)\n"), TEXT("NHM1")); return NULL; } memset(hashMap, 0, sizeof(HashMap)); hashMap->bucketCount = bucketCount; hashMap->buckets = malloc(sizeof(PHashBucket) * bucketCount); if (!hashMap->buckets) { _tprintf(TEXT("Out of memory (%s)\n"), TEXT("NHM2")); freeHashMap(hashMap); return NULL; } memset(hashMap->buckets, 0, sizeof(PHashBucket) * bucketCount); for (i = 0; i < hashMap->bucketCount; i++) { bucket = malloc(sizeof(HashBucket)); if (!bucket) { _tprintf(TEXT("Out of memory (%s)\n"), TEXT("NHM3")); freeHashMap(hashMap); return NULL; } memset(bucket, 0, sizeof(HashBucket)); hashMap->buckets[i] = bucket; } return hashMap; } /** * Calculate a bucketId based on the key. * * @param hashMap HashMap for which the bucket Id is being calculated. * @param key Key whose bucket Id is being calculated. * * @return The bucket Id. */ int getBucketId(PHashMap hashMap, const TCHAR *key) { size_t len = _tcslen(key); size_t i; TCHAR hash = 0; /* Loop over the characters and add all of the characters together. */ for (i = 0; i < len; i++) { hash = (hash + key[i]) & 0xffff; } return hash % hashMap->bucketCount; } /** * Puts a value into the HashMap. The key and value will both be cloned. * * @param hashMap HashMap to store the value into. * @param key The key to reference the value. * @param keySize The size of the key. * @param value The value to store. * @param valueSize The size of the value. * * @return TRUE if there were any problems. */ int hashMapPutKVVV(PHashMap hashMap, const void *key, size_t keySize, const void *value, size_t valueSize) { void *keyCopy; void *valueCopy; int bucketId; PHashBucket bucket; PHashEntry *thisEntryLoc; PHashEntry thisEntry; int cmp; PHashEntry newEntry; #ifdef _DEBUG_HASHMAP_DETAILED _tprintf(TEXT("hashMapPutKVVV(%p, %p, %d, %p, %d)\n"), hashMap, key, keySize, value, valueSize); #endif /* First create copies of the items being stored. */ keyCopy = malloc(keySize); if (!keyCopy) { _tprintf(TEXT("Out of memory (%s)\n"), TEXT("HMP1")); return TRUE; } memcpy(keyCopy, key, keySize); valueCopy = malloc(valueSize); if (!valueCopy) { _tprintf(TEXT("Out of memory (%s)\n"), TEXT("HMP2")); free(keyCopy); return TRUE; } memcpy(valueCopy, value, valueSize); /* Locate the bucket where the value should be stored. */ bucketId = getBucketId(hashMap, key); bucket = hashMap->buckets[bucketId]; #ifdef _DEBUG_HASHMAP_DETAILED _tprintf(TEXT(" keyCopy=%p, valueCopy=%p, bucketId=%d\n"), keyCopy, valueCopy, bucketId); #endif /* Figure out where in the bucket to store the value. */ thisEntryLoc = &(bucket->firstEntry); thisEntry = bucket->firstEntry; while (thisEntry) { cmp = memcmpHM(thisEntry->key, thisEntry->keySize, key, keySize); if (cmp > 0) { /* This entry's key is bigger, so we should be before it. */ newEntry = malloc(sizeof(HashEntry)); if (!newEntry) { _tprintf(TEXT("Out of memory (%s)\n"), TEXT("HMP3")); free(keyCopy); free(valueCopy); return TRUE; } newEntry->key = keyCopy; newEntry->keySize = keySize; newEntry->value = valueCopy; newEntry->valueSize = valueSize; newEntry->nextEntry = thisEntry; *thisEntryLoc = newEntry; bucket->size++; hashMap->size++; #ifdef _DEBUG_HASHMAP_DETAILED _tprintf(TEXT(" inserted entry -> bucketSize=%d hashMapSize=%d\n"), bucket->size, hashMap->size); #endif return FALSE; } else if (cmp == 0) { /* This is the exact same key so we are replacing the value. */ free(thisEntry->value); thisEntry->value = valueCopy; thisEntry->valueSize = valueSize; free(keyCopy); /* Not needed. */ #ifdef _DEBUG_HASHMAP_DETAILED _tprintf(TEXT(" replaced entry -> bucketSize=%d hashMapSize=%d\n"), bucket->size, hashMap->size); #endif return FALSE; } else { /* This entry's key is smaller so we should be after it. Keep looking. */ thisEntryLoc = &(thisEntry->nextEntry); thisEntry = thisEntry->nextEntry; } } /* If we get here then we need to append our value to the end. */ newEntry = malloc(sizeof(HashEntry)); if (!newEntry) { _tprintf(TEXT("Out of memory (%s)\n"), TEXT("HMP4")); free(keyCopy); free(valueCopy); return TRUE; } newEntry->key = keyCopy; newEntry->keySize = keySize; newEntry->value = valueCopy; newEntry->valueSize = valueSize; newEntry->nextEntry = NULL; /* This is the end. */ *thisEntryLoc = newEntry; bucket->size++; hashMap->size++; #ifdef _DEBUG_HASHMAP_DETAILED _tprintf(TEXT(" append entry -> bucketSize=%d hashMapSize=%d\n"), bucket->size, hashMap->size); #endif return FALSE; } /** * Puts a value into the HashMap. The key and value will both be cloned. * * @param hashMap HashMap to store the value into. * @param key The key to reference the value. * @param value The value to store. */ void hashMapPutKWVW(PHashMap hashMap, const TCHAR *key, const TCHAR *value) { size_t keySize = sizeof(TCHAR) * (_tcslen(key) + 1); size_t valueSize = sizeof(TCHAR) * (_tcslen(value) + 1); #ifdef _DEBUG_HASHMAP /* Can't use gtSTATIC here because of recursion. */ #if defined(UNICODE) && !defined(WIN32) _tprintf(TEXT("hashMapPutKWVW(map, \"%S\", \"%S\")\n"), key, value); #else _tprintf(TEXT("hashMapPutKWVW(map, \"%s\", \"%s\")\n"), key, value); #endif #endif hashMapPutKVVV(hashMap, key, keySize, value, valueSize); } /** * Puts a value into the HashMap. The key and value will both be cloned. * * @param hashMap HashMap to store the value into. * @param key The key to reference the value. * @param value The value to store. */ void hashMapPutKMBVW(PHashMap hashMap, const char *key, const TCHAR *value) { size_t keySize = sizeof(char) * (strlen(key) + 1); size_t valueSize = sizeof(TCHAR) * (_tcslen(value) + 1); hashMapPutKVVV(hashMap, key, keySize, value, valueSize); } /** * Puts an integer value into the HashMap. * Note: Avoid putting 0 in the hashtable as hashMapGetKWVI() also returns 0 if the key was not found. * * @param hashMap HashMap to store the value into. * @param key The key to reference the value. * @param value The value to store. */ void hashMapPutKWVI(PHashMap hashMap, const TCHAR *key, int value) { size_t keySize = sizeof(TCHAR) * (_tcslen(key) + 1); size_t valueSize = sizeof(int); hashMapPutKVVV(hashMap, key, keySize, &value, valueSize); } /** * Puts a value into the HashMap. The key and value will both be cloned. * * @param hashMap HashMap to store the value into. * @param key The key to reference the value. * @param value The value to store. */ void hashMapPutKIVW(PHashMap hashMap, int key, const TCHAR *value) { size_t keySize = sizeof(int); size_t valueSize = sizeof(TCHAR) * (_tcslen(value) + 1); hashMapPutKVVV(hashMap, &key, keySize, value, valueSize); } /** * Gets a value from the HashMap. * * @param hashMap HashMap from which to lookup the value. * @param key Key of the value being looked up. * @param keySize Size of the key. * @param valueSize Pointer to a size_t which will store the size of the returned value if non-NULL. * * @return a reference to the value. It should not be modified or freed. */ const void *hashMapGetKVVV(PHashMap hashMap, const void *key, size_t keySize, size_t *valueSize) { int bucketId; PHashBucket bucket; PHashEntry thisEntry; int cmp; /* Initialize the return size. */ if (valueSize) { *valueSize = 0; } /* Locate the bucket where the value should be stored. */ bucketId = getBucketId(hashMap, key); bucket = hashMap->buckets[bucketId]; /* Figure out where in the bucket to store the value. */ thisEntry = bucket->firstEntry; while (thisEntry) { cmp = memcmpHM(thisEntry->key, thisEntry->keySize, key, keySize); if (cmp > 0) { /* This entry's key is bigger, so we are past where it should be. */ return NULL; } else if (cmp == 0) { /* This is the value we were looking for. */ if (valueSize) { *valueSize = thisEntry->valueSize; } return thisEntry->value; } else { /* Not yet. */ thisEntry = thisEntry->nextEntry; } } /* We didn't find it. */ return NULL; } /** * Gets a value from the HashMap. * * @param hashMap HashMap from which to lookup the value. * @param key Key of the value being looked up. * * @return a reference to the value. It should not be modified or freed. */ const TCHAR *hashMapGetKWVW(PHashMap hashMap, const TCHAR *key) { size_t keySize = sizeof(TCHAR) * (_tcslen(key) + 1); return (const TCHAR *)hashMapGetKVVV(hashMap, key, keySize, NULL); } /** * Gets a value from the HashMap. * * @param hashMap HashMap from which to lookup the value. * @param key Key of the value being looked up. * * @return a reference to the value. It should not be modified or freed. */ const TCHAR *hashMapGetKMBVW(PHashMap hashMap, const char *key) { size_t keySize = sizeof(char) * (strlen(key) + 1); return (const TCHAR *)hashMapGetKVVV(hashMap, key, keySize, NULL); } /** * Gets an integer value from the HashMap. * * @param hashMap HashMap from which to lookup the value. * @param key Key of the value being looked up. * * @return the integer value or 0 if the key was not found. */ int hashMapGetKWVI(PHashMap hashMap, const TCHAR *key) { const void *value; size_t keySize = sizeof(TCHAR) * (_tcslen(key) + 1); value = hashMapGetKVVV(hashMap, key, keySize, NULL); if (!value) { return 0; } return *((int*)(value)); } /** * Gets an integer value from the HashMap. * * @param hashMap HashMap from which to lookup the value. * @param key Key of the value being looked up. * * @return the integer value or 0 if the key was not found. */ const TCHAR *hashMapGetKIVW(PHashMap hashMap, int key) { size_t keySize = sizeof(int); return (const TCHAR *)hashMapGetKVVV(hashMap, &key, keySize, NULL); } #ifdef _DEBUG_HASHMAP /** * Dumps the statistics of a HashMap. */ void dumpHashMapStats(PHashMap hashMap) { int i; #ifdef _DEBUG_HASHMAP_DETAILED int j; PHashEntry entry; #endif _tprintf(TEXT("HashMap: %p\n"), hashMap); _tprintf(TEXT(" size: %d\n"), hashMap->size); _tprintf(TEXT(" bucketCount: %d\n"), hashMap->bucketCount); if (hashMap->buckets) { for (i = 0; i < hashMap->bucketCount; i++) { PHashBucket bucket = hashMap->buckets[i]; if (bucket) { _tprintf(TEXT(" bucket[%d]: size: %d\n"), i, bucket->size); #ifdef _DEBUG_HASHMAP_DETAILED j = 0; entry = bucket->firstEntry; while (entry) { _tprintf(TEXT(" entry[%d]: key=%p (size=%d) value=%p (size=%d)\n"), j, entry->key, entry->keySize, entry->value, entry->valueSize); entry = entry->nextEntry; j++; } #endif } else { _tprintf(TEXT(" bucket[%d]: NULL\n"), i); } } } } #endif wrapper_3.5.51_src/src/c/wrapper_hashmap.h100644 0 0 11551 14333053650 15735 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ #ifndef _WRAPPER_HASHMAP #define _WRAPPER_HASHMAP /*#define _DEBUG_HASHMAP */ /*#define _DEBUG_HASHMAP_DETAILED*/ #include "wrapper_i18n.h" typedef struct HashEntry HashEntry, *PHashEntry; struct HashEntry { size_t keySize; void *key; size_t valueSize; void *value; PHashEntry nextEntry; }; typedef struct { int size; PHashEntry firstEntry; } HashBucket, *PHashBucket; typedef struct { int bucketCount; int size; PHashBucket *buckets; } HashMap, *PHashMap; /** * Frees up any memory used by a HashMap. Any values returned from the HashMap will also * cease to be valid. * * @param hashMap HashMap to be freed. */ extern void freeHashMap(PHashMap hashMap); /** * Creates an empty HashMap with the specified number of buckets. * * @param bucketCount The number of buckets. The search within a bucket is * linear so it should be large enough to store all data * without too many items in each bucket. * * @return The new HashMap. */ extern PHashMap newHashMap(int bucketCount); /** * Puts a value into the HashMap. The key and value will both be cloned. * * @param hashMap HashMap to store the value into. * @param key The key to reference the value. * @param keySize The size of the key. * @param value The value to store. * @param valueSize The size of the value. * * @return TRUE if there were any problems. */ extern int hashMapPutKVVV(PHashMap hashMap, const void *key, size_t keySize, const void *value, size_t valueSize); /** * Puts a value into the HashMap. The key and value will both be cloned. * * @param hashMap HashMap to store the value into. * @param key The key to reference the value. * @param value The value to store. */ extern void hashMapPutKWVW(PHashMap hashMap, const TCHAR *key, const TCHAR *value); /** * Puts a value into the HashMap. The key and value will both be cloned. * * @param hashMap HashMap to store the value into. * @param key The key to reference the value. * @param value The value to store. */ extern void hashMapPutKMBVW(PHashMap hashMap, const char *key, const TCHAR *value); /** * Puts an integer value into the HashMap. * Note: Avoid putting 0 in the hashtable as hashMapGetKWVI() also returns 0 if the key was not found. * * @param hashMap HashMap to store the value into. * @param key The key to reference the value. * @param value The value to store. */ extern void hashMapPutKWVI(PHashMap hashMap, const TCHAR *key, int value); /** * Puts a value into the HashMap. The key and value will both be cloned. * * @param hashMap HashMap to store the value into. * @param key The key to reference the value. * @param value The value to store. */ extern void hashMapPutKIVW(PHashMap hashMap, int key, const TCHAR *value); /** * Gets a value from the HashMap. * * @param hashMap HashMap from which to lookup the value. * @param key Key of the value being looked up. * @param keySize Size of the key. * @param valueSize Pointer to a size_t which will store the size of the returned value if non-NULL. * * @return a reference to the value. It should not be modified or freed. */ extern const void *hashMapGetKVVV(PHashMap hashMap, const void *key, size_t keySize, size_t *valueSize); /** * Gets a value from the HashMap. * * @param hashMap HashMap from which to lookup the value. * @param key Key of the value being looked up. * * @return a reference to the value. It should not be modified or freed. */ extern const TCHAR *hashMapGetKWVW(PHashMap hashMap, const TCHAR *key); /** * Gets a value from the HashMap. * * @param hashMap HashMap from which to lookup the value. * @param key Key of the value being looked up. * * @return a reference to the value. It should not be modified or freed. */ extern const TCHAR *hashMapGetKMBVW(PHashMap hashMap, const char *key); /** * Gets an integer value from the HashMap. * * @param hashMap HashMap from which to lookup the value. * @param key Key of the value being looked up. * * @return the integer value or 0 if the key was not found. */ extern int hashMapGetKWVI(PHashMap hashMap, const TCHAR *key); /** * Gets an integer value from the HashMap. * * @param hashMap HashMap from which to lookup the value. * @param key Key of the value being looked up. * * @return the integer value or 0 if the key was not found. */ extern const TCHAR *hashMapGetKIVW(PHashMap hashMap, int key); #ifdef _DEBUG_HASHMAP /** * Dumps the statistics of a HashMap. */ extern void dumpHashMapStats(PHashMap hashMap); #endif #endif wrapper_3.5.51_src/src/c/wrapper_i18n.c100644 0 0 221544 14333053650 15113 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ #ifdef WIN32 #include #include #else #ifndef FREEBSD #include #endif #include #include #include #endif #include #include #include #include #include #include "logger_base.h" #ifdef FREEBSD #include "wrapperinfo.h" #endif #ifndef TRUE #define TRUE -1 #endif #ifndef FALSE #define FALSE 0 #endif /** * Dynamically load the symbols for the iconv library */ #ifdef FREEBSD typedef void *iconv_t; static iconv_t (*wrapper_iconv_open)(const char *, const char *); static size_t (*wrapper_iconv)(iconv_t, const char **, size_t *, char **, size_t *); static int (*wrapper_iconv_close)(iconv_t); static char iconvLibNameMB[128]; static TCHAR iconvLibNameW[128]; #else #define wrapper_iconv_open iconv_open #define wrapper_iconv iconv #define wrapper_iconv_close iconv_close #endif #if defined(UNICODE) && defined(WIN32) /** * @param multiByteChars The MultiByte encoded source string. * @param encoding Encoding of the MultiByte characters. * @param outputBuffer If return is TRUE then this will be an error message. If return is FALSE then this will contain the * requested WideChars string. If there were any memory problems, the return will be TRUE and the * buffer will be set to NULL. In any case, it is the responsibility of the caller to free the output * buffer memory. * @param localizeErrorMessage TRUE if the error message can be localized. * * @return TRUE if there were problems, FALSE if Ok. * */ int multiByteToWideChar(const char *multiByteChars, int encoding, TCHAR **outputBufferW, int localizeErrorMessage) { const TCHAR *errorTemplate; size_t errorTemplateLen; int req; /* Clear the output buffer as a sanity check. Shouldn't be needed. */ *outputBufferW = NULL; req = MultiByteToWideChar(encoding, MB_ERR_INVALID_CHARS, multiByteChars, -1, NULL, 0); if (req <= 0) { if (GetLastError() == ERROR_NO_UNICODE_TRANSLATION) { errorTemplate = (localizeErrorMessage ? TEXT("Invalid multibyte sequence.") : TEXT("Invalid multibyte sequence.")); errorTemplateLen = _tcslen(errorTemplate) + 1; *outputBufferW = malloc(sizeof(TCHAR) * errorTemplateLen); if (*outputBufferW) { _sntprintf(*outputBufferW, errorTemplateLen, TEXT("%s"), errorTemplate); } else { /* Out of memory. *outputBufferW already NULL. */ } return TRUE; } else { errorTemplate = (localizeErrorMessage ? TEXT("Unexpected conversion error: %d") : TEXT("Unexpected conversion error: %d")); errorTemplateLen = _tcslen(errorTemplate) + 10 + 1; *outputBufferW = malloc(sizeof(TCHAR) * errorTemplateLen); if (*outputBufferW) { _sntprintf(*outputBufferW, errorTemplateLen, errorTemplate, GetLastError()); } else { /* Out of memory. *outputBufferW already NULL. */ } return TRUE; } } *outputBufferW = malloc((req + 1) * sizeof(TCHAR)); if (!(*outputBufferW)) { _tprintf(TEXT("Out of memory (%s%02d)"), TEXT("MBTWC"), 1); /* Out of memory. *outputBufferW already NULL. */ return TRUE; } MultiByteToWideChar(encoding, MB_ERR_INVALID_CHARS, multiByteChars, -1, *outputBufferW, req + 1); return FALSE; } #endif #if defined(UNICODE) && !defined(WIN32) #include /** * Converts a MultiByte encoded string to a WideChars string specifying the output encoding. * * @param multiByteChars The MultiByte encoded source string. * @param multiByteEncoding The source encoding. * @param interumEncoding The interum encoding before transforming to Wide Chars (On solaris this is the result encoding.) * If the ecoding is appended by "//TRANSLIT", "//IGNORE", "//TRANSLIT//IGNORE" then the conversion * will try to transliterate and or ignore invalid characters without warning. * @param outputBufferW If return is TRUE then this will be an error message. If return is FALSE then this will contain the * requested WideChars string. If there were any memory problems, the return will be TRUE and the * buffer will be set to NULL. In any case, it is the responsibility of the caller to free the output * buffer memory. * @param localizeErrorMessage TRUE if the error message can be localized. * * @return TRUE if there were problems, FALSE if Ok. */ int multiByteToWideChar(const char *multiByteChars, const char *multiByteEncoding, char *interumEncoding, wchar_t **outputBufferW, int localizeErrorMessage) { const TCHAR *errorTemplate; size_t errorTemplateLen; size_t iconv_value; char *nativeChar; char *nativeCharStart; size_t multiByteCharsLen; size_t nativeCharLen; size_t outBytesLeft; size_t inBytesLeft; #if defined(FREEBSD) || defined(SOLARIS) || (defined(AIX) && defined(USE_LIBICONV_GNU)) const char* multiByteCharsStart; #else char* multiByteCharsStart; #endif iconv_t conv_desc; int didIConv; size_t wideCharLen; int err; /* Clear the output buffer as a sanity check. Shouldn't be needed. */ *outputBufferW = NULL; multiByteCharsLen = strlen(multiByteChars); if (!multiByteCharsLen) { /* The input string is empty, so the output will be as well. */ *outputBufferW = malloc(sizeof(TCHAR)); if (*outputBufferW) { (*outputBufferW)[0] = TEXT('\0'); return FALSE; } else { /* Out of memory. *outputBufferW already NULL. */ return TRUE; } } /* First we need to convert from the multi-byte string to native. */ /* If the multiByteEncoding and interumEncoding encodings are equal then there is nothing to do. */ if ((strcmp(multiByteEncoding, interumEncoding) != 0) && strcmp(interumEncoding, "646") != 0) { conv_desc = wrapper_iconv_open(interumEncoding, multiByteEncoding); /* convert multiByte encoding to interum-encoding*/ if (conv_desc == (iconv_t)(-1)) { /* Initialization failure. */ err = errno; if (err == EINVAL) { errorTemplate = (localizeErrorMessage ? TEXT("Conversion from '% s' to '% s' is not supported.") : TEXT("Conversion from '% s' to '% s' is not supported.")); errorTemplateLen = _tcslen(errorTemplate) + strlen(multiByteEncoding) + strlen(interumEncoding) + 1; *outputBufferW = malloc(sizeof(TCHAR) * errorTemplateLen); if (*outputBufferW) { _sntprintf(*outputBufferW, errorTemplateLen, errorTemplate, multiByteEncoding, interumEncoding); } else { /* Out of memory. *outputBufferW already NULL. */ } return TRUE; } else { errorTemplate = (localizeErrorMessage ? TEXT("Initialization failure in iconv: %d") : TEXT("Initialization failure in iconv: %d")); errorTemplateLen = _tcslen(errorTemplate) + 10 + 1; *outputBufferW = malloc(sizeof(TCHAR) * errorTemplateLen); if (*outputBufferW) { _sntprintf(*outputBufferW, errorTemplateLen, errorTemplate, err); } else { /* Out of memory. *outputBufferW already NULL. */ } return TRUE; } } ++multiByteCharsLen; /* add 1 in order to store \0 - especially necessary in UTF-8 -> UTF-8 conversions. Note: it would be better to do it like in converterMBToMB(). */ /* We need to figure out how many bytes we need to store the native encoded string. */ nativeCharLen = multiByteCharsLen; do { #if defined(FREEBSD) || defined(SOLARIS) || (defined(AIX) && defined(USE_LIBICONV_GNU)) multiByteCharsStart = multiByteChars; #else multiByteCharsStart = (char *)multiByteChars; #endif nativeChar = malloc(nativeCharLen); if (!nativeChar) { wrapper_iconv_close(conv_desc); /* Out of memory. */ *outputBufferW = NULL; return TRUE; } nativeCharStart = nativeChar; /* Make a copy of nativeCharLen & multiByteCharsLen (Iconv will decrement inBytesLeft and increment outBytesLeft). */ inBytesLeft = multiByteCharsLen; outBytesLeft = nativeCharLen; iconv_value = wrapper_iconv(conv_desc, &multiByteCharsStart, &inBytesLeft, &nativeCharStart, &outBytesLeft); /* Handle failures. */ if (iconv_value == (size_t)-1) { /* See "man 3 iconv" for an explanation. */ err = errno; free(nativeChar); switch (err) { case EILSEQ: errorTemplate = (localizeErrorMessage ? TEXT("Invalid multibyte sequence.") : TEXT("Invalid multibyte sequence.")); errorTemplateLen = _tcslen(errorTemplate) + 1; *outputBufferW = malloc(sizeof(TCHAR) * errorTemplateLen); if (*outputBufferW) { _sntprintf(*outputBufferW, errorTemplateLen, errorTemplate); } else { /* Out of memory. *outputBufferW already NULL. */ } wrapper_iconv_close(conv_desc); return TRUE; case EINVAL: errorTemplate = (localizeErrorMessage ? TEXT("Incomplete multibyte sequence.") : TEXT("Incomplete multibyte sequence.")); errorTemplateLen = _tcslen(errorTemplate) + 1; *outputBufferW = malloc(sizeof(TCHAR) * errorTemplateLen); if (*outputBufferW) { _sntprintf(*outputBufferW, errorTemplateLen, errorTemplate); } else { /* Out of memory. *outputBufferW already NULL. */ } wrapper_iconv_close(conv_desc); return TRUE; case E2BIG: /* The output buffer was too small, extend it and redo. * iconv decrements inBytesLeft by the number of converted input bytes. * The remaining bytes to convert may not correspond exactly to the additional size * required in the output buffer, but it is a good value to minimize the number of * conversions while ensuring not to extend too much the output buffer. */ if (inBytesLeft > 0) { /* Testing that inBytesLeft is >0 should not be needed, but it's a * sanity check to make sure we never fall into an infinite loop. */ nativeCharLen += inBytesLeft; continue; } wrapper_iconv_close(conv_desc); return TRUE; default: errorTemplate = (localizeErrorMessage ? TEXT("Unexpected iconv error: %d") : TEXT("Unexpected iconv error: %d")); errorTemplateLen = _tcslen(errorTemplate) + 10 + 1; *outputBufferW = malloc(sizeof(TCHAR) * errorTemplateLen); if (*outputBufferW) { _sntprintf(*outputBufferW, errorTemplateLen, errorTemplate, err); } else { /* Out of memory. *outputBufferW already NULL. */ } wrapper_iconv_close(conv_desc); return TRUE; } } break; } while (TRUE); /* finish iconv */ if (wrapper_iconv_close(conv_desc)) { err = errno; free(nativeChar); errorTemplate = (localizeErrorMessage ? TEXT("Cleanup failure in iconv: %d") : TEXT("Cleanup failure in iconv: %d")); errorTemplateLen = _tcslen(errorTemplate) + 10 + 1; *outputBufferW = malloc(sizeof(TCHAR) * errorTemplateLen); if (*outputBufferW) { _sntprintf(*outputBufferW, errorTemplateLen, errorTemplate, err); } else { /* Out of memory. *outputBufferW already NULL. */ } return TRUE; } didIConv = TRUE; } else { nativeChar = (char *)multiByteChars; didIConv = FALSE; } /* now store the result into a wchar_t */ wideCharLen = mbstowcs(NULL, nativeChar, MBSTOWCS_QUERY_LENGTH); if (wideCharLen == (size_t)-1) { err = errno; if (didIConv) { free(nativeChar); } if (err == EILSEQ) { errorTemplate = (localizeErrorMessage ? TEXT("Invalid multibyte sequence.") : TEXT("Invalid multibyte sequence.")); errorTemplateLen = _tcslen(errorTemplate) + 1; } else { errorTemplate = (localizeErrorMessage ? TEXT("Unexpected iconv error: %d") : TEXT("Unexpected iconv error: %d")); errorTemplateLen = _tcslen(errorTemplate) + 10 + 1; } *outputBufferW = malloc(sizeof(TCHAR) * errorTemplateLen); if (*outputBufferW) { _sntprintf(*outputBufferW, errorTemplateLen, errorTemplate, err); } else { /* Out of memory. *outputBufferW already NULL. */ } return TRUE; } *outputBufferW = malloc(sizeof(wchar_t) * (wideCharLen + 1)); if (!(*outputBufferW)) { /* Out of memory. *outputBufferW already NULL. */ if (didIConv) { free(nativeChar); } return TRUE; } mbstowcs(*outputBufferW, nativeChar, wideCharLen + 1); (*outputBufferW)[wideCharLen] = TEXT('\0'); /* Avoid bufferflows caused by badly encoded characters. */ /* free the native char */ if (didIConv) { free(nativeChar); } return FALSE; } /** * Converts a MultiByte encoded string to a WideChars string using the locale encoding. * * @param multiByteChars The MultiByte encoded source string. * @param multiByteEncoding The source encoding (if NULL use the locale encoding). * @param outputBufferW If return is TRUE then this will be an error message. If return is FALSE then this will contain the * requested WideChars string. If there were any memory problems, the return will be TRUE and the * buffer will be set to NULL. In any case, it is the responsibility of the caller to free the output * buffer memory. * @param localizeErrorMessage TRUE if the error message can be localized. * * @return TRUE if there were problems, FALSE if Ok. */ int converterMBToWide(const char *multiByteChars, const char *multiByteEncoding, wchar_t **outputBufferW, int localizeErrorMessage) { char* loc; loc = nl_langinfo(CODESET); #ifdef MACOSX if (strlen(loc) == 0) { loc = "UTF-8"; } #endif if (multiByteEncoding) { return multiByteToWideChar(multiByteChars, multiByteEncoding, loc, outputBufferW, localizeErrorMessage); } else { return multiByteToWideChar(multiByteChars, loc, loc, outputBufferW, localizeErrorMessage); } } size_t _treadlink(TCHAR* exe, TCHAR* fullPath, size_t size) { char* cExe; char* cFullPath; size_t req; req = wcstombs(NULL, exe, 0); if (req == (size_t)-1) { return (size_t)-1; } cExe = malloc(req + 1); if (cExe) { wcstombs(cExe, exe, req + 1); cFullPath = malloc(size); if (cFullPath) { req = readlink(cExe, cFullPath, size); if (req == (size_t)-1) { free(cFullPath); free(cExe); return (size_t)-1; } req = mbstowcs(fullPath, cFullPath, size); if (req == (size_t)-1) { free(cFullPath); free(cExe); return (size_t)-1; } fullPath[size - 1] = TEXT('\0'); /* Avoid bufferflows caused by badly encoded characters. */ free(cFullPath); free(cExe); return req * sizeof(TCHAR); } else { free(cExe); } } return (size_t)-1; } /** * This Wrapper function internally does a malloc to generate the * Wide-char version of the return string. This must be freed by the caller. */ TCHAR* _tgetcwd(TCHAR *buf, size_t size) { char* cBuf; size_t len; if (buf) { cBuf = malloc(size); if (cBuf) { if (getcwd(cBuf, size) != NULL) { len = mbstowcs(buf, cBuf, size); if (len == (size_t)-1) { /* Failed. */ free(cBuf); return NULL; } buf[size - 1] = TEXT('\0'); /* Avoid bufferflows caused by badly encoded characters. */ free(cBuf); return buf; } free(cBuf); } } return NULL; } long _tpathconf(const TCHAR *path, int name) { char* cPath; size_t req; long retVal; req = wcstombs(NULL, path, 0); if (req == (size_t)-1) { return -1; } cPath = malloc(req + 1); if (cPath) { wcstombs(cPath, path, req + 1); retVal = pathconf(cPath, name); free(cPath); return retVal; } return -1; } /** * Set the current locale. * * This Wrapper function internally does a malloc to generate the * Wide-char version of the return string. This must be freed by the caller. * * @param category * @param locale The requested locale. TEXT("") for the default. * * @return NULL if there are any errors, otherwise return locale. */ TCHAR *_tsetlocale(int category, const TCHAR *locale) { char* cLocale; char* cReturn; TCHAR* tReturn; size_t req; if (locale) { req = wcstombs(NULL, locale, 0); if (req == (size_t)-1) { return NULL; } cLocale = malloc(sizeof(char) * (req + 1)); if (!cLocale) { return NULL; } wcstombs(cLocale, locale, req + 1); } else { cLocale = NULL; } cReturn = setlocale(category, cLocale); if (cLocale) { free(cLocale); } if (cReturn) { req = mbstowcs(NULL, cReturn, MBSTOWCS_QUERY_LENGTH); if (req != (size_t)-1) { tReturn = malloc(sizeof(TCHAR) * (req + 1)); if (tReturn) { mbstowcs(tReturn, cReturn, req + 1); tReturn[req] = TEXT('\0'); /* Avoid bufferflows caused by badly encoded characters. */ return tReturn; } } } return NULL; } int createWideFormat(const wchar_t *fmt, wchar_t **wFmt) { int i, result; if (wcsstr(fmt, TEXT("%s")) != NULL) { *wFmt = malloc(sizeof(wchar_t) * (wcslen(fmt) + 1)); if (*wFmt) { wcsncpy(*wFmt, fmt, wcslen(fmt) + 1); for (i = 0; i < wcslen(fmt); i++){ if (fmt[i] == TEXT('%') && i < wcslen(fmt) && fmt[i + 1] == TEXT('s') && (i == 0 || fmt[i - 1] != TEXT('%'))) { (*wFmt)[i + 1] = TEXT('S'); i++; } } (*wFmt)[wcslen(fmt)] = TEXT('\0'); } result = TRUE; } else { *wFmt = (wchar_t*)fmt; result = FALSE; } return result; } int _tprintf(const wchar_t *fmt,...) { int i, flag; wchar_t *wFmt = NULL; va_list args; flag = createWideFormat(fmt, &wFmt); if (wFmt) { va_start(args, fmt); i = vwprintf(wFmt, args); va_end (args); if (flag == TRUE) { free(wFmt); } return i; } return -1; } int _ftprintf(FILE *stream, const wchar_t *fmt, ...) { int i, flag; wchar_t *wFmt = NULL; va_list args; flag = createWideFormat(fmt, &wFmt); if (wFmt) { va_start(args, fmt); i = vfwprintf(stream, wFmt, args); va_end (args); if (flag == TRUE) { free(wFmt); } return i; } return -1; } int _sntprintf(TCHAR *str, size_t size, const TCHAR *fmt, ...) { int i, flag; wchar_t *wFmt = NULL; va_list args; flag = createWideFormat(fmt, &wFmt); if (wFmt) { va_start(args, fmt); i = vswprintf(str, size, wFmt, args); va_end (args); if (flag == TRUE) { free(wFmt); } return i; } return -1; } int _tremove(const TCHAR *path) { char* cPath; size_t req; int result; req = wcstombs(NULL, path, 0); if (req == (size_t)-1) { return -1; } cPath = malloc(req + 1); if (cPath) { wcstombs(cPath, path, req + 1); result = remove(cPath); free(cPath); return result; } return -1; } int _trename(const TCHAR *path, const TCHAR *to) { char* cPath; char* cTo; size_t req; int ret; ret = -1; req = wcstombs(NULL, path, 0); if (req == (size_t)-1) { return ret; } cPath = malloc(req + 1); if (cPath) { wcstombs(cPath, path, req + 1); req = wcstombs(NULL, to, 0); if (req == (size_t)-1) { free(cPath); return ret; } cTo = malloc(req + 1); if (cTo) { wcstombs(cTo, to, req + 1); ret = rename(cPath, cTo); free(cTo); } free(cPath); } return ret; } void _tsyslog(int priority, const TCHAR *message) { char* cMessage; size_t req; req = wcstombs(NULL, message, 0); if (req == (size_t)-1) { return; } cMessage = malloc(req + 1); if (cMessage) { wcstombs(cMessage, message, req + 1); syslog(priority, "%s", cMessage); free(cMessage); } } /** * This Wrapper function internally does a malloc to generate the * Wide-char version of the return string. This must be freed by the caller. * Only needed inside the following: * #if !defined(WIN32) && defined(UNICODE) * #endif */ TCHAR * _tgetenv( const TCHAR * name ) { char* cName; TCHAR* val; size_t req; char *cVal; req = wcstombs(NULL, name, 0); if (req == (size_t)-1) { return NULL; } cName = malloc(sizeof(char) * (req + 1)); if (cName) { wcstombs(cName, name, req + 1); cVal = getenv(cName); free(cName); if (cVal == NULL) { return NULL; } req = mbstowcs(NULL, cVal, MBSTOWCS_QUERY_LENGTH); if (req == (size_t)-1) { /* Failed. */ return NULL; } val = malloc(sizeof(TCHAR) * (req + 1)); if (val) { mbstowcs(val, cVal, req + 1); val[req] = TEXT('\0'); /* Avoid bufferflows caused by badly encoded characters. */ return val; } } return NULL; } FILE* _tfopen(const wchar_t* file, const wchar_t* mode) { int sizeFile, sizeMode; char* cFile; char* cMode; FILE *f = NULL; sizeFile = wcstombs(NULL, (wchar_t*)file, 0); if (sizeFile == (size_t)-1) { return NULL; } cFile= malloc(sizeFile + 1); if (cFile) { wcstombs(cFile, (wchar_t*) file, sizeFile + 1); sizeMode = wcstombs(NULL, (wchar_t*)mode, 0); if (sizeMode == (size_t)-1) { free(cFile); return NULL; } cMode= malloc(sizeMode + 1); if (cMode) { wcstombs(cMode, (wchar_t*) mode, sizeMode + 1); f = fopen(cFile, cMode); free(cMode); } free(cFile); } return f; } FILE* _tpopen(const wchar_t* command, const wchar_t* mode) { int sizeFile, sizeMode; char* cFile; char* cMode; FILE *f = NULL; sizeFile = wcstombs(NULL, (wchar_t*)command, 0); if (sizeFile == (size_t)-1) { return NULL; } cFile= malloc(sizeFile + 1); if (cFile) { wcstombs(cFile, (wchar_t*) command, sizeFile + 1); sizeMode = wcstombs(NULL, (wchar_t*)mode, 0); if (sizeMode == (size_t)-1) { free(cFile); return NULL; } cMode= malloc(sizeMode + 1); if (cMode) { wcstombs(cMode, (wchar_t*) mode, sizeMode + 1); f = popen(cFile, cMode); free(cMode); } free(cFile); } return f; } int _tunlink(const wchar_t* address) { int size; char *cAddress; size = wcstombs(NULL, (wchar_t*)address, 0); if (size == (size_t)-1) { return -1; } cAddress= malloc(size + 1); if (cAddress) { wcstombs(cAddress, (wchar_t*) address, size + 1); size = unlink(cAddress); free(cAddress); return size; } return -1; } int _tmkfifo(TCHAR* arg, mode_t mode) { size_t size; char *cStr; int r; r = -1; size = wcstombs(NULL, arg, 0); if (size == (size_t)-1) { return r; } cStr = malloc(size + 1); if (cStr) { wcstombs(cStr, arg, size + 1); r = mkfifo(cStr, mode); free(cStr); } return r; } int _tchdir(const TCHAR *path) { int r; size_t size; char *cStr; r = -1; size = wcstombs(NULL, path, 0); if (size == (size_t)-1) { return r; } cStr = malloc(size + 1); if (cStr) { wcstombs(cStr, path, size + 1); r = chdir(cStr); free(cStr); } return r; } int _texecvp(TCHAR* arg, TCHAR **cmd) { char** cCmd; char *cArg; int i, size; size_t req; for (i = 0; cmd[i] != NULL; i++) { ; } size = i; cCmd = malloc((i + 1) * sizeof *cCmd); if (cCmd) { for (i = 0; i < size; i++) { req = wcstombs(NULL, cmd[i], 0); if (req == (size_t)-1) { i--; for (; i > 0; i--) { free(cCmd[i]); } free(cCmd); return -1; } cCmd[i] = malloc(req + 1); if (cCmd[i]) { wcstombs(cCmd[i], cmd[i], req + 1); } else { i--; for (; i > 0; i--) { free(cCmd[i]); } free(cCmd); return -1; } } cCmd[size] = NULL; req = wcstombs(NULL, arg, 0); if (req == (size_t)-1) { for (; size >= 0; size--) { free(cCmd[size]); } free(cCmd); return -1; } cArg = malloc(req + 1); if (cArg) { wcstombs(cArg, arg, req + 1); i = execvp(cArg, cCmd); free(cArg); } else { i = -1; } for (; size >= 0; size--) { free(cCmd[size]); } free(cCmd); return i; } return -1; } #ifdef ECSCASECMP int wcscasecmp(const wchar_t* s1, const wchar_t* s2) { wint_t a1, a2; if (s1 == s2) { return 0; } do { a1 = towlower(*s1++); a2 = towlower(*s2++); if (a1 == L'\0') { break; } } while (a1 == a2); return a1 - a2; } #endif #if defined(HPUX) int _vsntprintf(wchar_t *ws, size_t n, const wchar_t *format, va_list arg) { /* z/OS shows unexpected behaviour if the format string is empty */ if (ws) { ws[0] = TEXT('\0'); } return vswprintf(ws, n, format, arg); } #endif int _texecve(TCHAR* arg, TCHAR **cmd, TCHAR** env) { char **cCmd; char *cArg; char **cEnv; int i, sizeCmd, sizeEnv; size_t req; for (i = 0; cmd[i] != NULL; i++) { ; } sizeCmd = i; cCmd = malloc((i + 1) * sizeof *cCmd); if (cCmd) { for (i = 0; i < sizeCmd; i++) { req = wcstombs(NULL, cmd[i], 0); if (req == (size_t)-1) { i--; for (; i > 0; i--) { free(cCmd[i]); } free(cCmd); return -1; } cCmd[i] = malloc(req + 1); if (cCmd[i]) { wcstombs(cCmd[i], cmd[i], req + 1); } else { i--; for (; i > 0; i--) { free(cCmd[i]); } free(cCmd); return -1; } } cCmd[sizeCmd] = NULL; for (i = 0; env[i] != NULL; i++) { ; } sizeEnv = i; cEnv = malloc((i + 1) * sizeof *cEnv); if (!cEnv) { for (; sizeCmd >= 0; sizeCmd--) { free(cCmd[sizeCmd]); } free(cCmd); return -1; } for (i = 0; i < sizeEnv; i++) { req = wcstombs(NULL, env[i], 0); if (req == (size_t)-1) { i--; for (; i > 0; i--) { free(cEnv[i]); } free(cEnv); for (; sizeCmd >= 0; sizeCmd--) { free(cCmd[sizeCmd]); } free(cCmd); return -1; } cEnv[i] = malloc(req + 1); if (cEnv[i]) { wcstombs(cEnv[i], env[i], req + 1); } else { i--; for (; i > 0; i--) { free(cEnv[i]); } free(cEnv); for (; sizeCmd >= 0; sizeCmd--) { free(cCmd[sizeCmd]); } free(cCmd); return -1; } } cEnv[sizeEnv] = NULL; req = wcstombs(NULL, arg, 0); if (req == (size_t)-1) { for (; sizeEnv >= 0; sizeEnv--) { free(cEnv[sizeEnv]); } free(cEnv); for (; sizeCmd >= 0; sizeCmd--) { free(cCmd[sizeCmd]); } free(cCmd); return -1; } cArg = malloc(req + 1); if (cArg) { wcstombs(cArg, arg, req + 1); i = execve(cArg, cCmd, cEnv); free(cArg); } else { i = -1; } for (; sizeEnv >= 0; sizeEnv--) { free(cEnv[sizeEnv]); } free(cEnv); for (; sizeCmd >= 0; sizeCmd--) { free(cCmd[sizeCmd]); } free(cCmd); return i; } return -1; } int _topen(const TCHAR *path, int oflag, mode_t mode) { char* cPath; int r; size_t size; size = wcstombs(NULL, path, 0); if (size == (size_t)-1) { return -1; } cPath = malloc(size + 1); if (cPath) { wcstombs(cPath, path, size + 1); r = open(cPath, oflag, mode); free(cPath); return r; } return -1; } #if defined(WRAPPER_USE_PUTENV) /** * Normal calls to putenv do not free the string parameter, but UNICODE calls can and should. */ int _tputenv(const TCHAR *string) { int r; size_t size; char *cStr; size = wcstombs(NULL, (wchar_t*)string, 0); if (size == (size_t)-1) { return -1; } cStr = malloc(size + 1); if (cStr) { wcstombs(cStr, string, size + 1); r = putenv(cStr); /* Can't free cStr as it becomes part of the environment. */ /* free(cstr); */ return r; } return -1; } #else int _tsetenv(const TCHAR *name, const TCHAR *value, int overwrite) { int r = -1; size_t size; char *cName; char *cValue; size = wcstombs(NULL, (wchar_t*)name, 0); if (size == (size_t)-1) { return -1; } cName = malloc(size + 1); if (cName) { wcstombs(cName, name, size + 1); size = wcstombs(NULL, (wchar_t*)value, 0); if (size == (size_t)-1) { free(cName); return -1; } cValue = malloc(size + 1); if (cValue) { wcstombs(cValue, value, size + 1); r = setenv(cName, cValue, overwrite); free(cValue); } free(cName); } return r; } void _tunsetenv(const TCHAR *name) { size_t size; char *cName; size = wcstombs(NULL, (wchar_t*)name, 0); if (size == (size_t)-1) { return; } cName = malloc(size + 1); if (cName) { wcstombs(cName, name, size + 1); unsetenv(cName); free(cName); } } #endif int _tstat(const wchar_t* filename, struct stat *buf) { int size; char *cFileName; size = wcstombs(NULL, (wchar_t*)filename, 0); if (size == (size_t)-1) { return -1; } cFileName = malloc(size + 1); if (cFileName) { wcstombs(cFileName, (wchar_t*) filename, size + 1); size = stat(cFileName, buf); free(cFileName); } return size; } int _tchown(const TCHAR *path, uid_t owner, gid_t group) { char* cPath; int r; size_t size; size = wcstombs(NULL, path, 0); if (size == (size_t)-1) { return -1; } cPath = malloc(size + 1); if (cPath) { wcstombs(cPath, path, size + 1); r = chown(cPath, owner, group); free(cPath); return r; } return -1; } /** * Expands symlinks and resolves /./, /../ and extra '/' characters to produce a * canonicalized absolute pathname. * On some platforms (e.g MACOSX), even if the full path could not be resolved, * the valid part will be copied to resolvedName until a non-existant folder is * encountered. resolvedName can then be used to point out where the problem was. * * @param fileName The file name to be resolved. * @param resolvedName A buffer large enough to hold the expanded path. * @param resolvedNameSize The size of the resolvedName buffer, should usually be PATH_MAX + 1. * * @return pointer to resolvedName if successful, otherwise NULL and errno is set to indicate the error. */ wchar_t* _trealpathN(const wchar_t* fileName, wchar_t *resolvedName, size_t resolvedNameSize) { char *cFile; char resolved[PATH_MAX + 1]; int sizeFile; int req; char* returnVal; int err = 0; sizeFile = wcstombs(NULL, fileName, 0); if (sizeFile == (size_t)-1) { return NULL; } cFile = malloc(sizeFile + 1); if (cFile) { /* Initialize the return value. */ resolvedName[0] = TEXT('\0'); wcstombs(cFile, fileName, sizeFile + 1); /* get the canonicalized absolute pathname */ resolved[0] = '\0'; returnVal = realpath(cFile, resolved); err = errno; free(cFile); if (strlen(resolved) > 0) { /* In case realpath failed, 'resolved' may contain a part of the path (until the invalid folder). * Example: cFile is "/home/user/alex/../nina" but "/home/user/nina" doesn't exist. * => realpath will return NULL and resolved will be set to "/home/user" */ req = mbstowcs(NULL, resolved, MBSTOWCS_QUERY_LENGTH); if (req == (size_t)-1) { if (err != 0) { /* use errno set by realpath() if it failed. */ errno = err; } return NULL; } mbstowcs(resolvedName, resolved, resolvedNameSize); resolvedName[resolvedNameSize - 1] = TEXT('\0'); /* Avoid bufferflows caused by badly encoded characters. */ } errno = err; if (returnVal == NULL) { return NULL; } else { return resolvedName; } } return NULL; } #endif /** * Fill a block of memory with zeros. * Use this function to ensure no sensitive data remains in memory. * * @param str A pointer to the starting address of the block of memory to fill with zeros. * @param size The size of the block of memory to fill with zeros, in bytes. */ void wrapperSecureZero(void* str, size_t size) { if (str) { #ifdef WIN32 SecureZeroMemory(str, size); #else memset (str, '\0', size); #ifdef __GNUC__ /* Compiler barrier. */ asm volatile ("" ::: "memory"); #endif #endif } } /** * Fill a block of memory with zeros, then free it. * Use this function to ensure no sensitive data remains in memory. * * @param str A pointer to the starting address of the block of memory to fill with zeros. * @param size The size of the block of memory to fill with zeros, in bytes. */ void wrapperSecureFree(void* str, size_t size) { wrapperSecureZero(str, size); free(str); } /** * Fill a Wide-char sequence with zeros, then free it. * Use this function to ensure no sensitive data remains in memory. * * @param str String to erase and free. */ void wrapperSecureFreeStrW(TCHAR* str) { if (str) { wrapperSecureFree(str, _tcslen(str) * sizeof(TCHAR)); } } /** * Fill a multi-byte sequence with zeros, then free it. * Use this function to ensure no sensitive data remains in memory. * * @param str String to erase and free. */ void wrapperSecureFreeStrMB(char* str) { if (str) { wrapperSecureFree(str, strlen(str)); } } #ifdef USE_USLEEP #define USLEEP_MAX (1000000 - 1) /** * Wrapper function for usleep() allowing to suspends thread execution for more than 1 second. * According to the doc, usec must be smaller than 1000000 on some systems (this is the case on z/OS). * This function splits the suspension time into intervals no longer than USLEEP_MAX and repeat as * many times as needed. * * @param usec suspension time in microsoconds * * @return 0 on success, -1 on error. */ int wrapperUsleep(unsigned long usec) { unsigned long chunk; while(usec > 0) { chunk = usec > USLEEP_MAX ? USLEEP_MAX : usec; if (usleep(chunk) == -1) { return -1; } usec -= chunk; } return 0; } #endif /** * Convert a string to lowercase. A new string will be allocated. * * @param value Input string * * @return The converted string. */ TCHAR* toLower(const TCHAR* value) { TCHAR* result; size_t len; size_t i; len = _tcslen(value); result = malloc(sizeof(TCHAR) * (len + 1)); if (!result) { outOfMemory(TEXT("TL"), 1); return NULL; } for (i = 0; i < len; i++) { result[i] = _totlower(value[i]); } result[len] = TEXT('\0'); return result; } /** * Convert a string to uppercase. A new string will be allocated. * * @param value Input string * * @return The converted string. */ TCHAR* toUpper(const TCHAR* value) { TCHAR* result; size_t len; size_t i; len = _tcslen(value); result = malloc(sizeof(TCHAR) * (len + 1)); if (!result) { outOfMemory(TEXT("TU"), 1); return NULL; } for (i = 0; i < len; i++) { result[i] = _totupper(value[i]); } result[len] = TEXT('\0'); return result; } /** * Clear any non-alphanumeric characters. * Generally the OS will ignore the canonical dashes and punctuation in the encoding notation when setting the locale. * This function is used when comparing two notations to check if they refer to the same encoding. * * @param bufferIn input string * @param bufferOut output string */ void clearNonAlphanumeric(TCHAR* bufferIn, TCHAR* bufferOut) { while (*bufferIn) { if (_istdigit(*bufferIn) || _istalpha(*bufferIn)) { *bufferOut = *bufferIn; bufferOut++; } bufferIn++; } *bufferOut = TEXT('\0'); } #ifndef WIN32 /** * Check if the encoding is specified with the canonical name. * Ex: 'UTF-8' is canonical, 'utf8' isn't. * * @param encoding * * @return TRUE if this is a canonical name, FALSE otherwise. */ int encodingIsCanonicalName(TCHAR* encoding) { TCHAR c; int i; for (i = 0; i < _tcslen(encoding); i++) { c = encoding[i]; if (c >= TEXT('A') && c <= TEXT('Z')) { return TRUE; } if (c == TEXT('-')) { return TRUE; } } return FALSE; } /** * Compares two encodings. * * @param encoding1 When using systemMode, this should be the system encoding * @param encoding2 * @param ignoreCase TRUE if the case should be ignored * TRUE if dashes and punctuation should be ignored. * * @return TRUE if the encodings are identical, FALSE otherwise. */ int compareEncodings(TCHAR* encoding1, TCHAR* encoding2, int ignoreCase, int ignorePunctuation) { TCHAR encoding1Buff[ENCODING_BUFFER_SIZE]; TCHAR encoding2Buff[ENCODING_BUFFER_SIZE]; TCHAR *enc1Ptr; TCHAR *enc2Ptr; if (encoding1 && encoding2) { if (ignorePunctuation) { clearNonAlphanumeric(encoding1, encoding1Buff); clearNonAlphanumeric(encoding2, encoding2Buff); enc1Ptr = encoding1Buff; enc2Ptr = encoding2Buff; } else { enc1Ptr = encoding1; enc2Ptr = encoding2; } if (ignoreCase) { return (strcmpIgnoreCase(enc1Ptr, enc2Ptr) == 0); } else { return (_tcscmp(enc1Ptr, enc2Ptr) == 0); } } return (!encoding1 && !encoding2); } /** * Compares two encodings with the rules of the OS. * On Linux the comparison ignores case, dashes and punctuation * (except when the encoding of the system locale is displayed with a canonical name, e.g. C.UTF-8). * On HPUX, Solaris, AIX and FreeBSD, the comparison is strict. * On MAC and zOS, the comparison ignores case, but is strict regarding dashes and punctuation. * * @param encoding1 system encoding * @param encoding2 other encoding * * @return TRUE if the encodings are identical, FALSE otherwise. */ int compareEncodingsSysMode(TCHAR* encoding1, TCHAR* encoding2) { int ignoreCase = FALSE; int ignorePunctuation = FALSE; #ifdef LINUX if (!encodingIsCanonicalName(encoding1)) { ignoreCase = TRUE; ignorePunctuation = TRUE; } #elif defined(MACOSX) || defined(ZOS) ignoreCase = TRUE; #endif return compareEncodings(encoding1, encoding2, ignoreCase, ignorePunctuation); } /** * Get the encoding of the current locale. * * @param buffer output buffer * * @return the buffer or NULL if the encoding could not be retrieved. */ TCHAR* getCurrentLocaleEncoding(TCHAR* buffer) { char* sysEncodingChar; size_t size; sysEncodingChar = nl_langinfo(CODESET); #ifdef MACOSX if (strlen(sysEncodingChar) == 0) { sysEncodingChar = "UTF-8"; } #endif size = mbstowcs(NULL, sysEncodingChar, MBSTOWCS_QUERY_LENGTH); if ((size > (size_t)0) && (size < (size_t)32)) { mbstowcs(buffer, sysEncodingChar, size + 1); buffer[size] = TEXT('\0'); return buffer; } return NULL; } #endif /** * Function to get the system encoding name/number for the encoding * of the conf file * * @para String holding the encoding from the conf file * * @return TRUE if not found, FALSE otherwise * */ #ifdef WIN32 int getEncodingByName(char* encodingMB, int *encoding) { #else int getEncodingByName(char* encodingMB, char** encoding) { #endif if (strIgnoreCaseCmp(encodingMB, "Shift_JIS") == 0) { #if defined(FREEBSD) || defined (AIX) || defined(MACOSX) *encoding = "SJIS"; #elif defined(WIN32) *encoding = 932; #else *encoding = "shiftjis"; #endif } else if (strIgnoreCaseCmp(encodingMB, "eucJP") == 0) { #if defined(AIX) *encoding = "IBM-eucJP"; #elif defined(WIN32) *encoding = 20932; #else *encoding = "eucJP"; #endif } else if (strIgnoreCaseCmp(encodingMB, "UTF-8") == 0) { #if defined(HPUX) *encoding = "utf8"; #elif defined(WIN32) *encoding = 65001; #else *encoding = "UTF-8"; #endif } else if (strIgnoreCaseCmp(encodingMB, "ISO-8859-1") == 0) { #if defined(WIN32) *encoding = 28591; #elif defined(LINUX) *encoding = "ISO-8859-1"; #else *encoding = "ISO8859-1"; #endif } else if (strIgnoreCaseCmp(encodingMB, "CP1252") == 0) { #if defined(WIN32) *encoding = 1252; #else *encoding = "CP1252"; #endif } else if (strIgnoreCaseCmp(encodingMB, "ISO-8859-2") == 0) { #if defined(WIN32) *encoding = 28592; #elif defined(LINUX) *encoding = "ISO-8859-2"; #else *encoding = "ISO8859-2"; #endif } else if (strIgnoreCaseCmp(encodingMB, "ISO-8859-3") == 0) { #if defined(WIN32) *encoding = 28593; #elif defined(LINUX) *encoding = "ISO-8859-3"; #else *encoding = "ISO8859-3"; #endif } else if (strIgnoreCaseCmp(encodingMB, "ISO-8859-4") == 0) { #if defined(WIN32) *encoding = 28594; #elif defined(LINUX) *encoding = "ISO-8859-4"; #else *encoding = "ISO8859-4"; #endif } else if (strIgnoreCaseCmp(encodingMB, "ISO-8859-5") == 0) { #if defined(WIN32) *encoding = 28595; #elif defined(LINUX) *encoding = "ISO-8859-5"; #else *encoding = "ISO8859-5"; #endif } else if (strIgnoreCaseCmp(encodingMB, "ISO-8859-6") == 0) { #if defined(WIN32) *encoding = 28596; #elif defined(LINUX) *encoding = "ISO-8859-6"; #else *encoding = "ISO8859-6"; #endif } else if (strIgnoreCaseCmp(encodingMB, "ISO-8859-7") == 0) { #if defined(WIN32) *encoding = 28597; #elif defined(LINUX) *encoding = "ISO-8859-7"; #else *encoding = "ISO8859-7"; #endif } else if (strIgnoreCaseCmp(encodingMB, "ISO-8859-8") == 0) { #if defined(WIN32) *encoding = 28598; #elif defined(LINUX) *encoding = "ISO-8859-8"; #else *encoding = "ISO8859-8"; #endif } else if (strIgnoreCaseCmp(encodingMB, "ISO-8859-9") == 0) { #if defined(WIN32) *encoding = 28599; #elif defined(LINUX) *encoding = "ISO-8859-9"; #else *encoding = "ISO8859-9"; #endif } else if (strIgnoreCaseCmp(encodingMB, "ISO-8859-10") == 0) { #if defined(WIN32) *encoding = 28600; #elif defined(LINUX) *encoding = "ISO-8859-10"; #else *encoding = "ISO8859-10"; #endif } else if (strIgnoreCaseCmp(encodingMB, "ISO-8859-11") == 0) { #if defined(WIN32) *encoding = 28601; #elif defined(LINUX) *encoding = "ISO-8859-11"; #else *encoding = "ISO8859-11"; #endif } else if (strIgnoreCaseCmp(encodingMB, "ISO-8859-13") == 0) { #if defined(WIN32) *encoding = 28603; #elif defined(LINUX) *encoding = "ISO-8859-13"; #else *encoding = "ISO8859-13"; #endif } else if (strIgnoreCaseCmp(encodingMB, "ISO-8859-14") == 0) { #if defined(WIN32) *encoding = 28604; #elif defined(LINUX) *encoding = "ISO-8859-14"; #else *encoding = "ISO8859-14"; #endif } else if (strIgnoreCaseCmp(encodingMB, "ISO-8859-15") == 0) { #if defined(WIN32) *encoding = 28605; #elif defined(LINUX) *encoding = "ISO-8859-15"; #else *encoding = "ISO8859-15"; #endif } else if (strIgnoreCaseCmp(encodingMB, "ISO-8859-16") == 0) { #if defined(WIN32) *encoding = 28606; #elif defined(LINUX) *encoding = "ISO-8859-16"; #else *encoding = "ISO8859-16"; #endif } else if (strIgnoreCaseCmp(encodingMB, "CP1250") == 0) { #if defined(WIN32) *encoding = 1250; #else *encoding = "CP1250"; #endif } else if (strIgnoreCaseCmp(encodingMB, "CP1251") == 0) { #if defined(WIN32) *encoding = 1251; #else *encoding = "CP1251"; #endif } else if (strIgnoreCaseCmp(encodingMB, "KOI8-R") == 0) { #if defined(WIN32) *encoding = 20866; #else *encoding = "KOI8-R"; #endif } else if (strIgnoreCaseCmp(encodingMB, "KOI8-U") == 0) { #if defined(WIN32) *encoding = 21866; #else *encoding = "KOI8-U"; #endif } else if (strIgnoreCaseCmp(encodingMB, "DEFAULT") == 0) { #ifdef WIN32 *encoding = GetACP(); #else *encoding = nl_langinfo(CODESET); #ifdef MACOSX if (strlen(*encoding) == 0) { *encoding = "UTF-8"; } #endif #endif } else { return TRUE; } return FALSE; } #ifdef WIN32 /** * Converts a Wide string into a specific multibyte encoded string. * * @prarm wideChars The Wide string to be converted. * @param outputBufferMB Returns a newly malloced buffer containing the target MB chars. * Will contain an error message if the function returns TRUE. * If this is NULL then there was an out of memory problem. * Caller must free this up. * @param outputEncoding Output encoding to use. * * @return -1 if there were any problems. size of the buffer in byte otherwise. */ int converterWideToMB(const TCHAR *wideChars, char **outputBufferMB, int outputEncoding) { char *errorTemplate; size_t errorTemplateLen; int req; /* Initialize the outputBuffer. */ *outputBufferMB = NULL; req = WideCharToMultiByte(outputEncoding, 0, wideChars, -1, NULL, 0, NULL, 0); if (req <= 0) { errorTemplate = "Unexpected conversion error: %d"; errorTemplateLen = strlen(errorTemplate) + 10 + 1; *outputBufferMB = malloc(errorTemplateLen); if (*outputBufferMB) { _snprintf(*outputBufferMB, errorTemplateLen, errorTemplate, wrapperGetLastError()); } else { /* Out of memory. *outputBufferW already NULL. */ } return -1; } *outputBufferMB = malloc((req + 1)); if (!(*outputBufferMB)) { outOfMemory(TEXT("CTW"), 1); /* Out of memory. *outputBufferW already NULL. */ return -1; } WideCharToMultiByte(outputEncoding, 0, wideChars, -1, *outputBufferMB, req + 1, NULL, 0); return req; } /** * Converts a native multibyte string into a specific multibyte encoded string. * * @param multiByteChars The original multi-byte chars. * @param inputEncoding The multi-byte encoding. * @param outputBufferMB Returns a newly malloced buffer containing the target MB chars. * Will contain an error message if the function returns TRUE. * If this is NULL then there was an out of memory problem. * Caller must free this up. * @param outputEncoding Output encoding to use. * * @return -1 if there were any problems. buffer size (>=0) if everything was Ok. */ int converterMBToMB(const char *multiByteChars, int inputEncoding, char **outputBufferMB, int outputEncoding) { TCHAR* tempBuffer = NULL; int result1 = 0; int result2; if (multiByteToWideChar(multiByteChars, inputEncoding, &tempBuffer, FALSE)) { if (!tempBuffer) { return -1; } /* The result will be -1 but we still need to convert the error message. */ result1 = -1; } result2 = converterWideToMB((const TCHAR*)tempBuffer, outputBufferMB, outputEncoding); if (result1 == -1) { return -1; } return result2; } #else #ifdef HPUX static int isIconvHpuxFixEnabled = FALSE; /** * Turns on or off a fix used in converterMBToMB() */ void toggleIconvHpuxFix(int value) { isIconvHpuxFixEnabled = value; } #endif /** * Converts a native multibyte string into a specific multibyte encoded string. * * @param multiByteChars The original multi-byte chars. * @param multiByteEncoding The multi-byte encoding. * @param outputBufferMB Returns a newly malloced buffer containing the target MB chars. * Will contain an error message if the function returns TRUE. * If this is NULL then there was an out of memory problem. * Caller must free this up. * @param outputEncoding Output encoding to use. * * @return -1 if there were any problems. buffer size (>=0) if everything was Ok. */ int converterMBToMB(const char *multiByteChars, const char *multiByteEncoding, char **outputBufferMB, const char *outputEncoding) { char *errorTemplate; size_t errorTemplateLen; size_t iconv_value; char *nativeChar; char *nativeCharStart; size_t multiByteCharsLen; int nativeCharLen = -1; size_t outBytesLeft; size_t inBytesLeft; #if defined(FREEBSD) || defined(SOLARIS) || (defined(AIX) && defined(USE_LIBICONV_GNU)) const char* multiByteCharsStart; #else char* multiByteCharsStart; #endif iconv_t conv_desc; int err; #ifdef HPUX int isIconvHpuxFixEnabledLocal = isIconvHpuxFixEnabled; #endif /* Clear the output buffer as a sanity check. Shouldn't be needed. */ *outputBufferMB = NULL; multiByteCharsLen = strlen(multiByteChars); /* If the multiByteEncoding and outputEncoding encodings are equal then there is nothing to do. */ if ((strcmp(multiByteEncoding, outputEncoding) != 0) && (strcmp(outputEncoding, "646") != 0) && (multiByteCharsLen > 0)) { conv_desc = wrapper_iconv_open(outputEncoding, multiByteEncoding); /* convert multiByte encoding to interum-encoding*/ if (conv_desc == (iconv_t)(-1)) { /* Initialization failure. */ err = errno; if (err == EINVAL) { errorTemplate = "Conversion from '%s' to '%s' is not supported."; errorTemplateLen = strlen(errorTemplate) + strlen(multiByteEncoding) + strlen(outputEncoding) + 1; *outputBufferMB = malloc(errorTemplateLen); if (*outputBufferMB) { snprintf(*outputBufferMB, errorTemplateLen, errorTemplate, multiByteEncoding, outputEncoding); } else { /* Out of memory. *outputBufferMB already NULL. */ } return -1; } else { errorTemplate = "Initialization failure in iconv: %d"; errorTemplateLen = strlen(errorTemplate) + 10 + 1; *outputBufferMB = malloc( errorTemplateLen); if (*outputBufferMB) { snprintf(*outputBufferMB, errorTemplateLen, errorTemplate, err); } else { /* Out of memory. *outputBufferMB already NULL. */ } return -1; } } /* We need to figure out how many bytes we need to store the native encoded string. */ nativeCharLen = multiByteCharsLen; #ifdef HPUX if (isIconvHpuxFixEnabledLocal) { multiByteCharsLen = multiByteCharsLen + (((multiByteCharsLen - 1) % 8) == 0 ? 0 : 8 - ((multiByteCharsLen - 1) % 8)); } #endif do { #if defined(FREEBSD) || defined(SOLARIS) || (defined(AIX) && defined(USE_LIBICONV_GNU)) multiByteCharsStart = multiByteChars; #else multiByteCharsStart = (char *)multiByteChars; #endif nativeChar = calloc(nativeCharLen + 1, 1); if (!nativeChar) { wrapper_iconv_close(conv_desc); /* Out of memory. */ *outputBufferMB = NULL; return -1; } nativeCharStart = nativeChar; /* Make a copy of nativeCharLen & multiByteCharsLen (Iconv will decrement inBytesLeft and increment outBytesLeft). */ inBytesLeft = multiByteCharsLen + 1; outBytesLeft = nativeCharLen + 1; iconv_value = wrapper_iconv(conv_desc, &multiByteCharsStart, &inBytesLeft, &nativeCharStart, &outBytesLeft); /* Handle failures. */ if (iconv_value == (size_t)-1) { /* See "man 3 iconv" for an explanation. */ err = errno; free(nativeChar); switch (err) { case EILSEQ: errorTemplate = "Invalid multibyte sequence."; errorTemplateLen = strlen(errorTemplate) + 1; #ifdef HPUX if (*outputBufferMB) { free(*outputBufferMB); } #endif *outputBufferMB = malloc(errorTemplateLen); if (*outputBufferMB) { snprintf(*outputBufferMB, errorTemplateLen, "%s", errorTemplate); } else { /* Out of memory. *outputBufferMB already NULL. */ } #ifdef HPUX /* This can happen when multiByteCharsLen was increased to workaround an Iconv bug. * Keep the error in the output buffer and try again using the original input string size. */ if (isIconvHpuxFixEnabledLocal) { multiByteCharsLen = strlen(multiByteChars); isIconvHpuxFixEnabledLocal = FALSE; continue; } #endif wrapper_iconv_close(conv_desc); return -1; case EINVAL: errorTemplate = "Incomplete multibyte sequence."; errorTemplateLen = strlen(errorTemplate) + 1; #ifdef HPUX if (*outputBufferMB) { free(*outputBufferMB); } #endif *outputBufferMB = malloc(errorTemplateLen); if (*outputBufferMB) { snprintf(*outputBufferMB, errorTemplateLen, "%s", errorTemplate); } else { /* Out of memory. *outputBufferMB already NULL. */ } #ifdef HPUX /* This can happen when multiByteCharsLen was increased to workaround an Iconv bug. * Keep the error in the output buffer and try again using the original input string size. */ if (isIconvHpuxFixEnabledLocal) { multiByteCharsLen = strlen(multiByteChars); isIconvHpuxFixEnabledLocal = FALSE; continue; } #endif wrapper_iconv_close(conv_desc); return -1; case E2BIG: /* The output buffer was too small, extend it and redo. * iconv decrements inBytesLeft by the number of converted input bytes. * The remaining bytes to convert may not correspond exactly to the additional size * required in the output buffer, but it is a good value to minimize the number of * conversions while ensuring not to extend too much the output buffer. */ if (inBytesLeft > 0) { /* Testing that inBytesLeft is >0 should not be needed, but it's a * sanity check to make sure we never fall into an infinite loop. */ #ifdef HPUX /* This can happen when multiByteCharsLen was increased to workaround an Iconv bug. * Try again using the original input string size. */ if (isIconvHpuxFixEnabledLocal && ((inBytesLeft == 1) || nativeCharLen > (strlen(multiByteChars) * 4))) { multiByteCharsLen = strlen(multiByteChars); isIconvHpuxFixEnabledLocal = FALSE; } #endif nativeCharLen += inBytesLeft; continue; } wrapper_iconv_close(conv_desc); return -1; default: #ifdef HPUX if (isIconvHpuxFixEnabled && !isIconvHpuxFixEnabledLocal && (err == 0)) { /* We got an error on the first loop, stored it in the output buffer and tried again without the HPUX fix. * If we get the Iconv bug (with errno=0) this time, then report the original error and return. */ wrapper_iconv_close(conv_desc); return -1; } if (*outputBufferMB) { free(*outputBufferMB); } #endif errorTemplate = "Unexpected iconv error: %d"; errorTemplateLen = strlen(errorTemplate) + 10 + 1; *outputBufferMB = malloc(errorTemplateLen); if (*outputBufferMB) { snprintf(*outputBufferMB, errorTemplateLen, errorTemplate, err); } else { /* Out of memory. *outputBufferMB already NULL. */ } #ifdef HPUX /* This can happen when multiByteCharsLen was increased to workaround an Iconv bug. * Keep the error in the output buffer and try again using the original input string size. */ if (isIconvHpuxFixEnabledLocal && (err != 0)) { multiByteCharsLen = strlen(multiByteChars); isIconvHpuxFixEnabledLocal = FALSE; continue; } #endif wrapper_iconv_close(conv_desc); return -1; } } break; } while (TRUE); #ifdef HPUX if (*outputBufferMB) { free(*outputBufferMB); *outputBufferMB = NULL; } #endif /* finish iconv */ if (wrapper_iconv_close(conv_desc)) { err = errno; free(nativeChar); errorTemplate = "Cleanup failure in iconv: %d"; errorTemplateLen = strlen(errorTemplate) + 10 + 1; *outputBufferMB = malloc(errorTemplateLen); if (*outputBufferMB) { snprintf(*outputBufferMB, errorTemplateLen, errorTemplate, err); } else { /* Out of memory. *outputBufferMB already NULL. */ } return -1; } } else { /* The source chars do not need to be converted. Copy them to make a consistant API. */ nativeCharLen = strlen(multiByteChars); nativeChar = malloc(sizeof(char) * (nativeCharLen + 1)); if (nativeChar) { snprintf(nativeChar, nativeCharLen + 1, "%s", multiByteChars); } else { /* Out of memory. *outputBufferMB already NULL. */ return -1; } } *outputBufferMB = nativeChar; return nativeCharLen; } /** * Converts a Wide string into a specific multibyte encoded string. * * @prarm wideChars The Wide string to be converted. * @param outputBufferMB Returns a newly malloced buffer containing the target MB chars. * Will contain an error message if the function returns TRUE. * If this is NULL then there was an out of memory problem. * Caller must free this up. * @param outputEncoding Output encoding to use (if NULL use the encoding of the locale). * * @return -1 if there were any problems. buffer size (>=0) if everything was Ok. */ int converterWideToMB(const TCHAR *wideChars, char **outputBufferMB, const char *outputEncoding) { char *errorTemplate; size_t errorTemplateLen; size_t len; char *interumBufferMB; char* encodingFrom; int result; /* Initialize the outputBuffer. */ *outputBufferMB = NULL; len = wcstombs(NULL, wideChars, 0); if (len == (size_t)-1) { errorTemplate = "Invalid multibyte sequence (0x%x)"; errorTemplateLen = strlen(errorTemplate) + 10 + 1; *outputBufferMB = malloc(errorTemplateLen); if (*outputBufferMB) { snprintf(*outputBufferMB, errorTemplateLen, errorTemplate, wrapperGetLastError()); } else { /* Out of memory. *outputBufferW already NULL. */ } return -1; } interumBufferMB = malloc(len + 1); if (!interumBufferMB) { return -1; } wcstombs(interumBufferMB, wideChars, len + 1); encodingFrom = nl_langinfo(CODESET); #ifdef MACOSX if (strlen(encodingFrom) == 0) { encodingFrom = "UTF-8"; } #endif if (outputEncoding && (strcmp(encodingFrom, outputEncoding) != 0)) { result = converterMBToMB(interumBufferMB, encodingFrom, outputBufferMB, outputEncoding); free(interumBufferMB); } else { /* The output encoding is the same as the one of the current locale. * No need to call converterMBToMB() which would cause an additional malloc/free */ *outputBufferMB = interumBufferMB; result = (int)strlen(*outputBufferMB); } return result; } #endif /** * Gets the error code for the last operation that failed. */ int wrapperGetLastError() { #ifdef WIN32 return WSAGetLastError(); #else return errno; #endif } /** * Corrects a path in place by replacing all '/' characters with '\' * on Windows platforms. Does nothing on NIX platforms. * * filename - Filename to be modified. Could be null. * * @return TRUE if the filename was changed, FALSE otherwise. */ int wrapperCorrectWindowsPath(TCHAR *filename) { int result = FALSE; #ifdef WIN32 TCHAR *c; if (filename) { c = (TCHAR *)filename; while((c = _tcschr(c, TEXT('/'))) != NULL) { c[0] = TEXT('\\'); result = TRUE; } } #endif return result; } /** * Corrects a path in place by replacing all '\' characters with '/' * on NIX platforms. Does nothing on Windows platforms. * * filename - Filename to be modified. Could be null. * * @return TRUE if the filename was changed, FALSE otherwise. */ int wrapperCorrectNixPath(TCHAR *filename) { int result = FALSE; #ifndef WIN32 TCHAR *c; if (filename) { c = (TCHAR *)filename; while((c = _tcschr(c, TEXT('\\'))) != NULL) { c[0] = TEXT('/'); result = TRUE; } } #endif return result; } #ifndef WIN32 /** * Check if the given encoding is supported by the iconv library. * We can't use the 'iconv -l' because it is not supported on all platforms. * * @return ICONV_ENCODING_SUPPORTED if the encoding is supported, * ICONV_ENCODING_KNOWN_ISSUE if the encoding exist on iconv but fails to convert some characters. * ICONV_ENCODING_NOT_SUPPORTED if the encoding is not supported. */ int getIconvEncodingMBSupport(const char* encodingMB) { iconv_t conv_desc; int ret; const char* fromcode = MB_UTF8; TCHAR *outputBufferW; if (encodingMB) { if (strcmp(encodingMB, fromcode) == 0) { /* On some platforms, it is not correct to open iconv with the same input and output encodings * (this is the case on HP-UX). We know that 'fromcode' should be supported, so return TRUE. * On AIX (and maybe other OS?), the case, dashes and punctuations are important! */ return ICONV_ENCODING_SUPPORTED; } conv_desc = wrapper_iconv_open(encodingMB, fromcode); if (conv_desc != (iconv_t)(-1)) { wrapper_iconv_close(conv_desc); /* On some platforms iconv may fail to convert some characters to certain encodings. * For example backslashs fail to be converted to 'SJIS' on FreeBSD 7. * The following condition can be improved as new issues are found. */ ret = multiByteToWideChar("\\", fromcode, (char *)encodingMB, &outputBufferW, FALSE); if (outputBufferW) { free(outputBufferW); } if (ret) { return ICONV_ENCODING_KNOWN_ISSUE; } else { return ICONV_ENCODING_SUPPORTED; } } } return ICONV_ENCODING_NOT_SUPPORTED; } /** * Check if the given encoding is supported by the iconv library. * * @return ICONV_ENCODING_SUPPORTED if the encoding is supported, * ICONV_ENCODING_KNOWN_ISSUE if the encoding exist on iconv but fails to convert some characters. * ICONV_ENCODING_NOT_SUPPORTED if the encoding is not supported. */ int getIconvEncodingSupport(const TCHAR* encoding) { size_t size; char *encodingMB = NULL; int result = FALSE; if (encoding) { size = wcstombs(NULL, encoding, 0); if (size > (size_t)0) { encodingMB = malloc(size + 1); if(encodingMB) { wcstombs(encodingMB, encoding, size + 1); result = getIconvEncodingMBSupport(encodingMB); free(encodingMB); } } } return result; } #endif #ifdef FREEBSD /** * Get the name of the iconv library that was loaded. * * @return the name of the iconv library (wide chars). */ TCHAR* getIconvLibName() { mbstowcs(iconvLibNameW, iconvLibNameMB, 128); return iconvLibNameW; } /** * Locate an iconv function in the dynamically loaded library. * * @param libHandle handle returned from a call to dlopen() * @param fptr pointer to the function * @param fname1 first name to search * @param fname2 second name to search * @param fname3 third name to search * * @return TRUE if there were any problems, FALSE otherwise. */ int locateIconvFunction(void *libHandle, void **fptr, const char *fname1, const char *fname2, const char *fname3) { const char *error1; const char *error2; const char *error3; void *func = *fptr; *(void**)(&func) = dlsym(libHandle, fname1); if (!func) { /* The string that dlerror is in a static buffer and should not be freed. It must be immediately used or copied. */ error1 = dlerror(); *(void**)(&func) = dlsym(libHandle, fname2); if (!func) { error2 = dlerror(); *(void**)(&func) = dlsym(libHandle, fname3); if (!func) { error3 = dlerror(); printf("Failed to locate the %s function from the iconv library (%s): %s\n", fname1, iconvLibNameMB, (error1 ? error1 : "")); printf("Failed to locate the %s function from the iconv library (%s): %s\n", fname2, iconvLibNameMB, (error2 ? error2 : "")); printf("Failed to locate the %s function from the iconv library (%s): %s\n", fname3, iconvLibNameMB, (error3 ? error3 : "")); printf("Unable to continue.\n"); return TRUE; } } } *fptr = func; return FALSE; } /** * Tries to load libiconv and then fallback in FreeBSD. * Unfortunately we can not do any pretty logging here as iconv is * required for all of that to work. * Limitation: currently the function will not try the next library * if the iconv functions failed to load correctly. * * @return TRUE if there were any problems, FALSE otherwise. */ int loadIconvLibrary() { void *libHandle; const char *error; /* After 2013-10-08 (254273), FreeBSD 10-x have the iconv functions in libc. * Unfortunately there is a problem on the handle when opening libc dynamically. * We assume there is always at least one of the following libraries on the system. */ /* iconv library name present from FreeBSD 7 to 9 */ strncpy(iconvLibNameMB, "/usr/local/lib/libiconv.so", 128); libHandle = dlopen(iconvLibNameMB, RTLD_NOW); /* Falling back to libbiconv library in FreeBSD 10 */ if (libHandle == NULL) { strncpy(iconvLibNameMB, "/usr/local/lib/libbiconv.so", 128); libHandle = dlopen(iconvLibNameMB, RTLD_NOW); } /* Falling back to libkiconv.4 in FreeBSD 10 */ if (libHandle == NULL && _tcscmp(wrapperBits, TEXT("32")) == 0) { /* If the 32-bit version of the Wrapper is running on a 64-bit system, * the correct library is /usr/lib32/libkiconv.so.4. * Be careful here as not being able to find the library doesn't * necessarily mean that the system is 32-bit. */ strncpy(iconvLibNameMB, "/usr/lib32/libkiconv.so.4", 128); libHandle = dlopen(iconvLibNameMB, RTLD_NOW); } if (libHandle == NULL) { strncpy(iconvLibNameMB, "/lib/libkiconv.so.4", 128); libHandle = dlopen(iconvLibNameMB, RTLD_NOW); } /* No library found, we cannot continue as we need iconv support */ if (!libHandle) { /* The string that dlerror is in a static buffer and should not be freed. It must be immediately used or copied. */ error = dlerror(); printf("Failed to locate the iconv library: %s\n", (error ? error : "")); printf("Unable to continue.\n"); return TRUE; } /* Look up the required functions. Return true if any of them could not be found. */ return locateIconvFunction(libHandle, (void**)&wrapper_iconv_open, "iconv_open", "libiconv_open", "__bsd_iconv_open") || locateIconvFunction(libHandle, (void**)&wrapper_iconv, "iconv", "libiconv", "__bsd_iconv") || locateIconvFunction(libHandle, (void**)&wrapper_iconv_close, "iconv_close", "libiconv_close", "__bsd_iconv_close"); } #endif #ifdef DEBUG_MALLOC /* There can't be any more malloc calls after the malloc2 function in this file. */ #undef malloc void *malloc2(size_t size, const char *file, int line, const char *func, const char *sizeVar) { void *ptr; #ifdef WIN32 wprintf(L"%S:%d:%S malloc(%S) -> malloc(%d)", file, line, func, sizeVar, size); #else wprintf(L"%s:%d:%s malloc(%s) -> malloc(%d)", file, line, func, sizeVar, size); #endif ptr = malloc(size); wprintf(L" -> %p\n", ptr); return ptr; } #endif wrapper_3.5.51_src/src/c/wrapper_i18n.h100644 0 0 54321 14333053650 15075 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ #ifndef _LOCALIZE #define _LOCALIZE #include #ifndef WIN32 #define MBSTOWCS_QUERY_LENGTH 0 #ifdef UNICODE #include #ifdef _sntprintf #undef _sntprintf #endif #include #include #include #include #include #include #include #include #include #define __max(x,y) (((x) > (y)) ? (x) : (y)) #define __min(x,y) (((x) < (y)) ? (x) : (y)) #if defined(SOLARIS) || defined(HPUX) #define WRAPPER_USE_PUTENV #endif #ifdef FREEBSD #include #endif #if defined(MACOSX) || defined(HPUX) || defined(FREEBSD) || defined(SOLARIS) #ifndef wcscasecmp extern int wcscasecmp(const wchar_t* s1, const wchar_t* s2); #define ECSCASECMP #endif #endif #define TEXT(x) L##x typedef wchar_t TCHAR; typedef wchar_t _TUCHAR; extern int _tprintf(const wchar_t *fmt,...) ; extern int multiByteToWideChar(const char *multiByteChars, const char *multiByteEncoding, char *interumEncoding, wchar_t **outputBuffer, int localizeErrorMessage); extern int converterMBToWide(const char *multiByteChars, const char *multiByteEncoding, wchar_t **outputBufferW, int localizeErrorMessage); #define _taccess _waccess #define _tstoi64 _wtoi64 #define _ttoi64 _wtoi64 #define cgetts _cgetws extern int _tchdir(const TCHAR *path); extern int _texecvp(TCHAR* arg, TCHAR **cmd); extern int _tmkfifo(TCHAR* arg, mode_t mode); #define _tchmod _wchmod extern int _tchown(const TCHAR *path, uid_t owner, gid_t group); #define _tcprintf _cwprintf #define _cputts _cputws #define _tcreat _wcreat #define _tcscanf _cwscanf #define _tctime64 _wctime64 #define _texecl _wexecl #define _texecle _wexecle #define _texeclp _wexeclp #define _texeclpe _wexeclpe #define _texecv _wexecv extern int _texecve(TCHAR* arg, TCHAR **cmd, TCHAR** env); #define _texecvpe _wexecvpe #define _tfdopen _wfdopen #define _fgettchar _fgetwchar #define _tfindfirst _wfindfirst #define _tfindnext64 _wfindnext64 #define _tfindnext _wfindnext #define _tfindnexti64 _wfindnexti64 #define _fputtchar _fputwchar #define _tfsopen _wfsopen #define _tfullpath _wfullpath #define _gettch _getwch #define _gettche _getwche extern TCHAR* _tgetcwd(TCHAR *buf, size_t size); #define _tgetdcwd _wgetdcwd #define _ltot _ltow #define _tmakepath _wmakepath #define _tmkdir _wmkdir #define _tmktemp _wmktemp extern int _topen(const TCHAR *path, int oflag, mode_t mode); #define _puttch _putwch #if defined(WRAPPER_USE_PUTENV) extern int _tputenv(const TCHAR *string); #else extern int _tsetenv(const TCHAR *name, const TCHAR *value, int overwrite); extern void _tunsetenv(const TCHAR *name); #endif #define _trmdir _wrmdir #define _sctprintf _scwprintf #define _tsearchenv _wsearchenv #define _sntscanf _snwscanf #define _tsopen _wsopen #define _tspawnl _wspawnl #define _tspawnle _wspawnle #define _tspawnlp _wspawnlp #define _tspawnlpe _wspawnlpe #define _tspawnv _wspawnv #define _tspawnve _wspawnve #define _tspawnvp _wspawnvp #define _tspawnvpe _wspawnvpe #define _tsplitpath _wsplitpath #define _tstat64 _wstat64 extern int _tstat(const wchar_t* filename, struct stat *buf); #define _tstati64 _wstati64 #define _tstrdate _wstrdate #define _tcsdec _wcsdec #define _tcsdup wcsdup /* replaced _wcsdup by wcsdup - but both not supported on Zos */ #define _tcsicmp wcscasecmp /* Intentionally do not allow use of _trealpath because it does not specify a buffer length. * #define _trealpath * Define our own _trealpathN below. */ extern wchar_t* _trealpathN(const wchar_t* fileName, wchar_t *resolvedName, size_t resolvedNameSize); #define _tcsicoll _wcsicoll #define _tcsinc _wcsinc #define _tcslwr _wcslwr #define _tcsnbcnt _wcsncnt #define _tcsnccnt _wcsncnt #define _tcsnccnt _wcsncnt #define _tcsnccoll _wcsncoll #define _tcsnextc _wcsnextc #define _tcsncicmp _wcsnicmp #define _tcsnicmp _wcsnicmp #define _tcsncicoll _wcsnicoll #define _tcsnicoll _wcsnicoll #define _tcsninc _wcsninc #define _tcsncset _wcsnset #define _tcsnset _wcsnset #define _tcsrev _wcsrev #define _tcsset _wcsset #define _tcsspnp _wcsspnp #define _tstrtime wcsftime #define _tcstoi64 _wcstoi64 #define _tcstoui64 _wcstoui64 #define _tcsupr _wcsupr #define _ttempnam _wtempnam #define _ui64tot _ui64tow #define _ultot _ultow #define _ungettch _ungetwch extern int _tunlink(const wchar_t* address); #define _tutime64 _wutime64 #define _tutime _wutime #define _vsctprintf _vscwprintf #if defined(HPUX) extern int _vsntprintf(wchar_t *ws, size_t n, const wchar_t *format, va_list arg); #else #define _vsntprintf vswprintf #endif #define _tasctime _wasctime #define _tstof _wtof #define _tstoi _wtoi #define _ttoi(x) wcstol(x, NULL, 10) #define _tstol _wtol #define _ttol _wtol #define _tctime _wctime #define _fgettc fgetwc #define _fgetts fgetws extern FILE* _tfopen(const wchar_t* file, const wchar_t* mode); extern FILE* _tpopen(const wchar_t* command, const wchar_t* mode); #define _fputtc fputwc #define _fputts fputws #define _tfreopen _wfreopen #define _ftscanf fwscanf #define _gettc getwc #define _gettchar getwchar /** * This Wrapper function internally does a malloc to generate the * Wide-char version of the return string. This must be freed by the caller. * Only needed inside the following: * #if !defined(WIN32) && defined(UNICODE) * #endif */ extern TCHAR * _tgetenv ( const TCHAR * name ); #define _getts getws #define _istalnum iswalnum #define _istalpha iswalpha #define _istascii iswascii #define _istcntrl iswcntrl #define _istdigit iswdigit #define _istgraph iswgraph #define _istleadbyte isleadbyte #define _istlower iswlower #define _istprint iswprint #define _istpunct iswpunct #define _istspace iswspace #define _istupper iswupper #define _istxdigit iswxdigit #define _tmain wmain #define _tperror _wperror /*_tprintf wprintf*/ #define _puttc putwc #define _puttchar putwchar #define _putts _putws extern int _tremove(const TCHAR *path); extern int _trename(const TCHAR *path, const TCHAR *to); extern void _topenlog(const TCHAR *ident, int logopt, int facility); extern void _tsyslog(int priority, const TCHAR *message); #define _tscanf wscanf extern TCHAR *_tsetlocale(int category, const TCHAR *locale) ; extern int _sntprintf(TCHAR *str, size_t size, const TCHAR *format, ...); #define _stprintf _sntprintf extern int _ftprintf(FILE *stream, const wchar_t *format, ...); #define _stscanf swscanf #define _tcscat wcscat #define _tcschr wcschr #define _tcscmp wcscmp #define _tcscoll wcscoll #define _tcscpy wcscpy #define _tcscspn wcscspn #define _tcserror _wcserror #define _tcsftime wcsftime #define _tcsclen wcslen #define _tcslen wcslen #define _tcsncat wcsncat #define _tcsnccat wcsncat #define _tcsnccmp wcsncmp #define _tcsncmp wcsncmp #define _tcsnccpy wcsncpy #define _tcsncpy wcsncpy #define _tcspbrk wcspbrk #define _tcsrchr wcsrchr #define _tcsspn wcsspn #define _tcsstr wcsstr #define _tcstod wcstod #define _tcstok wcstok #define _tcstol wcstol #define _tcstoul wcstoul #define _tcsxfrm wcsxfrm #define _tsystem _wsystem #define _ttmpnam _wtmpnam #define _totlower towlower #define _totupper towupper #define _ungettc ungetwc #define _vftprintf vfwprintf #define _vtprintf vwprintf #define _vstprintf vswprintf extern size_t _treadlink(TCHAR* exe, TCHAR* fullpath, size_t size); extern long _tpathconf(const TCHAR *path, int name); #else /* ASCII */ #define TEXT(x) x typedef char TCHAR; typedef unsigned char _TUCHAR; #define _tpathconf pathconf #define _taccess _access #define _treadlink readlink #define _tstoi64 _atoi64 #define _ttoi64 _atoi64 #define cgetts _cgets #define _tchdir chdir #define _tchmod chmod #define _tcprintf _cprintf #define _cputts _cputs #define _tcreat _creat #define _tcscanf _cscanf #define _tctime64 _ctime64 #define _tmkfifo mkfifo #define _texecl execl #define _texecle execle #define _texeclp execlp #define _texeclpe execlpe #define _texecv execv #define _texecve execve #define _texecvp execvp #define _texecvpe execvpe #define _tfdopen _fdopen #define _fgettchar _fgetchar #define _tfindfirst _findfirst #define _tfindnext64 _findnext64 #define _tfindnext _findnext #define _tfindnexti64 _findnexti64 #define _fputtchar _fputchar #define _tfsopen _fsopen #define _tfullpath _fullpath #define _gettch _getch #define _gettche _getche #define _tgetcwd getcwd #define _tgetdcwd getdcwd #define _ltot _ltoa #define _tmakepath _makepath #define _tmkdir _mkdir #define _tmktemp _mktemp #define _topen open #define _puttch _putch /*#define _tputenv putenv*/ #define _tsetenv setenv #define _tunsetenv unsetenv #define _trmdir _rmdir #define _sctprintf _scprintf #define _tsearchenv _searchenv #define _sntprintf _snprintf #define _sntscanf _snscanf #define _tsopen _sopen #define _tspawnl _spawnl #define _tspawnle _spawnle #define _tspawnlp _spawnlp #define _tspawnlpe _spawnlpe #define _tspawnv _spawnv #define _tspawnve _spawnve #define _tspawnvp _spawnvp #define _tspawnvpe _spawnvpe #define _tsplitpath _splitpath #define _tstat64 _stat64 #define _tstat stat #define _tstati64 _stati64 #define _tstrdate _strdate #define _tcsdec _strdec #define _tcsdup strdup /* replaced _strdup by strdup */ #define _tcsicmp strcasecmp #define _tcsicoll _stricoll #define _tcsinc _strinc /* Intentionally do not allow use of _trealpath because it does not specify a buffer length. * #define _trealpath realpath * Define our own _trealpathN below. */ #define _trealpathN(fileName, resolvedName, resolvedNameSize) realpath(fileName, resolvedName) #define _tcslwr _strlwr #define _tcsnbcnt _strncnt #define _tcsnccnt _strncnt #define _tcsnccnt _strncnt #define _tcsnccoll _strncoll #define _tcsnextc _strnextc #define _tcsncicmp _strnicmp #define _tcsnicmp _strnicmp #define _tcsncicoll _strnicoll #define _tcsnicoll _strnicoll #define _tcsninc _strninc #define _tcsncset _strnset #define _tcsnset _strnset #define _tcsrev _strrev #define _tcsset _strset #define _tcsspnp _strspnp #define _tstrtime strftime #define _tcstoi64 _strtoi64 #define _tcstoui64 _strtoui64 #define _tcsupr _strupr #define _ttempnam _tempnam #define _ui64tot _ui64toa #define _ultot _ultoa #define _ungettch _ungetch #define _tunlink unlink #define _tutime64 _utime64 #define _tutime _utime #define _vsctprintf _vscprintf #define _vsntprintf vsnprintf #define _tasctime asctime #define _tstof atof #define _tstoi atoi #define _ttoi atoi #define _tstol atol #define _ttol atol #define _tctime ctime #define _fgettc fgetc #define _fgetts fgets #define _tfopen fopen #define _tpopen popen #define _ftprintf fprintf #define _fputtc fputc #define _fputts fputs #define _tfreopen freopen #define _ftscanf fscanf #define _gettc getc #define _gettchar getchar #define _tgetenv getenv #define _getts gets #define _istalnum isalnum #define _istalpha isalpha #define _istascii isascii #define _istcntrl iscntrl #define _istdigit isdigit #define _istgraph isgraph #define _istlead islead #define _istleadbyte isleadbyte #define _istlegal islegal #define _istlower islower #define _istprint isprint #define _istpunct ispunct #define _istspace isspace #define _istupper isupper #define _istxdigit isxdigit #define _tmain main #define _tperror perror #define _tprintf printf #define _puttc putc #define _puttchar putchar #define _putts puts #define _tremove remove #define _trename rename #define _tscanf scanf #define _tsetlocale setlocale #define _sntprintf snprintf #define _stscanf sscanf #define _tcscat strcat #define _tcschr strchr #define _tcscmp strcmp #define _tcscoll strcoll #define _tcscpy strcpy #define _tcscspn strcspn #define _tcserror strerror #define _tcsftime strftime #define _tcsclen strlen #define _tcslen strlen #define _tcsncat strncat #define _tcsnccat strncat #define _tcsnccmp strncmp #define _tcsncmp strncmp #define _tcsnccpy strncpy #define _tcsncpy strncpy #define _tcspbrk strpbrk #define _tcsrchr strrchr #define _tcsspn strspn #define _tcsstr strstr #define _tcstod strtod #define _tcstok strtok #define _tcstol strtol #define _tcstoul strtoul #define _tcsxfrm strxfrm #define _tsystem system #define _ttmpnam tmpnam #define _totlower tolower #define _totupper toupper #define _ungettc ungetc #define _vftprintf vfprintf #define _vtprintf vprintf #define _vstprintf vsprintf #define _topenlog openlog #define _tsyslog syslog #endif #else /* WIN32 */ #include #include #include extern int multiByteToWideChar(const char *multiByteChars, int encoding, TCHAR **outputBufferW, int localizeErrorMessage); #endif /* Define boolean constants. */ #ifndef TRUE #define TRUE -1 #endif #ifndef FALSE #define FALSE 0 #endif #define STATUS_UNCHANGED 0 #define STATUS_ENABLED 1 #define STATUS_DISABLED 2 #define STATUS_UNKNOWN 3 #ifndef WIN32 #ifdef HPUX #define MB_UTF8 "utf8" #else #define MB_UTF8 "UTF-8" #endif #define __UTF8 MB_UTF8 #else #define __UTF8 CP_UTF8 #endif #define TCHAR_TAB TEXT('\t') #ifdef WIN32 #define FILE_SEPARATOR TEXT("\\") #define FILE_SEPARATOR_C TEXT('\\') #else #define FILE_SEPARATOR TEXT("/") #define FILE_SEPARATOR_C TEXT('/') #endif #define ENCODING_BUFFER_SIZE 32 /* ex: x-windows-iso2022jp */ void wrapperSecureZero(void* str, size_t size); void wrapperSecureFree(void* str, size_t size); void wrapperSecureFreeStrW(TCHAR* str); void wrapperSecureFreeStrMB(char* str); #ifdef USE_USLEEP int wrapperUsleep(unsigned long usec); #endif extern TCHAR* toLower(const TCHAR* value); extern TCHAR* toUpper(const TCHAR* value); extern void clearNonAlphanumeric(TCHAR* bufferIn, TCHAR* bufferOut); extern int compareEncodings(TCHAR* encoding1, TCHAR* encoding2, int ignoreCase, int ignorePunctuation); extern int compareEncodingsSysMode(TCHAR* encoding1, TCHAR* encoding2); #ifndef WIN32 /** * Get the encoding of the current locale. * * @param buffer output buffer * * @return the buffer or NULL if the encoding could not be retrieved. */ TCHAR* getCurrentLocaleEncoding(TCHAR* buffer); #endif #ifndef WIN32 #define ICONV_ENCODING_SUPPORTED 0 #define ICONV_ENCODING_KNOWN_ISSUE 1 #define ICONV_ENCODING_NOT_SUPPORTED 2 /** * Check if the given encoding is supported by the iconv library. * * @return ICONV_ENCODING_SUPPORTED if the encoding is supported, * ICONV_ENCODING_KNOWN_ISSUE if the encoding exist on iconv but fails to convert some characters. * ICONV_ENCODING_NOT_SUPPORTED if the encoding is not supported. */ int getIconvEncodingMBSupport(const char* encodingMB); /** * Check if the given encoding is supported by the iconv library. * * @return ICONV_ENCODING_SUPPORTED if the encoding is supported, * ICONV_ENCODING_KNOWN_ISSUE if the encoding exist on iconv but fails to convert some characters. * ICONV_ENCODING_NOT_SUPPORTED if the encoding is not supported. */ int getIconvEncodingSupport(const TCHAR* encoding); #endif #ifdef _LIBICONV_VERSION #ifdef AIX /* the AIX version of iconv.h doesn't have _LIBICONV_VERSION (need to check the other platforms) */ #define USE_LIBICONV_GNU #endif #endif #ifdef FREEBSD /** * Get the name of the iconv library that was loaded. * * @return the name of the iconv library (wide chars). */ extern TCHAR* getIconvLibName(); /** * Tries to load libiconv and then fallback in FreeBSD. * Unfortunately we can not do any pretty logging here as iconv is * required for all of that to work. * * @return TRUE if there were any problems, FALSE otherwise. */ extern int loadIconvLibrary(); #endif /** * Define a cross platform way to compare strings while ignoring case. */ #ifdef WIN32 #define strIgnoreCaseCmp _stricmp #else #define strIgnoreCaseCmp strcasecmp #endif #define strcmpIgnoreCase(str1, str2) _tcsicmp(str1, str2) /** * Function to get the system encoding name/number for the encoding * of the conf file * * @para String holding the encoding from the conf file * * @return TRUE if not found, FALSE otherwise * */ #ifdef WIN32 extern int getEncodingByName(char* encodingMB, int *encoding); #else extern int getEncodingByName(char* encodingMB, char** encoding); #endif #ifdef WIN32 /** * Converts a Wide string into a specific multibyte encoded string. * * @prarm wideChars The Wide string to be converted. * @param outputBufferMB Returns a newly malloced buffer containing the target MB chars. * Will contain an error message if the function returns TRUE. * If this is NULL then there was an out of memory problem. * Caller must free this up. * @param outputEncoding Output encoding to use. * * @return TRUE if there were any problems. False otherwise. */ extern int converterWideToMB(const TCHAR *wideChars, char **outputBufferMB, int outputEncoding); /** * Converts a native multibyte string into a specific multibyte encoded string. * * @param multiByteChars The original multi-byte chars. * @param inputEncoding The multi-byte encoding. * @param outputBufferMB Returns a newly malloced buffer containing the target MB chars. * Will contain an error message if the function returns TRUE. * If this is NULL then there was an out of memory problem. * Caller must free this up. * @param outputEncoding Output encoding to use. * * @return -1 if there were any problems. buffer size (>=0) if everything was Ok. */ extern int converterMBToMB(const char *multiByteChars, int inputEncoding, char **outputBufferMB, int outputEncoding); #else #ifdef HPUX /** * Turns on or off a fix used in converterMBToMB() */ void toggleIconvHpuxFix(int value); #endif /** * Converts a native multibyte string into a specific multibyte encoded string. * * @param multiByteChars The original multi-byte chars. * @param multiByteEncoding The multi-byte encoding. * @param outputBufferMB Returns a newly malloced buffer containing the target MB chars. * Will contain an error message if the function returns TRUE. * If this is NULL then there was an out of memory problem. * Caller must free this up. * @param outputEncoding Output encoding to use. * * @return -1 if there were any problems. buffer size (>=0) if everything was Ok. */ extern int converterMBToMB(const char *multiByteChars, const char *multiByteEncoding, char **outputBufferMB, const char *outputEncoding); /** * Converts a Wide string into a specific multibyte encoded string. * * @prarm wideChars The Wide string to be converted. * @param outputBufferMB Returns a newly malloced buffer containing the target MB chars. * Will contain an error message if the function returns TRUE. * If this is NULL then there was an out of memory problem. * Caller must free this up. * @param outputEncoding Output encoding to use. * * @return TRUE if there were any problems. False otherwise. */ extern int converterWideToMB(const TCHAR *wideChars, char **outputBufferMB, const char *outputEncoding); #endif /** * Gets the error code for the last operation that failed. */ extern int wrapperGetLastError(); /* * Corrects a path in place by replacing all '/' characters with '\' * on Windows platforms. Does nothing on NIX platforms. * * filename - Filename to be modified. Could be null. */ extern int wrapperCorrectWindowsPath(TCHAR *filename); /* * Corrects a path in place by replacing all '\' characters with '/' * on NIX platforms. Does nothing on Windows platforms. * * filename - Filename to be modified. Could be null. */ extern int wrapperCorrectNixPath(TCHAR *filename); #endif /* Helper defines used to help trace where certain calls are being made. */ /*#define DEBUG_MBSTOWCS*/ #ifdef DEBUG_MBSTOWCS #ifdef WIN32 #define mbstowcs(x,y,z) mbstowcs(x,y,z); wprintf(L"%S:%d:%S mbstowcs(%S, %S, %S) -> mbstowcs(%p, \"%S\", %d)\n", __FILE__, __LINE__, __FUNCTION__, #x, #y, #z, (void *)x, y, (int)z) #else #define mbstowcs(x,y,z) mbstowcs(x,y,z); wprintf(L"%s:%d:%s mbstowcs(%s, %s, %s) -> mbstowcs(%p, \"%s\", %d)\n", __FILE__, __LINE__, __FUNCTION__, #x, #y, #z, (void *)x, y, (int)z) #endif #endif /*#define DEBUG_MALLOC*/ #ifdef DEBUG_MALLOC extern void *malloc2(size_t size, const char *file, int line, const char *func, const char *sizeVar); #define malloc(x) malloc2(x, __FILE__, __LINE__, __FUNCTION__, #x) #ifdef WIN32 #define free(x) wprintf(L"%S:%d:%S free(%S) -> free(%p)\n", __FILE__, __LINE__, __FUNCTION__, #x, (void *)x); free(x) #else #define free(x) wprintf(L"%s:%d:%s free(%s) -> free(%p)\n", __FILE__, __LINE__, __FUNCTION__, #x, (void *)x); free(x) #endif #endif wrapper_3.5.51_src/src/c/wrapper_jvminfo.c100644 0 0 41012 14333053650 15752 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ #include #include #include #include #ifndef WIN32 #include #endif #include "logger.h" #include "wrapper.h" #include "wrapperinfo.h" #include "wrapper_jvminfo.h" #include "property.h" /** * Dispose a JavaVersion structure. * * @param javaVersion A pointer to JavaVersion. */ JavaVersion* createJavaVersion() { JavaVersion *javaVersion = malloc(sizeof(JavaVersion)); if (!javaVersion) { outOfMemory(TEXT("CJV"), 1); return NULL; } javaVersion->displayName = NULL; javaVersion->isUnknown = FALSE; return javaVersion; } /** * Dispose a JavaVersion structure. * * @param javaVersion A pointer to JavaVersion. */ void disposeJavaVersion(JavaVersion *javaVersion) { if (javaVersion) { if (javaVersion->displayName) { free(javaVersion->displayName); javaVersion->displayName = NULL; } free(javaVersion); } } /** * Parse the Java version (at the format returned by 'java -version') and retrieve the major, minor and revision components. * * @param javaVersionStr the string reprentation of the Java version as returned by 'java -version'. * @param major a pointer to the major component of the Java version. * @param minor a pointer to the minor component of the Java version. * @param revision a pointer to the revision component of the Java version. * @param defaultComponentValue value to use if minor or revision are not specified. * * @return TRUE if there is any error, FALSE otherwise. */ int parseJavaVersionInner(const TCHAR *javaVersionStr, unsigned int *major, unsigned int *minor, unsigned int *revision, unsigned int defaultComponentValue) { TCHAR c[16]; /* This size should be enough to get the version on the first line of the output. */ TCHAR *ptr; TCHAR *endptr; TCHAR *token; #if defined(UNICODE) && !defined(WIN32) TCHAR *state = NULL; #endif if (!javaVersionStr || (_tcslen(javaVersionStr) == 0)) { return TRUE; } /* Work on a copy. */ _tcsncpy(c, javaVersionStr, 15); c[15] = 0; /* JVM < 9 are at the format '1.x' - skip the first 2 characters. */ if ((*c == TEXT('1')) && (*(c+1) == TEXT('.'))) { ptr = c + 2; } else { ptr = c; } /* Get the major version. */ if (ptr[0] == TEXT('.')) { /* Make sure there is no leading delimiters that _tcstok() would skip. */ return TRUE; } token = _tcstok(ptr, TEXT(".") #if defined(UNICODE) && !defined(WIN32) , &state #endif ); errno = 0; *major = (unsigned int)_tcstoul(token, &endptr, 10); if ((errno != 0) || (token == endptr) || (*endptr)) { /* invalid || no digits were read || additional characters remain */ return TRUE; } if (major == 0) { return TRUE; } /* Get the minor version (the next delimiter can be a '_' or a '.'). */ token = _tcstok(NULL, TEXT("_.") #if defined(UNICODE) && !defined(WIN32) , &state #endif ); if (token) { if (*(token - 2) == 0) { /* Make sure _tcstok() didn't skip an empty token (processed delimiters are replaced by 0). */ return TRUE; } errno = 0; *minor = (unsigned int)_tcstoul(token, &endptr, 10); if ((errno != 0) || (token == endptr) || (*endptr)) { /* invalid || no digits were read || additional characters remain */ return TRUE; } /* Get the revision. */ token = _tcstok(NULL, TEXT("_.") #if defined(UNICODE) && !defined(WIN32) , &state #endif ); if (token) { if (*(token - 2) == 0) { /* Make sure _tcstok() didn't skip an empty token (processed delimiters are replaced by 0). */ return TRUE; } errno = 0; *revision = (unsigned int)_tcstoul(token, &endptr, 10); if ((errno != 0) || (token == endptr) || (*endptr)) { /* invalid || no digits were read || additional characters remain */ return TRUE; } } else { *revision = defaultComponentValue; } } else { *minor = defaultComponentValue; *revision = defaultComponentValue; } return FALSE; } /** * Parse the output of 'java -version' and retrieve the major, minor and revision components of the version. * Note: The output will modified by the function. * * @param javaVersionStr the string reprentation of the Java version as returned by 'java -version'. * @param defaultComponentValue value to use if minor or revision are not specified. * * @return a pointer to the newly created JavaVersion structure, or NULL on error. */ JavaVersion* parseJavaVersion(const TCHAR *javaVersionStr, unsigned int defaultComponentValue) { JavaVersion* result = NULL; if (javaVersionStr) { result = createJavaVersion(); if (result) { if (strcmpIgnoreCase(javaVersionStr, TEXT("UNLIMITED")) == 0) { result->major = UINT_MAX; result->minor = UINT_MAX; result->revision = UINT_MAX; } else if (parseJavaVersionInner(javaVersionStr, &result->major, &result->minor, &result->revision, defaultComponentValue)) { disposeJavaVersion(result); return NULL; } updateStringValue(&result->displayName, javaVersionStr); } } return result; } /** * Get the Java version from a configuration property. * * @param propName name of the configuration property. * @param defaultValue default value. * @param minVersion the minimum version allowed. * @param minVersionName if specified will be printed when the value * of the property is less than minVersion. * @param defaultComponentValue value to use if minor or revision are not specified. * * @param javaVersion A pointer to JavaVersion. */ JavaVersion* getJavaVersionProperty(TCHAR *propName, TCHAR *defaultValue, JavaVersion *minVersion, const TCHAR *minVersionName, unsigned int defaultComponentValue) { JavaVersion* result = NULL; const TCHAR* propValue; propValue = getNotEmptyStringProperty(properties, propName, defaultValue); if (propValue) { result = parseJavaVersion(propValue, defaultComponentValue); if (!result) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Encountered an invalid value for configuration property %s=%s."), propName, propValue); } else if (minVersion && compareJavaVersion(result, minVersion) < 0) { if (minVersionName) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Encountered an invalid value for configuration property %s=%s.\n The target version must be greater than or equal to the value of %s (%s)."), propName, result->displayName, minVersionName, minVersion->displayName); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Encountered an invalid value for configuration property %s=%s.\n The target version must be greater than or equal to %s."), propName, result->displayName, minVersion->displayName); } disposeJavaVersion(result); result = NULL; } } return result; } /** * Get the minimum required version of Java for the Wrapper to run. * * @return javaVersion A pointer to JavaVersion. */ JavaVersion* getMinRequiredJavaVersion() { JavaVersion* result = createJavaVersion(); if (result) { parseJavaVersionInner(wrapperJavacTargetVersion, &(result->major), &(result->minor), &(result->revision), 0); updateStringValue(&result->displayName, wrapperJavacTargetVersion); } return result; } /** * Get the maximum required version of Java for the Wrapper to run. * * @return javaVersion A pointer to JavaVersion. */ JavaVersion* getMaxRequiredJavaVersion() { JavaVersion* result = createJavaVersion(); if (result) { result->major = UINT_MAX; result->minor = UINT_MAX; result->revision = UINT_MAX; updateStringValue(&result->displayName, TEXT("UNLIMITED")); } return result; } /** * Parse the output of 'java -version' and retrieve the major, minor and revision components of the version. * Note: The output will modified by the function. * * @param javaOutput the output returned by 'java -version'. * @param pJavaVersionStr a pointer to the string reprentation of the Java version contained in javaOutput. * @param major a pointer to the major component of the Java version. * @param minor a pointer to the minor component of the Java version. * @param revision a pointer to the revision component of the Java version. * * @return TRUE if there is any error, FALSE otherwise. */ int parseOutputJavaVersionInner(TCHAR *javaOutput, TCHAR **pJavaVersionStr, unsigned int *major, unsigned int *minor, unsigned int *revision) { TCHAR *ptr; if (!javaOutput) { return TRUE; } /* Start after the first double quote. */ *pJavaVersionStr = _tcschr(javaOutput, TEXT('\"')); if (!*pJavaVersionStr) { /* Not found - fail. */ return TRUE; } (*pJavaVersionStr)++; /* Crop at the closing double quote. */ ptr = _tcschr(*pJavaVersionStr, TEXT('\"')); if (!ptr) { /* Not found - fail. */ return TRUE; } *ptr = 0; ptr = *pJavaVersionStr; while (*ptr) { if (!_istdigit(*ptr) && (*ptr != '.') && (*ptr != '_')) { /* Some JVM implementations (or customized JVMs) append a label after the version. * Examples: "1.8.0.14-hp-ux", "9-Raspbian". */ *ptr = 0; break; } ptr++; } return parseJavaVersionInner(*pJavaVersionStr, major, minor, revision, 0); } /** * Parse the output of 'java -version' and retrieve the major, minor and revision components of the version. * Note: The output will modified by the function. * * @param javaOutput the output returned by 'java -version'. * * @return a pointer to the newly created JavaVersion structure, or NULL on error. */ JavaVersion* parseOutputJavaVersion(TCHAR *javaOutput) { TCHAR* displayName; JavaVersion* result = NULL; if (javaOutput) { result = createJavaVersion(); if (result) { if (parseOutputJavaVersionInner(javaOutput, &displayName, &result->major, &result->minor, &result->revision)) { disposeJavaVersion(result); return NULL; } updateStringValue(&result->displayName, displayName); } } return result; } /** * Compare two versions of Java. * * @param version1 version of Java * @param version2 version of Java * * @return <0 if version1 < version2 * 0 if version1 == version2 * >0 if version1 > version2 */ int compareJavaVersion(JavaVersion *version1, JavaVersion* version2) { if (version1 && version2) { if (version1->major < version2->major) { return -1; } else if (version1->major > version2->major) { return 1; } else { if (version1->minor < version2->minor) { return -1; } else if (version1->minor > version2->minor) { return 1; } else { if (version1->revision < version2->revision) { return -1; } else if (version1->revision > version2->revision) { return 1; } } } } else if (version1) { return 1; } else if (version2) { return -1; } return 0; } /** * Parse the output of 'java -version' and retrieve the maker (implementation) of the JVM. * * @param output the output returned by 'java -version' (or only the line of the output containing the maker). * * @return an integer representing the JVM implementation: * JVM_VENDOR_UNKNOWN * JVM_VENDOR_ORACLE * JVM_VENDOR_OPENJDK * JVM_VENDOR_IBM */ int parseOutputJvmVendor(TCHAR* output) { if (output) { if (_tcsstr(output, TEXT("IBM"))) { return JVM_VENDOR_IBM; } else if (_tcsstr(output, TEXT("OpenJDK"))) { return JVM_VENDOR_OPENJDK; } else if (_tcsstr(output, TEXT("Java HotSpot"))) { return JVM_VENDOR_ORACLE; } } return JVM_VENDOR_UNKNOWN; } /** * Get the name of a JVM maker. * * @param maker an integer representing the JVM implementation: * JVM_VENDOR_UNKNOWN * JVM_VENDOR_ORACLE * JVM_VENDOR_OPENJDK * JVM_VENDOR_IBM * * @return the name of the JVM maker. */ const TCHAR* getJvmVendorName(int jvmVendor) { TCHAR *name; switch (jvmVendor) { case JVM_VENDOR_ORACLE: name = TEXT("Oracle"); break; case JVM_VENDOR_OPENJDK: name = TEXT("OpenJDK"); break; case JVM_VENDOR_IBM: name = TEXT("IBM"); break; default: name = TEXT("Unknown"); break; } return name; } /** * Parse the output of 'java -version' and retrieve the bits of the JVM. * * @param output the output returned by 'java -version' (or only the line of the output containing the bits). * * @return an integer which indicates the bits of the JVM: * JVM_BITS_64 * JVM_BITS_32 * JVM_BITS_UNKNOWN */ int parseOutputJvmBits(TCHAR* output, JavaVersion *optionalJavaVersion) { if (output) { /* This is experimental and may not work for all JVMs. * For example a IBM JVM will return the following output: * IBM J9 VM (build 2.3, J2RE 1.5.0 IBM J9 2.3 Linux ppc64-64 j9vmxp6423-20130203 (JIT enabled). * Should we add a check on the Maker? */ if (_tcsstr(output, TEXT("64-Bit")) || _tcsstr(output, TEXT("64-bit")) || _tcsstr(output, TEXT("64 Bit")) || _tcsstr(output, TEXT("64 bit")) || _tcsstr(output, TEXT("-64 ")) || _tcsstr(output, TEXT("ppc64"))) { return JVM_BITS_64; } else { #if defined(HPUX) || defined(MACOSX) || defined(SOLARIS) || defined(FREEBSD) /* On these systems, the JVM (for version < 9) can operate both in 32-bit and 64-bits. * To confirm the bits, we would need to first run 'java -version', then if and only if * the version is less than 9, run 'java -d64 -version' (-d64 is invalid for Java 9+). */ if ((optionalJavaVersion == NULL) || (optionalJavaVersion->major < 9)) { return JVM_BITS_UNKNOWN; } #else return JVM_BITS_32; #endif } } return JVM_BITS_UNKNOWN; } /** * Get a string representing the bits of the JVM. * * @param bits an integer which indicates the bits of the JVM: * JVM_BITS_64 * JVM_BITS_32 * JVM_BITS_UNKNOWN * * @return the string representing the bits of the JVM. */ const TCHAR* getJvmBitsName(int jvmBits) { TCHAR *name; switch (jvmBits) { case JVM_BITS_64: name = TEXT("64-Bit"); break; case JVM_BITS_32: name = TEXT("32-Bit"); break; default: name = TEXT("Unknown"); break; } return name; } wrapper_3.5.51_src/src/c/wrapper_jvminfo.h100644 0 0 12511 14333053650 15761 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ #ifndef _WRAPPER_JVMINFO_H #define _WRAPPER_JVMINFO_H #include "wrapper_i18n.h" #define JVM_VENDOR_UNKNOWN 0 #define JVM_VENDOR_ORACLE 1 #define JVM_VENDOR_OPENJDK 2 #define JVM_VENDOR_IBM 3 #define JVM_BITS_UNKNOWN 0 #define JVM_BITS_32 32 #define JVM_BITS_64 64 typedef struct JavaVersion JavaVersion; struct JavaVersion { TCHAR *displayName; /* The name that will be used to display the version in the log output. When it comes from the configuration, it is the value as set by the user. */ unsigned int major; /* The major component of the version. */ unsigned int minor; /* The minor component of the version. */ unsigned int revision; /* The revision component of the version. */ int isUnknown; /* Flag that indicates whether the Java version was parsed successfully or not. If not, it will be resolved to the lowest supported JVM version. */ }; /** * Dispose a JavaVersion structure. * * @param javaVersion A pointer to JavaVersion. */ void disposeJavaVersion(JavaVersion *javaVersion); /** * Get the Java version from a configuration property. * * @param propName name of the configuration property. * @param defaultValue default value. * @param minVersion the minimum version allowed. * @param minVersionName if specified will be printed when the value * of the property is less than minVersion. * @param defaultComponentValue value to use if minor or revision are not specified. * * @param javaVersion A pointer to JavaVersion. */ JavaVersion* getJavaVersionProperty(TCHAR *propName, TCHAR *defaultValue, JavaVersion *minVersion, const TCHAR *minVersionName, unsigned int defaultComponentValue); /** * Get the minimum required version of Java for the Wrapper to run. * * @param javaVersion A pointer to JavaVersion. */ JavaVersion* getMinRequiredJavaVersion(); /** * Get the maximum required version of Java for the Wrapper to run. * * @param javaVersion A pointer to JavaVersion. */ JavaVersion* getMaxRequiredJavaVersion(); /** * Parse the output of 'java -version' and retrieve the major, minor and revision components of the version. * Note: The output will modified by the function. * * @param javaOutput the output returned by 'java -version'. * * @return a pointer to the newly created JavaVersion structure, or NULL on error. */ JavaVersion* parseOutputJavaVersion(TCHAR *javaOutput); /** * Compare two versions of Java. * * @param version1 version of Java * @param version2 version of Java * * @return <0 if version1 < version2 * 0 if version1 == version2 * >0 if version1 > version2 */ int compareJavaVersion(JavaVersion *version1, JavaVersion* version2); /** * Parse the output of 'java -version' and retrieve the maker (implementation) of the JVM. * * @param output the output returned by 'java -version' (or only the line of the output containing the maker). * * @return an integer representing the JVM implementation: * JVM_VENDOR_UNKNOWN * JVM_VENDOR_ORACLE * JVM_VENDOR_OPENJDK * JVM_VENDOR_IBM */ int parseOutputJvmVendor(TCHAR* output); /** * Get the name of a JVM maker. * * @param maker an integer representing the JVM implementation: * JVM_VENDOR_UNKNOWN * JVM_VENDOR_ORACLE * JVM_VENDOR_OPENJDK * JVM_VENDOR_IBM * * @return the name of the JVM maker. */ const TCHAR* getJvmVendorName(int jvmVendor); /** * Parse the output of 'java -version' and retrieve the bits of the JVM. * * @param output the output returned by 'java -version' (or only the line of the output containing the bits). * * @return an integer which indicates the bits of the JVM: * JVM_BITS_64 * JVM_BITS_32 * JVM_BITS_UNKNOWN */ int parseOutputJvmBits(TCHAR* output, JavaVersion *optionalJavaVersion); /** * Get a string representing the bits of the JVM. * * @param bits an integer which indicates the bits of the JVM: * JVM_BITS_64 * JVM_BITS_32 * JVM_BITS_UNKNOWN * * @return the string representing the bits of the JVM. */ const TCHAR* getJvmBitsName(int jvmBits); #endif wrapper_3.5.51_src/src/c/wrapper_ulimit.c100644 0 0 42342 14333053650 15614 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ #ifndef WIN32 #include #include "logger.h" #include "property.h" #include "wrapper.h" #include "wrapper_ulimit.h" #if defined(LINUX) && defined(__USE_FILE_OFFSET64) #define WRAPPER_RLIM_INFINITY ((unsigned long int)(~0UL)) #else #define WRAPPER_RLIM_INFINITY RLIM_INFINITY #endif PResourceLimit getResourceProperty(Properties *properties, const TCHAR *propertyName, const int multiplier) { const TCHAR* value; PResourceLimit result; result = malloc(sizeof(ResourceLimit)); if (!result) { outOfMemoryQueued(TEXT("GRSP"), 1); } else { result->useCurrent = FALSE; result->useHard = FALSE; result->isValid = FALSE; value = getStringProperty(properties, propertyName, TEXT("current")); if ((strcmpIgnoreCase(value, TEXT("current")) == 0) || (value[0] == 0)) { result->value = 0; result->useCurrent = TRUE; result->isValid = TRUE; } else if (strcmpIgnoreCase(value, TEXT("hard")) == 0) { result->value = 0; if (_tcsstr(propertyName, TEXT("soft"))) { result->useHard = TRUE; } else { result->useCurrent = TRUE; } result->isValid = TRUE; } else if (strcmpIgnoreCase(value, TEXT("unlimited")) == 0) { result->value = WRAPPER_RLIM_INFINITY; result->isValid = TRUE; } else if (value[0] == TEXT('-')) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Encountered an invalid value '%s' in the %s property."), value, propertyName); } else { result->value = (rlim_t)(_tcstoul(value, NULL, 10) * multiplier); if (((result->value == 0) && (errno != 0))) { /* Failed to convert to an integer. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Encountered an invalid value '%s' in the %s property."), value, propertyName); } else { result->isValid = TRUE; } } } return result; } PResourceLimits getResourcePropertyPair(Properties *properties, const TCHAR *propertyBaseName, const int multiplier) { TCHAR propSoft[MAX_PROPERTY_NAME_LENGTH]; TCHAR propHard[MAX_PROPERTY_NAME_LENGTH]; PResourceLimit softLimit; PResourceLimit hardLimit; PResourceLimits result = NULL; _sntprintf(propSoft, MAX_PROPERTY_NAME_LENGTH, TEXT("%s.soft"), propertyBaseName); propSoft[MAX_PROPERTY_NAME_LENGTH-1] = 0; _sntprintf(propHard, MAX_PROPERTY_NAME_LENGTH, TEXT("%s.hard"), propertyBaseName); propHard[MAX_PROPERTY_NAME_LENGTH-1] = 0; softLimit = getResourceProperty(properties, propSoft, multiplier); hardLimit = getResourceProperty(properties, propHard, multiplier); if (softLimit && softLimit->isValid && hardLimit && hardLimit->isValid) { result = malloc(sizeof(ResourceLimits)); if (!result) { outOfMemoryQueued(TEXT("GRSPP"), 1); } else { result->rlim_cur = softLimit; result->rlim_max = hardLimit; } } else { if (softLimit) { free(softLimit); } if (hardLimit) { free(hardLimit); } } return result; } void disposeResourceLimits(PResourceLimits limits) { if (limits) { if (limits->rlim_cur) { free(limits->rlim_cur); } if (limits->rlim_max) { free(limits->rlim_max); } free(limits); } } TCHAR* printRlim(rlim_t value, TCHAR* buffer, const int divisor) { /* On Linux 32-bit, the value can be greater than (unsigned long int)(~0UL) * when the compilation option __USE_FILE_OFFSET64 is used. */ if (value >= WRAPPER_RLIM_INFINITY) { _sntprintf(buffer, 32, TEXT("unlimited")); } else { _sntprintf(buffer, 32, TEXT("%lu"), (unsigned long)(value/divisor)); } buffer[31] = 0; return buffer; } int setResourceLimits(int resourceId, const TCHAR* resourceName, const TCHAR* propertyBaseName, PResourceLimits confLimits, int strict, const int divisor) { struct rlimit oldLimits, newLimits, checkLimits; TCHAR limBuf1[32]; TCHAR limBuf2[32]; TCHAR limBuf3[32]; TCHAR limBuf4[32]; int logLevel; int errorNum = 0; int setResult; if (!confLimits->rlim_cur->useCurrent || !confLimits->rlim_max->useCurrent) { /* The user has specified limits for the number of open file descriptors. */ if (!confLimits->rlim_cur->useCurrent && !confLimits->rlim_cur->useHard && !confLimits->rlim_max->useCurrent && (confLimits->rlim_max->value < confLimits->rlim_cur->value)) { /* This is a configuration error, return 1 no matter we are strict or not. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("The soft limit (%s) for %s is set higher than the hard limit (%s)."), printRlim(confLimits->rlim_cur->value, limBuf1, divisor), resourceName, printRlim(confLimits->rlim_max->value, limBuf2, divisor)); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" Make sure to correctly set the values of the %s.soft and %s.hard properties."), propertyBaseName, propertyBaseName); return 1; } /* Get the limits for the resource. */ if (getrlimit(resourceId, &oldLimits) != 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to get the limits for %s: (0x%x)"), resourceName, errno); return 1; } /* Unless we fail to set the limits for some unknown reason, any error below will return 1 if we are strict, 0 otherwise. */ logLevel = strict ? LEVEL_FATAL : properties->logWarningLogLevel; /* Resolve the hard limit. */ if (confLimits->rlim_max->useCurrent) { /* Use the current value */ newLimits.rlim_max = oldLimits.rlim_max; } else { /* Use the configured value */ newLimits.rlim_max = confLimits->rlim_max->value; } /* Resolve the soft limit. */ if (confLimits->rlim_cur->useCurrent) { /* Use the current value */ newLimits.rlim_cur = oldLimits.rlim_cur; } else if (confLimits->rlim_cur->useHard) { /* Use the hard value */ newLimits.rlim_cur = newLimits.rlim_max; } else { /* Use the configured value */ newLimits.rlim_cur = confLimits->rlim_cur->value; } /* Resolve cases where the soft limit is greater than the hard limit. */ if (newLimits.rlim_max < newLimits.rlim_cur) { if (confLimits->rlim_max->useCurrent) { /* The user has only set the SOFT limit. */ log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("The soft limit (%s) for %s is set higher than the current hard limit (%s)."), printRlim(confLimits->rlim_cur->value, limBuf1, divisor), resourceName, printRlim(oldLimits.rlim_max, limBuf2, divisor)); if (strict) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" Make sure to correctly set the value of the %s.soft property."), propertyBaseName); return 1; } newLimits.rlim_cur = oldLimits.rlim_max; } else { /* The user has only set the HARD limit. */ log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("The hard limit (%s) for %s is set lower than the current soft limit (%s)."), printRlim(confLimits->rlim_max->value, limBuf1, divisor), resourceName, printRlim(oldLimits.rlim_cur, limBuf2, divisor)); if (strict) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" Make sure to correctly set the value of the %s.hard property."), propertyBaseName); return 1; } newLimits.rlim_cur = newLimits.rlim_max; } log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT(" Decreasing the soft limit to the value of the hard limit.")); } /* Try to set the limits */ setResult = setrlimit(resourceId, &newLimits); errorNum = errno; if (setResult == 0) { /* setrlimit() did not return an error but on some platforms, this doesn't mean that the configured values were set correctly. * For example on freeBSD, the limits for the number of open file descriptors can't be raised to a value greater than 1677, * but the function will silently increase the limits up to that maximum value. */ if (getrlimit(resourceId, &checkLimits) != 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to get the limits for %s: (0x%x)"), resourceName, errno); return 1; } if (checkLimits.rlim_max < confLimits->rlim_max->value) { /* Mark as an EINVAL error and continue. */ errorNum = EINVAL; setResult = -1; } else if (((checkLimits.rlim_max != newLimits.rlim_max) || (checkLimits.rlim_cur != newLimits.rlim_cur)) #if defined(LINUX) && defined(__USE_FILE_OFFSET64) && !defined(JSW64) /* On Linux 32-bit, we defined WRAPPER_RLIM_INFINITY to use the max value of unsigned long * (see comment near the definition of WRAPPER_RLIM_INFINITY). However, since we compile * with __USE_FILE_OFFSET64, the real unlimited value is the max of unsigned long long. * setrlimit() will set unlimited limits even with WRAPPER_RLIM_INFINITY being unsigned long, * but getrlimit() will collect greater values (max of unsigned long long). The limits are * set correctly, so just ignore this case. We will no longer need this when using C99. */ && !(newLimits.rlim_max == WRAPPER_RLIM_INFINITY && (checkLimits.rlim_max > newLimits.rlim_max)) && !(newLimits.rlim_cur == WRAPPER_RLIM_INFINITY && (checkLimits.rlim_cur > newLimits.rlim_cur)) #endif ) { /* This should never happen... */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to set the limits for %s (HARD: expected %s, got %s; SOFT: expected %s, got %s)."), resourceName, printRlim(newLimits.rlim_max , limBuf1, divisor), printRlim(checkLimits.rlim_max, limBuf2, divisor), printRlim(newLimits.rlim_cur , limBuf3, divisor), printRlim(checkLimits.rlim_cur, limBuf4, divisor)); return 1; } } if (setResult != 0) { /* Resolve cases where the configured hard limit is greater than the current hard limit. */ if ((oldLimits.rlim_max < confLimits->rlim_max->value) && ((errorNum == EPERM) || (errorNum == EINVAL))) { if (errorNum == EPERM) { log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("The process doesn't have sufficient privileges to raise the hard limit (from %s to %s) for %s."), printRlim(oldLimits.rlim_max, limBuf1, divisor), printRlim(confLimits->rlim_max->value, limBuf2, divisor), resourceName); } else { log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("Could not raise the hard limit (from %s to %s) for %s."), printRlim(oldLimits.rlim_max, limBuf1, divisor), printRlim(confLimits->rlim_max->value, limBuf2, divisor), resourceName); } if (strict) { if (errorNum == EPERM) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" Please run the Wrapper with sufficient privileges or adjust the value of the %s.hard property."), propertyBaseName); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" Please adjust the value of the %s.hard property."), propertyBaseName); } return 1; } newLimits.rlim_max = oldLimits.rlim_max; if (newLimits.rlim_max < newLimits.rlim_cur) { newLimits.rlim_cur = newLimits.rlim_max; log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT(" Ignoring the configured hard limit. Decreasing the configured soft limit to the value of the hard limit.")); } else { log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT(" Ignoring the configured hard limit.")); } /* Set again the limits. */ if (setrlimit(resourceId, &newLimits) != 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to set the limits for %s (0x%x)."), resourceName, errno); return 1; } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to set the limits for %s (0x%x)."), resourceName, errno); return 1; } } } return 0; } /** * Set the soft and hard resource limits. * For each resource, we can set the limits being strict or not. * - strict: the Wrapper will stop if it is not possible to set the limit as defined in the configuration. * - not strict: the Wrapper will try to adjust the hard and soft limits to be as close as possible to the * configuration and show warnings whenever a property is resolved to a different value. * The constraints are the following: - the soft limit can't be greater than the hard limit. * - the hard limit can only be raised by the root user. * * Returns 0 if no error. Otherwise returns 1. */ int loadResourcesLimitsConfiguration() { PResourceLimits nofileLimits; PResourceLimits dataLimits; int nofileStrict; int dataStrict; /* number of open file descriptors */ nofileLimits = getResourcePropertyPair(properties, TEXT("wrapper.ulimit.nofile"), 1); if (!nofileLimits) { return 1; } nofileStrict = getBooleanProperty(properties, TEXT("wrapper.ulimit.nofile.strict"), TRUE); if (setResourceLimits(RLIMIT_NOFILE, TEXT("the number of open file descriptors"), TEXT("wrapper.ulimit.nofile"), nofileLimits, nofileStrict, 1)) { disposeResourceLimits(nofileLimits); return 1; } disposeResourceLimits(nofileLimits); /* size of a process's data segment */ dataLimits = getResourcePropertyPair(properties, TEXT("wrapper.ulimit.data"), 1024); if (!dataLimits) { return 1; } dataStrict = getBooleanProperty(properties, TEXT("wrapper.ulimit.data.strict"), TRUE); if (setResourceLimits(RLIMIT_DATA, TEXT("the size of a process's data segment"), TEXT("wrapper.ulimit.data"), dataLimits, dataStrict, 1024)) { disposeResourceLimits(dataLimits); return 1; } disposeResourceLimits(dataLimits); return 0; } /** * Print out the soft and hard resource limits. */ void showResourceslimits() { struct rlimit limits; TCHAR limBuf1[32]; TCHAR limBuf2[32]; int logLevel = getLogLevelForName(getStringProperty(properties, TEXT("wrapper.ulimit.loglevel"), TEXT("DEBUG"))); if ((getLowLogLevel() <= logLevel) && (logLevel != LEVEL_NONE)) { if (getrlimit(RLIMIT_NOFILE, &limits) != 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to get the limits for the number of open file descriptors: (0x%x)"), errno); } else { log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("Number of open file descriptors limits: %s (soft), %s (hard)."), printRlim(limits.rlim_cur, limBuf1, 1), printRlim(limits.rlim_max, limBuf2, 1)); } if (getrlimit(RLIMIT_DATA, &limits) != 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to get the limits for the data segment size: (0x%x)"), errno); } else { log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("Data segment size limits: %s (soft), %s (hard)."), printRlim(limits.rlim_cur, limBuf1, 1024), printRlim(limits.rlim_max, limBuf2, 1024)); } } } #endif wrapper_3.5.51_src/src/c/wrapper_ulimit.h100644 0 0 3217 14333053650 15577 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ #ifndef WIN32 #ifndef _WRAPPER_ULIMIT_H #define _WRAPPER_ULIMIT_H #include #include "wrapper_i18n.h" typedef struct ResourceLimit ResourceLimit, *PResourceLimit; struct ResourceLimit { rlim_t value; int useCurrent; int useHard; int isValid; }; typedef struct ResourceLimits ResourceLimits, *PResourceLimits; struct ResourceLimits { ResourceLimit *rlim_max; ResourceLimit *rlim_cur; }; int loadResourcesLimitsConfiguration(); void showResourceslimits(); #endif #endif wrapper_3.5.51_src/src/c/wrapper_unix.c100644 0 0 323560 14333053650 15320 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ /** * Author: * Leif Mortenson * Ryan Shaw */ #ifndef WIN32 #ifdef LINUX #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "wrapper_i18n.h" #include "wrapper.h" #include "wrapperinfo.h" #include "property.h" #include "logger.h" #include "logger_file.h" #include "wrapper_file.h" #include "wrapper_encoding.h" #include #include #if defined(LINUX) || defined(MACOSX) || defined(AIX) #include #endif #ifndef USE_USLEEP #include #endif #ifndef getsid /* getpid links ok on Linux, but is not defined correctly. */ pid_t getsid(pid_t pid); #endif #define max(x,y) (((x) > (y)) ? (x) : (y)) #define min(x,y) (((x) < (y)) ? (x) : (y)) /* Define a global pipe descriptor so that we don't have to keep allocating * a new pipe each time a JVM is launched. */ int pipedes[2] = {-1, -1}; int pipeind[2] = {-1, -1}; /* pipe descriptor for stdin */ #define PIPE_READ_END 0 #define PIPE_WRITE_END 1 /** * maximum length for a user name should be 8, * but according to 'man useradd' it may be 32 */ #define MAX_USER_NAME_LENGTH 32 TCHAR wrapperClasspathSeparator = TEXT(':'); pthread_t javaIOThreadId; int javaIOThreadStarted = FALSE; int stopJavaIOThread = FALSE; int javaIOThreadStopped = FALSE; pthread_t javaINThreadId; int javaINThreadStarted = FALSE; int stopJavaINThread = FALSE; int javaINThreadStopped = FALSE; int timerThreadSet = FALSE; pthread_t timerThreadId; int timerThreadStarted = FALSE; int stopTimerThread = FALSE; int timerThreadStopped = FALSE; TICKS timerTicks = WRAPPER_TICK_INITIAL; TICKS stopSignalLastTick = WRAPPER_TICK_INITIAL; /** Flag which keeps track of whether or not PID files should be deleted on shutdown. */ int cleanUpPIDFilesOnExit = FALSE; /****************************************************************************** * Platform specific methods *****************************************************************************/ /** * exits the application after running shutdown code. */ void appExit(int exitCode, int argc, TCHAR** argv) { static int isExiting = FALSE; int i; /* Avoid being called more than once. */ if (isExiting) { return; } isExiting = TRUE; /* We only want to delete the pid files if we created them. Some Wrapper * invocations are meant to run in parallel with Wrapper instances * controlling a JVM. */ if (cleanUpPIDFilesOnExit) { /* Remove pid file. It may no longer exist. */ if (wrapperData->pidFilename) { _tunlink(wrapperData->pidFilename); } /* Remove lock file. It may no longer exist. */ if (wrapperData->lockFilename) { _tunlink(wrapperData->lockFilename); } /* Remove status file. It may no longer exist. */ if (wrapperData->statusFilename) { _tunlink(wrapperData->statusFilename); } /* Remove java status file if it was registered and created by this process. */ if (wrapperData->javaStatusFilename) { _tunlink(wrapperData->javaStatusFilename); } /* Remove java id file if it was registered and created by this process. */ if (wrapperData->javaIdFilename) { _tunlink(wrapperData->javaIdFilename); } /* Remove anchor file. It may no longer exist. */ if (wrapperData->anchorFilename) { _tunlink(wrapperData->anchorFilename); } } /* Common wrapper cleanup code. */ wrapperDispose(exitCode); #if defined(UNICODE) for (i = 0; i < argc; i++) { if (argv[i]) { free(argv[i]); } } if (argv) { free(argv); } #endif exit(exitCode); } void changePidFileGroup(const TCHAR* filename, gid_t newGroup) { if (filename && (newGroup != -1)) { if (_tchown(filename, -1, newGroup) == -1) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to change the group of %s. %s"), filename, getLastErrorText()); } } } /** * Writes a PID to disk. * * @param filename File to write to. * @param pid pid to write in the file. * @param newUmask Umask to use when creating the file. * @param newGroup Group to use when creating the file. * * @return 1 if there was an error, 0 if Ok. */ int writePidFile(const TCHAR *filename, DWORD pid, int newUmask, gid_t newGroup) { FILE *pid_fp = NULL; int old_umask; old_umask = umask(newUmask); pid_fp = _tfopen(filename, TEXT("w")); umask(old_umask); if (pid_fp != NULL) { changePidFileGroup(filename, newGroup); _ftprintf(pid_fp, TEXT("%d\n"), (int)pid); fclose(pid_fp); } else { return 1; } return 0; } /** * Send a signal to the JVM process asking it to dump its JVM state. */ void wrapperRequestDumpJVMState() { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Dumping JVM state.")); if (wrapperData->javaPID == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("JVM is currently not running.")); } else if (kill(wrapperData->javaPID, SIGQUIT) < 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Could not dump JVM state: %s"), getLastErrorText()); } } /** * Called when a signal is processed. This is actually called from within the main event loop * and NOT the signal handler. So it is safe to use the normal logging functions. * * @param sigNum Signal that was fired. * @param sigName Name of the signal for logging. * @param mode Action that should be taken. */ void takeSignalAction(int sigNum, const TCHAR *sigName, int mode) { if (wrapperData->ignoreSignals & WRAPPER_IGNORE_SIGNALS_WRAPPER) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s trapped, but ignored."), sigName); } else { switch (mode) { case WRAPPER_SIGNAL_MODE_RESTART: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s trapped. %s"), sigName, wrapperGetRestartProcessMessage()); wrapperRestartProcess(); break; case WRAPPER_SIGNAL_MODE_SHUTDOWN: if (wrapperData->exitRequested || wrapperData->restartRequested || (wrapperData->jState == WRAPPER_JSTATE_DOWN_CLEAN) || (wrapperData->jState == WRAPPER_JSTATE_STOP) || (wrapperData->jState == WRAPPER_JSTATE_STOPPING) || (wrapperData->jState == WRAPPER_JSTATE_STOPPED) || (wrapperData->jState == WRAPPER_JSTATE_KILLING) || (wrapperData->jState == WRAPPER_JSTATE_KILL) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_CHECK) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH_STDIN) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH)) { /* Signaled while we were already shutting down. */ if ((stopSignalLastTick == WRAPPER_TICK_INITIAL) || (wrapperGetTickAgeTicks(stopSignalLastTick, wrapperGetTicks()) >= wrapperData->forcedShutdownDelay)) { /* We want to ignore double signals which can be sent both by the script and the systems at almost the same time. */ if (wrapperData->isForcedShutdownDisabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s trapped. Already shutting down."), sigName); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s trapped. Forcing immediate shutdown."), sigName); /* Disable the thread dump on exit feature if it is set because it * should not be displayed when the user requested the immediate exit. */ wrapperData->requestThreadDumpOnFailedJVMExit = FALSE; wrapperKillProcess(FALSE); } } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s trapped. Shutting down."), sigName); /* Always force the shutdown as this is an external event. */ wrapperStopProcess(0, TRUE); stopSignalLastTick = wrapperGetTicks(); } /* Don't actually kill the process here. Let the application shut itself down */ /* To make sure that the JVM will not be restarted for any reason, * start the Wrapper shutdown process as well. */ if ((wrapperData->wState == WRAPPER_WSTATE_STOPPING) || (wrapperData->wState == WRAPPER_WSTATE_STOPPED)) { /* Already stopping. */ } else { wrapperSetWrapperState(WRAPPER_WSTATE_STOPPING); } break; case WRAPPER_SIGNAL_MODE_FORWARD: if (wrapperData->javaPID > 0) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("%s (%d) trapped. Forwarding to JVM process."), sigName, sigNum); } if (kill(wrapperData->javaPID, sigNum)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to forward %s signal to JVM process. %s"), sigName, getLastErrorText()); } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s trapped. Unable to forward signal to JVM because it is not running."), sigName); } break; case WRAPPER_SIGNAL_MODE_PAUSE: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s trapped. %s"), sigName, wrapperGetPauseProcessMessage()); wrapperPauseProcess(WRAPPER_ACTION_SOURCE_CODE_SIGNAL); break; case WRAPPER_SIGNAL_MODE_RESUME: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s trapped. %s"), sigName, wrapperGetResumeProcessMessage()); wrapperResumeProcess(WRAPPER_ACTION_SOURCE_CODE_SIGNAL); break; case WRAPPER_SIGNAL_MODE_CLOSE_LOGFILE: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s trapped. Closing the log file."), sigName); flushLogfile(); closeLogfile(); break; default: /* WRAPPER_SIGNAL_MODE_IGNORE */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s trapped, but ignored."), sigName); break; } } } /** * This function goes through and checks flags for each of several signals to see if they * have been fired since the last time this function was called. This is the only thread * which will ever clear these flags, but they can be set by other threads within the * signal handlers at ANY time. So only check the value of each flag once and reset them * immediately to decrease the chance of missing duplicate signals. */ void wrapperMaintainSignals() { if (!handleSignals) { return; } /* SIGINT */ if (wrapperData->signalInterruptTrapped) { wrapperData->signalInterruptTrapped = FALSE; takeSignalAction(SIGINT, TEXT("INT"), WRAPPER_SIGNAL_MODE_SHUTDOWN); } /* SIGQUIT */ if (wrapperData->signalQuitTrapped) { wrapperData->signalQuitTrapped = FALSE; if (wrapperData->signalQuitSkip) { /* When CTRL+'\' is captured by the terminal driver (in the kernel), SIGQUIT * is sent to the foreground process group of the current session. * Since the JVM and Wrapper processes belong to the same process group, * the JVM would receive the signal twice if we forward it. Instead, just log * a message and let the JVM handle the signal on its own. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Dumping JVM state.")); wrapperData->signalQuitSkip = FALSE; } else { wrapperRequestDumpJVMState(); } } /* SIGCHLD */ if (wrapperData->signalChildTrapped) { wrapperData->signalChildTrapped = FALSE; if (wrapperData->signalChildContinuedTrapped) { wrapperGetProcessStatus(wrapperGetTicks(), TRUE); wrapperData->signalChildContinuedTrapped = FALSE; } else { wrapperGetProcessStatus(wrapperGetTicks(), FALSE); } } /* SIGTERM */ if (wrapperData->signalTermTrapped) { wrapperData->signalTermTrapped = FALSE; takeSignalAction(SIGTERM, TEXT("TERM"), WRAPPER_SIGNAL_MODE_SHUTDOWN); } /* SIGHUP */ if (wrapperData->signalHUPTrapped) { wrapperData->signalHUPTrapped = FALSE; takeSignalAction(SIGHUP, TEXT("HUP"), wrapperData->signalHUPMode); } /* SIGUSR1 */ if (wrapperData->signalUSR1Trapped) { wrapperData->signalUSR1Trapped = FALSE; takeSignalAction(SIGUSR1, TEXT("USR1"), wrapperData->signalUSR1Mode); } #ifndef VALGRIND /* SIGUSR2 */ if (wrapperData->signalUSR2Trapped) { wrapperData->signalUSR2Trapped = FALSE; takeSignalAction(SIGUSR2, TEXT("USR2"), wrapperData->signalUSR2Mode); } #endif } /** * This is called from within signal handlers so NO MALLOCs are allowed here. */ const TCHAR* getSignalName(int signo) { switch (signo) { case SIGALRM: return TEXT("SIGALRM"); case SIGINT: return TEXT("SIGINT"); case SIGKILL: return TEXT("SIGKILL"); case SIGQUIT: return TEXT("SIGQUIT"); case SIGCHLD: return TEXT("SIGCHLD"); case SIGTERM: return TEXT("SIGTERM"); case SIGHUP: return TEXT("SIGHUP"); case SIGUSR1: return TEXT("SIGUSR1"); case SIGUSR2: return TEXT("SIGUSR2"); case SIGSEGV: return TEXT("SIGSEGV"); default: return TEXT("UNKNOWN"); } } /** * This is called from within signal handlers so NO MALLOCs are allowed here. */ const TCHAR* getSignalCodeDesc(int code) { switch (code) { #ifdef SI_USER case SI_USER: return TEXT("kill, sigsend or raise"); #endif #ifdef SI_KERNEL case SI_KERNEL: return TEXT("the kernel"); #endif case SI_QUEUE: return TEXT("sigqueue"); #ifdef SI_TIMER case SI_TIMER: return TEXT("timer expired"); #endif #ifdef SI_MESGQ case SI_MESGQ: return TEXT("mesq state changed"); #endif case SI_ASYNCIO: return TEXT("AIO completed"); #ifdef SI_SIGIO case SI_SIGIO: return TEXT("queued SIGIO"); #endif default: return TEXT("unknown"); } } /** * Describe a signal. This is called from within signal handlers so NO MALLOCs are allowed here. */ void descSignal(siginfo_t *sigInfo) { #ifdef SI_USER struct passwd *pw; #ifdef UNICODE size_t req; #endif TCHAR uName[MAX_USER_NAME_LENGTH + 1]; #endif /* Not supported on all platforms */ if (sigInfo == NULL) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Signal trapped. No details available.")); return; } if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Signal trapped. Details:")); log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, #if defined(UNICODE) TEXT(" signal number=%d (%S), source=\"%S\""), #else TEXT(" signal number=%d (%s), source=\"%s\""), #endif sigInfo->si_signo, getSignalName(sigInfo->si_signo), getSignalCodeDesc(sigInfo->si_code)); if (sigInfo->si_errno != 0) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, #if defined(UNICODE) TEXT(" signal err=%d, \"%S\""), #else TEXT(" signal err=%d, \"%s\""), #endif sigInfo->si_errno, strerror(sigInfo->si_errno)); } #ifdef SI_USER if (sigInfo->si_code == SI_USER) { pw = getpwuid(sigInfo->si_uid); if (pw == NULL) { _sntprintf(uName, MAX_USER_NAME_LENGTH + 1, TEXT("")); } else { #ifndef UNICODE _sntprintf(uName, MAX_USER_NAME_LENGTH + 1, TEXT("%s"), pw->pw_name); #else req = mbstowcs(NULL, pw->pw_name, MBSTOWCS_QUERY_LENGTH); if (req == (size_t)-1) { return; } if (req > MAX_USER_NAME_LENGTH) { req = MAX_USER_NAME_LENGTH; } mbstowcs(uName, pw->pw_name, req + 1); uName[req] = TEXT('\0'); /* Avoid bufferflows caused by badly encoded characters. */ #endif } /* It appears that the getsid function was added in version 1.3.44 of the linux kernel. */ log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, #ifdef UNICODE TEXT(" signal generated by PID: %d (Session PID: %d), UID: %d (%S)"), #else TEXT(" signal generated by PID: %d (Session PID: %d), UID: %d (%s)"), #endif sigInfo->si_pid, getsid(sigInfo->si_pid), sigInfo->si_uid, uName); } #endif } } /** * This function is only used for debugging and should always return FALSE * if the signal was correctly masked for the threads used by the Wrapper. */ static int checkSignalThreads(pthread_t currentThreadId, const TCHAR* signalName, const TCHAR* message) { const TCHAR* threadName = NULL; if (timerThreadSet && pthread_equal(currentThreadId, timerThreadId)) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The Timer thread received a '%s' signal.%s"), threadName, signalName, message); } else if (javaINThreadStarted && pthread_equal(currentThreadId, javaINThreadId)) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The JavaIN thread received a '%s' signal.%s"), threadName, signalName, message); } else if (javaIOThreadStarted && pthread_equal(currentThreadId, javaIOThreadId)) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The JavaIO thread received a '%s' signal.%s"), threadName, signalName, message); } else { return FALSE; } return TRUE; } /** * Handle alarm signals. We are getting them on solaris when running with * the tick timer. Not yet sure where they are coming from. */ void sigActionAlarm(int sigNum, siginfo_t *sigInfo, void *na) { /* On UNIX the calling thread is the actual thread being interrupted * so it has already been registered with logRegisterThread. */ descSignal(sigInfo); if (wrapperData->isDebugging) { if (!checkSignalThreads(pthread_self(), TEXT("SIGALRM"), TEXT(" Ignoring."))) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Received a '%s' signal.%s"), TEXT("SIGALRM"), TEXT(" Ignoring.")); } } } /** * Handle interrupt signals (i.e. Crtl-C). */ void sigActionInterrupt(int sigNum, siginfo_t *sigInfo, void *na) { /* On UNIX the calling thread is the actual thread being interrupted * so it has already been registered with logRegisterThread. */ descSignal(sigInfo); wrapperData->signalInterruptTrapped = TRUE; } /** * Handle quit signals (i.e. Crtl-\). */ void sigActionQuit(int sigNum, siginfo_t *sigInfo, void *na) { /* On UNIX the calling thread is the actual thread being interrupted * so it has already been registered with logRegisterThread. */ descSignal(sigInfo); if (wrapperData->isDebugging) { checkSignalThreads(pthread_self(), TEXT("SIGQUIT"), TEXT("")); } wrapperData->signalQuitTrapped = TRUE; #ifdef SI_KERNEL if (!wrapperData->javaNewProcessGroup && (sigInfo->si_code == SI_KERNEL)) { /* On Linux, when CTRL-\ is pressed, the signal is caught & dispatched by the kernel to the process group members. * If the Wrapper and JVM are running in the same process group, skip forwarding the signal to avoid double handling. * This would however only work on Linux and not when a signal is sent via killpg or kill with a negative pid. * A better solution is to run the JVM in a separate process group. * NOTE: This flag is always reset to FALSE when signals are maintained. */ wrapperData->signalQuitSkip = TRUE; } #endif } /** * Handle termination signals (i.e. machine is shutting down). */ void sigActionChildDeath(int sigNum, siginfo_t *sigInfo, void *na) { /* On UNIX, when a Child process changes state, a SIGCHLD signal is sent to the parent. * The parent should do a wait to make sure the child is cleaned up and doesn't become * a zombie process. */ descSignal(sigInfo); if (wrapperData->isDebugging) { if (!checkSignalThreads(pthread_self(), TEXT("SIGCHLD"), TEXT(" Checking JVM process status."))) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Received a '%s' signal.%s"), TEXT("SIGCHLD"), TEXT(" Checking JVM process status.")); } } /* This is set whenever any child signals that it has exited. * Inside the code we go on to check to make sure that we only test for the JVM. * Note: Can we test 'sigInfo->si_pid == wrapperData->javaPID'? * Is it supported on all platforms? No risk of returning 0 or a default value? * If we could distinguish between JVM from Event commands, it would also allow * us to figure out more efficiently when the laters complete, instead of polling. */ wrapperData->signalChildTrapped = TRUE; if (sigInfo->si_code == CLD_CONTINUED) { wrapperData->signalChildContinuedTrapped = TRUE; } } /** * Handle termination signals (i.e. machine is shutting down). */ void sigActionTermination(int sigNum, siginfo_t *sigInfo, void *na) { /* On UNIX the calling thread is the actual thread being interrupted * so it has already been registered with logRegisterThread. */ descSignal(sigInfo); wrapperData->signalTermTrapped = TRUE; } /** * Handle hangup signals. */ void sigActionHangup(int sigNum, siginfo_t *sigInfo, void *na) { /* On UNIX the calling thread is the actual thread being interrupted * so it has already been registered with logRegisterThread. */ descSignal(sigInfo); wrapperData->signalHUPTrapped = TRUE; } /** * Handle USR1 signals. */ void sigActionUSR1(int sigNum, siginfo_t *sigInfo, void *na) { /* On UNIX the calling thread is the actual thread being interrupted * so it has already been registered with logRegisterThread. */ descSignal(sigInfo); wrapperData->signalUSR1Trapped = TRUE; } /** * Handle USR2 signals. */ void sigActionUSR2(int sigNum, siginfo_t *sigInfo, void *na) { /* On UNIX the calling thread is the actual thread being interrupted * so it has already been registered with logRegisterThread. */ descSignal(sigInfo); wrapperData->signalUSR2Trapped = TRUE; } /** * Registers a single signal handler. */ int registerSigAction(int sigNum, void (*sigAction)(int, siginfo_t *, void *)) { struct sigaction newAct; newAct.sa_sigaction = sigAction; sigemptyset(&newAct.sa_mask); newAct.sa_flags = SA_SIGINFO; if (sigaction(sigNum, &newAct, NULL)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to register signal handler for signal %d. %s"), sigNum, getLastErrorText()); return 1; } return 0; } /** * Close the pipe used to redirect stdin to the JVM; */ void closeStdinPipe() { if (pipeind[PIPE_READ_END] != -1) { close(pipeind[PIPE_READ_END]); pipeind[PIPE_READ_END] = -1; } if (pipeind[PIPE_WRITE_END] != -1) { close(pipeind[PIPE_WRITE_END]); pipeind[PIPE_WRITE_END] = -1; } } /** * The main entry point for the javaIN thread which is started by * initializeJavaIN(). Once started, this thread will run for the * life of the process. * This thread is used to forward stdin to the Java process when it * it is launched in a new process group. The Wrapper should remain * the foreground process group to handle signals correctly, but the * file descriptor associated to stdin (0) is only valid within that * group. */ void *javaINRunner(void *arg) { sigset_t signal_mask; char *chBuf = NULL; char *pChBuf; int retS, retR, retW; int i; fd_set rfds; struct timeval tv; int skipRead = FALSE; javaINThreadStarted = TRUE; /* Immediately register this thread with the logger. */ logRegisterThread(WRAPPER_THREAD_JAVAIN); /* mask signals so the javaIN doesn't get any of these. */ sigemptyset(&signal_mask); sigaddset(&signal_mask, SIGTERM); sigaddset(&signal_mask, SIGINT); sigaddset(&signal_mask, SIGQUIT); sigaddset(&signal_mask, SIGALRM); sigaddset(&signal_mask, SIGCHLD); sigaddset(&signal_mask, SIGHUP); sigaddset(&signal_mask, SIGUSR1); #ifndef VALGRIND sigaddset(&signal_mask, SIGUSR2); #endif if (pthread_sigmask(SIG_BLOCK, &signal_mask, NULL) != 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Could not mask signals for %s thread."), TEXT("JavaIN")); } if (wrapperData->isJavaIOOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s thread started."), TEXT("JavaIN")); } /* Initialize a set of file descriptors which will only contain STDIN_FILENO. */ FD_ZERO(&rfds); /* The buffer size is fixed (can't be reloaded), so we only need to allocate once. */ chBuf = malloc(sizeof(char) * wrapperData->javaINBufferSize); if (!chBuf) { outOfMemory(TEXT("JINR"), 1); } else { while (!stopJavaINThread) { if (pipeind[PIPE_WRITE_END] == -1) { /* Java is not up? */ wrapperSleep(WRAPPER_TICK_MS); continue; } else if (fcntl(pipeind[PIPE_WRITE_END], F_SETFL, O_SYNC) == -1) { /* Make sure blocking mode is set on pipeind[PIPE_WRITE_END]. Write() will be interrupted if Java goes down. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to set write mode for Java's stdin. %s"), getLastErrorText()); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT(" Disable stdin.")); /* NOTE: It would be possible put this thread into sleep until a next JVM is restarted and try again fcntl on the new pipe created... * but this would consume CPU or would require a more complex logic to wake up the thread. Don't think this is needed for now. */ stopJavaINThread = TRUE; } else { /* Java (re-)started, and the pipe conntected to its stdin was (re-)created. */ #ifdef DEBUG_JAVAIN log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("DEBUG_JAVAIN: start read/write")); #endif /* Read/Write (again). Reset the flush flag. */ wrapperData->javaINFlushed = FALSE; do { if (skipRead) { /* We already read (happens when we don't flush on a jvm restart). */ retS = 1; } else { /* Make sure STDIN_FILENO is always present in rfds. It may have been removed by select on a previous call. */ if (!FD_ISSET(STDIN_FILENO, &rfds)) { FD_SET(STDIN_FILENO, &rfds); } /* Set the timeout to 1 tick and reinitialize it on each invocation as it may have been updated by a previous call. * Note: An alternative would be to use pselect() which doesn't update the timeout. */ tv.tv_sec = 0; tv.tv_usec = WRAPPER_TICK_MS * 1000; /** WAIT until there is something to read. **/ retS = select(STDIN_FILENO + 1, &rfds, NULL, NULL, &tv); } if ((retS == -1) && (errno != EINTR)) { /* Error other than signal interruption. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to wait for stdin to be ready for I/O.")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT(" Disable stdin.")); stopJavaINThread = TRUE; break; } else if (retS > 0) { if (skipRead) { skipRead = FALSE; retR = strlen(chBuf) * sizeof(char); } else { /* stdin is ready to be read. */ /** READ stdin. */ #ifdef DEBUG_JAVAIN log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("DEBUG_JAVAIN: read(STDIN_FILENO, chBuf, %d)"), wrapperData->javaINBufferSize - 1); #endif retR = read(STDIN_FILENO, chBuf, wrapperData->javaINBufferSize - 1); if (retR == -1) { /* Should not happpen... */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to read from stdin. %s"), getLastErrorText()); stopJavaINThread = TRUE; break; } else if (retR == 0) { /* End of stream. May happen for example when piping a file to the wrapper. */ stopJavaINThread = TRUE; break; } chBuf[retR / sizeof(char)] = 0; } /** WRITE to pipeind if it's still open. **/ if (pipeind[PIPE_WRITE_END] != -1) { pChBuf = chBuf; while ((retR > 0) && !stopJavaINThread) { /* Write to the write-end of the pipe which is connected to Java's stdin. We are blocking but write() will return -1 if Java goes down. */ #ifdef DEBUG_JAVAIN log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("DEBUG_JAVAIN: write(pipeind[PIPE_WRITE_END], pChBuf, %d)"), retR); #endif retW = write(pipeind[PIPE_WRITE_END], pChBuf, retR); if ((retW == -1) && (errno != EINTR)) { /* May happen when the was JVM stopped while we were writing. Make sure to close the pipe. */ #ifdef DEBUG_JAVAIN log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("DEBUG_JAVAIN: write failed (%d)"), errno); #endif close(pipeind[PIPE_WRITE_END]); pipeind[PIPE_WRITE_END] = -1; break; } else if (retW > 0) { pChBuf += (retW / sizeof(char)); retR -= retW; } } } } else { /* select() timed out or was interrupted by a signal. Continue. */ } } while (!stopJavaINThread && (pipeind[PIPE_WRITE_END] != -1)); /** FLUSH? **/ if (!stopJavaINThread) { if (!wrapperData->javaINFlush) { skipRead = TRUE; } else { /* We want to empty the pipe to have a clean start of stdin for the next JVM. */ wrapperData->javaINFlushing = TRUE; #ifdef DEBUG_JAVAIN log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("DEBUG_JAVAIN: flushing started")); #endif /* Make sure non-blocking mode is set on STDIN_FILENO. This is needed because: * - if we call again read() and there is no more byte, the blocking would prevent us from setting the wrapperData->javaINFlushed flag (unless we implement a timeout?) * - if we read less than the buffer size, it's no guarantee that we reached a 'blank'. It might just mean we are reading faster than the pipe is written. * - even if we would implement a timeout, there is always the possibility that we get no bytes for a shorter time and then a new series of bytes. */ if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) == -1) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to set read mode for stdin. %s"), getLastErrorText()); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT(" Disable stdin.")); stopJavaINThread = TRUE; } else { i = 0; while (!stopJavaINThread) { retR = read(STDIN_FILENO, chBuf, wrapperData->javaINBufferSize - 1); #ifdef DEBUG_JAVAIN /* To debug the flushing, it can be useful to print the number of bytes read on each loop (or error codes). */ _tprintf(TEXT("%d "), retR); #endif if (retR == 0) { /* End of stream. May happen for example when piping a file to the wrapper. */ stopJavaINThread = TRUE; break; } else if (retR == -1) { /* On a non-blocking call, EAGAIN means read() would have blocked without O_NONBLOCK, and EINTR can't happen. * When the buffer is emptied, there may be a slight delay until the pipe is refilled by the system. * We consider the flushing is finished after 10 consecutive 'EAGAIN' every ms. */ if ((errno == EAGAIN) && (i++ < 10)) { wrapperSleep(1); } else { break; } } else { /* retR > 0 */ /* reading (again) */ if (i > 0) { i = 0; } } } if (fcntl(STDIN_FILENO, F_SETFL, O_SYNC) == -1) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to set read mode for stdin. %s"), getLastErrorText()); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT(" Disable stdin.")); stopJavaINThread = TRUE; } } #ifdef DEBUG_JAVAIN log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("DEBUG_JAVAIN: flushing ended")); #endif /* We have read everything. If new bytes are coming from now on, they will be for the next JVM. */ wrapperData->javaINFlushed = TRUE; /* Important: must be set after javaINFlushed, because if the two flags are FALSE there is a chance we disable stdin. See jStateDownFlushStdin(). */ wrapperData->javaINFlushing = FALSE; } } } } free(chBuf); } closeStdinPipe(); javaINThreadStopped = TRUE; wrapperData->disableConsoleInputPermanent = TRUE; wrapperData->disableConsoleInput = TRUE; if (wrapperData->isJavaIOOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s thread stopped."), TEXT("JavaIN")); } return NULL; } /** * Creates a thread whose job is to forward stdin to the JVM when it is run in a separate process group. * * @return 1 if there were any problems, 0 otherwise. */ int initializeJavaIN() { int res; if (wrapperData->isJavaIOOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Launching %s thread."), TEXT("JavaIN")); } res = pthread_create(&javaINThreadId, NULL, /* No attributes. */ javaINRunner, NULL); /* No parameters need to be passed to the thread. */ if (res) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to create a %s thread: %d, %s"), TEXT("JavaIN"), res, getLastErrorText()); return 1; } else { if (pthread_detach(javaINThreadId)) { return 1; } return 0; } } void disposeJavaIN() { stopJavaINThread = TRUE; /* Wait until the javaIN thread is actually stopped to avoid timing problems. */ if (javaINThreadStarted) { while (!javaINThreadStopped) { #ifdef _DEBUG wprintf(TEXT("Waiting for %s thread to stop.\n"), TEXT("JavaIN")); #endif wrapperSleep(100); } pthread_cancel(javaINThreadId); } } /** * The main entry point for the javaio thread which is started by * initializeJavaIO(). Once started, this thread will run for the * life of the process. * * This thread will only be started if we are configured to use a * dedicated thread to read JVM output. */ void *javaIORunner(void *arg) { sigset_t signal_mask; int nextSleep; javaIOThreadStarted = TRUE; /* Immediately register this thread with the logger. */ logRegisterThread(WRAPPER_THREAD_JAVAIO); /* mask signals so the javaIO doesn't get any of these. */ sigemptyset(&signal_mask); sigaddset(&signal_mask, SIGTERM); sigaddset(&signal_mask, SIGINT); sigaddset(&signal_mask, SIGQUIT); sigaddset(&signal_mask, SIGALRM); sigaddset(&signal_mask, SIGCHLD); sigaddset(&signal_mask, SIGHUP); sigaddset(&signal_mask, SIGUSR1); #ifndef VALGRIND sigaddset(&signal_mask, SIGUSR2); #endif if (pthread_sigmask(SIG_BLOCK, &signal_mask, NULL) != 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Could not mask signals for %s thread."), TEXT("JavaIN")); } if (wrapperData->isJavaIOOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s thread started."), TEXT("JavaIO")); } nextSleep = TRUE; /* Loop until we are shutting down, but continue as long as there is more output from the JVM. */ while ((!stopJavaIOThread) || (!nextSleep)) { if (nextSleep) { /* Sleep as little as possible. */ wrapperSleep(1); } nextSleep = TRUE; if (wrapperData->pauseThreadJavaIO) { wrapperPauseThread(wrapperData->pauseThreadJavaIO, TEXT("javaio")); wrapperData->pauseThreadJavaIO = 0; } if (wrapperReadChildOutput(0)) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Pause reading child process output to share cycles.")); } nextSleep = FALSE; } } javaIOThreadStopped = TRUE; if (wrapperData->isJavaIOOutputEnabled) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s thread stopped."), TEXT("JavaIO")); } return NULL; } /** * Creates a process whose job is to loop and simply increment a ticks * counter. The tick counter can then be used as a clock as an alternative * to using the system clock. */ int initializeJavaIO() { int res; if (wrapperData->isJavaIOOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Launching %s thread."), TEXT("JavaIO")); } res = pthread_create(&javaIOThreadId, NULL, /* No attributes. */ javaIORunner, NULL); /* No parameters need to be passed to the thread. */ if (res) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to create a %s thread: %d, %s"), TEXT("JavaIO"), res, getLastErrorText()); return 1; } else { if (pthread_detach(javaIOThreadId)) { return 1; } return 0; } } void disposeJavaIO() { stopJavaIOThread = TRUE; /* Wait until the javaIO thread is actually stopped to avoid timing problems. */ if (javaIOThreadStarted) { while (!javaIOThreadStopped) { #ifdef _DEBUG wprintf(TEXT("Waiting for %s thread to stop.\n"), TEXT("JavaIO")); #endif wrapperSleep(100); } pthread_cancel(javaIOThreadId); } } /** * The main entry point for the timer thread which is started by * initializeTimer(). Once started, this thread will run for the * life of the process. * * This thread will only be started if we are configured NOT to * use the system time as a base for the tick counter. */ void *timerRunner(void *arg) { TICKS sysTicks; TICKS lastTickOffset = 0; TICKS tickOffset; TICKS nowTicks; int offsetDiff; int first = TRUE; sigset_t signal_mask; timerThreadStarted = TRUE; /* Immediately register this thread with the logger. */ logRegisterThread(WRAPPER_THREAD_TIMER); /* mask signals so the timer doesn't get any of these. */ sigemptyset(&signal_mask); sigaddset(&signal_mask, SIGTERM); sigaddset(&signal_mask, SIGINT); sigaddset(&signal_mask, SIGQUIT); sigaddset(&signal_mask, SIGALRM); sigaddset(&signal_mask, SIGCHLD); sigaddset(&signal_mask, SIGHUP); sigaddset(&signal_mask, SIGUSR1); #ifndef VALGRIND sigaddset(&signal_mask, SIGUSR2); #endif if (pthread_sigmask(SIG_BLOCK, &signal_mask, NULL) != 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Could not mask signals for timer thread.")); } if (wrapperData->isTickOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Timer thread started.")); } wrapperGetSystemTicks(); while (!stopTimerThread) { wrapperSleep(WRAPPER_TICK_MS); if (wrapperData->pauseThreadTimer) { wrapperPauseThread(wrapperData->pauseThreadTimer, TEXT("timer")); wrapperData->pauseThreadTimer = 0; } /* Get the tick count based on the system time. */ sysTicks = wrapperGetSystemTicks(); /* Lock the tick mutex whenever the "timerTicks" variable is accessed. */ if (wrapperData->useTickMutex && wrapperLockTickMutex()) { timerThreadStopped = TRUE; return NULL; } /* Advance the timer tick count. */ nowTicks = timerTicks++; if (wrapperData->useTickMutex && wrapperReleaseTickMutex()) { timerThreadStopped = TRUE; return NULL; } /* Calculate the offset between the two tick counts. This will always work due to overflow. */ tickOffset = sysTicks - nowTicks; /* The number we really want is the difference between this tickOffset and the previous one. */ offsetDiff = wrapperGetTickAgeTicks(lastTickOffset, tickOffset); if (first) { first = FALSE; } else { if (offsetDiff > wrapperData->timerSlowThreshold) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("The timer fell behind the system clock by %ldms."), offsetDiff * WRAPPER_TICK_MS); } else if (offsetDiff < -1 * wrapperData->timerFastThreshold) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("The system clock fell behind the timer by %ldms."), -1 * offsetDiff * WRAPPER_TICK_MS); } if (wrapperData->isTickOutputEnabled) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT( " Timer: ticks=0x%08x, system ticks=0x%08x, offset=0x%08x, offsetDiff=0x%08x"), nowTicks, sysTicks, tickOffset, offsetDiff); } } /* Store this tick offset for the next time through the loop. */ lastTickOffset = tickOffset; } timerThreadStopped = TRUE; if (wrapperData->isTickOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Timer thread stopped.")); } return NULL; } /** * Creates a process whose job is to loop and simply increment a ticks * counter. The tick counter can then be used as a clock as an alternative * to using the system clock. */ int initializeTimer() { int res; if (wrapperData->isTickOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Launching Timer thread.")); } res = pthread_create(&timerThreadId, NULL, /* No attributes. */ timerRunner, NULL); /* No parameters need to be passed to the thread. */ if (res) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to create a timer thread: %d, %s"), res, getLastErrorText()); timerThreadSet = TRUE; return 1; } else { if (pthread_detach(timerThreadId)) { timerThreadSet = TRUE; return 1; } timerThreadSet = FALSE; return 0; } } void disposeTimer() { stopTimerThread = TRUE; /* Wait until the timer thread is actually stopped to avoid timing problems. */ if (timerThreadStarted) { while (!timerThreadStopped) { #ifdef _DEBUG wprintf(TEXT("Waiting for timer thread to stop.\n")); #endif wrapperSleep(100); } pthread_cancel(timerThreadId); } } /** * Execute initialization code to get the wrapper set up. */ int wrapperInitializeRun() { int retval = 0; int res; /* Register any signal actions we are concerned with. */ if (registerSigAction(SIGALRM, sigActionAlarm) || registerSigAction(SIGINT, sigActionInterrupt) || registerSigAction(SIGQUIT, sigActionQuit) || registerSigAction(SIGCHLD, sigActionChildDeath) || registerSigAction(SIGTERM, sigActionTermination) || registerSigAction(SIGHUP, sigActionHangup) || registerSigAction(SIGUSR1, sigActionUSR1) #ifndef VALGRIND || registerSigAction(SIGUSR2, sigActionUSR2) #endif ) { retval = -1; } wrapperSetConsoleTitle(); if (wrapperData->useSystemTime) { /* We are going to be using system time so there is no reason to start up a timer thread. */ timerThreadSet = FALSE; /* Unable to set the timerThreadId to a null value on all platforms * timerThreadId = 0;*/ } else { /* Create and initialize a timer thread. */ if ((res = initializeTimer()) != 0) { return res; } } return retval; } /** * Cause the current thread to sleep for the specified number of milliseconds. * * @param ms Number of milliseconds to wait for. * @param interrupt TRUE to return when nanosleep was interrupted by a signal, * FALSE to continue sleeping the remaining time. * * @return the number of remaining ms to sleep if interrupted, -1 if there was an error, 0 otherwise. */ int wrapperSleepInterrupt(int ms, int interrupt) { int result; /* We want to use nanosleep if it is available, but make it possible for the user to build a version that uses usleep if they want. usleep does not behave nicely with signals thrown while sleeping. This was the believed cause of a hang experienced on one Solaris system. */ #ifdef USE_USLEEP if (wrapperData && wrapperData->isSleepOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Sleep: usleep %dms"), ms); } result = wrapperUsleep(ms * 1000); /* microseconds */ #else struct timespec ts; struct timespec tsRemaining; #ifdef HPUX int failed = FALSE; #endif if (ms >= 1000) { ts.tv_sec = ms / 1000; ts.tv_nsec = (ms % 1000) * 1000000; /* nanoseconds */ } else { ts.tv_sec = 0; ts.tv_nsec = ms * 1000000; /* nanoseconds */ } if (wrapperData && wrapperData->isSleepOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Sleep: nanosleep %dms"), ms); } #ifdef HPUX /* On HPUX, there is an issue when two threads call nanosleep() at the same time. * This happens for example when the timer thread calls wrapperSleep() while the main * thread is waiting for the output of 'java -version'. * According to the documentation, nanosleep() should be thread-safe, but the * documentation also states that errno should be set when the function fails, which * is not the case here (errno=0)! The implementation of nanosleep() may not be * correct on this platform (not sure if it was fixed on later versions of the OS). * To fix this issue, we can't really use a mutex because the timer thread would * constantly block the main thread. Instead, we will try calling again nanosleep * but we'll do it to only one time to avoid infinite loop. When this happens, the * first call probably did not sleep at all (and even if it did, it is not sure * whether the remaining time would be set correctly), so just sleep again the full * amount of time on the second call. */ tryagain: #endif errno = 0; while (((result = nanosleep(&ts, &tsRemaining)) == -1) && (errno == EINTR)) { if (interrupt) { return (tsRemaining.tv_sec * 1000) + (tsRemaining.tv_nsec / 1000000); } ts.tv_sec = tsRemaining.tv_sec; ts.tv_nsec = tsRemaining.tv_nsec; } if (result) { if (errno == EAGAIN) { /* On 64-bit AIX this happens once on shutdown. */ if (wrapperData && wrapperData->isSleepOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Sleep: nanosleep unavailable")); } if (interrupt) { return (tsRemaining.tv_sec * 1000) + (tsRemaining.tv_nsec / 1000000); } } else { #ifdef HPUX if ((errno == 0) && !failed) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("nanosleep(%dms) failed. %s. Trying again."), ms, getLastErrorText()); failed = TRUE; goto tryagain; } #endif log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("nanosleep(%dms) failed. %s"), ms, getLastErrorText()); } } #endif if (wrapperData && wrapperData->isSleepOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Sleep: awake")); } return result; } /** * Cause the current thread to sleep for the specified number of milliseconds. * This function will not be interrupted by signals. * * @param ms Number of milliseconds to wait for. */ void wrapperSleep(int ms) { wrapperSleepInterrupt(ms, FALSE); } /** * Detaches the Java process so the Wrapper will if effect forget about it. */ void wrapperDetachJava() { wrapperSetJavaState(WRAPPER_JSTATE_DOWN_CLEAN, 0, -1); /* Leave the stdout/stderr pipe in pipedes alone so we grab any remaining output. * They should have been redirected on the JVM side anyway. */ } /** * Build the command line used to get the Java version. * * @return TRUE if there were any problems. */ int wrapperBuildJavaVersionCommand() { TCHAR **strings; int i; /* If this is not the first time through, then dispose the old command array */ if (wrapperData->jvmVersionCommand) { i = 0; while(wrapperData->jvmVersionCommand[i] != NULL) { free(wrapperData->jvmVersionCommand[i]); wrapperData->jvmVersionCommand[i] = NULL; i++; } free(wrapperData->jvmVersionCommand); wrapperData->jvmVersionCommand = NULL; } strings = malloc(sizeof(TCHAR*)); if (!strings) { outOfMemory(TEXT("WBJVC1"), 1); return TRUE; } memset(strings, 0, sizeof(TCHAR *)); if (wrapperBuildJavaCommandArrayJavaCommand(strings, FALSE, 0, FALSE) < 0) { wrapperFreeStringArray(strings, 1); return TRUE; } if (wrapperResolveJavaVersionCommand(&(strings[0]), FALSE)) { wrapperFreeStringArray(strings, 1); return TRUE; } /* Allocate memory to hold array of version command strings. The array is itself NULL terminated */ wrapperData->jvmVersionCommand = malloc(sizeof(TCHAR *) * (2 + 1)); if (!wrapperData->jvmVersionCommand) { outOfMemory(TEXT("WBJVC"), 2); wrapperFreeStringArray(strings, 1); return TRUE; } memset(wrapperData->jvmVersionCommand, 0, sizeof(TCHAR *) * (2 + 1)); /* Java Command */ wrapperData->jvmVersionCommand[0] = malloc(sizeof(TCHAR) * (_tcslen(strings[0]) + 1)); if (!wrapperData->jvmVersionCommand[0]) { outOfMemory(TEXT("WBJVC"), 3); wrapperFreeStringArray(strings, 1); return TRUE; } _tcsncpy(wrapperData->jvmVersionCommand[0], strings[0], _tcslen(strings[0]) + 1); /* -version */ wrapperData->jvmVersionCommand[1] = malloc(sizeof(TCHAR) * (8 + 1)); if (!wrapperData->jvmVersionCommand[1]) { outOfMemory(TEXT("WBJVC"), 4); wrapperFreeStringArray(strings, 1); return TRUE; } _tcsncpy(wrapperData->jvmVersionCommand[1], TEXT("-version"), 8 + 1); /* NULL */ wrapperData->jvmVersionCommand[2] = NULL; wrapperFreeStringArray(strings, 1); return FALSE; } /** * Build the java command line. * * @return TRUE if there were any problems. */ int wrapperBuildJavaCommand() { TCHAR **strings; int length, i; /* If this is not the first time through, then dispose the old command array */ if (wrapperData->jvmCommand) { i = 0; while(wrapperData->jvmCommand[i] != NULL) { free(wrapperData->jvmCommand[i]); wrapperData->jvmCommand[i] = NULL; i++; } free(wrapperData->jvmCommand); wrapperData->jvmCommand = NULL; } /* First generate the classpath. */ if (wrapperData->classpath) { free(wrapperData->classpath); wrapperData->classpath = NULL; } if (wrapperBuildJavaClasspath(&wrapperData->classpath) < 0) { return TRUE; } /* Build the Java Command Strings */ strings = NULL; length = 0; if (wrapperBuildJavaCommandArray(&strings, &length, FALSE, wrapperData->classpath)) { wrapperFreeStringArray(strings, length); return TRUE; } /* Allocate memory to hold array of command strings. The array is itself NULL terminated */ wrapperData->jvmCommand = malloc(sizeof(TCHAR *) * (length + 1)); if (!wrapperData->jvmCommand) { outOfMemory(TEXT("WBJC"), 1); wrapperFreeStringArray(strings, length); return TRUE; } memset(wrapperData->jvmCommand, 0, sizeof(TCHAR *) * (length + 1)); /* number of arguments + 1 for a NULL pointer at the end */ for (i = 0; i <= length; i++) { if (i < length) { wrapperData->jvmCommand[i] = malloc(sizeof(TCHAR) * (_tcslen(strings[i]) + 1)); if (!wrapperData->jvmCommand[i]) { outOfMemory(TEXT("WBJC"), 2); wrapperFreeStringArray(strings, length); return TRUE; } _tcsncpy(wrapperData->jvmCommand[i], strings[i], _tcslen(strings[i]) + 1); wrapperData->jvmCommand[i] = wrapperPostProcessCommandElement(wrapperData->jvmCommand[i]); } else { wrapperData->jvmCommand[i] = NULL; } } /* Free up the temporary command array */ wrapperFreeStringArray(strings, length); return FALSE; } /** * Calculate the total length of the environment assuming that they are separated by spaces. */ size_t wrapperCalculateEnvironmentLength() { /* The compiler won't let us reference environ directly in the for loop on OSX because it is actually a function. */ char **environment = environ; size_t i; size_t len; size_t lenTotal; i = 0; lenTotal = 0; while (environment[i]) { /* All we need is the length so we don't actually have to convert them. */ len = mbstowcs(NULL, environment[i], MBSTOWCS_QUERY_LENGTH); if (len == (size_t)-1) { /* Invalid string. Skip. */ } else { /* Add length of variable + null + pointer to next element */ lenTotal += len + 1 + sizeof(char *); } i++; } /* Null termination of the list. */ lenTotal += sizeof(char *) + sizeof(char *); return lenTotal; } /** * Launch a JVM and collect the pid. * * @return TRUE if there were any problems, FALSE otherwise. */ int wrapperLaunchJvm(TCHAR** command, pid_t *pidPtr) { int i; pid_t proc; int execErrno; size_t lenCmd; size_t lenEnv; int useStdin; /* Create a single pipe for stdout and stderr (they will be merged). */ if (pipe(pipedes) < 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Could not init %s pipe: %s"), TEXT("stdout/stderr"), getLastErrorText()); return TRUE; } /* stdin is sent to the foreground process group (the group of the Wrapper). * - If the Java process is started in the same group, then it will also receive stdin, so we don't need to redirect it. * - If the Java process is started in a new group, redirection is needed (we want to keep the Wrapper running in the foreground process group to catch signals). */ if ((command != wrapperData->jvmVersionCommand) && wrapperData->javaNewProcessGroup && !wrapperData->disableConsoleInput) { useStdin = TRUE; if (pipe(pipeind) < 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Could not init %s pipe: %s"), TEXT("stdin"), getLastErrorText()); return TRUE; } /* When Java shuts down, the pipe is disconnected and a SIGPIPE signal is sent. Ignore it as it would cause the Wrapper process to exit. */ signal(SIGPIPE, SIG_IGN); } else { useStdin = FALSE; } /* Again make sure the log file is closed before forking. */ setLogfileAutoClose(TRUE); closeLogfile(); /* Reset the log duration so we get new counts from the time the JVM is launched. */ resetDuration(); /* Fork off the child. */ proc = fork(); if (proc == -1) { /* Fork failed. */ /* Restore the auto close flag. */ setLogfileAutoClose(wrapperData->logfileCloseTimeout == 0); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Could not spawn JVM process: %s"), getLastErrorText()); /* The fork failed so there is no child side. Close the pipes so we don't attempt to read them later. */ close(pipedes[PIPE_READ_END]); pipedes[PIPE_READ_END] = -1; close(pipedes[PIPE_WRITE_END]); pipedes[PIPE_WRITE_END] = -1; if (useStdin) { close(pipeind[PIPE_READ_END]); pipeind[PIPE_READ_END] = -1; close(pipeind[PIPE_WRITE_END]); pipeind[PIPE_WRITE_END] = -1; } return TRUE; } else if (proc == 0) { /* We are the child side. */ /* Set the umask of the JVM */ umask(wrapperData->javaUmask); /* The logging code causes some log corruption if logging is called from the * child of a fork. Not sure exactly why but most likely because the forked * child receives a copy of the mutex and thus synchronization is not working. * It is ok to log errors in here, but avoid output otherwise. * TODO: Figure out a way to fix this. Maybe using shared memory? */ close(pipedes[PIPE_READ_END]); pipedes[PIPE_READ_END] = -1; /* Send output to the pipe by duplicating the pipe fd and setting the copy as the stdout fd. */ if (dup2(pipedes[PIPE_WRITE_END], STDOUT_FILENO) < 0) { /* This process needs to end (no meaning to log an error without stdout/stderr). */ exit(wrapperData->errorExitCode); close(pipedes[PIPE_WRITE_END]); pipedes[PIPE_WRITE_END] = -1; return TRUE; /* Will not get here. */ } /* Send errors to the pipe by duplicating the pipe fd and setting the copy as the stderr fd. */ if (dup2(pipedes[PIPE_WRITE_END], STDERR_FILENO) < 0) { /* This process needs to end (no meaning to log an error without stdout/stderr). */ exit(wrapperData->errorExitCode); close(pipedes[PIPE_WRITE_END]); pipedes[PIPE_WRITE_END] = -1; return TRUE; /* Will not get here. */ } close(pipedes[PIPE_WRITE_END]); pipedes[PIPE_WRITE_END] = -1; if (useStdin) { close(pipeind[PIPE_WRITE_END]); pipeind[PIPE_WRITE_END] = -1; /* Send input to the pipe by duplicating the pipe fd and setting the copy as the stdin fd. */ if (dup2(pipeind[PIPE_READ_END], STDIN_FILENO) < 0) { /* This process needs to end. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("%sUnable to set JVM's stdin: %s"), LOG_FORK_MARKER, getLastErrorText()); close(pipeind[PIPE_READ_END]); pipeind[PIPE_READ_END] = -1; exit(wrapperData->errorExitCode); return TRUE; /* Will not get here. */ } close(pipeind[PIPE_READ_END]); pipeind[PIPE_READ_END] = -1; /* Java should be started in a new process group. */ if (setpgid(0, 0)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("%sFailed to set Java process as group leader. %s"), LOG_FORK_MARKER, getLastErrorText()); } } /* Child process: execute the JVM. */ _texecvp(command[0], command); execErrno = errno; /* We reached this point...meaning we were unable to start. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("%sUnable to start JVM: %s (%d)"), LOG_FORK_MARKER, getLastErrorText(), execErrno); if (execErrno == E2BIG) { /* Command line too long. */ /* Calculate the total length of the command line. */ lenCmd = 0; for (i = 0; command[i] != NULL; i++) { lenCmd += _tcslen(command[i]) + 1; } lenEnv = wrapperCalculateEnvironmentLength(); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("%s The generated command line plus the environment was larger than the maximum allowed."), LOG_FORK_MARKER); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("%s The current length is %d bytes of which %d is the command line, and %d is the environment."), LOG_FORK_MARKER, lenCmd + lenEnv + 1, lenCmd, lenEnv); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("%s It is not possible to calculate an exact maximum length as it depends on a number of factors for each system."), LOG_FORK_MARKER); /* TODO: Figure out a way to inform the Wrapper not to restart and try again as repeatedly doing this is meaningless. */ } if (wrapperData->isAdviserEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("%s"), LOG_FORK_MARKER ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("%s------------------------------------------------------------------------"), LOG_FORK_MARKER ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("%sAdvice:"), LOG_FORK_MARKER ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("%sUsually when the Wrapper fails to start the JVM process, it is because"), LOG_FORK_MARKER ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("%sof a problem with the value of the configured Java command. Currently:"), LOG_FORK_MARKER ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("%swrapper.java.command=%s"), LOG_FORK_MARKER, getStringProperty(properties, TEXT("wrapper.java.command"), TEXT("java"))); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("%sPlease make sure that the PATH or any other referenced environment"), LOG_FORK_MARKER ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("%svariables are correctly defined for the current environment."), LOG_FORK_MARKER ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("%s------------------------------------------------------------------------"), LOG_FORK_MARKER ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("%s"), LOG_FORK_MARKER ); } /* This process needs to end. */ if (command == wrapperData->jvmVersionCommand) { /* Set an exit code to distinguish cases where we failed to execute the java command. */ exit(127); } else { exit(wrapperData->errorExitCode); } return TRUE; /* Will not get here. */ } else { /* We are the parent side and need to assume that at this point the JVM is up. */ *pidPtr = proc; /* Close the write end as it is not used. */ close(pipedes[PIPE_WRITE_END]); pipedes[PIPE_WRITE_END] = -1; if (useStdin) { /* Close the read end as it is not used. */ close(pipeind[PIPE_READ_END]); pipeind[PIPE_READ_END] = -1; } /* The pipedes & pipeind arrays are global so do not close the other ends of the pipes. */ /* Restore the auto close flag. */ setLogfileAutoClose(wrapperData->logfileCloseTimeout == 0); /* Mark our sides of the pipes so that they won't block * and will close on exec, so new children won't see them. */ if (fcntl(pipedes[PIPE_READ_END], F_SETFL, O_NONBLOCK) < 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to set JVM output handle to non blocking mode: %s (%d)"), getLastErrorText(), errno); } if (fcntl(pipedes[PIPE_READ_END], F_SETFD, FD_CLOEXEC) < 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to set JVM output handle to close on JVM exit: %s (%d)"), getLastErrorText(), errno); } if (useStdin) { /* Mark our side of the pipe so that it will block on writing if the pipe is full * and will close on exec, so new children won't see it. */ if (fcntl(pipeind[PIPE_WRITE_END], F_SETFL, O_SYNC) < 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to set JVM input handle to non blocking mode: %s (%d)"), getLastErrorText(), errno); } if (fcntl(pipeind[PIPE_WRITE_END], F_SETFD, FD_CLOEXEC) < 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to set JVM input handle to close on JVM exit: %s (%d)"), getLastErrorText(), errno); } } return FALSE; } } /** * Create a child process to print the Java version running the command: * /path/to/java -version * After printing the java version, the process is terminated. * * In case the JVM is slow to start, it will time out after * the number of seconds set in "wrapper.java.version.timeout". * * @return One of the following state: * JAVA_VERSION_LAUNCH_FAILED * JAVA_VERSION_WAIT_FAILED * JAVA_VERSION_KILL_FAILED * JAVA_VERSION_COMPLETED */ int wrapperLaunchJavaVersion() { int i; int blockTimeout; /* max time (in ms) to wait for the child process to terminate */ int ret; /* result of waitpid */ int status; /* status of child process */ pid_t pid; /* pid of the child process */ int exitCode; int result = JAVA_VERSION_COMPLETED; /* If the JVM version printout is requested then log its command line first. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Java Command Line (Query Java Version):")); for (i = 0; wrapperData->jvmVersionCommand[i] != NULL; i++) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" Command[%d] : %s"), i, wrapperData->jvmVersionCommand[i]); } } /* Force using the encoding of the current locale to read the output of the Java version * (we know this this JVM is launched without system properties specifying a different encoding). */ resetJvmOutputEncoding(FALSE); if (wrapperLaunchJvm(wrapperData->jvmVersionCommand, &pid)) { return JAVA_VERSION_LAUNCH_FAILED; } /* Note: on CentOS, in case we don't call waitpid(), then the child process becomes a zombie */ /* If the user sets the value to 0, then we will wait indefinitely. * NOTE: it would be better to create a new state in the main event loop as this would allow the user to interrupt with CTRL-C. */ blockTimeout = getIntProperty(properties, TEXT("wrapper.java.version.timeout"), DEFAULT_JAVA_VERSION_TIMEOUT) * 1000; if (blockTimeout > 0) { while (((ret = waitpid(pid, &status, WNOHANG)) == 0) && (blockTimeout > 0)) { #ifdef _DEBUG _tprintf(TEXT("Child process: Java version: waiting... ret=%d waitpidStatLoc=%d blockTimeout=%d\n"), ret, status, blockTimeout); #endif wrapperSleep(100); blockTimeout -= 100; } } else { /* Wait indefinitely. */ ret = waitpid(pid, &status, 0); } if (ret > 0) { /* Process completed - we know that nothing more can be written to stdout/stderr. */ if (WIFEXITED(status)) { exitCode = WEXITSTATUS(status); } else { /* This should never happen (unless the process crashed or was interrupted by a signal). * Even if that would be the case, it doesn't hurt to try to read and parse the output. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Child process: Java version: did not exit normally.")); exitCode = 0; } if (exitCode == 0) { /* The command completed successfully. */ wrapperReadJavaVersionOutput(TRUE); } else { if (exitCode == 127) { /* We called exit(127) in the child process, which means the java command could not execute normally. * This is similar to Windows when wrapperLaunchJvm() returns TRUE. * Most probably we won't get any output from the Java command itself, but we still need to print the Wrapper messages of the forked child. */ } else { /* The child process terminated (within '_texecvp') with an 'exit'. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Child process: Java version: terminated with an exit code of %d."), exitCode); } /* Set a flag so that there will be no attempt to parse the Java version. * This flag is reset to FALSE whenever wrapperReadJavaVersionOutput() is called. */ wrapperData->jvmVersionFailed = TRUE; /* Read the output of the forked child. It may contain: * - the output of command that failed (if any), which may give us a clue of what the problem is. * - messages of the Wrapper. Those should start with 'LOG_FORK_MARKER' so that log_printf format them as if they were printed by the parent. */ while (wrapperReadChildOutput(250)) { } if (exitCode == 127) { return TRUE; } else { /* Resolve the Java version to its default value. */ wrapperSetJavaVersion(NULL); } } } else { if (ret == 0) { /* Timed out. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Child process: Java version: timed out")); } else { /* Wait failed. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Child process: Java version: wait failed")); } if ((kill(pid, SIGKILL) != 0) && (errno != ESRCH)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Child process: Java version: kill failed - %s"), getLastErrorText()); result = JAVA_VERSION_KILL_FAILED; } else { /* The process is now killed. */ /* Reset wrapperData->javaVersion. */ if (wrapperData->javaVersion) { disposeJavaVersion(wrapperData->javaVersion); wrapperData->javaVersion = NULL; } /* There might be no output but read all the pipe anyway. */ wrapperReadJavaVersionOutput(FALSE); /* Continue only if, by chance, the Java version was found. */ if (!wrapperData->javaVersion) { result = JAVA_VERSION_WAIT_FAILED; } } } return result; } /** * Launches a JVM process and stores it internally. * * @return TRUE if there were any problems. When this happens the Wrapper will not try to restart. */ int wrapperLaunchJavaApp() { static int javaIOThreadSet = FALSE; static int javaINThreadSet = FALSE; int i; pid_t pid; /* Update the CLASSPATH in the environment if requested so the JVM can access it. */ if (wrapperData->environmentClasspath) { setEnv(TEXT("CLASSPATH"), wrapperData->classpath, ENV_SOURCE_APPLICATION); } /* Log the application java command line */ if (wrapperData->commandLogLevel != LEVEL_NONE) { log_printf(WRAPPER_SOURCE_WRAPPER, wrapperData->commandLogLevel, TEXT("Java Command Line:")); for (i = 0; wrapperData->jvmCommand[i] != NULL; i++) { log_printf(WRAPPER_SOURCE_WRAPPER, wrapperData->commandLogLevel, TEXT(" Command[%d] : %s"), i, wrapperData->jvmCommand[i]); } if (wrapperData->environmentClasspath) { log_printf(WRAPPER_SOURCE_WRAPPER, wrapperData->commandLogLevel, TEXT(" Classpath in Environment : %s"), wrapperData->classpath); } } if (wrapperData->runWithoutJVM) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Not launching a JVM because %s was set to TRUE."), TEXT("wrapper.test.no_jvm")); wrapperData->exitCode = 0; return TRUE; } if (wrapperData->javaNewProcessGroup && !wrapperData->disableConsoleInput) { /* Create and initialize a javaIN thread. */ if (!javaINThreadSet) { if (initializeJavaIN()) { return TRUE; } javaINThreadSet = TRUE; } } if (wrapperData->useJavaIOThread) { /* Create and initialize a javaIO thread. */ if (!javaIOThreadSet) { if (initializeJavaIO()) { return TRUE; } javaIOThreadSet = TRUE; } } /* Now launch the JVM process. */ if (wrapperLaunchJvm(wrapperData->jvmCommand, &pid)) { wrapperData->exitCode = wrapperData->errorExitCode; return TRUE; } /* Reset the exit code when we launch a new JVM. */ wrapperData->exitCode = 0; /* Reset the stopped flag. */ wrapperData->jvmStopped = FALSE; /* We keep a reference to the process id. */ wrapperData->javaPID = pid; /* Log the PID of the new JVM. */ if (wrapperData->pidLogLevel != LEVEL_NONE) { log_printf(WRAPPER_SOURCE_WRAPPER, wrapperData->pidLogLevel, TEXT("JVM started (PID=%d)"), wrapperData->javaPID); } /* If a java pid filename is specified then write the pid of the java process. */ if (wrapperData->javaPidFilename) { if (writePidFile(wrapperData->javaPidFilename, wrapperData->javaPID, wrapperData->javaPidFileUmask, wrapperData->javaPidFileGroup)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to write the Java PID file: %s"), wrapperData->javaPidFilename); } } /* If a java id filename is specified then write the Id of the java process. */ if (wrapperData->javaIdFilename) { if (writePidFile(wrapperData->javaIdFilename, wrapperData->jvmRestarts, wrapperData->javaIdFileUmask, wrapperData->javaIdFileGroup)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to write the Java Id file: %s"), wrapperData->javaIdFilename); } } return FALSE; } /** * Returns a tick count that can be used in combination with the * wrapperGetTickAgeSeconds() function to perform time keeping. */ TICKS wrapperGetTicks() { TICKS ticks; if (wrapperData->useSystemTime) { /* We want to return a tick count that is based on the current system time. */ ticks = wrapperGetSystemTicks(); } else { /* Lock the tick mutex whenever the "timerTicks" variable is accessed. */ if (wrapperData->useTickMutex && wrapperLockTickMutex()) { return 0; } /* Return a snapshot of the current tick count. */ ticks = timerTicks; if (wrapperData->useTickMutex && wrapperReleaseTickMutex()) { return 0; } } return ticks; } /** * Outputs a a log entry describing what the memory dump columns are. */ void wrapperDumpMemoryBanner() { /* Not yet implemented on UNIX platforms. */ } /** * Outputs a log entry at regular intervals to track the memory usage of the * Wrapper and its JVM. */ void wrapperDumpMemory() { struct rusage wUsage; struct rusage jUsage; if (getrusage(RUSAGE_SELF, &wUsage)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Call to getrusage failed for Wrapper process: %s"), getLastErrorText()); return; } /* The Children is only going to show the value for terminated children. */ if (getrusage(RUSAGE_CHILDREN, &jUsage)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Call to getrusage failed for Java process: %s"), getLastErrorText()); return; } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Wrapper Memory: maxrss=%ld, ixrss=%ld, idrss=%ld, isrss=%ld, minflt=%ld, majflt=%ld, nswap=%ld, inblock=%ld, oublock=%ld, msgsnd=%ld, msgrcv=%ld, nsignals=%ld, nvcsw=%ld, nvcsw=%ld"), wUsage.ru_maxrss, wUsage.ru_ixrss, wUsage.ru_idrss, wUsage.ru_isrss, wUsage.ru_minflt, wUsage.ru_majflt, wUsage.ru_nswap, wUsage.ru_inblock, wUsage.ru_oublock, wUsage.ru_msgsnd, wUsage.ru_msgrcv, wUsage.ru_nsignals, wUsage.ru_nvcsw, wUsage.ru_nvcsw); } /** * Outputs a log entry at regular intervals to track the CPU usage over each * interval for the Wrapper and its JVM. */ void wrapperDumpCPUUsage() { struct rusage wUsage; struct rusage jUsage; if (getrusage(RUSAGE_SELF, &wUsage)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Call to getrusage failed for Wrapper process: %s"), getLastErrorText()); return; } /* The Children is only going to show the value for terminated children. */ if (getrusage(RUSAGE_CHILDREN, &jUsage)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Call to getrusage failed for Java process: %s"), getLastErrorText()); return; } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Wrapper CPU: system %ld.%03ld, user %ld.%03ld Java CPU: system %ld.%03ld, user %ld.%03ld"), wUsage.ru_stime.tv_sec, wUsage.ru_stime.tv_usec / 1000, wUsage.ru_utime.tv_sec, wUsage.ru_utime.tv_usec / 1000, jUsage.ru_stime.tv_sec, jUsage.ru_stime.tv_usec / 1000, jUsage.ru_utime.tv_sec, jUsage.ru_utime.tv_usec / 1000); } /** * Checks on the status of the JVM Process. * Returns WRAPPER_PROCESS_UP or WRAPPER_PROCESS_DOWN */ int wrapperGetProcessStatus(TICKS nowTicks, int childContinued) { int retval; int status; int exitCode; int res; if (wrapperData->javaPID <= 0) { /* We do not think that a JVM is currently running so return that it is down. * If we call waitpid with 0, it will wait for any child and cause problems with the event commands. */ return WRAPPER_PROCESS_DOWN; } retval = waitpid(wrapperData->javaPID, &status, WNOHANG | WUNTRACED); if (retval == 0) { /* Up and running. */ if (childContinued && wrapperData->jvmStopped) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("JVM process was continued.")); wrapperData->jvmStopped = FALSE; } res = WRAPPER_PROCESS_UP; } else if (retval < 0) { if (errno == ECHILD) { if ((wrapperData->jState == WRAPPER_JSTATE_DOWN_CHECK) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH_STDIN) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_CLEAN) || (wrapperData->jState == WRAPPER_JSTATE_STOPPED)) { res = WRAPPER_PROCESS_DOWN; wrapperJVMProcessExited(nowTicks, 0); return res; } else { /* Process is gone. Happens after a SIGCHLD is handled. Normal. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("JVM process is gone.")); } } else { /* Error requesting the status. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to request JVM process status: %s"), getLastErrorText()); } exitCode = wrapperData->errorExitCode; res = WRAPPER_PROCESS_DOWN; wrapperJVMProcessExited(nowTicks, exitCode); } else { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" WIFEXITED=%d"), WIFEXITED(status)); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" WIFSTOPPED=%d"), WIFSTOPPED(status)); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" WIFSIGNALED=%d"), WIFSIGNALED(status)); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" WTERMSIG=%d"), WTERMSIG(status)); #endif /* Get the exit code of the process. */ if (WIFEXITED(status)) { /* JVM has exited. */ exitCode = WEXITSTATUS(status); res = WRAPPER_PROCESS_DOWN; wrapperJVMProcessExited(nowTicks, exitCode); } else if (WIFSIGNALED(status)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("JVM received a signal %s (%d)."), getSignalName(WTERMSIG(status)), WTERMSIG(status)); res = WRAPPER_PROCESS_UP; } else if (WIFSTOPPED(status)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("JVM process was stopped. It will be killed if the ping timeout expires.")); wrapperData->jvmStopped = TRUE; res = WRAPPER_PROCESS_UP; } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("JVM process signaled the Wrapper unexpectedly.")); res = WRAPPER_PROCESS_UP; } } return res; } /** * This function does nothing on Unix machines. */ void wrapperReportStatus(int useLoggerQueue, int status, int errorCode, int waitHint) { return; } /** * Reads a single block of data from the child pipe. * * @param blockBuffer Pointer to the buffer where the block will be read. * @param blockSize Maximum number of bytes to read. * @param readCount Pointer to an int which will hold the number of bytes * actually read by the call. * * Returns TRUE if there were any problems, FALSE otherwise. */ int wrapperReadChildOutputBlock(char *blockBuffer, int blockSize, int *readCount) { if (pipedes[PIPE_READ_END] == -1) { /* The child is not up. */ *readCount = 0; return FALSE; } #ifdef FREEBSD /* Work around FreeBSD Bug #kern/64313 * http://www.freebsd.org/cgi/query-pr.cgi?pr=kern/64313 * * When linked with the pthreads library the O_NONBLOCK flag is being reset * on the pipedes[PIPE_READ_END] handle. Not sure yet of the exact event that is causing * this, but once it happens reads will start to block even though calls * to fcntl(pipedes[PIPE_READ_END], F_GETFL) say that the O_NONBLOCK flag is set. * Calling fcntl(pipedes[PIPE_READ_END], F_SETFL, O_NONBLOCK) again will set the flag back * again and cause it to start working correctly. This may only need to * be done once, however, because F_GETFL does not return the accurate * state there is no reliable way to check. Be safe and always set the * flag. */ if (fcntl(pipedes[PIPE_READ_END], F_SETFL, O_NONBLOCK) < 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "Failed to set JVM output handle to non blocking mode to read child process output: %s (%d)"), getLastErrorText(), errno); return TRUE; } #endif /* Fill read buffer. */ *readCount = read(pipedes[PIPE_READ_END], blockBuffer, blockSize); if (*readCount < 0) { /* No more bytes available, return for now. But make sure that this was not an error. */ if (errno == EAGAIN) { /* Normal, the call would have blocked as there is no data available. */ } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "Failed to read console output from the JVM: %s (%d)"), getLastErrorText(), errno); return TRUE; } } else if (*readCount == 0) { /* We reached the EOF. This means that the other end of the pipe was closed. */ close(pipedes[PIPE_READ_END]); pipedes[PIPE_READ_END] = -1; } return FALSE; } /** * Transform a program into a daemon. * * The idea is to first fork, then make the child a session leader, * and then fork again, so that it, (the session group leader), can * exit. This means that we, the grandchild, as a non-session group * leader, can never regain a controlling terminal. */ void daemonize(int argc, TCHAR** argv) { pid_t pid; int fd; /* Set the auto close flag and close the logfile before doing any forking to avoid * duplicate open files. */ setLogfileAutoClose(TRUE); closeLogfile(); /* first fork */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Spawning intermediate process...")); } if ((pid = fork()) < 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Could not spawn daemon process: %s"), getLastErrorText()); appExit(wrapperData->errorExitCode, argc, argv); } else if (pid != 0) { /* Intermediate process is now running. This is the original process, so exit. */ /* If the main process was not launched in the background, then we want to make * the console output look nice by making sure that all output from the * intermediate and daemon threads are complete before this thread exits. * Sleep for 0.5 seconds. */ wrapperSleep(500); /* Call exit rather than appExit as we are only exiting this process. */ exit(0); } /* become session leader */ if (setsid() == -1) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("setsid() failed: %s"), getLastErrorText()); appExit(wrapperData->errorExitCode, argc, argv); } signal(SIGHUP, SIG_IGN); /* don't let future opens allocate controlling terminals */ /* Redirect stdin, stdout and stderr before closing to prevent the shell which launched * the Wrapper from hanging when it exits. */ fd = _topen(TEXT("/dev/null"), O_RDWR, 0); if (fd != -1) { close(STDIN_FILENO); dup2(fd, STDIN_FILENO); close(STDOUT_FILENO); dup2(fd, STDOUT_FILENO); close(STDERR_FILENO); dup2(fd, STDERR_FILENO); if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) { close(fd); } } /* Console output was disabled above, so make sure the console log output is disabled * so we don't waste any CPU formatting and sending output to '/dev/null'/ */ toggleLogDestinations(LOG_DESTINATION_CONSOLE, FALSE); /* second fork */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Spawning daemon process...")); } if ((pid = fork()) < 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Could not spawn daemon process: %s"), getLastErrorText()); appExit(wrapperData->errorExitCode, argc, argv); } else if (pid != 0) { /* Daemon process is now running. This is the intermediate process, so exit. */ /* Call exit rather than appExit as we are only exiting this process. */ exit(0); } /* Restore the auto close flag in the daemonized process. */ setLogfileAutoClose(wrapperData->logfileCloseTimeout == 0); } /** * Sets the working directory to that of the current executable */ int setWorkingDir(TCHAR *app) { TCHAR *szPath; TCHAR* pos; /* Get the full path and filename of this program */ if ((szPath = findPathOf(app, TEXT("Wrapper binary"), TRUE)) == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to get the path for '%s' - %s"), app, getLastErrorText()); return 1; } /* The wrapperData->isDebugging flag will never be set here, so we can't really use it. */ #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Executable Name: %s"), szPath); #endif /* To get the path, strip everything off after the last '\' */ pos = _tcsrchr(szPath, TEXT('/')); if (pos == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to extract path from: %s"), szPath); free(szPath); return 1; } else { /* Clip the path at the position of the last backslash */ pos[0] = (TCHAR)0; } /* Set a variable to the location of the binary. */ setEnv(TEXT("WRAPPER_BIN_DIR"), szPath, ENV_SOURCE_APPLICATION); if (wrapperSetWorkingDir(szPath)) { free(szPath); return 1; } free(szPath); return 0; } /******************************************************************************* * Main function * *******************************************************************************/ #ifndef CUNIT #ifdef UNICODE int main(int argc, char **cargv) { size_t req; TCHAR **argv; #else int main(int argc, char **argv) { #endif #if defined(_DEBUG) || defined(UNICODE) int i; #endif TCHAR *retLocale; int localeSet; TCHAR *envLang; TCHAR *logFilePath; int ret; #ifdef FREEBSD /* In the case of FreeBSD, we need to dynamically load and initialize the iconv library to work with all versions of FreeBSD. */ if (loadIconvLibrary()) { /* Already reported. */ /* Don't call appExit here as we are not far enough along. */ return 1; } #endif /* Set the default locale here so any startup error messages will have a chance of working. * This should be done before converting cargv to argv, because there might be accentued letters in cargv. */ envLang = _tgetenv(TEXT("LANG")); retLocale = _tsetlocale(LC_ALL, TEXT("")); if (!retLocale) { /* On some platforms (i.e. Linux ARM), the locale can't be set if LC_ALL is empty. * In such case, set LC_ALL to the value of LANG and try again. */ setEnv(TEXT("LC_ALL"), envLang, ENV_SOURCE_APPLICATION); retLocale = _tsetlocale(LC_ALL, TEXT("")); } if (retLocale) { #if defined(UNICODE) free(retLocale); if (envLang) { free(envLang); } #endif localeSet = TRUE; } else { /* Do not free envLang yet. We will use it below to print a warning. */ localeSet = FALSE; } #ifdef UNICODE /* Create UNICODE versions of the argv array for internal use. */ argv = malloc(argc * sizeof(TCHAR *)); if (!argv) { _tprintf(TEXT("Out of Memory in Main\n")); appExit(1, 0, NULL); return 1; } for (i = 0; i < argc; i++) { req = mbstowcs(NULL, cargv[i], MBSTOWCS_QUERY_LENGTH); if (req == (size_t)-1) { _tprintf(TEXT("Encoding problem with arguments in Main\n")); free(argv); appExit(1, 0, NULL); return 1; } argv[i] = malloc(sizeof(TCHAR) * (req + 1)); if (!argv[i]) { _tprintf(TEXT("Out of Memory in Main\n")); while (--i > 0) { free(argv[i]); } free(argv); appExit(1, 0, NULL); return 1; } mbstowcs(argv[i], cargv[i], req + 1); argv[i][req] = TEXT('\0'); /* Avoid bufferflows caused by badly encoded characters. */ } #endif if ((argc > 1) && (argv[1][0] == TEXT('-')) && isPromptCallCommand(&argv[1][1])) { /* This is a request from the launching script. All log output should be disabled. */ toggleLogDestinations(LOG_DESTINATION_ALL, FALSE); } else { for (i = 1; i < argc; i++) { /* Disable console output if this should be a daemonized process. */ if (!strcmpIgnoreCase(argv[i], TEXT("wrapper.daemonize=true"))) { toggleLogDestinations(LOG_DESTINATION_CONSOLE, FALSE); break; } } } if (wrapperInitialize()) { appExit(1, argc, argv); return 1; /* For compiler. */ } #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Wrapper DEBUG build!")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Logging initialized.")); #endif /* Get the current process. */ wrapperData->wrapperPID = getpid(); if (setWorkingDir(argv[0])) { appExit(1, argc, argv); return 1; /* For compiler. */ } #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Working directory set.")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Arguments:")); for (i = 0; i < argc; i++) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" argv[%d]=%s"), i, argv[i]); } #endif /* Parse the command and configuration file from the command line. */ if (!wrapperParseArguments(argc, argv)) { appExit(1, argc, argv); return 1; /* For compiler. */ } if (!localeSet) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Unable to set the locale to '%s'. Please make sure $LC_* and $LANG are correct."), (envLang ? envLang : TEXT(""))); #if defined(UNICODE) if (envLang) { free(envLang); } #endif } wrapperLoadHostName(); /* At this point, we have a command, confFile, and possibly additional arguments. */ if (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("?")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-help"))) { /* User asked for the usage. */ setSimpleLogLevels(); wrapperUsage(argv[0]); appExit(0, argc, argv); return 0; /* For compiler. */ } else if (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("v")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-version"))) { /* User asked for version. */ setSimpleLogLevels(); wrapperVersionBanner(); appExit(0, argc, argv); return 0; /* For compiler. */ } else if (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("h")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-hostid"))) { /* User asked for version. */ setSimpleLogLevels(); wrapperVersionBanner(); showHostIds(LEVEL_STATUS, TRUE); appExit(0, argc, argv); return 0; /* For compiler. */ } /* Load the properties. */ /* To make the WRAPPER_LANG references in the configuration work correctly, * it is necessary to load the configuration twice. * The first time, we want to ignore the return value. Any errors will be * suppressed and will get reported again the second time through. */ /* From version 3.5.27, the community edition will also preload the configuration properties. */ wrapperLoadConfigurationProperties(TRUE); if (wrapperLoadConfigurationProperties(FALSE)) { /* Unable to load the configuration. Any errors will have already * been reported. */ if (wrapperData->argConfFileDefault && !wrapperData->argConfFileFound) { /* The config file that was being looked for was default and * it did not exist. Show the usage. */ wrapperUsage(argv[0]); } else { /* There might have been some queued messages logged on configuration load. Queue the following message to make it appear last. */ log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" The Wrapper will stop.")); } appExit(wrapperData->errorExitCode, argc, argv); return 1; /* For compiler. */ } /* Set the default umask of the Wrapper process. */ umask(wrapperData->umask); if (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("-translate"))) { setSimpleLogLevels(); /* Print out the string so the caller sees it as its translated output. */ _tprintf(TEXT("%s"), argv[2]); /* Reset silent mode as some queued messages may be printed. */ toggleLogDestinations(LOG_DESTINATION_ALL, FALSE); appExit(0, argc, argv); return 0; /* For compiler. */ } else if (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("-jvm_bits"))) { /* Generate the command used to get the Java version but don't stop on failure. */ if (!wrapperBuildJavaVersionCommand()) { wrapperLaunchJavaVersion(); } appExit(wrapperData->jvmBits, argc, argv); return 0; /* For compiler. */ } else if (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("-request_delta_binary_bits"))) { /* Otherwise return the binary bits */ appExit(_tcscmp(wrapperBits, TEXT("64")) == 0 ? 64 : 32, argc, argv); return 0; /* For compiler. */ } else if (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("-request_log_file"))) { setSimpleLogLevels(); /* Always try to (re)convert to an absolute path (because non-existent relative paths are left unchanged by the logger). */ logFilePath = getAbsolutePathOfFile(getLogfilePath(), TEXT(""), LEVEL_NONE, FALSE); if (logFilePath) { /* Check if the path is writable. */ if (!checkLogfileDir(FALSE)) { /* Expand the path (if some tokens are used for rolling) */ generateCurrentLogFileName(&logFilePath); /* Print out the log file path. */ _tprintf(TEXT("%s"), logFilePath); ret = 0; } else { ret = 1; } free(logFilePath); } else { ret = 1; } /* Reset silent mode as some queued messages may be printed. */ toggleLogDestinations(LOG_DESTINATION_ALL, FALSE); appExit(ret, argc, argv); return ret; /* For compiler. */ } else if (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("-request_default_log_file"))) { setSimpleLogLevels(); /* Always try to (re)convert to an absolute path (because non-existent relative paths are left unchanged by the logger). */ logFilePath = getAbsolutePathOfFile(getDefaultLogfilePath(), TEXT(""), LEVEL_NONE, FALSE); if (logFilePath) { /* Check if the path is writable. */ if (!checkLogfileDir(TRUE)) { /* The default log file doesn't contain token for rolling. */ /* Print out the log file path. */ _tprintf(TEXT("%s"), logFilePath); ret = 0; } else { ret = 1; } free(logFilePath); } else { ret = 1; } /* Reset silent mode as some queued messages may be printed. */ toggleLogDestinations(LOG_DESTINATION_ALL, FALSE); appExit(ret, argc, argv); return ret; /* For compiler. */ } else if (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("c")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-console"))) { /* Run as a console application */ /* fork to a Daemonized process if configured to do so. */ if (wrapperData->daemonize) { daemonize(argc, argv); /* We are now daemonized, so mark this as being a service. */ wrapperData->isConsole = FALSE; /* When we daemonize the Wrapper, its PID changes. Because of the * WRAPPER_PID environment variable, we need to set it again here * and then reload the configuration in case the PID is referenced * in the configuration. */ /* Get the current process. */ wrapperData->wrapperPID = getpid(); if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Reloading configuration.")); } /* If the working dir has been changed then we need to restore it before * the configuration can be reloaded. This is needed to support relative * references to include files. */ if (wrapperData->workingDir && wrapperData->originalWorkingDir) { if (wrapperSetWorkingDir(wrapperData->originalWorkingDir)) { /* Failed to restore the working dir. Shutdown the Wrapper */ /* There might have been some queued messages logged on configuration load. Queue the following message to make it appear last. */ log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" The Wrapper will stop.")); appExit(wrapperData->errorExitCode, argc, argv); return 1; /* For compiler. */ } } /* Load the properties. */ if (wrapperLoadConfigurationProperties(FALSE)) { /* Unable to load the configuration. Any errors will have already * been reported. */ if (wrapperData->argConfFileDefault && !wrapperData->argConfFileFound) { /* The config file that was being looked for was default and * it did not exist. Show the usage. */ wrapperUsage(argv[0]); } else { /* There might have been some queued messages logged on configuration load. Queue the following message to make it appear last. */ log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" The Wrapper will stop.")); } appExit(wrapperData->errorExitCode, argc, argv); return 1; /* For compiler. */ } } if (checkPidFile()) { /* The pid file exists and we are strict, so exit (cleanUpPIDFilesOnExit has not been turned on yet, so we will exit without cleaning the pid files). */ appExit(wrapperData->errorExitCode, argc, argv); return 1; /* For compiler. */ } /* From now on: * - all pid files will be cleaned when the Wrapper exits, * - any existing file will be owerwritten. */ cleanUpPIDFilesOnExit = TRUE; if (wrapperWriteStartupPidFiles()) { appExit(wrapperData->errorExitCode, argc, argv); return 1; /* For compiler. */ } if (wrapperData->isConsole) { appExit(wrapperRunConsole(), argc, argv); } else { appExit(wrapperRunService(), argc, argv); } return 0; /* For compiler. */ } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unrecognized option: -%s"), wrapperData->argCommand); wrapperUsage(argv[0]); appExit(wrapperData->errorExitCode, argc, argv); return 1; /* For compiler. */ } } #endif #endif /* ifndef WIN32 */ wrapper_3.5.51_src/src/c/wrapper_win.c100644 0 0 1431334 14333053652 15154 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ /** * Author: * Leif Mortenson */ #ifdef WIN32 /* the following constant must be defined before wincrypt.h */ #define CERT_CHAIN_PARA_HAS_EXTRA_FIELDS #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "psapi.h" #include #include #include #include "wrapper_i18n.h" #include "resource.h" #include "wrapper.h" #include "wrapperinfo.h" #include "property.h" #include "logger.h" #include "wrapper_file.h" #include "wrapper_encoding.h" /*#define WRAPPER_DEBUG_CONTROL_HANDLER */ /*#define WRAPPER_DEBUG_MESSAGES */ /*#define WRAPPER_DEBUG_CERTIFICATE */ /* The largest possible command line length on Windows. */ #define MAX_COMMAND_LINE_LEN 32766 #define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) typedef struct { LPWSTR lpszProgramName; LPWSTR lpszPublisherLink; LPWSTR lpszMoreInfoLink; } SPROG_PUBLISHERINFO, *PSPROG_PUBLISHERINFO; #ifndef POLICY_AUDIT_SUBCATEGORY_COUNT /* The current SDK is pre-Vista. Add the required definitions. */ typedef struct _TOKEN_ELEVATION { DWORD TokenIsElevated; } TOKEN_ELEVATION, *PTOKEN_ELEVATION; #define TokenElevation TokenOrigin + 3 #endif /***************************************************************************** * Win32 specific variables and procedures * *****************************************************************************/ SERVICE_STATUS ssStatus; SERVICE_STATUS_HANDLE sshStatusHandle; #define SYSTEM_PATH_MAX_LEN 256 static TCHAR *systemPath[SYSTEM_PATH_MAX_LEN]; static HANDLE wrapperChildStdoutWr = INVALID_HANDLE_VALUE; static HANDLE wrapperChildStdoutRd = INVALID_HANDLE_VALUE; TCHAR wrapperClasspathSeparator = TEXT(';'); HANDLE startupThreadHandle; DWORD startupThreadId; TICKS startupThreadStartTicks; int startupThreadStarted = FALSE; int startupThreadStopped = FALSE; int startupThreadTimeout; HANDLE javaIOThreadHandle; DWORD javaIOThreadId; int javaIOThreadStarted = FALSE; int stopJavaIOThread = FALSE; int javaIOThreadStopped = FALSE; HANDLE timerThreadHandle; DWORD timerThreadId; int timerThreadStarted = FALSE; int stopTimerThread = FALSE; int timerThreadStopped = FALSE; TICKS timerTicks = WRAPPER_TICK_INITIAL; HANDLE messageThreadHandle; DWORD messageThreadId; int messageThreadStarted = FALSE; int stopMessageThread = FALSE; int messageThreadStopped = FALSE; /** Flag which keeps track of whether or not the CTRL-C key has been pressed. */ int ctrlCTrapped = FALSE; TICKS ctrlCTrappedLastTick = WRAPPER_TICK_INITIAL; /** Flag which keeps track of whether or not PID files should be deleted on shutdown. */ int cleanUpPIDFilesOnExit = FALSE; /* Dynamically loadedfunction types. */ typedef SERVICE_STATUS_HANDLE(*FTRegisterServiceCtrlHandlerEx)(LPCTSTR, LPHANDLER_FUNCTION_EX, LPVOID); typedef BOOL(*FTWTSQuerySessionInformation)(IN HANDLE, IN DWORD, IN WTS_INFO_CLASS, LPWSTR*, DWORD*); typedef void(*FTWTSFreeMemory)(IN PVOID); typedef BOOL(*FTWTSRegisterSessionNotification)(HWND, DWORD); typedef BOOL(*FTWTSUnRegisterSessionNotification)(HWND); typedef BOOL(*FTShutdownBlockReasonCreate)(HWND, LPCWSTR); typedef BOOL(*FTShutdownBlockReasonDestroy)(HWND); /* Dynamically loaded functions. */ FARPROC OptionalGetProcessTimes = NULL; FARPROC OptionalGetProcessMemoryInfo = NULL; FTRegisterServiceCtrlHandlerEx OptionalRegisterServiceCtrlHandlerEx = NULL; FTWTSQuerySessionInformation OptionalWTSQuerySessionInformation = NULL; FTWTSFreeMemory OptionalWTSFreeMemory = NULL; FTWTSRegisterSessionNotification OptionalWTSRegisterSessionNotification = NULL; FTWTSUnRegisterSessionNotification OptionalWTSUnRegisterSessionNotification = NULL; FTShutdownBlockReasonCreate OptionalShutdownBlockReasonCreate = NULL; FTShutdownBlockReasonDestroy OptionalShutdownBlockReasonDestroy = NULL; /****************************************************************************** * Windows specific code ******************************************************************************/ PDH_HQUERY pdhQuery = NULL; PDH_HCOUNTER pdhCounterPhysicalDiskAvgQueueLen = NULL; PDH_HCOUNTER pdhCounterPhysicalDiskAvgWriteQueueLen = NULL; PDH_HCOUNTER pdhCounterPhysicalDiskAvgReadQueueLen = NULL; PDH_HCOUNTER pdhCounterMemoryPageFaultsPSec = NULL; PDH_HCOUNTER pdhCounterMemoryTransitionFaultsPSec = NULL; PDH_HCOUNTER pdhCounterProcessWrapperPageFaultsPSec = NULL; PDH_HCOUNTER pdhCounterProcessJavaPageFaultsPSec = NULL; #if(_WIN32_WINNT < 0x0600) #define WM_WTSSESSION_CHANGE 0x02B1 #define WTS_CONSOLE_CONNECT 0x1 #define WTS_CONSOLE_DISCONNECT 0x2 #define WTS_REMOTE_CONNECT 0x3 #define WTS_REMOTE_DISCONNECT 0x4 #define WTS_SESSION_LOGON 0x5 #define WTS_SESSION_LOGOFF 0x6 #define WTS_SESSION_LOCK 0x7 #define WTS_SESSION_UNLOCK 0x8 #define WTS_SESSION_REMOTE_CONTROL 0x9 #define WTS_SESSION_CREATE 0xa #define WTS_SESSION_TERMINATE 0xb #define SM_REMOTECONTROL 0x2001 #endif #if(_WIN32_WINNT < 0x0600) #define WM_DWMNCRENDERINGCHANGED 0x031f #endif #ifdef WRAPPER_DEBUG_MESSAGES const TCHAR *_wrapper_getMessageName(UINT message) { /* https://wiki.winehq.org/List_Of_Windows_Messages */ switch (message) { case WM_CREATE: return TEXT("WM_CREATE"); /* 0x0001 */ case WM_DESTROY: return TEXT("WM_DESTROY"); /* 0x0002 */ case WM_SIZE: return TEXT("WM_SIZE"); /* 0x0005 */ case WM_ACTIVATE: return TEXT("WM_ACTIVATE"); /* 0x0006 */ case WM_SETFOCUS: return TEXT("WM_SETFOCUS"); /* 0x0007 */ case WM_KILLFOCUS: return TEXT("WM_KILLFOCUS"); /* 0x0008 */ case WM_PAINT: return TEXT("WM_PAINT"); /* 0x000f */ case WM_GETTEXT: return TEXT("WM_GETTEXT"); /* 0x000d */ case WM_CLOSE: return TEXT("WM_CLOSE"); /* 0x0010 */ case WM_ERASEBKGND: return TEXT("WM_ERASEBKGND"); /* 0x0014 */ case WM_ENDSESSION: return TEXT("WM_ENDSESSION"); /* 0x0016 */ case WM_QUERYENDSESSION: return TEXT("WM_QUERYENDSESSION"); /* 0x0017 */ case WM_SHOWWINDOW: return TEXT("WM_SHOWWINDOW"); /* 0x0018 */ case WM_WININICHANGE: return TEXT("WM_WININICHANGE"); /* 0x001a */ case WM_ACTIVATEAPP: return TEXT("WM_ACTIVATEAPP"); /* 0x001c */ case WM_TIMECHANGE: return TEXT("WM_TIMECHANGE"); /* 0x001e */ case WM_SETCURSOR: return TEXT("WM_SETCURSOR"); /* 0x0020 */ case WM_MOUSEACTIVATE: return TEXT("WM_MOUSEACTIVATE"); /* 0x0021 */ case WM_GETMINMAXINFO: return TEXT("WM_GETMINMAXINFO"); /* 0x0024 */ case 0x003b: return TEXT("WM_UNKNOWN_003B"); /* 0x003b - Not sure what this is. */ case WM_WINDOWPOSCHANGING: return TEXT("WM_WINDOWPOSCHANGING"); /* 0x0046 */ case WM_WINDOWPOSCHANGED: return TEXT("WM_WINDOWPOSCHANGED"); /* 0x0047 */ case WM_GETICON: return TEXT("WM_GETICON"); /* 0x007f */ case WM_NCCREATE: return TEXT("WM_NCCREATE"); /* 0x0081 */ case WM_NCDESTROY: return TEXT("WM_NCDESTROY"); /* 0x0082 */ case WM_NCCALCSIZE: return TEXT("WM_NCCALCSIZE"); /* 0x0083 */ case WM_NCHITTEST: return TEXT("WM_NCHITTEST"); /* 0x0084 */ case WM_NCPAINT: return TEXT("WM_NCPAINT"); /* 0x0085 */ case WM_NCACTIVATE: return TEXT("WM_NCACTIVATE"); /* 0x0086 */ case WM_NCMOUSEMOVE: return TEXT("WM_NCMOUSEMOVE"); /* 0x00a0 */ case WM_INITDIALOG: return TEXT("WM_INITDIALOG"); /* 0x0110 */ case WM_COMMAND: return TEXT("WM_COMMAND"); /* 0x0111 */ case WM_SYSCOMMAND: return TEXT("WM_SYSCOMMAND"); /* 0x0112 */ case WM_MENUSELECT: return TEXT("WM_MENUSELECT"); /* 0x011f */ case WM_ENTERIDLE: return TEXT("WM_ENTERIDLE"); /* 0x0121 */ case WM_UNINITMENUPOPUP: return TEXT("WM_UNINITMENUPOPUP"); /* 0x0125 */ case WM_CTLCOLORBTN: return TEXT("WM_CTLCOLORBTN"); /* 0x0135 */ case WM_CTLCOLORDLG: return TEXT("WM_CTLCOLORDLG"); /* 0x0136 */ case WM_CTLCOLORSTATIC: return TEXT("WM_CTLCOLORSTATIC"); /* 0x0138 */ case WM_MOUSEMOVE: return TEXT("WM_MOUSEMOVE"); /* 0x0200 */ case WM_ENTERMENULOOP: return TEXT("WM_ENTERMENULOOP"); /* 0x0211 */ case WM_EXITMENULOOP: return TEXT("WM_EXITMENULOOP"); /* 0x0212 */ case WM_CAPTURECHANGED: return TEXT("WM_CAPTURECHANGED"); /* 0x0215 */ case WM_POWERBROADCAST: return TEXT("WM_POWERBROADCAST"); /* 0x0218 */ case WM_DEVICECHANGE: return TEXT("WM_DEVICECHANGE"); /* 0x0219 */ case WM_IME_SETCONTEXT: return TEXT("WM_IME_SETCONTEXT"); /* 0x0281 */ case WM_IME_NOTIFY: return TEXT("WM_IME_NOTIFY"); /* 0x0282 */ case WM_NCMOUSELEAVE: return TEXT("WM_NCMOUSELEAVE"); /* 0x02a2 */ case WM_WTSSESSION_CHANGE: return TEXT("WM_WTSSESSION_CHANGE"); /* 0x02b1 */ case WM_DWMNCRENDERINGCHANGED: return TEXT("WM_DWMNCRENDERINGCHANGED");/* 0x031f */ case WM_USER: return TEXT("WM_USER"); /* 0x0400 */ case 0xc1c6: return TEXT("WM_UNKNOWN_C1C6"); /* 0xc1c6 - Not sure what this is. */ default: return TEXT("UNKNOWN"); } } void _wrapper_debugMessage(TCHAR *handlerName, HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s(hwnd=%p, message=%s(0x%04x), wParam(l:0x%04x, h:0x%04x), lParam(l:0x%04x, h:0x%04x))"), handlerName, hwnd, _wrapper_getMessageName(message), message, LOWORD(wParam), HIWORD(wParam), LOWORD(lParam), HIWORD(lParam)); } #else #define _wrapper_debugMessage(handlerName, hwnd, message, wParam, lParam) #endif HWND _wrapper_messageWindowHWND; const TCHAR messageWindowClassName[] = TEXT("wrapperMessageWindowClass"); #define FILEPATHSIZE 1024 /** * Tests whether or not the current OS is at or below the version of Windows NT. * * @return TRUE if NT 4.0 or earlier, FALSE otherwise. */ BOOL isWindowsNT4_0OrEarlier() { OSVERSIONINFOEX osvi; BOOL bOsVersionInfoEx; BOOL retval; /* Try calling GetVersionEx using the OSVERSIONINFOEX structure. * If that fails, try using the OSVERSIONINFO structure. */ retval = TRUE; ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); #pragma warning(push) #pragma warning(disable : 4996) /* Visual Studio 2013 deprecates GetVersionEx but we still want to use it. */ if (!(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *)&osvi))) { /* If OSVERSIONINFOEX doesn't work, try OSVERSIONINFO. */ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); if (!GetVersionEx((OSVERSIONINFO *)&osvi)) { retval = TRUE; } } #pragma warning(pop) if (osvi.dwMajorVersion <= 4) { retval = TRUE; } else { retval = FALSE; } return retval; } void loadDLLProcs() { HMODULE kernel32Mod; HMODULE psapiMod; HMODULE advapi32Mod; HMODULE user32Mod; HMODULE wtsapi32Mod; /* The KERNEL32 module was added in NT 3.5. */ if ((kernel32Mod = GetModuleHandle(TEXT("KERNEL32.DLL"))) == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The %s file was not found. Some functions will be disabled."), TEXT("KERNEL32.DLL")); } else { if ((OptionalGetProcessTimes = GetProcAddress(kernel32Mod, "GetProcessTimes")) == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The %s function is not available in this %s version. Some functions will be disabled."), TEXT("GetProcessTimes"), TEXT("KERNEL32.DLL")); } } /* The PSAPI module was added in NT 4.0. */ if ((psapiMod = LoadLibrary(TEXT("PSAPI.DLL"))) == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The %s file was not found. Some functions will be disabled."), TEXT("PSAPI.DLL")); } else { if ((OptionalGetProcessMemoryInfo = GetProcAddress(psapiMod, "GetProcessMemoryInfo")) == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The %s function is not available in this %s version. Some functions will be disabled."), TEXT("GetProcessMemoryInfo"), TEXT("PSAPI.DLL")); } } /* The ADVAPI32 module was added in NT 5.0. */ if ((advapi32Mod = LoadLibrary(TEXT("ADVAPI32.DLL"))) == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The %s file was not found. Some functions will be disabled."), TEXT("ADVAPI32.DLL")); } else { if ((OptionalRegisterServiceCtrlHandlerEx = (FTRegisterServiceCtrlHandlerEx)GetProcAddress(advapi32Mod, "RegisterServiceCtrlHandlerExW")) == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The %s function is not available in this %s version. Some functions will be disabled."), TEXT("RegisterServiceCtrlHandlerExW"), TEXT("ADVAPI32.DLL")); } } /* The USER32 module was added in Vista, Server 2008. */ if ((user32Mod = LoadLibrary(TEXT("USER32.DLL"))) == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The %s file was not found. Some functions will be disabled."), TEXT("USER32.DLL")); } else { if ((OptionalShutdownBlockReasonCreate = (FTShutdownBlockReasonCreate)GetProcAddress(user32Mod, "ShutdownBlockReasonCreate")) == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The %s function is not available in this %s version. Some functions will be disabled."), TEXT("ShutdownBlockReasonCreate"), TEXT("USER32.DLL")); } if ((OptionalShutdownBlockReasonDestroy = (FTShutdownBlockReasonDestroy)GetProcAddress(user32Mod, "ShutdownBlockReasonDestroy")) == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The %s function is not available in this %s version. Some functions will be disabled."), TEXT("ShutdownBlockReasonDestroy"), TEXT("USER32.DLL")); } } /* The WTSAPI32 module was added in Vista, Server 2008. */ if ((wtsapi32Mod = LoadLibrary(TEXT("WTSAPI32.DLL"))) == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The %s file was not found. Some functions will be disabled."), TEXT("WTSAPI32.DLL")); } else { if ((OptionalWTSQuerySessionInformation = (FTWTSQuerySessionInformation)GetProcAddress(wtsapi32Mod, "WTSQuerySessionInformationW")) == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The %s function is not available in this %s version. Some functions will be disabled."), TEXT("WTSQuerySessionInformationW"), TEXT("WTSAPI32.DLL")); } if ((OptionalWTSFreeMemory = (FTWTSFreeMemory)GetProcAddress(wtsapi32Mod, "WTSFreeMemory")) == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The %s function is not available in this %s version. Some functions will be disabled."), TEXT("WTSFreeMemory"), TEXT("WTSAPI32.DLL")); } if ((OptionalWTSRegisterSessionNotification = (FTWTSRegisterSessionNotification)GetProcAddress(wtsapi32Mod, "WTSRegisterSessionNotification")) == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The %s function is not available in this %s version. Some functions will be disabled."), TEXT("WTSRegisterSessionNotification"), TEXT("WTSAPI32.DLL")); } if ((OptionalWTSUnRegisterSessionNotification = (FTWTSUnRegisterSessionNotification)GetProcAddress(wtsapi32Mod, "WTSUnRegisterSessionNotification")) == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The %s function is not available in this %s version. Some functions will be disabled."), TEXT("WTSUnRegisterSessionNotification"), TEXT("WTSAPI32.DLL")); } } } /** * Builds an array in memory of the system path. */ int buildSystemPath() { TCHAR *envBuffer; size_t len, i; TCHAR *c, *lc; /* Get the length of the PATH environment variable. */ len = GetEnvironmentVariable(TEXT("PATH"), NULL, 0); if (len == 0) { /* PATH not set on this system. Not an error. */ systemPath[0] = NULL; return 0; } /* Allocate the memory to hold the PATH */ envBuffer = malloc(sizeof(TCHAR) * len); if (!envBuffer) { outOfMemory(TEXT("BSP"), 1); return 1; } GetEnvironmentVariable(TEXT("PATH"), envBuffer, (DWORD)len); #ifdef _DEBUG _tprintf(TEXT("Getting the system path: %s\n"), envBuffer); #endif /* Build an array of the path elements. To make it easy, just * assume there won't be more than 255 path elements. Verified * in the loop. */ i = 0; lc = envBuffer; /* Get the elements ending in a ';' */ while (((c = _tcschr(lc, TEXT(';'))) != NULL) && (i < SYSTEM_PATH_MAX_LEN - 2)) { len = (int)(c - lc); systemPath[i] = malloc(sizeof(TCHAR) * (len + 1)); if (!systemPath[i]) { outOfMemory(TEXT("BSP"), 2); return 1; } memcpy(systemPath[i], lc, sizeof(TCHAR) * len); systemPath[i][len] = TEXT('\0'); #ifdef _DEBUG _tprintf(TEXT("PATH[%d]=%s\n"), i, systemPath[i]); #endif lc = c + 1; i++; } /* There should be one more value after the last ';' */ len = _tcslen(lc); systemPath[i] = malloc(sizeof(TCHAR) * (len + 1)); if (!systemPath[i]) { outOfMemory(TEXT("BSP"), 3); return 1; } _tcsncpy(systemPath[i], lc, len + 1); #ifdef _DEBUG _tprintf(TEXT("PATH[%d]=%s\n"), i, systemPath[i]); #endif i++; /* NULL terminate the array. */ systemPath[i] = NULL; #ifdef _DEBUG _tprintf(TEXT("PATH[%d]=\n"), i); #endif i++; /* Release the environment variable memory. */ free(envBuffer); return 0; } void disposeSystemPath() { int i = 0; /* Loop over and free each of the strings in the array */ while(systemPath[i] != NULL) { free(systemPath[i]); systemPath[i] = NULL; i++; } } TCHAR** wrapperGetSystemPath() { return systemPath; } /** * Initializes the invocation mutex. Returns 1 if the mutex already exists * or can not be created. 0 if this is the first instance. */ HANDLE invocationMutexHandle = NULL; int initInvocationMutex() { TCHAR *mutexName; if (wrapperData->isSingleInvocation) { mutexName = malloc(sizeof(TCHAR) * (30 + _tcslen(wrapperData->serviceName) + 1)); if (!mutexName) { outOfMemory(TEXT("IIM"), 1); wrapperData->exitCode = wrapperData->errorExitCode; return 1; } _sntprintf(mutexName, 30 + _tcslen(wrapperData->serviceName) + 1, TEXT("Global\\Java Service Wrapper - %s"), wrapperData->serviceName); if (!(invocationMutexHandle = CreateMutex(NULL, FALSE, mutexName))) { free(mutexName); if (GetLastError() == ERROR_ACCESS_DENIED) { /* Most likely the app is running as a service and we tried to run it as a console. */ if (wrapperServiceStatus(wrapperData->serviceName, wrapperData->serviceDisplayName, FALSE) & 0x2) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("ERROR: Another instance of the %s application is already running as a service."), wrapperData->serviceName); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("ERROR: Another instance of the %s application is already running on a different user account."), wrapperData->serviceName); } wrapperData->exitCode = wrapperData->errorExitCode; return 1; } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("ERROR: Unable to create the single invocation mutex. %s"), getLastErrorText()); wrapperData->exitCode = wrapperData->errorExitCode; return 1; } } else { free(mutexName); } if (GetLastError() == ERROR_ALREADY_EXISTS) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("ERROR: Another instance of the %s application is already running."), wrapperData->serviceName); wrapperData->exitCode = wrapperData->errorExitCode; return 1; } } wrapperData->exitCode = 0; return 0; } HANDLE mainExitMutexHandle = NULL; /** * Obtains a lock on the Main Exit mutex * Used to make sure that WM_ENDSESSION doesn't return before the Wrapper is disposed. */ int lockMainExitMutex() { switch (WaitForSingleObject(mainExitMutexHandle, INFINITE)) { case WAIT_ABANDONED: _tprintf(TEXT("Main Exit mutex was abandoned.\n")); return -1; case WAIT_FAILED: _tprintf(TEXT("Main Exit mutex wait failed.\n")); return -1; case WAIT_TIMEOUT: _tprintf(TEXT("Main Exit mutex wait timed out.\n")); return -1; default: /* Ok */ break; } return 0; } /** Releases a lock on the Main Exit mutex. */ int releaseMainExitMutex() { if (!ReleaseMutex(mainExitMutexHandle)) { _tprintf( TEXT("Failed to release Main Exit mutex. %s\n"), getLastErrorText()); return -1; } return 0; } void disposeMessageThread() { stopMessageThread = TRUE; /* Wait until the message thread is actually stopped to avoid timing problems. */ if (messageThreadStarted) { while (!messageThreadStopped) { #ifdef _DEBUG wprintf(TEXT("Waiting for Message thread to stop.\n")); #endif Sleep(10); } } } /** * exits the application after running shutdown code. */ void appExit(int exitCode) { static int isExiting = FALSE; /* Avoid being called more than once. */ if (isExiting) { return; } isExiting = TRUE; /* We only want to delete the pid files if we created them. Some Wrapper * invocations are meant to run in parallel with Wrapper instances * controlling a JVM. */ if (cleanUpPIDFilesOnExit) { /* Remove pid file. It may no longer exist. */ if (wrapperData->pidFilename) { _tunlink(wrapperData->pidFilename); } /* Remove lock file. It may no longer exist. */ if (wrapperData->lockFilename) { _tunlink(wrapperData->lockFilename); } /* Remove status file. It may no longer exist. */ if (wrapperData->statusFilename) { _tunlink(wrapperData->statusFilename); } /* Remove java status file if it was registered and created by this process. */ if (wrapperData->javaStatusFilename) { _tunlink(wrapperData->javaStatusFilename); } /* Remove java id file if it was registered and created by this process. */ if (wrapperData->javaIdFilename) { _tunlink(wrapperData->javaIdFilename); } /* Remove anchor file. It may no longer exist. */ if (wrapperData->anchorFilename) { _tunlink(wrapperData->anchorFilename); } } /* Close the invocation mutex if we created or looked it up. */ if (invocationMutexHandle) { CloseHandle(invocationMutexHandle); invocationMutexHandle = NULL; } #if defined(WRAPPER_DEBUG_MESSAGES) log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Disposing...")); #endif /* Common wrapper cleanup code. */ wrapperDispose(exitCode); /* Do this here to unregister the syslog resources on exit.*/ /*unregisterSyslogMessageFile(); */ if (mainExitMutexHandle) { #ifdef WRAPPER_DEBUG_MESSAGES _tprintf(TEXT("Sleep for 3 seconds to make sure that WM_ENDSESSION is waiting.")); fflush(NULL); wrapperSleep(3000); _tprintf(TEXT("Release the Main Exit mutex.")); fflush(NULL); #endif /* Release the mutex of the main thread to allow the message thread to terminate. */ releaseMainExitMutex(); /* At this point there is not guaranty that the system is still running. Once WM_ENDSESSION has returned, the session can exit anytime. */ #ifdef WRAPPER_DEBUG_MESSAGES _tprintf(TEXT("Close the Main Exit mutex and dispose the message thread.")); fflush(NULL); #endif /* We need to dispose the message Thread after releasing the lock on mainExitMutexHandle, else the thread * will be blocked while handling WM_ENDSESSION and disposeMessageThread() will wait indefinitely. */ disposeMessageThread(); /* Flush everything in case some messages were printed with _tprintf() */ fflush(NULL); } exit(exitCode); } /** * This function should be called when the Wrapper needs to exit after performing actions that did not involve the JVM. * The exit code should always be the exit code of the Wrapper! */ void wrapperExit(int exitCode) { if (exitCode == 0) { appExit(0); } else { appExit(wrapperData->errorExitCode); } } #ifndef WRAPPERW int canRunInteractive() { static int firstCall = TRUE; static int result = FALSE; HKEY hKey; DWORD data; DWORD cbData = sizeof(DWORD); DWORD error; /* It is ok to store the result in a static variable, because a service needs to be restarted for NoInteractiveServices to take effect. */ if (firstCall) { firstCall = FALSE; if (isElevated()) { if (!isVista()) { /* Windows XP and lower support interactive services. */ result = TRUE; } else { /* Starting from Windows Vista, interactive services are not allowed except if the registry was edited. */ if ((error = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Control\\Windows"), 0, KEY_READ, (PHKEY) &hKey)) == ERROR_SUCCESS) { if ((error = RegQueryValueEx(hKey, TEXT("NoInteractiveServices"), NULL, NULL, (LPBYTE) &data, &cbData)) == ERROR_SUCCESS) { if (data == 0) { result = TRUE; } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Failed to read the value of 'NoInteractiveServices' in the registry (0x%x)."), error); } RegCloseKey(hKey); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Failed to open the registry key to check if services can be run interactively (0x%x)."), error); } } } } return result; } #endif /** * Returns TRUE if the Wrapper process is associated with a console or if one * will be allocated a later stage. This function should be called after the * configuration file has been read. */ int wrapperProcessHasVisibleConsole() { #ifndef WRAPPERW if (wrapperData->isConsole) { return TRUE; } #endif /* Not a console application. */ if (wrapperData->configured) { /* wrapperData->ntShowWrapperConsole=TRUE => wrapperData->ntAllocConsole=TRUE * (list up all cases with wrapperData->generateConsole, wrapperData->ntAllocConsole, wrapperData->ntShowWrapperConsole to verify it) */ return (wrapperData->ntShowWrapperConsole #ifndef WRAPPERW && wrapperData->ntServiceInteractive && canRunInteractive() #endif ); } else { /* This will work if the configuration is not loaded yet, but the conf file needs to be read at least. */ return (getBooleanProperty(properties, TEXT("wrapper.ntservice.console"), FALSE) #ifndef WRAPPERW && getBooleanProperty(properties, TEXT("wrapper.ntservice.interactive"), FALSE) && canRunInteractive() #endif ); } } /** * Writes the specified Id or PID to disk. * * filename: File to write to. * pid: pid to write in the file. * strict: If true then an error will be reported and the call will fail if the * file already exists. * * return 1 if there was an error, 0 if Ok. */ int writePidFile(const TCHAR *filename, DWORD pid, int newUmask) { FILE *pid_fp = NULL; int old_umask; old_umask = _umask(newUmask); pid_fp = _tfopen(filename, TEXT("w")); _umask(old_umask); if (pid_fp != NULL) { _ftprintf(pid_fp, TEXT("%d\n"), pid); fclose(pid_fp); } else { return 1; } return 0; } /** * Initialize the pipe which will be used to capture the output from the child * process. */ int wrapperInitChildPipe() { SECURITY_ATTRIBUTES saAttr; HANDLE childStdoutRd = INVALID_HANDLE_VALUE; /* Set the bInheritHandle flag so pipe handles are inherited. */ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); saAttr.lpSecurityDescriptor = NULL; saAttr.bInheritHandle = TRUE; /* Create a pipe for the child process's STDOUT. */ if (!CreatePipe(&childStdoutRd, &wrapperChildStdoutWr, &saAttr, wrapperData->javaIOBufferSize)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Stdout pipe creation failed Err(%ld : %s)"), GetLastError(), getLastErrorText()); return -1; } /* Create a noninheritable read handle and close the inheritable read handle. */ if (!DuplicateHandle(GetCurrentProcess(), childStdoutRd, GetCurrentProcess(), &wrapperChildStdoutRd, 0, FALSE, DUPLICATE_SAME_ACCESS)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("DuplicateHandle failed")); return -1; } CloseHandle(childStdoutRd); return 0; } /** * Handler to take care of the case where the user hits CTRL-C when the wrapper * is being run as a console. * * Handlers are called in the reverse order that they are registered until one * returns TRUE. So last registered is called first until the default handler * is called. */ int wrapperConsoleHandler(int key) { if (!handleSignals) { return FALSE; } /* Immediately register this thread with the logger. */ logRegisterThread(WRAPPER_THREAD_SIGNAL); /* Enclose the contents of this call in a try catch block so we can * display and log useful information should the need arise. */ __try { switch (key) { case CTRL_C_EVENT: /* The user hit CTRL-C. Can only happen when run as a console. */ #ifdef WRAPPER_DEBUG_CONTROL_HANDLER log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Console Handler: trapped signal 'CTRL_C_EVENT'")); #endif if (wrapperData->ignoreSignals & WRAPPER_IGNORE_SIGNALS_WRAPPER) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("CTRL-C trapped, but ignored.")); } else { wrapperData->ctrlEventCTRLCTrapped = TRUE; } break; case CTRL_CLOSE_EVENT: /* The user tried to close the console. Can only happen when run as a console. */ #ifdef WRAPPER_DEBUG_CONTROL_HANDLER log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Console Handler: trapped signal 'CTRL_CLOSE_EVENT'")); #endif if (wrapperData->ignoreSignals & WRAPPER_IGNORE_SIGNALS_WRAPPER) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Close trapped, but ignored.")); } else { wrapperData->ctrlEventCloseTrapped = TRUE; } break; case CTRL_BREAK_EVENT: /* The user hit CTRL-BREAK */ #ifdef WRAPPER_DEBUG_CONTROL_HANDLER log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Console Handler: trapped signal 'CTRL_BREAK_EVENT'")); #endif log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("CTRL-BREAK/PAUSE trapped. Asking the JVM to dump its state.")); /* If the java process was launched using the same console, ie where * processflags=CREATE_NEW_PROCESS_GROUP; then the java process will * also get this message, so it can be ignored here. */ /* If we ever do something here, remember that this can't be called directly from here. wrapperRequestDumpJVMState(); */ break; case CTRL_LOGOFF_EVENT: /* Windows 7+: This will never be received if the gdi32.dll or user32.dll libraries are loaded. */ #ifdef WRAPPER_DEBUG_CONTROL_HANDLER log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Console Handler: trapped signal 'CTRL_LOGOFF_EVENT' Tick %d"), wrapperGetTicks()); #endif wrapperData->ctrlEventLogoffTrapped = TRUE; break; case CTRL_SHUTDOWN_EVENT: /* Windows 7+: This will never be received if the gdi32.dll or user32.dll libraries are loaded. */ #ifdef WRAPPER_DEBUG_CONTROL_HANDLER log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Console Handler: trapped signal 'CTRL_SHUTDOWN_EVENT' Tick %d"), wrapperGetTicks()); #endif wrapperData->ctrlEventShutdownTrapped = TRUE; break; default: /* Unknown. Don't quit here. */ log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Trapped unexpected console signal (%d). Ignored."), key); } } __except (exceptionFilterFunction(GetExceptionInformation())) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("<-- Wrapper Stopping due to error in console handler.")); appExit(wrapperData->errorExitCode); } return TRUE; /* We handled the event. */ } /****************************************************************************** * Platform specific methods *****************************************************************************/ /** * Send a signal to the JVM process asking it to dump its JVM state. */ void wrapperRequestDumpJVMState() { if (wrapperData->javaProcess != NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Dumping JVM state.")); if (wrapperData->javaPID == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("JVM is currently not running.")); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Sending BREAK event to process group %ld."), wrapperData->javaPID); if (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, wrapperData->javaPID) == 0) { if (wrapperData->generateConsole || wrapperData->ntAllocConsole) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to send BREAK event to JVM process to generate a thread dump. Err(%ld : %s)"), GetLastError(), getLastErrorText()); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to send BREAK event to JVM process to generate a thread dump because a console does not exist.\n Please see the wrapper.ntservice.generate_console property.")); } } } } } /** * Build the command line used to get the Java version. * * @return TRUE if there were any problems. */ int wrapperBuildJavaVersionCommand() { size_t commandLen; TCHAR **strings; /* If this is not the first time through, then dispose the old command */ if (wrapperData->jvmVersionCommand) { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Clearing up old java version command line")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Old Command Line \"%s\""), wrapperData->jvmCommand); #endif free(wrapperData->jvmVersionCommand); wrapperData->jvmVersionCommand = NULL; } strings = malloc(sizeof(TCHAR*)); if (!strings) { outOfMemory(TEXT("WBJVC"), 1); return TRUE; } memset(strings, 0, sizeof(TCHAR *)); if (wrapperBuildJavaCommandArrayJavaCommand(strings, TRUE, 0, FALSE) < 0) { wrapperFreeStringArray(strings, 1); return TRUE; } if (wrapperResolveJavaVersionCommand(&(strings[0]), TRUE)) { wrapperFreeStringArray(strings, 1); return TRUE; } /* Build a single string from the array that will be used to request the Java version. * The first element of the command array will always be the java binary. */ /* Calculate the length */ commandLen = _tcslen(strings[0]); commandLen += 1; /* Space */ commandLen += _tcslen(TEXT("-version")); commandLen++; /* '\0' */ /* Build the actual command */ wrapperData->jvmVersionCommand = malloc(sizeof(TCHAR) * commandLen); if (!wrapperData->jvmVersionCommand) { outOfMemory(TEXT("WBJVC"), 2); wrapperFreeStringArray(strings, 1); return TRUE; } _sntprintf(wrapperData->jvmVersionCommand, commandLen, TEXT("%s -version"), strings[0]); wrapperFreeStringArray(strings, 1); return FALSE; } /** * Build the java command line. * * @return TRUE if there were any problems. */ int wrapperBuildJavaCommand() { size_t commandLen; size_t commandLen2; TCHAR **strings; int length; int i; /* If this is not the first time through, then dispose the old command */ if (wrapperData->jvmCommand) { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Clearing up old command line")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Old Command Line \"%s\""), wrapperData->jvmCommand); #endif free(wrapperData->jvmCommand); wrapperData->jvmCommand = NULL; } /* First generate the classpath. */ if (wrapperData->classpath) { free(wrapperData->classpath); wrapperData->classpath = NULL; } if (wrapperBuildJavaClasspath(&wrapperData->classpath) < 0) { return TRUE; } /* Build the Java Command Strings */ strings = NULL; length = 0; if (wrapperBuildJavaCommandArray(&strings, &length, TRUE, wrapperData->classpath)) { /* Failed. */ wrapperFreeStringArray(strings, length); return TRUE; } #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("JVM Command Line Parameters")); for (i = 0; i < length; i++) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("%d : %s"), i, strings[i]); } #endif /* Build a single string from the array */ /* Calculate the length */ commandLen = 0; for (i = 0; i < length; i++) { if (i > 0) { commandLen++; /* Space */ } commandLen += _tcslen(strings[i]); } commandLen++; /* '\0' */ commandLen2 = commandLen; /* Build the actual command */ wrapperData->jvmCommand = malloc(sizeof(TCHAR) * commandLen2); if (!wrapperData->jvmCommand) { outOfMemory(TEXT("WBJC"), 1); wrapperFreeStringArray(strings, length); return TRUE; } commandLen = 0; for (i = 0; i < length; i++) { if (i > 0) { wrapperData->jvmCommand[commandLen++] = TEXT(' '); } _sntprintf(wrapperData->jvmCommand + commandLen, commandLen2 - commandLen, TEXT("%s"), strings[i]); commandLen += _tcslen(strings[i]); } wrapperData->jvmCommand[commandLen++] = TEXT('\0'); wrapperData->jvmCommand = wrapperPostProcessCommandElement(wrapperData->jvmCommand); /* Free up the temporary command array */ wrapperFreeStringArray(strings, length); return FALSE; } /** * Allocates an hidden console. (fix for the console flicker bug). * The size of the console will be minimized and its position will be set outside of the screen. * This function should be used when running as a Windows interactive service or with wrapperw. * * @return TRUE if the console was allocated, FALSE if it could not be allocated. */ int wrapperAllocHiddenConsole() { static RECT defaultWorkarea = { 0, 0, 7680, 4320}; int result; int propertiesInShortcut = FALSE; DWORD errorAlloc = ERROR_SUCCESS; RECT workarea; DWORD size, posi; TCHAR hexPosi[9]; LONG nError; HKEY hRootKey = HKEY_CURRENT_USER; HKEY hKey; const TCHAR* consoleSubKeyBase = TEXT("Console\\"); const TCHAR* valueSize = TEXT("WindowSize"); const TCHAR* valuePosi = TEXT("WindowPosition"); TCHAR* consoleSubKey; size_t nSubKeyLen = 0; int i = 0; const int nMaxRestoreAttempts = 10; int nAttempts = 1; STARTUPINFO startupInfo; TCHAR *startupTitle; /* First get the coordinates of the right-bottom corner of the screen. We will then set the console origin to this position. * There are cases where the console reappears on the screen if its position is set entierely outside the work area, * so it is best to have at least one corner of the console touching the boundaries of the screen. * We choose the right-bottom corner because there is no need to subtract the console dimensions (which are actually unknown). */ if(!SystemParametersInfo(SPI_GETWORKAREA, 0, &workarea, 0)) { /* If the function fails, lets assume a very big workarea (8K) to make sure the console position will be set out of the screen. */ workarea = defaultWorkarea; } /* Edit the console properties before allocation. Those properties can either be stored in the registry or in the shortcut that launched the application. */ GetStartupInfo(&startupInfo); startupTitle = startupInfo.lpTitle; if (!propertiesInShortcut) { /* In the registry we can store console properties by adding keys with names matching the console title. */ /* First build the key name */ nSubKeyLen = _tcslen(consoleSubKeyBase) + _tcslen(startupTitle) + 1; consoleSubKey = (TCHAR*)malloc(sizeof(TCHAR) * nSubKeyLen); if (consoleSubKey == NULL) { outOfMemory(TEXT("WHCBA"), 1); return FALSE; } _tcsncpy(consoleSubKey, consoleSubKeyBase, nSubKeyLen); _tcsncat(consoleSubKey, startupTitle, nSubKeyLen - _tcslen(consoleSubKey)); /* The characters '\\' are reserved for sub key delimiters and thus should replaced by '_' in the key name (starting after consoleSubKeyBase). */ for (i = (int)_tcslen(consoleSubKeyBase); i < (int)nSubKeyLen; i++) { if (consoleSubKey[i] == TEXT('\\')) { consoleSubKey[i] = TEXT('_'); } } /* Now create the registry key with its size and position properties * (as a precaution, the key is set volatile and will not persist on system restart) */ nError = RegCreateKeyEx(hRootKey, consoleSubKey, 0, NULL, REG_OPTION_VOLATILE, KEY_WRITE, NULL, &hKey, NULL); if (nError != ERROR_SUCCESS) { RegCloseKey( hKey ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Failed to create key 'HKEY_CURRENT_USER\\%s' in registry. Error: %ld"), consoleSubKey, nError); } else { size = isWin10OrHigher() ? 0x00000001 : 0x00010001; /* windows 10 allows height of 0px */ nError = RegSetValueEx(hKey, valueSize, 0, REG_DWORD, (LPBYTE)&size, sizeof(DWORD)); if (nError != ERROR_SUCCESS) { RegCloseKey( hKey ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Failed to set the value %s for the key 'HKEY_CURRENT_USER\\%s' in registry. Error: %ld"), valueSize, consoleSubKey, nError); } else { _sntprintf(hexPosi, 9, TEXT("%04x%04x"), workarea.bottom, workarea.right); /* The first four bytes represent the position of the window on the X axis. The last four bytes represent the position of the window on the Y axis.*/ posi = (DWORD)_tcstol((const TCHAR*)hexPosi, NULL, 16); nError = RegSetValueEx(hKey, valuePosi, 0, REG_DWORD, (LPBYTE)&posi, sizeof(DWORD)); RegCloseKey( hKey ); if (nError != ERROR_SUCCESS) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Failed to set the value %s for the key 'HKEY_CURRENT_USER\\%s' in registry. Error: %ld"), valuePosi, consoleSubKey, nError); } } } /* Rem: If the key could not be set properly, a brief flicker may be visible when the console is shown and then hidden. */ } /* Allocates a new console for the calling process.*/ result = AllocConsole(); if (!result) { errorAlloc = GetLastError(); } if (!propertiesInShortcut) { /* Restore the registry. * No matter if the previous actions succeeded or not, lets clean up the keys so that the modified properties do not apply on future console allocations. * This is required as the console may later be set visible. */ while (TRUE) { /* RegDeleteKeyEx() is for deleting platform specific key, which is not our case.*/ nError = RegDeleteKey(hRootKey, consoleSubKey); if (nError != ERROR_SUCCESS) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Attempt #%d to delete key 'HKEY_CURRENT_USER\\%s' in registry failed. Error: %ld"), nAttempts, consoleSubKey, nError); if (++nAttempts < nMaxRestoreAttempts) { wrapperSleep(200); continue; } } break; } /* Rem: If the key could not be restored properly, an exception could (unlikely!) happen: * Next time AllocConsole() is called with the exact same application parameters, * if the registry key remains and if wrapperData->ntShowWrapperConsole is set to TRUE, then the console will appear out of the screen. * => As a precaution we could try again to remove the registry key at that time (not implemented yet). */ if (nAttempts == nMaxRestoreAttempts) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to delete key 'HKEY_CURRENT_USER\\%s' in registry."), consoleSubKey); } free(consoleSubKey); } /* Finally, lets set the errorAlloc as the last error and return whether if failed or not.*/ if (!result) { SetLastError(errorAlloc); } return (errorAlloc == ERROR_SUCCESS); } int hideConsoleWindow(HWND consoleHandle, const TCHAR *name) { RECT workarea; RECT normalPositionRect; WINDOWPLACEMENT consolePlacement; if (IsWindowVisible(consoleHandle)) { memset(&consolePlacement, 0, sizeof(WINDOWPLACEMENT)); consolePlacement.length = sizeof(WINDOWPLACEMENT); /* on Windows 10 the console will reappear at the position 'rcNormalPosition' when calling SetWindowPlacement(). To avoid another brief flicker, lets set this position out of the screen. */ if(SystemParametersInfo(SPI_GETWORKAREA, 0, &workarea, 0)) { normalPositionRect.left = workarea.right; normalPositionRect.top = workarea.bottom; normalPositionRect.right = workarea.right; normalPositionRect.bottom = workarea.bottom; } else { normalPositionRect.left = 99999; normalPositionRect.top = 99999; normalPositionRect.right = 99999; normalPositionRect.bottom = 99999; } consolePlacement.rcNormalPosition = normalPositionRect; #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("%s console window is visible, attempt to hide."), name); #endif /* Hide the Window. */ consolePlacement.showCmd = SW_HIDE; if (!SetWindowPlacement(consoleHandle, &consolePlacement)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to set window placement information: %s"), getLastErrorText()); } if (IsWindowVisible(consoleHandle)) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Failed to hide the %s console window."), name); } return FALSE; } else { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("%s console window hidden successfully."), name); } return TRUE; } } else { /* Already hidden */ return TRUE; } } /* Define the number of recent Java windows to store. Keep this value reasonably low to avoid * affecting performance. If the application opens more windows than this number, the code * below may consider the foreground window as new and show/hide the console again. a brief * flicker may be observed but the Wrapper will continue to run normally. */ #define JAVA_WINDOWS_BUFFER_SIZE 16 /** * This function fixes a bug where the hidden console reappears in the taskbar whenever * the Java application creates a new full screen window with the focus set on it. * This would only occur if there was no other icon belonging to the application in * the taskbar (so potentially on any new windows if previous ones were dialogs). * This function performs some check on the foreground window and re-hides the console * if the conditions of the bug are met. */ void fixConsoleTaskBar() { static HWND prevHwd; static int prevHwdIsJavaWin = FALSE; static int prevHwdIsFixed = FALSE; static HWND prevJavaHwds[JAVA_WINDOWS_BUFFER_SIZE]; static int prevJavaHwdsIndex; HWND hwd; RECT hwdRect; RECT workAreaRect; DWORD pid; int i; if (wrapperData->javaPID == 0) { /* Java process doesn't exist. */ return; } hwd = GetForegroundWindow(); if (hwd) { if (hwd != prevHwd) { /* The focus has changed. Is this a new Java window? */ prevHwd = hwd; prevHwdIsFixed = FALSE; for (i = 0; i < JAVA_WINDOWS_BUFFER_SIZE; i++) { if (prevJavaHwds[i] == hwd) { /* This is a Java window but it already caused the bug before and we fixed it. * As far as I could test, a window that caused the bug once will not cause * it again, even when it is hidden and showed or resized to full screen. */ prevHwdIsJavaWin = TRUE; return; } } GetWindowThreadProcessId(hwd, &pid); if (!wrapperCheckPPid(pid, wrapperData->javaPID, 0, NULL)) { /* The window doesn't belong to the Java application. */ prevHwdIsJavaWin = FALSE; return; } } else if (!prevHwdIsJavaWin || prevHwdIsFixed) { /* The window has not changed and we know it will not cause a bug. */ return; } /* Else: the focus did not change and the window belongs to the java application. * It may cause the bug if it was resized since we last check it, so continue. */ /* This is a Java window. Is it full screen? */ prevHwdIsJavaWin = TRUE; if (!GetWindowRect(hwd, &hwdRect) || !SystemParametersInfo(SPI_GETWORKAREA, 0, &workAreaRect, 0) || (((hwdRect.right - hwdRect.left) >= (workAreaRect.right - workAreaRect.left)) && ((hwdRect.bottom - hwdRect.top) >= (workAreaRect.bottom - workAreaRect.top)))) { /* This window is larger than the work area or we failed to retrieve its size or the size * of the work area. This may have caused the hidden console to reappear in the taskbar. * Show and hide it again to fix this issue. A brief flicker may be observed as it may * have appeared anytime since the last iteration of the main loop. Remember the window * to not process it again. (also remember it if we fail to retrieve its size or the size * of the work area as this would most likely happen again on the next check) */ prevJavaHwds[prevJavaHwdsIndex] = hwd; prevJavaHwdsIndex = (prevJavaHwdsIndex + 1) % JAVA_WINDOWS_BUFFER_SIZE; ShowWindow(wrapperData->wrapperConsoleHWND, SW_SHOW); ShowWindow(wrapperData->wrapperConsoleHWND, SW_HIDE); prevHwdIsFixed = TRUE; } } } /** * Look for and hide the wrapper or JVM console windows if they should be hidden. * Some users have reported that if the user logs on to windows quickly after booting up, * the console window will be redisplayed even though it was hidden once. This function * is called on each iteration of the main loop to ensure the consoles are always hidden. * Users have reported that the console can be redisplayed when a user logs back in or * switches users, or when the Java application opens full screen windows. */ void wrapperCheckConsoleWindows() { /* See if the Wrapper console needs to be hidden. */ if (wrapperData->wrapperConsoleHide && (wrapperData->wrapperConsoleHWND != NULL)) { if (hideConsoleWindow(wrapperData->wrapperConsoleHWND, TEXT("Wrapper"))) { fixConsoleTaskBar(); wrapperData->wrapperConsoleVisible = FALSE; } } /* See if the Java console needs to be hidden. */ if (wrapperData->jvmConsoleHandle != NULL) { if (hideConsoleWindow(wrapperData->jvmConsoleHandle, TEXT("JVM"))) { wrapperData->jvmConsoleVisible = FALSE; } } } HWND findConsoleWindow( TCHAR *title ) { HWND consoleHandle; int i = 0; /* Allow up to 2 seconds for the window to show up, but don't hang * up if it doesn't */ consoleHandle = NULL; while ((!consoleHandle) && (i < 200)) { wrapperSleep(10); consoleHandle = FindWindow(TEXT("ConsoleWindowClass"), title); i++; } return consoleHandle; } void showConsoleWindow(HWND consoleHandle, const TCHAR *name) { WINDOWPLACEMENT consolePlacement; if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Show %s console window with which JVM is launched."), name); } if (GetWindowPlacement(consoleHandle, &consolePlacement)) { /* Show the Window. */ consolePlacement.showCmd = SW_SHOW; if (!SetWindowPlacement(consoleHandle, &consolePlacement)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to set window placement information: %s"), getLastErrorText()); } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to obtain window placement information: %s"), getLastErrorText()); } } /** * The main entry point for the startup thread which is started by * wrapperRunCommon(). Once started, this thread will run for the * life of the startup and then exit. * * This thread only exists so that certain tasks which take an * undetermined amount of time can run without affecting the startup * time of the Wrapper. */ DWORD WINAPI startupRunner(LPVOID parameter) { /* In case there are ever any problems in this thread, enclose it in a try catch block. */ __try { startupThreadStarted = TRUE; /* Immediately register this thread with the logger. */ logRegisterThread(WRAPPER_THREAD_STARTUP); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Startup thread started.")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Attempting to verify the binary signature.")); verifyEmbeddedSignature(); } __except (exceptionFilterFunction(GetExceptionInformation())) { /* This call is not queued to make sure it makes it to the log prior to a shutdown. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Fatal error in the Startup thread.")); startupThreadStopped = TRUE; /* Before appExit() */ appExit(wrapperData->errorExitCode); return 1; /* For the compiler, we will never get here. */ } startupThreadStopped = TRUE; if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Startup thread stopped.")); } return 0; } /** * Creates a thread whose job is to process some startup actions which could take a while to * complete. This function will automatically wait for a configured length of time for the * thread to complete. If it does not complete within the predetermined amount of time then * it will continue to avoid slowing down the Wrapper startup. * * This startup timeout can be controlled with the wrapper.startup_thread.timeout property. */ int initializeStartup() { TICKS nowTicks; TICKS timeoutTicks; if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Launching Startup thread.")); } /* These values need to be set before starting the thread, because they are also used in the thread. */ startupThreadTimeout = propIntMin(propIntMax(getIntProperty(properties, TEXT("wrapper.startup_thread.timeout"), 2), 0), 3600); startupThreadStartTicks = wrapperGetTicks(); startupThreadHandle = CreateThread( NULL, /* No security attributes as there will not be any child processes of the thread. */ 0, /* Use the default stack size. */ startupRunner, NULL, /* No parameters need to passed to the thread. */ 0, /* Start the thread running immediately. */ &startupThreadId); if (!startupThreadHandle) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to create a Startup thread: %s"), getLastErrorText()); return 1; } /* Wait until the startup thread completes or the timeout expires. */ nowTicks = startupThreadStartTicks; timeoutTicks = wrapperAddToTicks(nowTicks, startupThreadTimeout); #ifdef DEBUG_STARTUP log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("startupThreadTimeout: %d, nowTicks(%d) - timeoutTicks(%d) = %d"), startupThreadTimeout, nowTicks, timeoutTicks, wrapperGetTickAgeTicks(timeoutTicks, nowTicks)); #endif while ((!startupThreadStopped) && (wrapperGetTickAgeTicks(timeoutTicks, nowTicks) < 0)) { #ifdef DEBUG_STARTUP log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" Waiting for startup... %08x < %08x"), nowTicks, timeoutTicks); #endif wrapperSleep(10); nowTicks = wrapperGetTicks(); } if (startupThreadStopped) { #ifdef DEBUG_STARTUP log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("%s completed."), TEXT("Startup")); #endif if ((wrapperData->wState == WRAPPER_WSTATE_STOPPING) || (wrapperData->wState == WRAPPER_WSTATE_STOPPED)) { appExit(wrapperData->errorExitCode); } } else { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Startup timed out. Continuing in background.")); } } return 0; } void disposeStartup() { /* Wait until the javaIO thread is actually stopped to avoid timing problems. */ if (startupThreadStarted && !startupThreadStopped) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Waiting for Startup thread to complete...")); } while (!startupThreadStopped) { #ifdef _DEBUG wprintf(TEXT("Waiting for Startup thread to stop.\n")); #endif wrapperSleep(100); } } } int wrapperHandleMessageQueue() { MSG msg; int cnt = 0; if (_wrapper_messageWindowHWND) { while (cnt < 10) { if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { return cnt; } cnt++; } } return cnt; } #ifndef ENDSESSION_CLOSEAPP #define ENDSESSION_CLOSEAPP 0x00000001 #endif #ifndef ENDSESSION_CRITICAL #define ENDSESSION_CRITICAL 0x40000000 #endif #ifndef ENDSESSION_LOGOFF #define ENDSESSION_LOGOFF 0x80000000 #endif LRESULT CALLBACK messageWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { TCHAR *username; DWORD usernameSize; _wrapper_debugMessage(TEXT("messageWndProc"), hwnd, message, wParam, lParam); switch(message) { case WM_QUERYENDSESSION: /* A request to end the session is being made. We can try to block that by returning 0. */ if (wrapperData->isMessageOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Session End Query%s%s%s"), (wParam & ENDSESSION_CLOSEAPP ? TEXT(" CloseApp") : TEXT("")), (wParam & ENDSESSION_CRITICAL ? TEXT(" Critical") : TEXT("")), (wParam & ENDSESSION_LOGOFF ? TEXT(" Logoff") : TEXT(""))); } /* Always return TRUE to indicate that the application is OK to exit the session. * Do not clean up now because another application may abort the exit. * If the session exit should indeed happen, then the system will send a WM_ENDSESSION * that we will trap to handle a clean shutdown. */ return TRUE; case WM_ENDSESSION: /* This is a notification that the session is actually ending. The session will end at any time after this returns. * We need to block in here to process our own shutdown to make sure the session stays open long enough for us to shutdown cleanly. */ if (wrapperData->isMessageOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Session End%s%s%s"), (wParam & ENDSESSION_CLOSEAPP ? TEXT(" CloseApp") : TEXT("")), (wParam & ENDSESSION_CRITICAL ? TEXT(" Critical") : TEXT("")), (wParam & ENDSESSION_LOGOFF ? TEXT(" Logoff") : TEXT(""))); } if (wParam) { /* We already checked that OptionalShutdownBlockReasonCreate is not NULL when starting the message thread. */ OptionalShutdownBlockReasonCreate(hwnd, getStringProperty(properties, TEXT("wrapper.user_logoffs.message"), TEXT("Waiting for the Wrapper to shutdown."))); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("User logged out. Shutting down.")); /* Send messages to notify the WrapperManager in case the Java Application has registered CTRL events. */ wrapperProtocolFunction(WRAPPER_MSG_FIRE_CTRL_EVENT, TEXT("WRAPPER_CTRL_LOGOFF_EVENT")); if (!(lParam & ENDSESSION_LOGOFF)) { /* The system is shutting down or rebooting. */ wrapperProtocolFunction(WRAPPER_MSG_FIRE_CTRL_EVENT, TEXT("WRAPPER_CTRL_SHUTDOWN_EVENT")); } wrapperStopProcess(0, TRUE); /* Wait until the mutex is released, meaning the application has disposed and is about to exit. */ #ifdef WRAPPER_DEBUG_MESSAGES _tprintf(TEXT("Waiting for the Main Exit mutex to be released (WM_ENDSESSION, TheadId = #%d)."), GetCurrentThreadId()); fflush(NULL); #endif lockMainExitMutex(); /* We are ready to exit. Release the mutex and dispose it. We don't need it anymore */ releaseMainExitMutex(); CloseHandle(mainExitMutexHandle); mainExitMutexHandle = NULL; /* Not sure if the following is needed but the API says we should ShutdownBlockReasonDestroy() after ShutdownBlockReasonCreate(). */ if (OptionalShutdownBlockReasonDestroy) { OptionalShutdownBlockReasonDestroy(hwnd); } #ifdef WRAPPER_DEBUG_MESSAGES wrapperSleep(25); _tprintf(TEXT("Finished WM_ENDSESSION.")); fflush(NULL); #endif /* Flush everything in case some messages were printed with _tprintf() */ fflush(NULL); } /* Always return 0. */ return FALSE; case WM_WTSSESSION_CHANGE: /* >= Windows Vista, Windows Server 2008 */ if (OptionalWTSQuerySessionInformation && OptionalWTSFreeMemory) { if (OptionalWTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, (DWORD)lParam, WTSUserName, &username, &usernameSize)) { if (username) { if (_tcslen(username) <= 0) { /* Empty string, no name. */ OptionalWTSFreeMemory(username); username = NULL; } } } } switch(wParam) { case WTS_CONSOLE_CONNECT: if (wrapperData->isMessageOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Session Console Connect: %s"), (username ? username : TEXT("-"))); } break; case WTS_CONSOLE_DISCONNECT: if (wrapperData->isMessageOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Session Console Disconnect: %s"), (username ? username : TEXT("-"))); } break; case WTS_REMOTE_CONNECT: if (wrapperData->isMessageOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Session Remote Connect: %s"), (username ? username : TEXT("-"))); } break; case WTS_REMOTE_DISCONNECT: if (wrapperData->isMessageOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Session Remote Disconnect: %s"), (username ? username : TEXT("-"))); } break; case WTS_SESSION_LOGON: if (wrapperData->isMessageOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Session Logon: %s"), (username ? username : TEXT("-"))); } break; case WTS_SESSION_LOGOFF: if (wrapperData->isMessageOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Session Logoff: %s"), (username ? username : TEXT("-"))); } break; case WTS_SESSION_LOCK: if (wrapperData->isMessageOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Session Lock: %s"), (username ? username : TEXT("-"))); } break; case WTS_SESSION_UNLOCK: if (wrapperData->isMessageOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Session Unlock: %s"), (username ? username : TEXT("-"))); } break; case WTS_SESSION_REMOTE_CONTROL: if (wrapperData->isMessageOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Session Remote Control: %s (now %d)"), (username ? username : TEXT("-")), GetSystemMetrics(SM_REMOTECONTROL)); } break; case WTS_SESSION_CREATE: if (wrapperData->isMessageOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Session Create: %s"), (username ? username : TEXT("-"))); } break; case WTS_SESSION_TERMINATE: if (wrapperData->isMessageOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Session Terminate: %s"), (username ? username : TEXT("-"))); } break; default: break; } if (username) { OptionalWTSFreeMemory(username); } return FALSE; default: return DefWindowProc(hwnd, message, wParam, lParam); } } void _wrapper_createMessageWindow() { WNDCLASSEX wc; HWND hwnd; wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = messageWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = wrapperData->wrapperHInstance; wc.hIcon = NULL; wc.hCursor = NULL; wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = NULL; wc.lpszClassName = messageWindowClassName; wc.hIconSm = NULL; if (!RegisterClassEx(&wc)) { return; } hwnd = CreateWindowEx( 0, messageWindowClassName, TEXT(""), 0, CW_USEDEFAULT, CW_USEDEFAULT, 30, 90, NULL, NULL, GetModuleHandle(NULL), NULL); if (hwnd != NULL) { _wrapper_messageWindowHWND = hwnd; /* We don't want to actually show the window. ShowWindow(hwnd, SW_SHOWNORMAL); */ UpdateWindow(hwnd); } } /** * The main entry point for the Message thread which is started by * initializeMessageThread(). Once started, this thread will run for the * life of the process. */ DWORD WINAPI messageRunner(LPVOID parameter) { int nextSleep; /* In case there are ever any problems in this thread, enclose it in a try catch block. */ __try { messageThreadStarted = TRUE; /* Immediately register this thread with the logger. */ logRegisterThread(WRAPPER_THREAD_MESSAGE); if (wrapperData->isMessageOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Message thread started.")); } _wrapper_createMessageWindow(); /* Register to receive messages that we are interested in. */ if (_wrapper_messageWindowHWND && OptionalWTSRegisterSessionNotification) { if (!OptionalWTSRegisterSessionNotification(_wrapper_messageWindowHWND, NOTIFY_FOR_THIS_SESSION)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to register to receive session change notifications. (%d): %s"), GetLastError(), getLastErrorText()); } } nextSleep = TRUE; /* Loop until we are shutting down, but continue as long as there are messages. */ while ((!stopMessageThread) || (!nextSleep)) { if (nextSleep) { /* Sleep as little as possible. */ Sleep(1); } nextSleep = (wrapperHandleMessageQueue() <= 0); } if (_wrapper_messageWindowHWND) { if (OptionalWTSUnRegisterSessionNotification) { OptionalWTSUnRegisterSessionNotification(_wrapper_messageWindowHWND); } /* We finished to process all messages, so destroy the window. */ DestroyWindow(_wrapper_messageWindowHWND); _wrapper_messageWindowHWND = NULL; } } __except (exceptionFilterFunction(GetExceptionInformation())) { /* This call is not queued to make sure it makes it to the log prior to a shutdown. */ if (isLogInitialized()) { /* There is a small risk that the log gets disposed while printing this message as we are in a separated thread... */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Fatal error in the Message thread.")); } else { _tprintf(TEXT("Fatal error in the Message thread.")); } messageThreadStopped = TRUE; if (wrapperData && (wrapperData->wState != WRAPPER_WSTATE_STOPPING) && (wrapperData->wState != WRAPPER_WSTATE_STOPPED)) { wrapperStopProcess(0, TRUE); } return 1; } messageThreadStopped = TRUE; if (wrapperData && wrapperData->isMessageOutputEnabled) { if (isLogInitialized()) { /* There is a small risk that the log gets disposed while printing this message as we are in a separated thread... */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Message thread stopped.")); } else { _tprintf(TEXT("Message thread stopped.")); } } return 0; } /** * Creates a thread whose job is to loop and process system messages, * and handle a clean shutdown when a request to end the session was made. */ int initializeMessageThread() { if (wrapperData->isMessageOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Launching Message thread.")); } messageThreadHandle = CreateThread( NULL, /* No security attributes as there will not be any child processes of the thread. */ 0, /* Use the default stack size. */ messageRunner, NULL, /* No parameters need to passed to the thread. */ 0, /* Start the thread running immediately. */ &messageThreadId); if (!messageThreadHandle) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to create a Message thread: %s"), getLastErrorText()); return 1; } else { return 0; } } /** * This function checks if the Wrapper can/should trap system messages in order * to initiate a clean shutdown when the user logs off. Those messages are * trapped from a hidden window in a separate thread, which both should only * be created when necessary (when this function returns TRUE). * * @return TRUE if System Messages can be trapped, FALSE otherwise. */ int canTrapSystemMessages() { DWORD sessionId; /* Before Windows Vista/Server 2008, CTRL_SHUTDOWN_EVENT events could be trapped from both services and console apps * to shutdown the Wrapper cleanly. Since Vista, a new API was introduce to detect logoffs via system messages. */ if (isVista() && OptionalShutdownBlockReasonCreate) { /* The WM_QUERYENDSESSION and WM_ENDSESSION messages cannot be trapped when running in the session #0. * Session #0 is the session in which the services are running, but Wrapper instances running as a * console can only run in that session if they are spawn from a service. */ if (!ProcessIdToSessionId(GetCurrentProcessId(), &sessionId)) { _tprintf(TEXT("Failed to retrieve the session id of the current process. %s\n"), getLastErrorText()); /* Assume that we are not in session #0 if we are running as a console (which is the case if not spawn from a service). * It doesn't hurt even if we are wrong. System messages will simply not be trapped. */ if (wrapperData->isConsole) { return TRUE; } } else if (sessionId != 0) { /* So we are a console application running on a session that is not #0. */ return TRUE; } } return FALSE; } /** * The main entry point for the javaio thread which is started by * initializeJavaIO(). Once started, this thread will run for the * life of the process. * * This thread will only be started if we are configured to use a * dedicated thread to read JVM output. */ DWORD WINAPI javaIORunner(LPVOID parameter) { int nextSleep; /* In case there are ever any problems in this thread, enclose it in a try catch block. */ __try { javaIOThreadStarted = TRUE; /* Immediately register this thread with the logger. */ logRegisterThread(WRAPPER_THREAD_JAVAIO); if (wrapperData->isJavaIOOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s thread started."), TEXT("JavaIO")); } nextSleep = TRUE; /* Loop until we are shutting down, but continue as long as there is more output from the JVM. */ while ((!stopJavaIOThread) || (!nextSleep)) { if (nextSleep) { /* Sleep as little as possible. */ wrapperSleep(1); } nextSleep = TRUE; if (wrapperData->pauseThreadJavaIO) { wrapperPauseThread(wrapperData->pauseThreadJavaIO, TEXT("javaio")); wrapperData->pauseThreadJavaIO = 0; } if (wrapperReadChildOutput(0)) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Pause reading child process output to share cycles.")); } nextSleep = FALSE; } } } __except (exceptionFilterFunction(GetExceptionInformation())) { /* This call is not queued to make sure it makes it to the log prior to a shutdown. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Fatal error in the %s thread."), TEXT("JavaIO")); javaIOThreadStopped = TRUE; /* Before appExit() */ appExit(wrapperData->errorExitCode); return 1; /* For the compiler, we will never get here. */ } javaIOThreadStopped = TRUE; if (wrapperData->isJavaIOOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s thread stopped."), TEXT("JavaIO")); } return 0; } /** * Creates a thread whose job is to loop and process the stdio and stderr output from the JVM. */ int initializeJavaIO() { if (wrapperData->isJavaIOOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Launching %s thread."), TEXT("JavaIO")); } javaIOThreadHandle = CreateThread( NULL, /* No security attributes as there will not be any child processes of the thread. */ 0, /* Use the default stack size. */ javaIORunner, NULL, /* No parameters need to passed to the thread. */ 0, /* Start the thread running immediately. */ &javaIOThreadId); if (!javaIOThreadHandle) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to create a %s thread: %s"), TEXT("JavaIO"), getLastErrorText()); return 1; } else { return 0; } } void disposeJavaIO() { stopJavaIOThread = TRUE; /* Wait until the javaIO thread is actually stopped to avoid timing problems. */ if (javaIOThreadStarted) { while (!javaIOThreadStopped) { #ifdef _DEBUG wprintf(TEXT("Waiting for %s thread to stop.\n"), TEXT("JavaIO")); #endif wrapperSleep(100); } } } /** * The main entry point for the timer thread which is started by * initializeTimer(). Once started, this thread will run for the * life of the process. * * This thread will only be started if we are configured NOT to * use the system time as a base for the tick counter. */ DWORD WINAPI timerRunner(LPVOID parameter) { TICKS sysTicks; TICKS lastTickOffset = 0; TICKS tickOffset; TICKS nowTicks; int offsetDiff; int first = TRUE; /* In case there are ever any problems in this thread, enclose it in a try catch block. */ __try { timerThreadStarted = TRUE; /* Immediately register this thread with the logger. */ logRegisterThread(WRAPPER_THREAD_TIMER); if (wrapperData->isTickOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Timer thread started.")); } wrapperGetSystemTicks(); while (!stopTimerThread) { wrapperSleep(WRAPPER_TICK_MS); if (wrapperData->pauseThreadTimer) { wrapperPauseThread(wrapperData->pauseThreadTimer, TEXT("timer")); wrapperData->pauseThreadTimer = 0; } /* Get the tick count based on the system time. */ sysTicks = wrapperGetSystemTicks(); /* Lock the tick mutex whenever the "timerTicks" variable is accessed. */ if (wrapperData->useTickMutex && wrapperLockTickMutex()) { timerThreadStopped = TRUE; return 1; } /* Advance the timer tick count. */ nowTicks = timerTicks++; if (wrapperData->useTickMutex && wrapperReleaseTickMutex()) { timerThreadStopped = TRUE; return 1; } /* Calculate the offset between the two tick counts. This will always work due to overflow. */ tickOffset = sysTicks - nowTicks; /* The number we really want is the difference between this tickOffset and the previous one. */ offsetDiff = wrapperGetTickAgeTicks(lastTickOffset, tickOffset); if (first) { first = FALSE; } else { if (offsetDiff > wrapperData->timerSlowThreshold) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT( "The timer fell behind the system clock by %dms."), (int)(offsetDiff * WRAPPER_TICK_MS)); } else if (offsetDiff < -1 * wrapperData->timerFastThreshold) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT( "The system clock fell behind the timer by %dms."), (int)(-1 * offsetDiff * WRAPPER_TICK_MS)); } if (wrapperData->isTickOutputEnabled) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT( " Timer: ticks=%08x, system ticks=%08x, offset=%08x, offsetDiff=%08x"), nowTicks, sysTicks, tickOffset, offsetDiff); } } /* Store this tick offset for the next time through the loop. */ lastTickOffset = tickOffset; } } __except (exceptionFilterFunction(GetExceptionInformation())) { /* This call is not queued to make sure it makes it to the log prior to a shutdown. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Fatal error in the Timer thread.")); timerThreadStopped = TRUE; /* Before appExit() */ appExit(wrapperData->errorExitCode); return 1; /* For the compiler, we will never get here. */ } timerThreadStopped = TRUE; if (wrapperData->isTickOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Timer thread stopped.")); } return 0; } /** * Creates a process whose job is to loop and simply increment a ticks * counter. The tick counter can then be used as a clock as an alternative * to using the system clock. */ int initializeTimer() { if (wrapperData->isTickOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Launching Timer thread.")); } timerThreadHandle = CreateThread( NULL, /* No security attributes as there will not be any child processes of the thread. */ 0, /* Use the default stack size. */ timerRunner, NULL, /* No parameters need to passed to the thread. */ 0, /* Start the thread running immediately. */ &timerThreadId); if (!timerThreadHandle) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to create a timer thread: %s"), getLastErrorText()); return 1; } else { return 0; } } void disposeTimer() { stopTimerThread = TRUE; /* Wait until the timer thread is actually stopped to avoid timing problems. */ if (timerThreadStarted) { while (!timerThreadStopped) { #ifdef _DEBUG wprintf(TEXT("Waiting for timer thread to stop.\n")); #endif wrapperSleep(100); } } CloseHandle(timerThreadHandle); } int initializeWinSock() { WORD ws_version=MAKEWORD(1, 1); WSADATA ws_data; int res; /* Initialize Winsock */ if ((res = WSAStartup(ws_version, &ws_data)) != 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Cannot initialize Windows socket DLLs.")); return res; } return 0; } /** * Collects the current process's username and domain name. * * @return TRUE if there were any problems. */ int collectUserInfo() { int result; DWORD processId; HANDLE hProcess; HANDLE hProcessToken; TOKEN_USER *tokenUser; DWORD tokenUserSize; TCHAR *sidText; DWORD userNameSize; DWORD domainNameSize; SID_NAME_USE sidType; processId = wrapperData->wrapperPID; wrapperData->userName = NULL; wrapperData->domainName = NULL; if (hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, processId)) { if (OpenProcessToken(hProcess, TOKEN_QUERY, &hProcessToken)) { GetTokenInformation(hProcessToken, TokenUser, NULL, 0, &tokenUserSize); tokenUser = (TOKEN_USER *)malloc(tokenUserSize); if (!tokenUser) { outOfMemory(TEXT("CUI"), 1); result = TRUE; } else { if (GetTokenInformation(hProcessToken, TokenUser, tokenUser, tokenUserSize, &tokenUserSize)) { /* Get the text representation of the sid. */ if (ConvertSidToStringSid(tokenUser->User.Sid, &sidText) == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to convert SId to String: %s"), getLastErrorText()); result = TRUE; } else { /* We now have an SID, use it to lookup the account. */ userNameSize = 0; domainNameSize = 0; LookupAccountSid(NULL, tokenUser->User.Sid, NULL, &userNameSize, NULL, &domainNameSize, &sidType); wrapperData->userName = (TCHAR*)malloc(sizeof(TCHAR) * userNameSize); if (!wrapperData->userName) { outOfMemory(TEXT("CUI"), 2); result = TRUE; } else { wrapperData->domainName = (TCHAR*)malloc(sizeof(TCHAR) * domainNameSize); if (!wrapperData->domainName) { outOfMemory(TEXT("CUI"), 3); result = TRUE; } else { if (LookupAccountSid(NULL, tokenUser->User.Sid, wrapperData->userName, &userNameSize, wrapperData->domainName, &domainNameSize, &sidType)) { /* Success. */ result = FALSE; } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to get the current username and domain: %s"), getLastErrorText()); result = TRUE; } } } LocalFree(sidText); } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to get token information: %s"), getLastErrorText()); result = TRUE; } free(tokenUser); } CloseHandle(hProcessToken); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to open process token: %s"), getLastErrorText()); result = TRUE; } CloseHandle(hProcess); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to open process: %s"), getLastErrorText()); result = TRUE; } return result; } /** * Execute initialization code to get the wrapper set up. */ int wrapperInitializeRun() { HANDLE hStdout; HANDLE hStdErr; HANDLE hStdIn; struct _timeb timebNow; FILE *pfile; time_t now; int nowMillis; int res; TCHAR titleBuffer[80]; int allocConsoleSucceed; int canDisplayConsole; HANDLE process = GetCurrentProcess(); TCHAR* szOS; /* Set the process priority. */ if (!SetPriorityClass(process, wrapperData->ntServicePriorityClass)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to set the process priority: %s"), getLastErrorText()); } /* Initialize the random seed. */ _ftime(&timebNow); now = (time_t)timebNow.time; nowMillis = timebNow.millitm; srand(nowMillis); /* Initialize the pipe to capture the child process output */ if ((res = wrapperInitChildPipe()) != 0) { return res; } /* Initialize the Wrapper console handle to null */ wrapperData->wrapperConsoleHWND = NULL; /* The Wrapper will not have its own console when running as a service. We need to create one here. */ if ((!wrapperData->isConsole) && (wrapperData->ntAllocConsole)) { canDisplayConsole = wrapperData->ntServiceInteractive && canRunInteractive(); if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Allocating a console for the service.")); } /* Set a flag to keep track of whether the console should be hidden. */ wrapperData->wrapperConsoleHide = canDisplayConsole && !wrapperData->ntShowWrapperConsole; if (wrapperData->wrapperConsoleHide) { /* Create the console out of the screen. We'll still need to hide its window so that the icon disappear from the taskbar. */ allocConsoleSucceed = wrapperAllocHiddenConsole(); if (allocConsoleSucceed) { /* Generate a unique time for the console so we can look for it below. */ _sntprintf(titleBuffer, 80, TEXT("Wrapper Console Id %d-%d (Do not close)"), wrapperData->wrapperPID, rand()); #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Wrapper console title: %s"), titleBuffer); #endif SetConsoleTitle(titleBuffer); if (wrapperData->wrapperConsoleHWND = findConsoleWindow(titleBuffer)) { wrapperData->wrapperConsoleVisible = TRUE; if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Found console window.")); } /* Attempt to hide the console window here once so it goes away as quickly as possible. * This may not succeed yet however. If the system is still coming up. */ wrapperCheckConsoleWindows(); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Failed to locate the console window so it can be hidden.")); } } } else { allocConsoleSucceed = AllocConsole(); } if (!allocConsoleSucceed) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("ERROR: Unable to allocate a console for the service: %s"), getLastErrorText()); if (getLastError() == ERROR_GEN_FAILURE) { szOS = calloc(OSBUFSIZE, sizeof(TCHAR)); if (szOS) { GetOSDisplayString(&szOS); } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT(" It has been observed that some systems fail to allocate a console with error code 0x%04x.\n We are still investigating why this is happening. Please report this message to support@tanukisoftware.com, as it will help us fix the issue.\n The Wrapper PID was %d, the system was %s.\n A workaround is to set wrapper.ntservice.generate_console to FALSE, but the Wrapper won't be able to perform thread dumps without a console."), getLastError(), wrapperData->wrapperPID, szOS ? szOS : TEXT("unknown")); if (szOS) { free(szOS); } } return 1; } /* A console, which got created by AllocConsole, does not have stdin/out/err set, so all printf's are not being displayed. Set the buffer explicitly here. */ hStdIn = GetStdHandle(STD_INPUT_HANDLE); if (hStdIn == INVALID_HANDLE_VALUE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("ERROR: Unable to get the new stdin handle: %s"), getLastErrorText()); return 1; } pfile = _tfdopen( _open_osfhandle((long)hStdIn, _O_TEXT), TEXT("r") ); /* Assign the STD_INPUT_HANDLE fd to stdin*/ *stdin = *pfile; /* set the stream to non buffering */ setvbuf( stdin, NULL, _IONBF, 0 ); hStdout = GetStdHandle(STD_OUTPUT_HANDLE); if (hStdout == INVALID_HANDLE_VALUE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("ERROR: Unable to get the new stdout handle: %s"), getLastErrorText()); return 1; } pfile = _tfdopen( _open_osfhandle((long)hStdout, _O_TEXT), TEXT("w") ); *stdout = *pfile; setvbuf( stdout, NULL, _IONBF, 0 ); hStdErr = GetStdHandle(STD_ERROR_HANDLE); if (hStdErr == INVALID_HANDLE_VALUE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("ERROR: Unable to get the new stderr handle: %s"), getLastErrorText()); return 1; } pfile = _tfdopen( _open_osfhandle((long)hStdErr, _O_TEXT), TEXT("w") ); *stderr = *pfile; setvbuf( stderr, NULL, _IONBF, 0 ); /* If we get here then we created a new console for the Wrapper. If direct console was enabled then we need * to reenable it here as any previous attempted log entries will have reset the direct mode. */ setConsoleDirect(getBooleanProperty(properties, TEXT("wrapper.console.direct"), TRUE)); } wrapperSetConsoleTitle(); /* Set the handler to trap console signals. This must be done after the console * is created or it will not be applied to that console. */ #ifdef WRAPPER_DEBUG_CONTROL_HANDLER log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Console Handler: Register handler.")); #endif SetConsoleCtrlHandler((PHANDLER_ROUTINE)wrapperConsoleHandler, TRUE); /* Collect the HINSTANCE and HWND references. */ wrapperData->wrapperHInstance = GetModuleHandle(NULL); if (!wrapperData->wrapperConsoleHWND) { wrapperData->wrapperConsoleHWND = GetConsoleWindow(); } if (wrapperData->useSystemTime) { /* We are going to be using system time so there is no reason to start up a timer thread. */ timerThreadHandle = NULL; timerThreadId = 0; } else { /* Create and initialize a timer thread. */ if ((res = initializeTimer()) != 0) { return res; } } if (canTrapSystemMessages()) { if (!(mainExitMutexHandle = CreateMutex(NULL, FALSE, NULL))) { _tprintf(TEXT("Failed to create Main Exit mutex. %s\n"), getLastErrorText()); return 1; } if (lockMainExitMutex()) { return 1; } if ((res = initializeMessageThread()) != 0) { return res; } } if (wrapperData->isPageFaultOutputEnabled) { wrapperInitializeProfileCounters(); } return 0; } /** * Cause the current thread to sleep for the specified number of milliseconds. * * @param ms Number of milliseconds to wait for. */ void wrapperSleep(int ms) { if (wrapperData && wrapperData->isSleepOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Sleep: sleep %dms"), ms); } Sleep(ms); if (wrapperData && wrapperData->isSleepOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Sleep: awake")); } } /** * Detaches the Java process so the Wrapper will if effect forget about it. */ void wrapperDetachJava() { wrapperSetJavaState(WRAPPER_JSTATE_DOWN_CLEAN, 0, -1); } /** * Reports the status of the wrapper to the service manager * Possible status values: * WRAPPER_WSTATE_STARTING * WRAPPER_WSTATE_STARTED * WRAPPER_WSTATE_PAUSING * WRAPPER_WSTATE_PAUSED * WRAPPER_WSTATE_RESUMING * WRAPPER_WSTATE_STOPPING * WRAPPER_WSTATE_STOPPED */ void wrapperReportStatus(int useLoggerQueue, int status, int errorCode, int waitHint) { int natState; TCHAR *natStateName; static DWORD dwCheckPoint = 1; BOOL bResult = TRUE; if (!wrapperData->isConsole) { switch (status) { case WRAPPER_WSTATE_STARTING: natState = SERVICE_START_PENDING; natStateName = TEXT("SERVICE_START_PENDING"); break; case WRAPPER_WSTATE_STARTED: natState = SERVICE_RUNNING; natStateName = TEXT("SERVICE_RUNNING"); break; case WRAPPER_WSTATE_PAUSING: natState = SERVICE_PAUSE_PENDING; natStateName = TEXT("SERVICE_PAUSE_PENDING"); break; case WRAPPER_WSTATE_PAUSED: natState = SERVICE_PAUSED; natStateName = TEXT("SERVICE_PAUSED"); break; case WRAPPER_WSTATE_RESUMING: natState = SERVICE_CONTINUE_PENDING; natStateName = TEXT("SERVICE_CONTINUE_PENDING"); break; case WRAPPER_WSTATE_STOPPING: natState = SERVICE_STOP_PENDING; natStateName = TEXT("SERVICE_STOP_PENDING"); break; case WRAPPER_WSTATE_STOPPED: natState = SERVICE_STOPPED; natStateName = TEXT("SERVICE_STOPPED"); break; default: log_printf_queue(useLoggerQueue, WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unknown status: %d"), status); return; } ssStatus.dwControlsAccepted = 0; if (natState != SERVICE_START_PENDING) { ssStatus.dwControlsAccepted |= SERVICE_ACCEPT_STOP; #ifdef SUPPORT_PRESHUTDOWN if (wrapperData->ntPreshutdown) { ssStatus.dwControlsAccepted |= SERVICE_ACCEPT_PRESHUTDOWN; } else { #endif ssStatus.dwControlsAccepted |= SERVICE_ACCEPT_SHUTDOWN; #ifdef SUPPORT_PRESHUTDOWN } #endif if (wrapperData->pausable) { ssStatus.dwControlsAccepted |= SERVICE_ACCEPT_PAUSE_CONTINUE; } } if (isWindowsNT4_0OrEarlier()) { /* Old Windows - Does not support power events. */ } else { /* Supports power events. */ ssStatus.dwControlsAccepted |= SERVICE_ACCEPT_POWEREVENT; } /* if (wrapperData->isDebugging) { log_printf_queue(useLoggerQueue, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, " Service %s accepting STOP=%s, SHUTDOWN=%s, PAUSE/CONTINUE=%s, POWEREVENT=%s", natStateName, (ssStatus.dwControlsAccepted & SERVICE_ACCEPT_STOP ? "True" : "False"), (ssStatus.dwControlsAccepted & SERVICE_ACCEPT_SHUTDOWN ? "True" : "False"), (ssStatus.dwControlsAccepted & SERVICE_ACCEPT_PAUSE_CONTINUE ? "True" : "False"), (ssStatus.dwControlsAccepted & SERVICE_ACCEPT_POWEREVENT ? "True" : "False")); } */ ssStatus.dwCurrentState = natState; if (errorCode == 0) { ssStatus.dwWin32ExitCode = NO_ERROR; ssStatus.dwServiceSpecificExitCode = 0; } else { ssStatus.dwWin32ExitCode = ERROR_SERVICE_SPECIFIC_ERROR; ssStatus.dwServiceSpecificExitCode = errorCode; } ssStatus.dwWaitHint = waitHint; if ((natState == SERVICE_RUNNING) || (natState == SERVICE_STOPPED) || (natState == SERVICE_PAUSED)) { ssStatus.dwCheckPoint = 0; } else { ssStatus.dwCheckPoint = dwCheckPoint++; } if (wrapperData->isStateOutputEnabled) { log_printf_queue(useLoggerQueue, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("calling SetServiceStatus with status=%s, waitHint=%d, checkPoint=%u, errorCode=%d"), natStateName, waitHint, dwCheckPoint, errorCode); } if (!(bResult = SetServiceStatus(sshStatusHandle, &ssStatus))) { log_printf_queue(useLoggerQueue, WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("SetServiceStatus failed")); } } } /** * Reads a single block of data from the child pipe. * * @param blockBuffer Pointer to the buffer where the block will be read. * @param blockSize Maximum number of bytes to read. * @param readCount Pointer to an int which will hold the number of bytes * actually read by the call. * * Returns TRUE if there were any problems, FALSE otherwise. */ int wrapperReadChildOutputBlock(char *blockBuffer, int blockSize, int *readCount) { DWORD currentBlockAvail; /* See how many characters are available in the pipe so we can say how much to read. */ if (!PeekNamedPipe(wrapperChildStdoutRd, NULL, 0, NULL, ¤tBlockAvail, NULL)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to peek at output from the JVM: %s"), getLastErrorText()); return TRUE; } #ifdef DEBUG_CHILD_OUTPUT log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Peeked %d chars from pipe."), currentBlockAvail); #endif if (currentBlockAvail > 0) { /* Attempt to read in an additional CHILD_BLOCK_SIZE characters. */ if (!ReadFile(wrapperChildStdoutRd, blockBuffer, blockSize, readCount, NULL)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to read output from the JVM: %s"), getLastErrorText()); return TRUE; } #ifdef DEBUG_CHILD_OUTPUT log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Read %d chars from pipe."), *readCount); #endif } else { *readCount = 0; } return FALSE; } /** * Checks on the status of the JVM Process. * Returns WRAPPER_PROCESS_UP or WRAPPER_PROCESS_DOWN */ int wrapperGetProcessStatus(TICKS nowTicks, int childContinued) { int res; DWORD exitCode; TCHAR *exName; switch (WaitForSingleObject(wrapperData->javaProcess, 0)) { case WAIT_ABANDONED: case WAIT_OBJECT_0: res = WRAPPER_PROCESS_DOWN; /* Get the exit code of the process. */ if (!GetExitCodeProcess(wrapperData->javaProcess, &exitCode)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Critical error: unable to obtain the exit code of the JVM process: %s"), getLastErrorText()); appExit(wrapperData->errorExitCode); } if (exitCode == STILL_ACTIVE) { /* Should never happen, but check for it. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("The JVM returned JVM exit code was STILL_ACTIVE.") ); } /* If the JVM crashed then GetExitCodeProcess could have returned an uncaught exception. */ exName = getExceptionName(exitCode, TRUE); if (exName != NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The JVM process terminated due to an uncaught exception: %s (0x%08x)"), exName, exitCode); /* Reset the exit code as the exeption value will confuse users. */ exitCode = wrapperData->errorExitCode; } wrapperJVMProcessExited(nowTicks, exitCode); break; case WAIT_TIMEOUT: res = WRAPPER_PROCESS_UP; break; default: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Critical error: wait for JVM process failed: %s"), getLastErrorText()); appExit(wrapperData->errorExitCode); break; } return res; } /** * Launch a JVM and collect the process information. * * @return TRUE if there were any problems, FALSE otherwise. */ int wrapperLaunchJvm(TCHAR* command, PROCESS_INFORMATION *pprocess_info) { STARTUPINFO startup_info; int ret; TCHAR titleBuffer[80]; int hideConsole; int old_umask; int err; /* Do not show another console for the new process */ /*int processflags=CREATE_NEW_PROCESS_GROUP | DETACHED_PROCESS; */ /* Show a console for the new process */ /*int processflags=CREATE_NEW_PROCESS_GROUP | CREATE_NEW_CONSOLE; */ /* Create a new process group as part of this console so that signals can */ /* be sent to the JVM. */ DWORD processflags = CREATE_NEW_PROCESS_GROUP; /* Add the priority class of the new process to the processflags */ processflags |= wrapperData->ntServicePriorityClass; /* Generate a unique time for the console so we can look for it below. */ _sntprintf(titleBuffer, 80, TEXT("Wrapper Controlled JVM Console Id %d-%d (Do not close)"), wrapperData->wrapperPID, rand()); /* Initialize a STARTUPINFO structure to use for the new process. */ startup_info.cb=sizeof(STARTUPINFO); startup_info.lpReserved=NULL; startup_info.lpDesktop=NULL; startup_info.lpTitle=titleBuffer; startup_info.dwX=0; startup_info.dwY=0; startup_info.dwXSize=0; startup_info.dwYSize=0; startup_info.dwXCountChars=0; startup_info.dwYCountChars=0; startup_info.dwFillAttribute=0; /* Set the default flags which will not hide any windows opened by the JVM. */ /* Using Show Window and SW_HIDE seems to make it impossible to show any windows when the 32-bit version runs as a service. startup_info.dwFlags=STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW; startup_info.wShowWindow=SW_HIDE; */ startup_info.dwFlags=STARTF_USESTDHANDLES; startup_info.wShowWindow=0; hideConsole = FALSE; if (wrapperData->isConsole) { /* We are running as a console so no special console handling needs to be done. */ } else { /* Running as a service. */ if (wrapperData->ntAllocConsole) { /* A console was allocated when the service was started so the JVM will not create * its own. */ if ((wrapperData->wrapperConsoleHWND) && (wrapperData->wrapperConsoleHide)) { /* The console exists but is currently hidden. */ if (!wrapperData->ntHideJVMConsole) { /* In order to support older JVMs we need to show the console when the * JVM is launched. We need to remember to hide it below. */ showConsoleWindow(wrapperData->wrapperConsoleHWND, TEXT("Wrapper")); wrapperData->wrapperConsoleVisible = TRUE; wrapperData->wrapperConsoleHide = FALSE; /* Temporarily disable the hide flag so the event loop won't hide it while we are launching the JVM. */ hideConsole = TRUE; } } } else { /* A console does not yet exist so the JVM will create and display one itself. */ if (wrapperData->ntHideJVMConsole) { /* The console that the JVM creates should be surpressed and never shown. * JVMs of version 1.4.0 and above will still display a GUI. But older JVMs * will not. */ startup_info.dwFlags=STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES; startup_info.wShowWindow=SW_HIDE; } else { /* The new JVM console should be allowed to be displayed. But we need to * remember to hide it below. */ hideConsole = TRUE; } } } startup_info.cbReserved2 = 0; startup_info.lpReserved2 = NULL; startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); startup_info.hStdOutput = wrapperChildStdoutWr; startup_info.hStdError = wrapperChildStdoutWr; /* Initialize a PROCESS_INFORMATION structure to use for the new process */ pprocess_info->hProcess = NULL; pprocess_info->hThread = NULL; pprocess_info->dwProcessId = 0; pprocess_info->dwThreadId = 0; /* Make sure the log file is closed before the Java process is created. Failure to do * so will give the Java process a copy of the open file. This means that this process * will not be able to rename the file even after closing it because it will still be * open in the Java process. Also set the auto close flag to make sure that other * threads do not reopen the log file as the new process is being created. */ setLogfileAutoClose(TRUE); closeLogfile(); /* Reset the log duration so we get new counts from the time the JVM is launched. */ resetDuration(); /* Set the umask of the JVM (it doesn't seem to work on Windows) */ old_umask = _umask(wrapperData->javaUmask); /* Create the new process */ ret=CreateProcess(NULL, command, /* the command line to start */ NULL, /* process security attributes */ NULL, /* primary thread security attributes */ TRUE, /* handles are inherited */ processflags, /* we specify new process group */ NULL, /* use parent's environment */ NULL, /* use the Wrapper's current working directory */ &startup_info, /* STARTUPINFO pointer */ pprocess_info); /* PROCESS_INFORMATION pointer */ err = getLastError(); /* Restore the umask. */ _umask(old_umask); /* As soon as the new process is created, restore the auto close flag. */ setLogfileAutoClose(wrapperData->logfileCloseTimeout == 0); /* Check if virtual machine started */ if (ret==FALSE) { /* Make sure the process was launched correctly. */ if (err!=NO_ERROR) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to execute Java command. %s"), getErrorText(err, NULL)); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" %s"), command); wrapperData->javaProcess = NULL; if (wrapperData->isAdviserEnabled) { if ((err == ERROR_FILE_NOT_FOUND) || (err == ERROR_PATH_NOT_FOUND)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("--------------------------------------------------------------------") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("Advice:" )); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("Usually when the Wrapper fails to start the JVM process, it is\nbecause of a problem with the value of the configured Java command.\nCurrently:" )); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("wrapper.java.command=%s"), getStringProperty(properties, TEXT("wrapper.java.command"), TEXT("java"))); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("Please make sure that the PATH or any other referenced environment\nvariables are correctly defined for the current environment." )); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("--------------------------------------------------------------------") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("") ); } else if (err == ERROR_ACCESS_DENIED) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("--------------------------------------------------------------------") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT( "Advice:" )); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT( "Access denied errors when attempting to launch the Java process are\nusually caused by strict access permissions assigned to the\ndirectory in which Java is installed." )); if (!wrapperData->isConsole) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT( "Unless you have configured the Wrapper to run as a different user\nwith wrapper.ntservice.account property, the Wrapper and its JVM\nwill be as the SYSTEM user by default when run as a service." )); } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("--------------------------------------------------------------------") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("") ); } } /* This is always a permanent problem. */ CloseHandle(pprocess_info->hProcess); CloseHandle(pprocess_info->hThread); return TRUE; } } /* Now check if we have a process handle again for the Swedish WinNT bug */ if (pprocess_info->hProcess == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("can not execute \"%s\""), command); CloseHandle(pprocess_info->hThread); return TRUE; } if (hideConsole) { /* Now that the JVM has been launched we need to hide the console that it * is using. */ if (wrapperData->wrapperConsoleHWND) { /* The wrapper's console needs to be hidden. */ wrapperData->wrapperConsoleHide = TRUE; } else { /* We need to locate the console that was created by the JVM on launch * and hide it. */ wrapperData->jvmConsoleHandle = findConsoleWindow(titleBuffer); wrapperData->jvmConsoleVisible = TRUE; /* This will be cleared if the check call successfully hides it. */ } wrapperCheckConsoleWindows(); } return FALSE; } /** * Create a child process to print the Java version running the command: * /path/to/java -version * After printing the java version, the process is terminated. * * In case the JVM is slow to start, it will time out after * the number of seconds set in "wrapper.java.version.timeout". * * @return One of the following state: * JAVA_VERSION_LAUNCH_FAILED * JAVA_VERSION_WAIT_FAILED * JAVA_VERSION_KILL_FAILED * JAVA_VERSION_COMPLETED */ int wrapperLaunchJavaVersion() { PROCESS_INFORMATION process_info; int blockTimeout; DWORD exitCode; DWORD ret; int result = JAVA_VERSION_COMPLETED; if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Java Command Line (Query Java Version):")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" Command: %s"), wrapperData->jvmVersionCommand); } /* Force using the encoding of the current locale to read the output of the Java version * (we know this this JVM is launched without system properties specifying a different encoding). */ resetJvmOutputEncoding(FALSE); if (wrapperLaunchJvm(wrapperData->jvmVersionCommand, &process_info)) { return JAVA_VERSION_LAUNCH_FAILED; } /* If the user set the value to 0, then we will wait indefinitely. * NOTE: it would be better to create a new state in the main event loop as this would allow the user to press CTRL-C. */ blockTimeout = getIntProperty(properties, TEXT("wrapper.java.version.timeout"), DEFAULT_JAVA_VERSION_TIMEOUT) * 1000; if (blockTimeout <= 0) { blockTimeout = INFINITE; } ret = WaitForSingleObject(process_info.hProcess, blockTimeout); if (ret == WAIT_OBJECT_0) { /* Process completed - we know that nothing more can be written to stdout/stderr. */ if (!GetExitCodeProcess(process_info.hProcess, &exitCode)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Child process: Java version: unable to obtain the exit code - %s"), getLastErrorText()); /* Assume it's ok */ exitCode = ERROR_SUCCESS; } if (exitCode == ERROR_SUCCESS) { wrapperReadJavaVersionOutput(TRUE); } else { /* The command was found and executed (wrapperLaunchJvm returned FALSE), but failed. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Child process: Java version: terminated with an exit code of %d."), exitCode); /* Set a flag so that there will be no attempt to parse the Java version. * This flag is reset to FALSE whenever wrapperReadJavaVersionOutput() is called. */ wrapperData->jvmVersionFailed = TRUE; /* Read all messages of the output as it may give us a clue of what the problem is. */ while (wrapperReadChildOutput(250)) { } /* Resolve the Java version to its default value. */ wrapperSetJavaVersion(NULL); } } else { if (ret == WAIT_TIMEOUT) { /* Timed out. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Child process: Java version: timed out")); } else { /* Wait failed. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Child process: Java version: wait failed")); } if (TerminateProcess(process_info.hProcess, 1) == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Child process: Java version: kill failed - %s"), getLastErrorText()); result = JAVA_VERSION_KILL_FAILED; } else { /* The process is now killed. */ /* Reset wrapperData->javaVersion. */ if (wrapperData->javaVersion) { disposeJavaVersion(wrapperData->javaVersion); wrapperData->javaVersion = NULL; } /* There might be no output but read all the pipe anyway. */ wrapperReadJavaVersionOutput(FALSE); /* Continue only if, by chance, the Java version was found. */ if (!wrapperData->javaVersion) { result = JAVA_VERSION_WAIT_FAILED; } } } CloseHandle(process_info.hProcess); CloseHandle(process_info.hThread); return result; } /** * Start the Java application and store internaly the JVM process. * * @return TRUE if there were any problems. When this happens the Wrapper will not try to restart. */ int wrapperLaunchJavaApp() { static int javaIOThreadSet = FALSE; PROCESS_INFORMATION process_info; size_t len; /* Update the CLASSPATH in the environment if requested so the JVM can access it. */ if (wrapperData->environmentClasspath) { if (setEnv(TEXT("CLASSPATH"), wrapperData->classpath, ENV_SOURCE_APPLICATION)) { /* This can happen if the classpath is too long on Windows. */ wrapperData->javaProcess = NULL; wrapperData->exitCode = wrapperData->errorExitCode; return TRUE; } } /* Make sure the classpath is not too long. */ len = _tcslen(wrapperData->jvmCommand); if (len > MAX_COMMAND_LINE_LEN) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("The generated Java command line has a length of %d, which is longer than the Windows maximum of %d characters."), len, MAX_COMMAND_LINE_LEN); if (!wrapperData->environmentClasspath) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" You may be able to shorten your command line by setting wrapper.java.classpath.use_environment.")); } wrapperData->javaProcess = NULL; wrapperData->exitCode = wrapperData->errorExitCode; return TRUE; } /* Log the application java command line */ if (wrapperData->commandLogLevel != LEVEL_NONE) { log_printf(WRAPPER_SOURCE_WRAPPER, wrapperData->commandLogLevel, TEXT("Java Command Line:")); log_printf(WRAPPER_SOURCE_WRAPPER, wrapperData->commandLogLevel, TEXT(" Command: %s"), wrapperData->jvmCommand); if (wrapperData->environmentClasspath) { log_printf(WRAPPER_SOURCE_WRAPPER, wrapperData->commandLogLevel, TEXT(" Classpath in Environment : %s"), wrapperData->classpath); } } if (wrapperData->runWithoutJVM) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Not launching a JVM because %s was set to TRUE."), TEXT("wrapper.test.no_jvm")); wrapperData->exitCode = 0; return TRUE; } if (wrapperData->useJavaIOThread) { /* Create and initialize a javaIO thread. */ if (!javaIOThreadSet) { if (initializeJavaIO()) { return TRUE; } javaIOThreadSet = TRUE; } } else { javaIOThreadHandle = NULL; javaIOThreadId = 0; } /* Now launch the JVM process. */ if (wrapperLaunchJvm(wrapperData->jvmCommand, &process_info)) { wrapperData->javaProcess = NULL; wrapperData->exitCode = wrapperData->errorExitCode; return TRUE; } /* Reset the exit code when we launch a new JVM. */ wrapperData->exitCode = 0; /* We keep a reference to the process handle, but need to close the thread handle. */ wrapperData->javaProcess = process_info.hProcess; wrapperData->javaPID = process_info.dwProcessId; CloseHandle(process_info.hThread); /* Log the PID of the new JVM. */ if (wrapperData->pidLogLevel != LEVEL_NONE) { log_printf(WRAPPER_SOURCE_WRAPPER, wrapperData->pidLogLevel, TEXT("JVM started (PID=%d)"), wrapperData->javaPID); } /* If a java pid filename is specified then write the pid of the java process. */ if (wrapperData->javaPidFilename) { if (writePidFile(wrapperData->javaPidFilename, wrapperData->javaPID, wrapperData->javaPidFileUmask)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to write the Java PID file: %s"), wrapperData->javaPidFilename); } } /* If a java id filename is specified then write the id of the java process. */ if (wrapperData->javaIdFilename) { if (writePidFile(wrapperData->javaIdFilename, wrapperData->jvmRestarts, wrapperData->javaIdFileUmask)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to write the Java Id file: %s"), wrapperData->javaIdFilename); } } return FALSE; } /** * Returns a tick count that can be used in combination with the * wrapperGetTickAgeSeconds() function to perform time keeping. */ TICKS wrapperGetTicks() { TICKS ticks; if (wrapperData->useSystemTime) { /* We want to return a tick count that is based on the current system time. */ ticks = wrapperGetSystemTicks(); } else { /* Lock the tick mutex whenever the "timerTicks" variable is accessed. */ if (wrapperData->useTickMutex && wrapperLockTickMutex()) { return 0; } /* Return a snapshot of the current tick count. */ ticks = timerTicks; if (wrapperData->useTickMutex && wrapperReleaseTickMutex()) { return 0; } } return ticks; } /** * Outputs a a log entry describing what the memory dump columns are. */ void wrapperDumpMemoryBanner() { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Wrapper memory: PageFaultcount, WorkingSetSize (Peak), QuotaPagePoolUsage (Peak), QuotaNonPagedPoolUsage (Peak), PageFileUsage (Peak) Java memory: PageFaultcount, WorkingSetSize (Peak), QuotaPagePoolUsage (Peak), QuotaNonPagedPoolUsage (Peak), PageFileUsage (Peak) System memory: MemoryLoad, Available/PhysicalSize (%%), Available/PageFileSize (%%), Available/VirtualSize (%%), ExtendedVirtualSize")); } /** * Outputs a log entry at regular intervals to track the memory usage of the * Wrapper and its JVM. */ void wrapperDumpMemory() { PROCESS_MEMORY_COUNTERS wCounters; PROCESS_MEMORY_COUNTERS jCounters; MEMORYSTATUSEX statex; if (OptionalGetProcessMemoryInfo) { /* Start with the Wrapper process. */ if (OptionalGetProcessMemoryInfo(wrapperData->wrapperProcess, &wCounters, sizeof(wCounters)) == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Call to GetProcessMemoryInfo failed for Wrapper process %08x: %s"), wrapperData->wrapperPID, getLastErrorText()); return; } if (wrapperData->javaProcess != NULL) { /* Next the Java process. */ if (OptionalGetProcessMemoryInfo(wrapperData->javaProcess, &jCounters, sizeof(jCounters)) == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Call to GetProcessMemoryInfo failed for Java process %08x: %s"), wrapperData->javaPID, getLastErrorText()); return; } } else { memset(&jCounters, 0, sizeof(jCounters)); } statex.dwLength = sizeof(statex); GlobalMemoryStatusEx(&statex); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Wrapper memory: %lu, %lu (%lu), %lu (%lu), %lu (%lu), %lu (%lu) Java memory: %lu, %lu (%lu), %lu (%lu), %lu (%lu), %lu (%lu) System memory: %lu%%, %I64u/%I64u (%u%%), %I64u/%I64u (%u%%), %I64u/%I64u (%u%%), %I64u"), wCounters.PageFaultCount, wCounters.WorkingSetSize, wCounters.PeakWorkingSetSize, wCounters.QuotaPagedPoolUsage, wCounters.QuotaPeakPagedPoolUsage, wCounters.QuotaNonPagedPoolUsage, wCounters.QuotaPeakNonPagedPoolUsage, wCounters.PagefileUsage, wCounters.PeakPagefileUsage, jCounters.PageFaultCount, jCounters.WorkingSetSize, jCounters.PeakWorkingSetSize, jCounters.QuotaPagedPoolUsage, jCounters.QuotaPeakPagedPoolUsage, jCounters.QuotaNonPagedPoolUsage, jCounters.QuotaPeakNonPagedPoolUsage, jCounters.PagefileUsage, jCounters.PeakPagefileUsage, statex.dwMemoryLoad, statex.ullAvailPhys, statex.ullTotalPhys, (int)(100 * statex.ullAvailPhys / statex.ullTotalPhys), statex.ullAvailPageFile, statex.ullTotalPageFile, (int)(100 * statex.ullAvailPageFile / statex.ullTotalPageFile), statex.ullAvailVirtual, statex.ullTotalVirtual, (int)(100 * statex.ullAvailVirtual / statex.ullTotalVirtual), statex.ullAvailExtendedVirtual); } } DWORD filetimeToMS(FILETIME* filetime) { LARGE_INTEGER li; memcpy(&li, filetime, sizeof(li)); li.QuadPart /= 10000; return li.LowPart; } /** * Outputs a log entry at regular intervals to track the CPU usage over each * interval for the Wrapper and its JVM. * * In order to make sense of the timing values, it is also necessary to see how * far the system performance counter has progressed. By carefully comparing * these values, it is possible to very accurately calculate the CPU usage over * any period of time. */ LONGLONG lastPerformanceCount = 0; LONGLONG lastWrapperKernelTime = 0; LONGLONG lastWrapperUserTime = 0; LONGLONG lastJavaKernelTime = 0; LONGLONG lastJavaUserTime = 0; LONGLONG lastIdleKernelTime = 0; LONGLONG lastIdleUserTime = 0; void wrapperDumpCPUUsage() { LARGE_INTEGER count; LARGE_INTEGER frequency; LARGE_INTEGER li; LONGLONG performanceCount; FILETIME creationTime; FILETIME exitTime; FILETIME wKernelTime; FILETIME wUserTime; FILETIME jKernelTime; FILETIME jUserTime; DWORD wKernelTimeMs; /* Will overflow in 49 days of usage. */ DWORD wUserTimeMs; DWORD wTimeMs; DWORD jKernelTimeMs; DWORD jUserTimeMs; DWORD jTimeMs; double age; double wKernelPercent; double wUserPercent; double wPercent; double jKernelPercent; double jUserPercent; double jPercent; if (OptionalGetProcessTimes) { if (!QueryPerformanceCounter(&count)) { /* no high-resolution performance counter support. */ return; } if (!QueryPerformanceFrequency(&frequency)) { } performanceCount = count.QuadPart; /* Start with the Wrapper process. */ if (!OptionalGetProcessTimes(wrapperData->wrapperProcess, &creationTime, &exitTime, &wKernelTime, &wUserTime)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Call to GetProcessTimes failed for Wrapper process %08x: %s"), wrapperData->wrapperPID, getLastErrorText()); return; } if (wrapperData->javaProcess != NULL) { /* Next the Java process. */ if (!OptionalGetProcessTimes(wrapperData->javaProcess, &creationTime, &exitTime, &jKernelTime, &jUserTime)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Call to GetProcessTimes failed for Java process %08x: %s"), wrapperData->javaPID, getLastErrorText()); return; } } else { memset(&jKernelTime, 0, sizeof(jKernelTime)); memset(&jUserTime, 0, sizeof(jUserTime)); lastJavaKernelTime = 0; lastJavaUserTime = 0; } /* Convert the times to ms. */ wKernelTimeMs = filetimeToMS(&wKernelTime); wUserTimeMs = filetimeToMS(&wUserTime); wTimeMs = wKernelTimeMs + wUserTimeMs; jKernelTimeMs = filetimeToMS(&jKernelTime); jUserTimeMs = filetimeToMS(&jUserTime); jTimeMs = jKernelTimeMs + jUserTimeMs; /* Calculate the number of seconds since the last call. */ age = (double)(performanceCount - lastPerformanceCount) / frequency.QuadPart; /* Calculate usage percentages. */ memcpy(&li, &wKernelTime, sizeof(li)); wKernelPercent = 100.0 * ((li.QuadPart - lastWrapperKernelTime) / 10000000.0) / age; lastWrapperKernelTime = li.QuadPart; memcpy(&li, &wUserTime, sizeof(li)); wUserPercent = 100.0 * ((li.QuadPart - lastWrapperUserTime) / 10000000.0) / age; lastWrapperUserTime = li.QuadPart; wPercent = wKernelPercent + wUserPercent; memcpy(&li, &jKernelTime, sizeof(li)); jKernelPercent = 100.0 * ((li.QuadPart - lastJavaKernelTime) / 10000000.0) / age; lastJavaKernelTime = li.QuadPart; memcpy(&li, &jUserTime, sizeof(li)); jUserPercent = 100.0 * ((li.QuadPart - lastJavaUserTime) / 10000000.0) / age; lastJavaUserTime = li.QuadPart; jPercent = jKernelPercent + jUserPercent; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Wrapper CPU: kernel %ldms (%5.2f%%), user %ldms (%5.2f%%), total %ldms (%5.2f%%) Java CPU: kernel %ldms (%5.2f%%), user %ldms (%5.2f%%), total %ldms (%5.2f%%)"), wKernelTimeMs, wKernelPercent, wUserTimeMs, wUserPercent, wTimeMs, wPercent, jKernelTimeMs, jKernelPercent, jUserTimeMs, jUserPercent, jTimeMs, jPercent); lastPerformanceCount = performanceCount; } } void wrapperInitializeProfileCounters() { PDH_STATUS pdhStatus; FARPROC pdhAddUnlocalizedCounter; BOOL couldLoad; HMODULE dbgHelpDll = GetModuleHandle(TEXT("Pdh.dll")); if( dbgHelpDll == NULL) { couldLoad = FALSE; } else { if (isVista()) { #ifdef UNICODE pdhAddUnlocalizedCounter = GetProcAddress(dbgHelpDll, "PdhAddEnglishCounterW"); #else pdhAddUnlocalizedCounter = GetProcAddress(dbgHelpDll, "PdhAddEnglishCounterA"); #endif } else { #ifdef UNICODE pdhAddUnlocalizedCounter = GetProcAddress(dbgHelpDll, "PdhAddCounterW"); #else pdhAddUnlocalizedCounter = GetProcAddress(dbgHelpDll, "PdhAddCounterA"); #endif } if(pdhAddUnlocalizedCounter == NULL) { couldLoad = FALSE; } else { couldLoad = TRUE; } } /* We want to set up system profile monitoring to keep track of the state of the system. */ pdhStatus = PdhOpenQuery(NULL, 0, &pdhQuery); if (pdhStatus != ERROR_SUCCESS) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Failed to initialize profiling: 0x%x"), pdhStatus); pdhQuery = NULL; } else { pdhStatus = (PDH_STATUS)pdhAddUnlocalizedCounter(pdhQuery, TEXT("\\PhysicalDisk(_Total)\\Avg. Disk Queue Length"), 0, &pdhCounterPhysicalDiskAvgQueueLen); if (pdhStatus != ERROR_SUCCESS) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Failed to initialize profiling counter %d: 0x%x"), 1, pdhStatus); } pdhStatus = (PDH_STATUS)pdhAddUnlocalizedCounter(pdhQuery, TEXT("\\PhysicalDisk(_Total)\\Avg. Disk Write Queue Length"), 0, &pdhCounterPhysicalDiskAvgWriteQueueLen); if (pdhStatus != ERROR_SUCCESS) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Failed to initialize profiling counter %d: 0x%x"), 2, pdhStatus); } pdhStatus = (PDH_STATUS)pdhAddUnlocalizedCounter(pdhQuery, TEXT("\\PhysicalDisk(_Total)\\Avg. Disk Read Queue Length"), 0, &pdhCounterPhysicalDiskAvgReadQueueLen); if (pdhStatus != ERROR_SUCCESS) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Failed to initialize profiling counter %d: 0x%x"), 3, pdhStatus); } pdhStatus = (PDH_STATUS)pdhAddUnlocalizedCounter(pdhQuery, TEXT("\\Memory\\Page Faults/sec"), 0, &pdhCounterMemoryPageFaultsPSec); if (pdhStatus != ERROR_SUCCESS) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Failed to initialize profiling counter %d: 0x%x"), 4, pdhStatus); } pdhStatus = (PDH_STATUS)pdhAddUnlocalizedCounter(pdhQuery, TEXT("\\Memory\\Transition Faults/sec"), 0, &pdhCounterMemoryTransitionFaultsPSec); if (pdhStatus != ERROR_SUCCESS) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Failed to initialize profiling counter %d: 0x%x"), 5, pdhStatus); } pdhStatus = (PDH_STATUS)pdhAddUnlocalizedCounter(pdhQuery, TEXT("\\Process(wrapper)\\Page Faults/sec"), 0, &pdhCounterProcessWrapperPageFaultsPSec); if (pdhStatus != ERROR_SUCCESS) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Failed to initialize profiling counter %d: 0x%x"), 6, pdhStatus); } pdhStatus = (PDH_STATUS)pdhAddUnlocalizedCounter(pdhQuery, TEXT("\\Process(java)\\Page Faults/sec"), 0, &pdhCounterProcessJavaPageFaultsPSec); if (pdhStatus != ERROR_SUCCESS) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Failed to initialize profiling counter %d: 0x%x"), 7, pdhStatus); } if (couldLoad && dbgHelpDll != NULL) { FreeLibrary(dbgHelpDll); } /* This is the first call, since for some equations (e.g. for average) 2 values need to be polled */ PdhCollectQueryData(pdhQuery); /* PdhGetCounterInfo to get info about the counters like scale, etc. */ } } void wrapperDumpPageFaultUsage() { PDH_STATUS pdhStatus; DWORD counterType; PDH_FMT_COUNTERVALUE counterValue; double diskQueueLen = 0; double diskQueueWLen = 0; double diskQueueRLen = 0; double pageFaults = 0; double transitionPageFaults = 0; double wrapperPageFaults = 0; double javaPageFaults = 0; if (pdhQuery == NULL) { return; } pdhStatus = PdhCollectQueryData(pdhQuery); if (pdhStatus == ERROR_SUCCESS) { pdhStatus = PdhGetFormattedCounterValue(pdhCounterPhysicalDiskAvgQueueLen, PDH_FMT_DOUBLE, &counterType, &counterValue); if (pdhStatus == ERROR_SUCCESS) { diskQueueLen = counterValue.doubleValue; /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("\\PhysicalDisk(_Total)\\Avg. Disk Queue Length : %d %10.5f"), counterValue.CStatus, counterValue.doubleValue);*/ } pdhStatus = PdhGetFormattedCounterValue(pdhCounterPhysicalDiskAvgWriteQueueLen, PDH_FMT_DOUBLE, &counterType, &counterValue); if (pdhStatus == ERROR_SUCCESS) { diskQueueWLen = counterValue.doubleValue; /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("\\PhysicalDisk(_Total)\\Avg. Disk Write Queue Length : %d %10.5f"), counterValue.CStatus, counterValue.doubleValue);*/ } pdhStatus = PdhGetFormattedCounterValue(pdhCounterPhysicalDiskAvgReadQueueLen, PDH_FMT_DOUBLE, &counterType, &counterValue); if (pdhStatus == ERROR_SUCCESS) { diskQueueRLen = counterValue.doubleValue; /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("\\PhysicalDisk(_Total)\\Avg. Disk Read Queue Length : %d %10.5f"), counterValue.CStatus, counterValue.doubleValue);*/ } pdhStatus = PdhGetFormattedCounterValue(pdhCounterMemoryPageFaultsPSec, PDH_FMT_DOUBLE, &counterType, &counterValue); if (pdhStatus == ERROR_SUCCESS) { pageFaults = counterValue.doubleValue; /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("\\Memory\\Page Faults/sec : %d %10.5f"), counterValue.CStatus, counterValue.doubleValue);*/ } pdhStatus = PdhGetFormattedCounterValue(pdhCounterMemoryTransitionFaultsPSec, PDH_FMT_DOUBLE, &counterType, &counterValue); if (pdhStatus == ERROR_SUCCESS) { transitionPageFaults = counterValue.doubleValue; /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("\\Memory\\Transition Faults/sec : %d %10.5f"), counterValue.CStatus, counterValue.doubleValue);*/ } pdhStatus = PdhGetFormattedCounterValue(pdhCounterProcessWrapperPageFaultsPSec, PDH_FMT_DOUBLE, &counterType, &counterValue); if (pdhStatus == ERROR_SUCCESS) { wrapperPageFaults = counterValue.doubleValue; /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("\\Process(wrapper)\\Page Faults/sec : %d %10.5f"), counterValue.CStatus, counterValue.doubleValue);*/ } pdhStatus = PdhGetFormattedCounterValue(pdhCounterProcessJavaPageFaultsPSec, PDH_FMT_DOUBLE, &counterType, &counterValue); if (pdhStatus == ERROR_SUCCESS) { javaPageFaults = counterValue.doubleValue; /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("\\Process(java)\\Page Faults/sec : %d %10.5f"), counterValue.CStatus, counterValue.doubleValue);*/ } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Page Faults (Total:%8.2f%8.2f:%8.2f Wrapper:%7.2f (%7.2f%%) Java:%7.2f (%7.2f%%)) Queue Len (Total:%7.2f Read:%7.2f Write:%7.2f)"), pageFaults, transitionPageFaults, pageFaults - transitionPageFaults, wrapperPageFaults, (pageFaults > 0 ? 100 * wrapperPageFaults / pageFaults : 0), javaPageFaults, (pageFaults > 0 ? 100 * javaPageFaults / pageFaults : 0), diskQueueLen, diskQueueRLen, diskQueueWLen); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Failed to collect profile data: 0x%x"), pdhStatus); } } void disposeProfileCounters() { if (pdhQuery != NULL) { PdhCloseQuery(pdhQuery); pdhQuery = NULL; } } /****************************************************************************** * NT Service Methods *****************************************************************************/ /** * This function goes through and checks flags for each of several signals to see if they * have been fired since the last time this function was called. This is the only thread * which will ever clear these flags, but they can be set by other threads within the * signal handlers at ANY time. So only check the value of each flag once and reset them * immediately to decrease the chance of missing duplicate signals. */ void wrapperMaintainControlCodes() { /* Allow for a large integer + \0 */ TCHAR buffer[11]; int ctrlCodeLast; int quit = FALSE; int halt = FALSE; /* CTRL_C_EVENT */ if (wrapperData->ctrlEventCTRLCTrapped) { wrapperData->ctrlEventCTRLCTrapped = FALSE; /* Always quit. If the user has pressed CTRL-C previously then we want to force * an immediate shutdown. */ if (ctrlCTrapped) { /* Pressed CTRL-C more than once. */ if (wrapperGetTickAgeTicks(ctrlCTrappedLastTick, wrapperGetTicks()) >= wrapperData->forcedShutdownDelay) { /* We want to ignore double signals which can be sent both by the script and the systems at almost the same time. */ if (wrapperData->isForcedShutdownDisabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s trapped. Already shutting down."), TEXT("CTRL-C")); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s trapped. Forcing immediate shutdown."), TEXT("CTRL-C")); halt = TRUE; } } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s trapped. Shutting down."), TEXT("CTRL-C")); ctrlCTrapped = TRUE; ctrlCTrappedLastTick = wrapperGetTicks(); } quit = TRUE; } /* CTRL_CLOSE_EVENT */ if (wrapperData->ctrlEventCloseTrapped) { wrapperData->ctrlEventCloseTrapped = FALSE; /* Always quit. If the user has tried to close the console previously then we want to force * an immediate shutdown. */ if (ctrlCTrapped) { /* Pressed Close or CTRL-C more than once. */ if (wrapperGetTickAgeTicks(ctrlCTrappedLastTick, wrapperGetTicks()) >= wrapperData->forcedShutdownDelay) { /* We want to ignore double signals which can be sent both by the script and the systems at almost the same time. */ if (wrapperData->isForcedShutdownDisabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s trapped. Already shutting down."), TEXT("Close")); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s trapped. Forcing immediate shutdown."), TEXT("Close")); halt = TRUE; } } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s trapped. Shutting down."), TEXT("Close")); ctrlCTrapped = TRUE; ctrlCTrappedLastTick = wrapperGetTicks(); } quit = TRUE; } /* CTRL_LOGOFF_EVENT * Windows 7+: This will never be received if the gdi32.dll or user32.dll libraries are loaded. */ if (wrapperData->ctrlEventLogoffTrapped) { wrapperData->ctrlEventLogoffTrapped = FALSE; /* Happens when the user logs off. We should quit when run as a */ /* console, but stay up when run as a service. */ if ((wrapperData->isConsole) && (!wrapperData->ignoreUserLogoffs)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("User logged out. Shutting down.")); quit = TRUE; } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("User logged out. Ignored.")); quit = FALSE; } } /* CTRL_SHUTDOWN_EVENT * Windows 7+: This will never be received if the gdi32.dll or user32.dll libraries are loaded. */ if (wrapperData->ctrlEventShutdownTrapped) { wrapperData->ctrlEventShutdownTrapped = FALSE; /* Happens when the machine is shutdown or rebooted. Always quit. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Machine is shutting down.")); quit = TRUE; } /* Queued control codes. */ while (wrapperData->ctrlCodeQueueReadIndex != wrapperData->ctrlCodeQueueWriteIndex) { ctrlCodeLast = wrapperData->ctrlCodeQueue[wrapperData->ctrlCodeQueueReadIndex]; #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Process queued control code: %d (r:%d w:%d)"), ctrlCodeLast, wrapperData->ctrlCodeQueueReadIndex, wrapperData->ctrlCodeQueueWriteIndex); #endif wrapperData->ctrlCodeQueueReadIndex++; if (wrapperData->ctrlCodeQueueReadIndex >= CTRL_CODE_QUEUE_SIZE ) { wrapperData->ctrlCodeQueueReadIndex = 0; } _sntprintf(buffer, 11, TEXT("%d"), ctrlCodeLast); wrapperProtocolFunction(WRAPPER_MSG_SERVICE_CONTROL_CODE, buffer); } /* SERVICE_CONTROL_PAUSE */ if (wrapperData->ctrlCodePauseTrapped) { wrapperData->ctrlCodePauseTrapped = FALSE; /* Tell the wrapper to pause */ wrapperPauseProcess(WRAPPER_ACTION_SOURCE_CODE_WINDOWS_SERVICE_MANAGER); } /* SERVICE_CONTROL_CONTINUE */ if (wrapperData->ctrlCodeContinueTrapped) { wrapperData->ctrlCodeContinueTrapped = FALSE; /* Tell the wrapper to resume */ wrapperResumeProcess(WRAPPER_ACTION_SOURCE_CODE_WINDOWS_SERVICE_MANAGER); } /* SERVICE_CONTROL_STOP */ if (wrapperData->ctrlCodeStopTrapped) { wrapperData->ctrlCodeStopTrapped = FALSE; /* Request to stop the service. Report SERVICE_STOP_PENDING */ /* to the service control manager before calling ServiceStop() */ /* to avoid a "Service did not respond" error. */ wrapperReportStatus(FALSE, WRAPPER_WSTATE_STOPPING, wrapperData->exitCode, wrapperData->ntShutdownWaitHint * 1000); /* Tell the wrapper to shutdown normally */ /* Always force the shutdown as this is an external event. */ wrapperStopProcess(0, TRUE); /* To make sure that the JVM will not be restarted for any reason, * start the Wrapper shutdown process as well. * In this case we do not want to allow any exit filters to be used * so setting this here will force the shutdown. */ if ((wrapperData->wState == WRAPPER_WSTATE_STOPPING) || (wrapperData->wState == WRAPPER_WSTATE_STOPPED)) { /* Already stopping. */ } else { wrapperSetWrapperState(WRAPPER_WSTATE_STOPPING); } } #ifdef SUPPORT_PRESHUTDOWN /* SERVICE_CONTROL_PRESHUTDOWN */ if (wrapperData->ctrlCodePreShutdownTrapped) { wrapperData->ctrlCodePreShutdownTrapped = FALSE; /* Request to stop the service. Report SERVICE_STOP_PENDING */ /* to the service control manager before calling ServiceStop() */ /* to avoid a "Service did not respond" error. */ wrapperReportStatus(FALSE, WRAPPER_WSTATE_STOPPING, wrapperData->exitCode, wrapperData->ntShutdownWaitHint * 1000); /* Tell the wrapper to shutdown normally */ /* Always force the shutdown as this is an external event. */ wrapperStopProcess(0, TRUE); /* To make sure that the JVM will not be restarted for any reason, * start the Wrapper shutdown process as well. */ if ((wrapperData->wState == WRAPPER_WSTATE_STOPPING) || (wrapperData->wState == WRAPPER_WSTATE_STOPPED)) { /* Already stopping. */ } else { wrapperSetWrapperState(WRAPPER_WSTATE_STOPPING); } } #endif /* SERVICE_CONTROL_SHUTDOWN */ if (wrapperData->ctrlCodeShutdownTrapped) { wrapperData->ctrlCodeShutdownTrapped = FALSE; /* Request to stop the service. Report SERVICE_STOP_PENDING */ /* to the service control manager before calling ServiceStop() */ /* to avoid a "Service did not respond" error. */ wrapperReportStatus(FALSE, WRAPPER_WSTATE_STOPPING, wrapperData->exitCode, wrapperData->ntShutdownWaitHint * 1000); /* Tell the wrapper to shutdown normally */ /* Always force the shutdown as this is an external event. */ wrapperStopProcess(0, TRUE); /* To make sure that the JVM will not be restarted for any reason, * start the Wrapper shutdown process as well. */ if ((wrapperData->wState == WRAPPER_WSTATE_STOPPING) || (wrapperData->wState == WRAPPER_WSTATE_STOPPED)) { /* Already stopping. */ } else { wrapperSetWrapperState(WRAPPER_WSTATE_STOPPING); } } /* The configured thread dump control code */ if (wrapperData->ctrlCodeDumpTrapped) { wrapperData->ctrlCodeDumpTrapped = FALSE; wrapperRequestDumpJVMState(); } if (quit) { if (halt) { /* Disable the thread dump on exit feature if it is set because it * should not be displayed when the user requested the immediate exit. */ wrapperData->requestThreadDumpOnFailedJVMExit = FALSE; wrapperKillProcess(FALSE); } else { /* Always force the shutdown as this is an external event. */ wrapperStopProcess(0, TRUE); } /* Don't actually kill the process here. Let the application shut itself down */ /* To make sure that the JVM will not be restarted for any reason, * start the Wrapper shutdown process as well. */ if ((wrapperData->wState == WRAPPER_WSTATE_STOPPING) || (wrapperData->wState == WRAPPER_WSTATE_STOPPED)) { /* Already stopping. */ } else { wrapperSetWrapperState(WRAPPER_WSTATE_STOPPING); } } } /** * The service control handler is called by the service manager when there are * events for the service. registered using a call to * RegisterServiceCtrlHandler in wrapperServiceMain. * * Note on PowerEvents prior to win2k: http://blogs.msdn.com/heaths/archive/2005/05/18/419791.aspx */ DWORD WINAPI wrapperServiceControlHandlerEx(DWORD dwCtrlCode, DWORD dwEvtType, LPVOID lpEvtData, LPVOID lpCntxt) { DWORD result = result = NO_ERROR; /* Forward the control code off to the JVM. */ DWORD controlCode = dwCtrlCode; /* Enclose the contents of this call in a try catch block so we can * display and log useful information should the need arise. */ __try { /* if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("ServiceControlHandlerEx(%d, %d, %p, %p)"), dwCtrlCode, dwEvtType, lpEvtData, lpCntxt); } */ /* This thread appears to always be the same as the main thread. * Just to be safe reregister it. */ logRegisterThread(WRAPPER_THREAD_MAIN); if (dwCtrlCode == SERVICE_CONTROL_POWEREVENT) { switch (dwEvtType) { case PBT_APMQUERYSUSPEND: /* 0x0 */ /* system is hiberating * send off power resume event */ if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_POWEREVENT(PBT_APMQUERYSUSPEND)")); } controlCode = 0x0D00; break; case PBT_APMQUERYSUSPENDFAILED: /* 0x2 */ /* system is waking up * send off power resume event */ if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_POWEREVENT(PBT_APMQUERYSUSPENDFAILED)")); } controlCode = 0x0D02; break; case PBT_APMSUSPEND:/* 0x4 */ /* system is waking up * send off power resume event */ if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_POWEREVENT(PBT_APMSUSPEND)")); } controlCode = 0x0D04; break; case PBT_APMRESUMECRITICAL: /* 0x6 */ /* system is waking up * send off power resume event */ if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_POWEREVENT(PBT_APMRESUMECRITICAL)")); } controlCode = 0x0D06; break; case PBT_APMRESUMESUSPEND: /* 0x7 */ /* system is waking up * send off power resume event */ if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_POWEREVENT(PBT_APMRESUMESUSPEND)")); } controlCode = 0x0D07; break; case PBT_APMBATTERYLOW: /* 0x9 */ /* batter is low warning. */ if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_POWEREVENT(PBT_APMBATTERYLOW)")); } controlCode = 0x0D09; break; case PBT_APMPOWERSTATUSCHANGE: /* 0xA */ /* the status of system power changed. */ if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_POWEREVENT(PBT_APMPOWERSTATUSCHANGE)")); } controlCode = 0x0D0A; break; case PBT_APMOEMEVENT: /* 0xB */ /* there was an OEM event. */ if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_POWEREVENT(PBT_APMOEMEVENT)")); } controlCode = 0x0D0B; break; case PBT_APMRESUMEAUTOMATIC: /* 0x12 */ /* system is waking up */ if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_POWEREVENT(PBT_APMRESUMEAUTOMATIC)")); } controlCode = 0x0D12; break; /* The following STANDBY values do not appear to be used but are defined in WinUser.h. */ /*case PBT_APMQUERYSTANDBY:*/ /* 0x1 */ /*case PBT_APMQUERYSTANDBYFAILED:*/ /* 0x3 */ /*case PBT_APMSTANDBY:*/ /* 0x5 */ /*case PBT_APMRESUMESTANDBY:*/ /* 0x8 */ default: /* Unexpected generic powerevent code */ if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_POWEREVENT(%d)"), dwEvtType); } break; } } /* Forward the control code off to the JVM. Write the signals into a rotating queue so we can process more than one per loop. */ if ((wrapperData->ctrlCodeQueueWriteIndex == wrapperData->ctrlCodeQueueReadIndex - 1) || ((wrapperData->ctrlCodeQueueWriteIndex == CTRL_CODE_QUEUE_SIZE - 1) && (wrapperData->ctrlCodeQueueReadIndex == 0))) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Control code queue overflow (%d:%d). Dropping control code: %d\n"), wrapperData->ctrlCodeQueueWriteIndex, wrapperData->ctrlCodeQueueReadIndex, controlCode); } else { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Enqueue control code: %d (r:%d w:%d)"), controlCode, wrapperData->ctrlCodeQueueReadIndex, wrapperData->ctrlCodeQueueWriteIndex); #endif wrapperData->ctrlCodeQueue[wrapperData->ctrlCodeQueueWriteIndex] = controlCode; wrapperData->ctrlCodeQueueWriteIndex++; if (wrapperData->ctrlCodeQueueWriteIndex >= CTRL_CODE_QUEUE_SIZE) { wrapperData->ctrlCodeQueueWriteIndex = 0; wrapperData->ctrlCodeQueueWrapped = TRUE; } } switch(dwCtrlCode) { case SERVICE_CONTROL_PAUSE: if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_PAUSE")); } wrapperData->ctrlCodePauseTrapped = TRUE; break; case SERVICE_CONTROL_CONTINUE: if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_CONTINUE")); } wrapperData->ctrlCodeContinueTrapped = TRUE; break; case SERVICE_CONTROL_STOP: if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_STOP")); } wrapperData->ctrlCodeStopTrapped = TRUE; break; case SERVICE_CONTROL_INTERROGATE: if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_INTERROGATE")); } /* This case MUST be processed, even though we are not */ /* obligated to do anything substantial in the process. */ break; case SERVICE_CONTROL_POWEREVENT: // we handled it if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_POWEREVENT (handled)")); } break; #ifdef SUPPORT_PRESHUTDOWN case SERVICE_CONTROL_PRESHUTDOWN: if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_PRESHUTDOWN")); } wrapperData->ctrlCodePreShutdownTrapped = TRUE; break; #endif case SERVICE_CONTROL_SHUTDOWN: if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_SHUTDOWN")); } wrapperData->ctrlCodeShutdownTrapped = TRUE; break; default: if ((wrapperData->threadDumpControlCode > 0) && (dwCtrlCode == wrapperData->threadDumpControlCode)) { if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_(%d) Request Thread Dump."), dwCtrlCode); } wrapperData->ctrlCodeDumpTrapped = TRUE; } else { /* Any other cases... Did not handle */ if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT(" SERVICE_CONTROL_(%d) Not handled."), dwCtrlCode); } result = ERROR_CALL_NOT_IMPLEMENTED; } break; } /* After invocation of this function, we MUST call the SetServiceStatus */ /* function, which is accomplished through our ReportStatus function. We */ /* must do this even if the current status has not changed. */ wrapperReportStatus(TRUE, wrapperData->wState, 0, 0); } __except (exceptionFilterFunction(GetExceptionInformation())) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("<-- Wrapper Stopping due to error in service control handler.")); appExit(wrapperData->errorExitCode); } return result; } /** * The service control handler is called by the service manager when there are * events for the service. registered using a call to * RegisterServiceCtrlHandler in wrapperServiceMain. */ void WINAPI wrapperServiceControlHandler(DWORD dwCtrlCode) { /* if (wrapperData->isDebugging) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Service(%d)"), dwCtrlCode); } */ wrapperServiceControlHandlerEx(dwCtrlCode, 0, NULL, NULL); } /** * The wrapperServiceMain function is the entry point for the NT service. * It is called by the service manager. */ void WINAPI wrapperServiceMain(DWORD dwArgc, LPTSTR *lpszArgv) { int timeout; /* Enclose the contents of this call in a try catch block so we can * display and log useful information should the need arise. */ __try { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("wrapperServiceMain()")); #endif /* Immediately register this thread with the logger. */ logRegisterThread(WRAPPER_THREAD_SRVMAIN); /* Call RegisterServiceCtrlHandler immediately to register a service control */ /* handler function. The returned SERVICE_STATUS_HANDLE is saved with global */ /* scope, and used as a service id in calls to SetServiceStatus. */ if (OptionalRegisterServiceCtrlHandlerEx) { /* Use RegisterServiceCtrlHandlerEx if available. */ sshStatusHandle = OptionalRegisterServiceCtrlHandlerEx( wrapperData->serviceName, wrapperServiceControlHandlerEx, (LPVOID)1); } else { sshStatusHandle = RegisterServiceCtrlHandler( wrapperData->serviceName, wrapperServiceControlHandler); } if (!sshStatusHandle) { goto finally; } /* The global ssStatus SERVICE_STATUS structure contains information about the */ /* service, and is used throughout the program in calls made to SetStatus through */ /* the ReportStatus function. */ ssStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ssStatus.dwServiceSpecificExitCode = 0; /* Do setup now that the service is initialized. */ /* Initialize the invocation mutex as necessary, exit if it already exists. */ if (initInvocationMutex()) { appExit(wrapperData->exitCode); return; /* For clarity. */ } /* Get the current process. */ wrapperData->wrapperProcess = GetCurrentProcess(); wrapperData->wrapperPID = GetCurrentProcessId(); if (checkPidFile()) { /* The pid file exists and we are strict, so exit (cleanUpPIDFilesOnExit has not been turned on yet, so we will exit without cleaning the pid files). */ appExit(wrapperData->errorExitCode); return; /* For clarity. */ } /* From now on: * - all pid files will be cleaned when the Wrapper exits, * - any existing file will be owerwritten. */ cleanUpPIDFilesOnExit = TRUE; if (wrapperWriteStartupPidFiles()) { appExit(wrapperData->errorExitCode); return; /* For clarity. */ } /* If we could guarantee that all initialization would occur in less than one */ /* second, we would not have to report our status to the service control manager. */ /* For good measure, we will assign SERVICE_START_PENDING to the current service */ /* state and inform the service control manager through our ReportStatus function. */ if (wrapperData->startupTimeout > 0) { timeout = wrapperData->startupTimeout * 1000; } else { timeout = 86400000; // Set infinity at 1 day. } /* Before entering the main loop, it makes sens to use wrapperData->startupTimeout instead of wrapperData->ntStartupWaitHint. */ wrapperReportStatus(FALSE, WRAPPER_WSTATE_STARTING, 0, timeout); /* Now actually start the service */ wrapperRunService(); finally: /* Report that the service has stopped and set the correct exit code. */ wrapperReportStatus(FALSE, WRAPPER_WSTATE_STOPPED, wrapperData->exitCode, 1000); #ifdef _DEBUG /* The following message will not always appear on the screen if the STOPPED * status was set above. But the code in the appExit function below always * appears to be getting executed. Looks like some kind of a timing issue. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Exiting service process.")); #endif /* Actually exit the process, returning the current exit code. */ appExit(wrapperData->exitCode); } __except (exceptionFilterFunction(GetExceptionInformation())) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("<-- Wrapper Stopping due to error in service main.")); appExit(wrapperData->errorExitCode); } } /** * Reads a password from the console and then returns it as a malloced string. * This is only called once so the memory can leak. */ TCHAR *readPassword() { TCHAR *buffer; TCHAR c; int cnt = 0; buffer = malloc(sizeof(TCHAR) * 65); if (!buffer) { outOfMemory(TEXT("RP"), 1); appExit(0); return NULL; } buffer[0] = 0; do { c = _gettch(); switch (c) { case 0x03: /* Ctrl-C */ _tprintf(TEXT("\n") ); appExit(0); break; case 0x08: /* Backspace */ if (cnt > 0) { _tprintf(TEXT("%c %c"), 0x08, 0x08); cnt--; buffer[cnt] = 0; } break; case 0x00: /* Arrow key. */ case 0xe0: /* Skip the next character as well. */ _gettch(); break; case 0x0d: /* CR */ case 0x0a: /* LF */ /* Done */ break; default: if (cnt < 64) { /* For now, ignore any non-standard ascii characters. */ if ((c >= 0x20) && (c < 0x7f)) { if (wrapperData->ntServicePasswordPromptMask) { _tprintf(TEXT("*")); } else { _tprintf(TEXT("%c"), c); } buffer[cnt] = c; buffer[cnt + 1] = 0; cnt++; } } break; } /*printf("(%02x)", c);*/ } while ((c != 0x0d) && (c != 0x0a)); _tprintf(TEXT("\n")); return buffer; } /** * RETURNS TRUE if the current Windows OS supports SHA-2 code-signning certificates */ static BOOL isSHA2CertificateSupported(int strict) { OSVERSIONINFOEX osver; osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); #pragma warning(push) #pragma warning(disable : 4996) /* Visual Studio 2013 deprecates GetVersionEx but we still want to use it. */ if (GetVersionEx((LPOSVERSIONINFO)&osver) && osver.dwPlatformId == VER_PLATFORM_WIN32_NT) { if ((osver.dwMajorVersion > 6) || ((osver.dwMajorVersion == 6) && (osver.dwMinorVersion >= 1))) { /* Windows 7 and Windows Server 2008 R2 have full SHA-2 support */ return TRUE; } else if (!strict) { /* Partial support */ if ((osver.dwMajorVersion >= 6 || /* Windows Vista/Windows Server 2008 and higher */ (osver.dwMajorVersion == 5 && osver.dwMinorVersion == 1 && osver.wServicePackMajor == 3))) { /* Windows XP SP3 (there is no SP3 and thus no SHA-2 support for Win XP 64-bit), Windows server 2003 is also not supported. */ return TRUE; } } } #pragma warning(pop) return FALSE; } /** * RETURNS TRUE if the current Windows OS is Windows 10 or higher... */ BOOL isWin10OrHigher() { OSVERSIONINFO osver; osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); #pragma warning(push) #pragma warning(disable : 4996) /* Visual Studio 2013 deprecates GetVersionEx but we still want to use it. */ if (GetVersionEx(&osver) && osver.dwPlatformId == VER_PLATFORM_WIN32_NT && osver.dwMajorVersion >= 10) { return TRUE; } #pragma warning(pop) return FALSE; } /** * RETURNS TRUE if the current Windows OS is Windows Vista or later... */ BOOL isVista() { OSVERSIONINFO osver; osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); #pragma warning(push) #pragma warning(disable : 4996) /* Visual Studio 2013 deprecates GetVersionEx but we still want to use it. */ if (GetVersionEx(&osver) && osver.dwPlatformId == VER_PLATFORM_WIN32_NT && osver.dwMajorVersion >= 6) { return TRUE; } #pragma warning(pop) return FALSE; } /** * RETURNS TRUE if the current Windows OS is Windows XP or later... */ BOOL isWinXP() { OSVERSIONINFO osver; osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); #pragma warning(push) #pragma warning(disable : 4996) /* Visual Studio 2013 deprecates GetVersionEx but we still want to use it. */ if (GetVersionEx(&osver) && osver.dwPlatformId == VER_PLATFORM_WIN32_NT) { if (osver.dwMajorVersion > 5 || osver.dwMajorVersion == 5 && osver.dwMinorVersion >= 1) { return TRUE; } } #pragma warning(pop) return FALSE; } BOOL isSecondary() { return (getStringProperty(properties, TEXT("wrapper.internal.namedpipe"), NULL) != NULL) ? TRUE : FALSE; } BOOL isElevated() { TOKEN_ELEVATION te = {0}; BOOL bIsElevated = FALSE; HRESULT hResult = E_FAIL; // assume an error occurred HANDLE hToken = NULL; DWORD dwReturnLength = 0; if (isVista()) { if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken)) { return bIsElevated ; } if (!GetTokenInformation(hToken, TokenElevation, &te, sizeof(te), &dwReturnLength)) { ; } else { hResult = te.TokenIsElevated ? S_OK : S_FALSE; bIsElevated = (te.TokenIsElevated != 0); } CloseHandle(hToken); return bIsElevated; } else { return TRUE; } } void wrapperCheckForMappedDrives() { TCHAR **propertyNames; TCHAR **propertyValues; long unsigned int *propertyIndices; int i; int advice = 0; if (!wrapperData->ntServiceAccount) { advice = wrapperGetUNCFilePath(getFileSafeStringProperty(properties, TEXT("wrapper.logfile"), TEXT("wrapper.log")), advice); advice = wrapperGetUNCFilePath(getFileSafeStringProperty(properties, TEXT("wrapper.logfile.purge.pattern"), TEXT("")), advice); advice = wrapperGetUNCFilePath(getFileSafeStringProperty(properties, TEXT("wrapper.pidfile"), NULL), advice); advice = wrapperGetUNCFilePath(getFileSafeStringProperty(properties, TEXT("wrapper.java.pidfile"), NULL), advice); advice = wrapperGetUNCFilePath(getFileSafeStringProperty(properties, TEXT("wrapper.lockfile"), NULL), advice); advice = wrapperGetUNCFilePath(getFileSafeStringProperty(properties, TEXT("wrapper.java.idfile"), NULL), advice); advice = wrapperGetUNCFilePath(getFileSafeStringProperty(properties, TEXT("wrapper.statusfile"), NULL), advice); advice = wrapperGetUNCFilePath(getFileSafeStringProperty(properties, TEXT("wrapper.java.statusfile"), NULL), advice); advice = wrapperGetUNCFilePath(getFileSafeStringProperty(properties, TEXT("wrapper.commandfile"), NULL), advice); advice = wrapperGetUNCFilePath(getFileSafeStringProperty(properties, TEXT("wrapper.anchorfile"), NULL), advice); i = 0; if (getStringProperties(properties, TEXT("wrapper.java.library.path."), TEXT(""), wrapperData->ignoreSequenceGaps, FALSE, &propertyNames, &propertyValues, &propertyIndices)) { /* Failed */ return ; } while (propertyNames[i]) { if (propertyValues[i]) { advice = wrapperGetUNCFilePath(propertyValues[i], advice); i++; } } i = 0; if (getStringProperties(properties, TEXT("wrapper.java.classpath."), TEXT(""), wrapperData->ignoreSequenceGaps, FALSE, &propertyNames, &propertyValues, &propertyIndices)) { /* Failed */ return ; } while (propertyNames[i]) { if (propertyValues[i]) { advice = wrapperGetUNCFilePath(propertyValues[i], advice); i++; } } } } /** * Generates the full binary path to register with the service manager when * installing a service. * * @param buffer Buffer that will hold the binaryPath. If NULL, the required * length will be calculated and stored in reqBufferSize * @param reqBufferSize Pointer to an int that will store the required length in character * of the buffer that was used or is required. * * @return 0 if succeeded. */ int buildServiceBinaryPath(TCHAR *buffer, size_t *reqBufferLen) { DWORD moduleFileNameSize; TCHAR *moduleFileName; DWORD usedLen; TCHAR drive[4]; TCHAR* uncTempBuffer; DWORD uncSize; int pathMapped; int pathMapFailed = FALSE; UNIVERSAL_NAME_INFO* unc; int i; int k; size_t originalSize; if (reqBufferLen) { originalSize = *reqBufferLen; } else { originalSize = 0; } /* We will calculate the size used. */ if (buffer) { buffer[0] = TEXT('\0'); } *reqBufferLen = 1; /* Get the full path and filename of this program. Need to loop to make sure we get it all. */ moduleFileNameSize = 0; moduleFileName = NULL; do { moduleFileNameSize += 100; moduleFileName = malloc(sizeof(TCHAR) * moduleFileNameSize); if (!moduleFileName) { outOfMemory(TEXT("BSBP"), 1); return 1; } /* On Windows XP and 2000, GetModuleFileName will return exactly "moduleFileNameSize" and * leave moduleFileName in an unterminated state in the event that the module file name is too long. * Newer versions of Windows will set the error code to ERROR_INSUFFICIENT_BUFFER but we can't rely on that. */ /* Important : For win XP getLastError() is unchanged if the buffer is too small, so if we don't reset the last error first, we may actually test an old pending error. */ SetLastError(ERROR_SUCCESS); usedLen = GetModuleFileName(NULL, moduleFileName, moduleFileNameSize); if (usedLen == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to resolve the full Wrapper path - %s"), getLastErrorText()); return 1; } else if ((usedLen == moduleFileNameSize) || (getLastError() == ERROR_INSUFFICIENT_BUFFER)) { /* Buffer too small. Loop again. */ free(moduleFileName); moduleFileName = NULL; } } while (!moduleFileName); /* Always start with the full path to the binary. */ /* If the moduleFileName contains spaces, it needs to be quoted */ /* Resolve to UNC-Name if we are on a mapped drive */ if ((_tcslen(moduleFileName) >= 3) && (moduleFileName[1] == TEXT(':')) && (moduleFileName[2] == TEXT('\\'))) { _tcsncpy(drive, moduleFileName, 3); drive[3] = TEXT('\0'); } else { drive[0] = TEXT('\0'); } pathMapped = FALSE; if ((drive[0] != TEXT('\0')) && (GetDriveType(drive) == DRIVE_REMOTE)) { /* The Wrapper binary is located on a Network Drive. Try to resolve the original Universal path. We need to get a buffer big enough. */ uncSize = 0; moduleFileNameSize = 100; do{ uncTempBuffer = malloc((moduleFileNameSize) * sizeof(TCHAR)); if (!uncTempBuffer) { outOfMemory(TEXT("BSBP"), 2); return 1; } unc = (UNIVERSAL_NAME_INFO *) uncTempBuffer; k = WNetGetUniversalName(moduleFileName, UNIVERSAL_NAME_INFO_LEVEL, unc, &moduleFileNameSize); if (k == ERROR_MORE_DATA) { free(uncTempBuffer); } } while (k == ERROR_MORE_DATA); uncSize = moduleFileNameSize; if (k != NO_ERROR) { if (buffer) { /* Otherwise logged on the next pass. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to resolve Universal Path of mapped network path: %s (%s)"), moduleFileName, getLastErrorText()); } pathMapFailed = TRUE; } else { /* Now we know the size. Create the unc buffer. */ if (_tcschr(unc->lpUniversalName, TEXT(' ')) == NULL) { if (buffer) { _tcsncat(buffer, unc->lpUniversalName, originalSize); } *reqBufferLen += _tcslen(unc->lpUniversalName); } else { if (buffer) { _tcsncat(buffer, TEXT("\""), originalSize); _tcsncat(buffer, unc->lpUniversalName, originalSize); _tcsncat(buffer, TEXT("\""), originalSize); } *reqBufferLen += (1 + _tcslen(unc->lpUniversalName) + 1); } pathMapped = TRUE; free(uncTempBuffer); } } if (!pathMapped) { if (_tcschr(moduleFileName, TEXT(' ')) == NULL) { if (buffer) { _tcsncat(buffer, moduleFileName, originalSize); } *reqBufferLen += _tcslen(moduleFileName); } else { if (buffer) { _tcsncat(buffer, TEXT("\""), originalSize); _tcsncat(buffer, moduleFileName, originalSize); _tcsncat(buffer, TEXT("\""), originalSize); } *reqBufferLen += (1 + _tcslen(moduleFileName) + 1); } } free(moduleFileName); /* Next write the command to start the service. */ if (buffer) { _tcsncat(buffer, TEXT(" -s "), originalSize); } *reqBufferLen += 4; /* Third, the configuration file. */ /* If the wrapperData->configFile contains spaces, it needs to be quoted */ /* Try to convert the config file to a UNC path as well. */ if (!wrapperData->configFile) { if (buffer) { _tcsncat(buffer, TEXT("-"), originalSize); } *reqBufferLen += 1; } else { if ((_tcslen(wrapperData->configFile) >= 3) && (wrapperData->configFile[1] == TEXT(':')) && (wrapperData->configFile[2] == TEXT('\\'))) { _tcsncpy(drive, wrapperData->configFile, 3); drive[3] = TEXT('\0'); } else { drive[0] = TEXT('\0'); } pathMapped = FALSE; if ((drive[0] != TEXT('\0')) && (GetDriveType(drive) == DRIVE_REMOTE)) { /* The Wrapper config file is located on a Network Drive. Try to resolve the original Universal path. We need to get a buffer big enough. */ moduleFileNameSize = 100; uncSize = 0; do { uncTempBuffer = malloc((moduleFileNameSize) * sizeof(TCHAR)); if (!uncTempBuffer) { outOfMemory(TEXT("BSBP"), 3); return 1; } unc = (UNIVERSAL_NAME_INFO *) uncTempBuffer; k = WNetGetUniversalName(wrapperData->configFile, UNIVERSAL_NAME_INFO_LEVEL, unc, &moduleFileNameSize); if (k == ERROR_MORE_DATA) { free(uncTempBuffer); } } while (k == ERROR_MORE_DATA); if (k != NO_ERROR) { if (buffer) { /* Otherwise logged on the next pass. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to resolve Universal Path of mapped network path: %s (%s)"), wrapperData->configFile, getLastErrorText()); } pathMapFailed = TRUE; } else { /* Now we know the size. Create the unc buffer. */ if (_tcschr(unc->lpUniversalName, TEXT(' ')) == NULL) { if (buffer) { _tcsncat(buffer, unc->lpUniversalName, originalSize); } *reqBufferLen += _tcslen(unc->lpUniversalName); } else { if (buffer) { _tcsncat(buffer, TEXT("\""), originalSize); _tcsncat(buffer, unc->lpUniversalName, originalSize); _tcsncat(buffer, TEXT("\""), originalSize); } *reqBufferLen += (1 + _tcslen(unc->lpUniversalName) + 1); } pathMapped = TRUE; free(uncTempBuffer); unc = NULL; } } if (!pathMapped) { if (_tcschr(wrapperData->configFile, TEXT(' ')) == NULL) { if (buffer) { _tcsncat(buffer, wrapperData->configFile, originalSize); } *reqBufferLen += _tcslen(wrapperData->configFile); } else { if (buffer) { _tcsncat(buffer, TEXT("\""), originalSize); _tcsncat(buffer, wrapperData->configFile, originalSize); _tcsncat(buffer, TEXT("\""), originalSize); } *reqBufferLen += (1 + _tcslen(wrapperData->configFile) + 1); } } if (pathMapFailed) { if (buffer) { /* Otherwise logged on the next pass. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("There were problems converting mapped network paths the Universal Path format. This may cause the service to fail to start now or when the system is rebooted.")); } } } i = 0; if ((wrapperData->argCount >= 1) && (_tcsstr(wrapperData->argValues[0], TEXT("wrapper.internal.namedpipe=")) != NULL)) { /* This property is used by wrapperm. It should not be copied to the service command line. */ i++; } /* All other arguments need to be appended as is. */ for (; i < wrapperData->argCount; i++) { /* For security reasons, skip the wrapper.ntservice.account and * wrapper.ntservice.password properties if they are declared on the * command line. They will not be needed once the service is * installed. Having them in the registry would be an obvious * security leak. */ if ((_tcsstr(wrapperData->argValues[i], TEXT("wrapper.ntservice.account")) == NULL) && (_tcsstr(wrapperData->argValues[i], TEXT("wrapper.ntservice.password")) == NULL)) { if (buffer) { _tcsncat(buffer, TEXT(" "), originalSize); } *reqBufferLen += 1; /* If the argument contains spaces, it needs to be quoted */ if (_tcschr(wrapperData->argValues[i], TEXT(' ')) == NULL) { if (buffer) { _tcsncat(buffer, wrapperData->argValues[i], originalSize); } *reqBufferLen += _tcslen(wrapperData->argValues[i]); } else { if (buffer) { _tcsncat(buffer, TEXT("\""), originalSize); _tcsncat(buffer, wrapperData->argValues[i], originalSize); _tcsncat(buffer, TEXT("\""), originalSize); } *reqBufferLen += 1 + _tcslen(wrapperData->argValues[i]) + 1; } } } /* If there are any passthrough variables. Then they also need to be appended as is. */ if (wrapperData->javaArgValueCount > 0) { if (buffer) { _tcsncat(buffer, TEXT(" --"), originalSize); } *reqBufferLen += 3; for (i = 0; i < wrapperData->javaArgValueCount; i++) { if (buffer) { _tcsncat(buffer, TEXT(" "), originalSize); } *reqBufferLen += 1; /* If the argument contains spaces, it needs to be quoted */ if (_tcschr(wrapperData->javaArgValues[i], TEXT(' ')) == NULL) { if (buffer) { _tcsncat(buffer, wrapperData->javaArgValues[i], originalSize); } *reqBufferLen += _tcslen(wrapperData->javaArgValues[i]); } else { if (buffer) { _tcsncat(buffer, TEXT("\""), originalSize); _tcsncat(buffer, wrapperData->javaArgValues[i], originalSize); _tcsncat(buffer, TEXT("\""), originalSize); } *reqBufferLen += (1 + _tcslen(wrapperData->javaArgValues[i]) + 1); } } } return 0; } #ifndef STATUS_SUCCESS #define STATUS_SUCCESS ((NTSTATUS)0x00000000L) #endif void InitLsaString(PLSA_UNICODE_STRING LsaString, LPWSTR String) { size_t StringLength; if (String == NULL) { LsaString->Buffer = NULL; LsaString->Length = 0; LsaString->MaximumLength = 0; return; } StringLength = wcslen(String); LsaString->Buffer = String; LsaString->Length = (USHORT) StringLength * sizeof(WCHAR); LsaString->MaximumLength=(USHORT)(StringLength+1) * sizeof(WCHAR); } NTSTATUS OpenPolicy(LPWSTR ServerName, DWORD DesiredAccess, PLSA_HANDLE PolicyHandle) { LSA_OBJECT_ATTRIBUTES ObjectAttributes; LSA_UNICODE_STRING ServerString; PLSA_UNICODE_STRING Server = NULL; ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes)); if (ServerName != NULL) { InitLsaString(&ServerString, ServerName); Server = &ServerString; } return LsaOpenPolicy(Server, &ObjectAttributes, DesiredAccess, PolicyHandle); } /** * Checks if pc is part of Domain, workgroup or standalone. * * @returns 1 if it's part of Domain, 2 for workgroup, 3 for stand alone, 0 if there was an error */ int checkDomain() { LSA_HANDLE PolicyHandle; NTSTATUS status; PPOLICY_PRIMARY_DOMAIN_INFO ppdiDomainInfo; PWKSTA_INFO_100 pwkiWorkstationInfo; DWORD netret; wchar_t* ResName; int ret = 0; netret = NetWkstaGetInfo(NULL, 100, (LPBYTE *)&pwkiWorkstationInfo); #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("checkDomain: NetWkstaGetInfo returned %d"), netret); #endif if (netret == NERR_Success) { status = OpenPolicy(NULL, GENERIC_READ | POLICY_VIEW_LOCAL_INFORMATION, &PolicyHandle); #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("checkDomain: OpenPolicy returned %d\n"), status); #endif if (!status) { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("checkDomain: LsaQueryInformationPolicy call ahead")); #endif status = LsaQueryInformationPolicy(PolicyHandle, PolicyPrimaryDomainInformation, &ppdiDomainInfo); #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("checkDomain: LsaQueryInformationPolicy returned %d"), status); #endif if (!status) { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("checkDomain: LsaQueryInformationPolicy:ppdiDomainInfo->maxlen = %d, len=%d, buffer=%s, strlen=%d"), ppdiDomainInfo->Name.MaximumLength,ppdiDomainInfo->Name.Length ,ppdiDomainInfo->Name.Buffer, wcslen(ppdiDomainInfo->Name.Buffer)); #endif ResName = malloc((wcslen(ppdiDomainInfo->Name.Buffer) + 1 ) * sizeof(wchar_t)); if (ResName) { _tcsncpy(ResName, ppdiDomainInfo->Name.Buffer, wcslen(ppdiDomainInfo->Name.Buffer) + 1); if (ppdiDomainInfo->Sid) { ret = 1; } else { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("checkDomain: comparing %s vs. %s"), ResName, pwkiWorkstationInfo->wki100_computername); #endif if (_tcsncmp(ResName, pwkiWorkstationInfo->wki100_computername, wcslen(pwkiWorkstationInfo->wki100_computername))) { ret = 2; } else { ret = 3; } } free(ResName); } LsaFreeMemory((LPVOID)ppdiDomainInfo); } LsaClose(PolicyHandle); } NetApiBufferFree(pwkiWorkstationInfo); } return ret; } /** * Helperfunction which gets the Security Policy Handle of the specified system * @param referencedDomainName, the system of which the Security Policy Handle should get retrieved * * @return the Handle of the Security Policy, NULL in case of any error */ LSA_HANDLE wrapperGetPolicyHandle(TCHAR* referencedDomainName) { NTSTATUS ntsResult; LSA_HANDLE lsahPolicyHandle; int k; k = checkDomain(); #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("checkDomain returns %d."), k); #endif if (k > 0) { if (k > 1) { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("OpenPolicy call %s "), referencedDomainName); #endif ntsResult = OpenPolicy(referencedDomainName, /* Name of the target system. */ POLICY_LOOKUP_NAMES | POLICY_CREATE_ACCOUNT, /* Desired access permissions. */ &lsahPolicyHandle); /*Receives the policy handle. */ #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("OpenPolicy returns %d."), ntsResult); #endif } else { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("OpenPolicy call NULL.")); #endif ntsResult = OpenPolicy(NULL, /* Name of the target system. */ POLICY_LOOKUP_NAMES | POLICY_CREATE_ACCOUNT, /* Desired access permissions. */ &lsahPolicyHandle); /*Receives the policy handle. */ #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("OpenPolicy returns %d."), ntsResult); #endif } if (ntsResult != STATUS_SUCCESS) { /* An error occurred. Display it as a win32 error code. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("OpenPolicy failed %lu"), LsaNtStatusToWinError(ntsResult)); return NULL; } } return lsahPolicyHandle; } /** * Helper function which gets the SID and domain of a given account name. * * @param lpszAccountName, the account namespace * @param referencedDomainName, output buffer for the domain * * @return the SID of the account, 0 in case of any error */ PSID wrapperLookupName(LPCTSTR lpszAccountName, WCHAR **referencedDomainName) { PSID Sid; DWORD cbReferencedDomainName, cbSid, lastError; SID_NAME_USE eUse; LPCTSTR formattedAccountName; #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("lookupname: %s"), lpszAccountName); #endif if (_tcsstr(lpszAccountName, TEXT(".\\")) == lpszAccountName) { formattedAccountName = lpszAccountName + 2; } else { formattedAccountName= lpszAccountName; } #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("lookupname:formatedname %s"), formattedAccountName); #endif cbReferencedDomainName = cbSid = 0; if (LookupAccountName(NULL, formattedAccountName, NULL, &cbSid, NULL, &cbReferencedDomainName, &eUse)) { /* A straight success - that can't be... */ return 0; } lastError = GetLastError(); if (lastError != ERROR_INSUFFICIENT_BUFFER) { /* Any error except the one above is fatal.. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to lookup the account (%s): %d - %s"), lpszAccountName, lastError, getLastErrorText()); return 0; } #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("lookupname:cbSID %d ; cbDomain %d"), cbSid, cbReferencedDomainName); #endif if (!(Sid = (PSID)malloc(cbSid))) { outOfMemory(TEXT("WLN"), 1); return 0; } *referencedDomainName = (LPTSTR)calloc((cbReferencedDomainName ), sizeof(TCHAR)); if (!(*referencedDomainName)) { LocalFree(Sid); outOfMemory(TEXT("WLN"), 2); return 0; } if (!LookupAccountName(NULL, formattedAccountName, Sid, &cbSid, *referencedDomainName, &cbReferencedDomainName, &eUse)) { free(*referencedDomainName); free(Sid); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to lookup the account (%s): %d - %s"), lpszAccountName, lastError, getLastErrorText()); return 0; } #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("lookupname:cbreferencedDomain %s"), *referencedDomainName); #endif return Sid; } #define PRIVILEGE_UNKNOWN -1 #define PRIVILEGE_NOT_GRANTED 0 #define PRIVILEGE_GRANTED 1 /** * This functions checks if the account has a privilege for the given security policy. * * @param PolicyHandle A handle to a Policy object. * @param AccountSID Pointer to the SID of the account. * @param privilege Name of the privilege. * * @return TRUE if the account has the privilege, FALSE otherwise. */ int wrapperAccountHasPrivilege(LSA_HANDLE PolicyHandle, PSID AccountSID, const TCHAR* privilege) { PLSA_UNICODE_STRING userRights = NULL; ULONG countOfRights = 0; ULONG i; NTSTATUS ntsResult; int result; ntsResult = LsaEnumerateAccountRights(PolicyHandle, /* An open policy handle. */ AccountSID, /* The target SID. */ &userRights, /* The privileges. */ &countOfRights); /* Number of privileges. */ if (ntsResult == STATUS_SUCCESS) { result = PRIVILEGE_NOT_GRANTED; for (i = 0; i < countOfRights; i++) { if (_tcscmp(userRights[i].Buffer, privilege) == 0) { result = PRIVILEGE_GRANTED; break; } } } else if (LsaNtStatusToWinError(ntsResult) == SCESTATUS_RECORD_NOT_FOUND) { /* no privilege found (this is normal) */ result = PRIVILEGE_NOT_GRANTED; } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to enumerate the account permissions: %lu"), LsaNtStatusToWinError(ntsResult)); result = PRIVILEGE_UNKNOWN; } if (userRights) { LsaFreeMemory(userRights); } return result; } /** * This functions adds the 'Log on as a service' privileges to the user account * * @param PolicyHandle A handle to a Policy object. * @param AccountSID Pointer to the SID of the account. * * @return FALSE if successful, TRUE otherwise */ BOOL wrapperAddPrivileges(LSA_HANDLE PolicyHandle, PSID AccountSID) { PLSA_UNICODE_STRING pointer; NTSTATUS ntsResult; ULONG counter = 1; WCHAR privileges[] = SE_SERVICE_LOGON_NAME; int retVal = TRUE; /* Create an LSA_UNICODE_STRING for the privilege names. */ pointer = malloc(sizeof(LSA_UNICODE_STRING)); if (pointer == NULL) { outOfMemory(TEXT("WAP"), 1); } else { InitLsaString(pointer, privileges); ntsResult = LsaAddAccountRights(PolicyHandle, /* An open policy handle. */ AccountSID, /* The target SID. */ pointer, /* The privileges. */ counter); /* Number of privileges. */ free(pointer); if (ntsResult == STATUS_SUCCESS) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Successfully added 'Log on as a service' permission.")); retVal = FALSE; } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to add 'Log on as a service' permission: %lu"), LsaNtStatusToWinError(ntsResult)); } } return retVal; } /** * This functions removes the 'Log on as a service' privileges from the user account * * @param PolicyHandle A handle to a Policy object. * @param AccountSID Pointer to the SID of the account. * * @return FALSE if successful, TRUE otherwise */ BOOL wrapperRemovePrivileges(LSA_HANDLE PolicyHandle, PSID AccountSID) { PLSA_UNICODE_STRING pointer; NTSTATUS ntsResult; ULONG counter = 1; WCHAR privileges[] = SE_SERVICE_LOGON_NAME; int retVal = TRUE; /* Create an LSA_UNICODE_STRING for the privilege names. */ pointer = malloc(sizeof(LSA_UNICODE_STRING)); if (pointer == NULL) { outOfMemory(TEXT("WAP"), 1); } else { InitLsaString(pointer, privileges); ntsResult = LsaRemoveAccountRights(PolicyHandle, /* An open policy handle. */ AccountSID, /* The target SID. */ FALSE, /* AllRights */ pointer, /* The privileges. */ counter); /* Number of privileges. */ free(pointer); if (ntsResult == STATUS_SUCCESS) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Successfully removed 'Log on as a service' permission.")); retVal = FALSE; } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to remove 'Log on as a service' permission: %lu"), LsaNtStatusToWinError(ntsResult)); } } return retVal; } static int setupSyslogRegistration(int silent) { int result = 0; if (getSyslogRegister()) { /* don't even check if the registration was made, force installation in case some key or value were out of date. */ if (!silent) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Registering to the Event Log system...")); } result = registerSyslogMessageFile(TRUE, FALSE); } else if (!silent) { disableSysLog(TRUE); /* it can be useful to deactivate the registration from the configuration file, especially if the setup include more tasks in the future. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Do not register to the Event Log because the property wrapper.syslog.ident.enable is set to FALSE.")); } return result; } static int teardownSyslogRegistration(int silent) { int result = 0; /* always make sure to clean the registry when calling teardown. */ if (syslogMessageFileRegistered(FALSE)) { if (!silent) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Unregistering from the Event Log system...")); } /* set the syslog level to NONE to avoid a warning in disableSysLog(). */ setSyslogLevelInt(LEVEL_NONE); result = unregisterSyslogMessageFile(FALSE); } else if (!silent) { disableSysLog(TRUE); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("%s was not registered to the Event Log system."), getSyslogEventSourceName()); } return result; } /** * Setup the Wrapper * Execute installation tasks that require to be elevated. * * @param silent can be used to skip INFO messages (errors & warnings will still be shown). * * Returns 1 if there were any problems. */ int wrapperSetup(int silent) { int result = setupSyslogRegistration(silent); /* more setup actions can be added here. */ if ((result == 0) && (!silent)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Setup done successfully.")); } return result; } /** * Teardown the Wrapper * Execute deletion tasks that require to be elevated. * * @param silent can be used to skip INFO messages (errors & warnings will still be shown). * * Returns 1 if there were any problems. */ int wrapperTeardown(int silent) { int result = teardownSyslogRegistration(silent); /* more teardown actions can be added here. */ if ((result == 0) && (!silent)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Teardown done successfully.")); } return result; } /** * Get the trustee SID based on its name. * * @param name The trustee name. * @param ppSid pointer to a variable that receives a pointer to the SID to look up. */ void getTrusteeSidFromName(const TCHAR* trusteeName, PSID *ppSid) { DWORD sidSize = 0; TCHAR domainName[512]; DWORD domainNameSize = 0; SID_NAME_USE sidType; PSID pSid; TCHAR* pTrusteeName = (TCHAR*)trusteeName; if (_tcsstr(trusteeName, TEXT(".\\")) == trusteeName) { /* '.\' is used for the local computer, but LookupAccountName() will fail to retrieve the SID when it's present, so skip it. */ pTrusteeName += 2; } LookupAccountName(NULL, pTrusteeName, NULL, &sidSize, NULL, &domainNameSize, &sidType); pSid = malloc(sidSize); if (!pSid) { outOfMemory(TEXT("GTSFN"), 1); } else if (LookupAccountName(NULL, pTrusteeName, pSid, &sidSize, domainName, &domainNameSize, &sidType)) { *ppSid = pSid; } } /*#define DEBUG_SERVICE_ACCOUNT*/ #define UNMANAGED_SERVICE_ACCOUNT 0 #define MANAGED_SERVICE_ACCOUNT 1 #define GROUP_MANAGED_SERVICE_ACCOUNT 2 #define VIRTUAL_SERVICE_ACCOUNT 3 #define NETWORK_SERVICE_ACCOUNT 4 #define LOCAL_SERVICE_ACCOUNT 5 #define LOCAL_SYSTEM_ACCOUNT 6 /** * Get the type of a service account. * * @param account the account name to verify. * @param serviceName the name of the service (used only for virtual accounts). * * @return one of the following constants representing the account type: * UNMANAGED_SERVICE_ACCOUNT * MANAGED_SERVICE_ACCOUNT * GROUP_MANAGED_SERVICE_ACCOUNT * VIRTUAL_SERVICE_ACCOUNT * NETWORK_SERVICE_ACCOUNT * LOCAL_SERVICE_ACCOUNT * LOCAL_SYSTEM_ACCOUNT */ static int getServiceAccountType(const TCHAR* account, const TCHAR* serviceName) { PSID pSid = NULL; TCHAR* str; size_t len; int result = UNMANAGED_SERVICE_ACCOUNT; IADs *pObject; HRESULT hr; BSTR retval; TCHAR* stringSid; TCHAR ldapPath[SECURITY_MAX_SID_SIZE + 14]; if (!account) { /* LocalSystem is the default account when NULL is specified. */ return LOCAL_SYSTEM_ACCOUNT; } /* Try to get the SID of the account. */ getTrusteeSidFromName(account, &pSid); if (IsValidSid(pSid)) { #ifdef DEBUG_SERVICE_ACCOUNT if (ConvertSidToStringSid(pSid, &stringSid)) { _tprintf(TEXT("Account '%s' has SID '%s'.\n"), account, stringSid); LocalFree((HLOCAL)stringSid); } #endif /* Check if this is a well-known account. */ if (IsWellKnownSid(pSid, WinLocalSystemSid)) { #ifdef DEBUG_SERVICE_ACCOUNT _tprintf(TEXT("Account '%s' is the built-in LocalSystem service account.\n"), account); #endif result = LOCAL_SYSTEM_ACCOUNT; } else if (IsWellKnownSid(pSid, WinLocalServiceSid)) { #ifdef DEBUG_SERVICE_ACCOUNT _tprintf(TEXT("Account '%s' is the built-in LocalService service account.\n"), account); #endif result = LOCAL_SERVICE_ACCOUNT; } else if (IsWellKnownSid(pSid, WinNetworkServiceSid)) { #ifdef DEBUG_SERVICE_ACCOUNT _tprintf(TEXT("Account '%s' is the built-in NetworkService service account.\n"), account); #endif result = NETWORK_SERVICE_ACCOUNT; } else { /* Check if this is a (group-)managed service account. This is dome using COM objects. */ if (ConvertSidToStringSid(pSid, &stringSid)) { hr = CoInitialize(NULL); if (!FAILED(hr)) { _sntprintf(ldapPath, SECURITY_MAX_SID_SIZE + 14, TEXT("LDAP://"), stringSid); hr = ADsGetObject(ldapPath, &IID_IADsUser, (void**) &pObject); if (SUCCEEDED(hr)) { pObject->lpVtbl->get_Class(pObject, &retval); /* Check that the class contains 'ManagedServiceAccount'. */ if (_tcsstr(retval, TEXT("GroupManagedServiceAccount"))) { #ifdef DEBUG_SERVICE_ACCOUNT _tprintf(TEXT("Account '%s' is a group-managed service account.\n"), account); #endif result = GROUP_MANAGED_SERVICE_ACCOUNT; } else if (_tcsstr(retval, TEXT("ManagedServiceAccount"))) { #ifdef DEBUG_SERVICE_ACCOUNT _tprintf(TEXT("Account '%s' is a managed service account.\n"), account); #endif result = MANAGED_SERVICE_ACCOUNT; } SysFreeString(retval); pObject->lpVtbl->Release(pObject); } CoUninitialize(); } LocalFree((HLOCAL)stringSid); } } } else { #ifdef DEBUG_SERVICE_ACCOUNT _tprintf(TEXT("Failed to retrieve SID for account '%s'. %s\n"), account, getLastErrorText()); #endif /* We can't use getTrusteeSidFromName() to retrieve the SID of the LocalSystem account, so use a string comparison (space is optional, case-insensitive). */ str = _tcschr(account, TEXT('\\')); if (str && (_tcslen(str) > 1) && ((strcmpIgnoreCase(str + 1, TEXT("LocalSystem")) == 0) || (strcmpIgnoreCase(str + 1, TEXT("Local System")) == 0))) { #ifdef DEBUG_SERVICE_ACCOUNT _tprintf(TEXT("LocalSystem account.\n")); #endif result = LOCAL_SYSTEM_ACCOUNT; } else { /* A virtual account doesn't exist until the service is created. It is established in the format NT SERVICE\. */ len = 11 + _tcslen(serviceName) + 1; str = malloc(sizeof(TCHAR) * len); if (!str) { outOfMemory(TEXT("ISSA"), 2); } else { _sntprintf(str, len, TEXT("NT SERVICE\\%s"), serviceName); } if (strcmpIgnoreCase(account, str) == 0) { #ifdef DEBUG_SERVICE_ACCOUNT _tprintf(TEXT("Account '%s' is a virtual service account.\n"), account); #endif result = VIRTUAL_SERVICE_ACCOUNT; } free(str); } } return result; } static const TCHAR* getServiceAccountTypeStr(const TCHAR* account, const TCHAR* serviceName) { switch (getServiceAccountType(account, serviceName)) { case UNMANAGED_SERVICE_ACCOUNT: return TEXT("Unmanaged"); case MANAGED_SERVICE_ACCOUNT: return TEXT("Standalone managed"); case GROUP_MANAGED_SERVICE_ACCOUNT: return TEXT("Group-managed"); case VIRTUAL_SERVICE_ACCOUNT: return TEXT("Virtual"); case NETWORK_SERVICE_ACCOUNT: return TEXT("Network Service"); case LOCAL_SERVICE_ACCOUNT: return TEXT("Local Service"); case LOCAL_SYSTEM_ACCOUNT: return TEXT("Local System"); default: return TEXT("Unknown"); } } /** * Install the Wrapper as an NT Service using the information and service * name in the current configuration file. * * Stores the parameters with the service name so that the wrapper.conf file * can be located at runtime. */ int wrapperInstall() { SC_HANDLE schService; SC_HANDLE schSCManager; DWORD serviceType; DWORD startType; DWORD accountType; #ifdef SUPPORT_PRESHUTDOWN SERVICE_PRESHUTDOWN_INFO preShutdownInfo; #endif size_t binaryPathLen; TCHAR *binaryPath; int result = 0; HKEY hKey; TCHAR regPath[ 1024 ]; TCHAR domain[ 1024 ]; TCHAR account[ 1024 ]; TCHAR *tempAccount; TCHAR *ntServicePassword = NULL; DWORD dsize = 1024, dwDesiredAccess; HANDLE hToken = NULL; LPCWSTR lpszUsername; LPCWSTR lpszDomain; DWORD error; size_t len; int exit = FALSE; int logOnAsServiceAdded = FALSE; int logOnAsServiceOriginalStatus = PRIVILEGE_UNKNOWN; LSA_HANDLE PolicyHandle; PSID AccountSID = NULL; TCHAR *referencedDomainName; /* Initialization */ dwDesiredAccess = 0; /* Before prompting any info, check if the service is already installed. */ if (wrapperServiceStatus(wrapperData->serviceName, wrapperData->serviceDisplayName, FALSE) & 0x1) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to install the %s service - %s"), wrapperData->serviceDisplayName, getErrorText(ERROR_SERVICE_EXISTS, NULL)); return 1; } /* Generate the service binary path. We need to figure out how big the buffer needs to be. */ if (buildServiceBinaryPath(NULL, &binaryPathLen)) { /* Failed a reason should have been given. But show result. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to install the %s service"), wrapperData->serviceDisplayName); return 1; } binaryPath = malloc(binaryPathLen * sizeof(TCHAR)); if (!binaryPath) { outOfMemory(TEXT("WI"), 1); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to install the %s service"), wrapperData->serviceDisplayName); return 1; } if (buildServiceBinaryPath(binaryPath, &binaryPathLen)) { /* Failed a reason should have been given. But show result. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to install the %s service"), wrapperData->serviceDisplayName); free(binaryPath); return 1; } if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Service command: %s"), binaryPath); } if (wrapperData->ntServicePrompt) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Prompting for account (DOMAIN\\ACCOUNT)...")); _tprintf(TEXT("Please input the domain name [%s]: "), wrapperData->domainName); if (isElevated() && isSecondary()) { _tprintf(TEXT("n")); fflush(NULL); } _fgetts(domain, dsize, stdin); if (!domain || _tcscmp(domain, TEXT("\n")) == 0) { _sntprintf(domain, dsize, TEXT("%s"), wrapperData->domainName); } else if (domain[_tcslen(domain) - 1] == TEXT('\n')) { domain[_tcslen(domain) - 1] = TEXT('\0'); } _tprintf(TEXT("Please input the account name [%s]: "), wrapperData->userName); if (isElevated() && isSecondary()) { _tprintf(TEXT("n")); fflush(NULL); } _fgetts(account, dsize, stdin); if (!account || _tcscmp(account, TEXT("\n")) == 0) { _sntprintf(account, dsize, TEXT("%s"), wrapperData->userName); } else if (account[_tcslen(account) - 1] == TEXT('\n')) { account[_tcslen(account) - 1] = TEXT('\0'); } tempAccount = malloc((_tcslen(domain) + _tcslen(account) + 2) * sizeof(TCHAR)); if (!tempAccount) { outOfMemory(TEXT("WI"), 2); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to install the %s service"), wrapperData->serviceDisplayName); free(binaryPath); return 1; } _sntprintf(tempAccount, _tcslen(domain) + _tcslen(account) + 2, TEXT("%s\\%s"), domain, account); updateStringValue(&wrapperData->ntServiceAccount, tempAccount); free(tempAccount); } if (wrapperData->ntServiceAccount) { /* Check that the account is correctly formatted and retrieve the domain and username. */ lpszUsername = _tcschr(wrapperData->ntServiceAccount, TEXT('\\')); if (lpszUsername && _tcslen(lpszUsername) > 1) { lpszUsername++; len = _tcslen(wrapperData->ntServiceAccount) - _tcslen(lpszUsername) - 1; if ((len < 1) || (len > 1023)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Invalid format for the account name '%s'. Please use {Domain}\\{UserName} or .\\{UserName}."), wrapperData->ntServiceAccount); free(binaryPath); return 1; } else if ((len == 1) && (lpszUsername[0] == TEXT('.'))) { lpszDomain = NULL; } else { _tcsncpy(domain, wrapperData->ntServiceAccount, len); domain[len] = 0; lpszDomain = domain; } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Invalid format for the account name '%s'. Please use {Domain}\\{UserName} or .\\{UserName}."), wrapperData->ntServiceAccount); free(binaryPath); return 1; } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Account type: %s"), getServiceAccountTypeStr(wrapperData->ntServiceAccount, wrapperData->serviceName)); if (wrapperData->ntServicePasswordPrompt) { /* Prompt the user for a password. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Prompting for account password...")); _tprintf(TEXT("Please input the password for account '%s': "), wrapperData->ntServiceAccount); if (isElevated() && isSecondary()) { _tprintf(TEXT("p")); fflush(NULL); /* as this here is from the secondary instance we can read with _fgetts */ wrapperData->ntServicePassword = calloc(65, sizeof(TCHAR)); if (!wrapperData->ntServicePassword) { outOfMemory(TEXT("WI"), 3); free(binaryPath); return 1; } _fgetts(wrapperData->ntServicePassword, 65, stdin); } else { wrapperData->ntServicePassword = readPassword(); } } /* Make sure that an empty length password is null. */ ntServicePassword = wrapperData->ntServicePassword; if ((ntServicePassword != NULL) && (_tcslen(ntServicePassword) <= 0)) { ntServicePassword = NULL; } accountType = getServiceAccountType(wrapperData->ntServiceAccount, wrapperData->serviceName); if (((accountType == VIRTUAL_SERVICE_ACCOUNT) || (accountType == MANAGED_SERVICE_ACCOUNT) || (accountType == GROUP_MANAGED_SERVICE_ACCOUNT)) && ntServicePassword) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The password must not be set for a managed service account or a virtual account.")); } else if (accountType == UNMANAGED_SERVICE_ACCOUNT) { /* Automatically add privilege for the account to log on as a service. * This is not needed (and would actually fail!) for built-in service accounts. * NOTE: Even though 'Local Security Policy' (Administrative Tools) is not available on Windows Home, * this function will actually succeed to add the privilege. */ if (wrapperData->ntServiceAddLogonAsService) { /* Each function, if it fails, will return its own errors. Then LogonUserW() should also fail below and, depending on the error, we will show the appropriate message to the user. */ AccountSID = wrapperLookupName(wrapperData->ntServiceAccount, &referencedDomainName); if (AccountSID) { if ((PolicyHandle = wrapperGetPolicyHandle(referencedDomainName)) != NULL) { logOnAsServiceOriginalStatus = wrapperAccountHasPrivilege(PolicyHandle, AccountSID, SE_SERVICE_LOGON_NAME); if (logOnAsServiceOriginalStatus != PRIVILEGE_GRANTED) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Adding 'Log on as a service' privilege to %s. Original status: %s"), wrapperData->ntServiceAccount, logOnAsServiceOriginalStatus == PRIVILEGE_NOT_GRANTED ? TEXT("not granted") : TEXT("unknown")); if (!wrapperAddPrivileges(PolicyHandle, AccountSID)) { logOnAsServiceAdded = TRUE; } } } free(referencedDomainName); } } /* Try to authenticate in order to detect any logon failure during installation (this must be done after adding privileges!). * This should not be done with build-in service accounts which don't have a password, otherwise an "Access is denied" * error will be returned by LogonUserW(). */ /* NOTE: if the password is NULL or blank, the LogonUserW function will return the following error: * "Account restrictions are preventing this user from signing in. For example: blank passwords aren't allowed, * sign-in times are limited, or a policy restriction has been enforced." * => It is possible (but not recommended!) to bypass this restriction by disabling the following security policy: * Administrative Tools -> Local Security Policies -> Local Policies (left panel) -> Security Options * -> turn "Accounts: Limit local account use of blank passwords to console logon only" to disabled.. */ if (!LogonUserW(lpszUsername, lpszDomain, ntServicePassword, LOGON32_LOGON_SERVICE, LOGON32_PROVIDER_DEFAULT, &hToken)) { error = GetLastError(); exit = getBooleanProperty(properties, TEXT("wrapper.ntservice.authentication_strict"), TRUE); switch(error) { case 0x569: /* No 'Log on as a service' privilege. * This can be fixed after installation by changing the security policy of the system. Print a warning and continue. */ if ((logOnAsServiceOriginalStatus != PRIVILEGE_GRANTED) && !logOnAsServiceAdded) { /* Ask the user to add the privilege manually. Is there a way to detect the setting is available? */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Authentication attempt for '%s' failed - %s\n Please manually add '%s' to the list of accounts allowed to 'Log on as a service' in your Security Policy Settings.\n Depending on your version of Windows, this setting might not be available."), lpszUsername, getErrorText(error, NULL), wrapperData->ntServiceAccount); } else { /* This may happen if there is a 'Deny log on as a service' security policy for the account. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Authentication attempt for '%s' failed - %s\n Make sure that the account is not prevented from logging on as a service ('Deny log on as a service' policy setting)."), lpszUsername, getErrorText(error, NULL)); } exit = FALSE; break; case 0x52E: /* The user name or password is incorrect. */ log_printf(WRAPPER_SOURCE_WRAPPER, exit ? LEVEL_FATAL : LEVEL_DEBUG, TEXT("Authentication attempt for '%s' failed - %s"), lpszUsername, getErrorText(error, NULL)); if ((wrapperData->ntServicePasswordPrompt) && ((GetKeyState(VK_CAPITAL) & 0x0001) != 0)) { log_printf(WRAPPER_SOURCE_WRAPPER, exit ? LEVEL_WARN : LEVEL_DEBUG, TEXT(" Caps Lock is On!"), getErrorText(error, NULL)); } break; case 0x52F: /* "Account restrictions are preventing this user from signing in...." (see above). * Log the error but don't advise to bypass the restriction as this would apply to all services * and remote accounts, which is a security concern! */ log_printf(WRAPPER_SOURCE_WRAPPER, exit ? LEVEL_FATAL : LEVEL_DEBUG, TEXT("Authentication attempt for '%s' failed - %s"), lpszUsername, getErrorText(error, NULL)); break; default: log_printf(WRAPPER_SOURCE_WRAPPER, exit ? LEVEL_FATAL : LEVEL_DEBUG, TEXT("Authentication attempt for '%s' failed - %s"), lpszUsername, getErrorText(error, NULL)); } } if (hToken) { CloseHandle(hToken); } if (exit) { if ((logOnAsServiceOriginalStatus == PRIVILEGE_NOT_GRANTED) && logOnAsServiceAdded) { wrapperRemovePrivileges(PolicyHandle, AccountSID); } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s service not installed."), wrapperData->serviceDisplayName); if (PolicyHandle) { LsaClose(PolicyHandle); } free(AccountSID); wrapperSecureFreeStrW(wrapperData->ntServicePassword); wrapperData->ntServicePassword = NULL; free(binaryPath); return 1; } } } /* Decide on the service type */ if (wrapperData->ntServiceInteractive) { serviceType = SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS; } else { serviceType = SERVICE_WIN32_OWN_PROCESS; } /* Next, get a handle to the service control manager */ schSCManager = OpenSCManager( NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE ); if (schSCManager) { startType = wrapperData->ntServiceStartType; if (result != 1) { #ifdef SUPPORT_PRESHUTDOWN if (wrapperData->ntPreshutdown) { dwDesiredAccess |= SERVICE_CHANGE_CONFIG; } #endif schService = CreateService(schSCManager, /* SCManager database */ wrapperData->serviceName, /* name of service */ wrapperData->serviceDisplayName, /* name to display */ dwDesiredAccess, /* desired access */ serviceType, /* service type */ startType, /* start type */ SERVICE_ERROR_NORMAL, /* error control type */ binaryPath, /* service's binary */ wrapperData->ntServiceLoadOrderGroup, /* load ordering group */ NULL, /* tag identifier not used because they are used for driver level services. */ wrapperData->ntServiceDependencies, /* dependencies */ wrapperData->ntServiceAccount, /* LocalSystem account if NULL */ ntServicePassword); /* NULL or empty for no password */ if (wrapperData->ntServicePassword) { /* Clear the password from memory */ wrapperSecureFreeStrW(wrapperData->ntServicePassword); wrapperData->ntServicePassword = NULL; } if (schService) { /* Have the service, add a description to the registry. */ _sntprintf(regPath, 1024, TEXT("SYSTEM\\CurrentControlSet\\Services\\%s"), wrapperData->serviceName); if ((wrapperData->serviceDescription != NULL && _tcslen(wrapperData->serviceDescription) > 0) && (RegOpenKeyEx(HKEY_LOCAL_MACHINE, regPath, 0, KEY_WRITE, (PHKEY) &hKey) == ERROR_SUCCESS)) { /* Set Description key in registry */ RegSetValueEx(hKey, TEXT("Description"), (DWORD) 0, (DWORD) REG_SZ, (LPBYTE)wrapperData->serviceDescription, (int)(sizeof(TCHAR) * (_tcslen(wrapperData->serviceDescription) + 1))); RegCloseKey(hKey); } #ifdef SUPPORT_PRESHUTDOWN if (result == 0 && wrapperData->ntPreshutdown) { preShutdownInfo.dwPreshutdownTimeout = wrapperData->ntPreshutdownTimeout * 1000; /* Lets always update the preshutdown timeout as future versions of Windows might use a different default value. */ if (!ChangeServiceConfig2(schService, SERVICE_CONFIG_PRESHUTDOWN_INFO, &preShutdownInfo)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to set the preshutdown timeout of the %s service. - %s"), wrapperData->serviceDisplayName, getLastErrorText()); wrapperRemove(); result = 1; } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Set the preshutdown timeout of the %s service to %d secs."), wrapperData->serviceDisplayName, wrapperData->ntPreshutdownTimeout); } #endif if (result !=1) { /* Service was installed. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s service installed."), wrapperData->serviceDisplayName); } /* Close the handle to this service object */ CloseServiceHandle(schService); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to install the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); result = 1; } } /* Close the handle to the service control manager database */ CloseServiceHandle(schSCManager); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to install the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); if (isVista() && !isElevated()) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Performing this action requires that you run as an elevated process.")); } result = 1; } if ((result == 1) && (logOnAsServiceOriginalStatus == PRIVILEGE_NOT_GRANTED) && logOnAsServiceAdded) { wrapperRemovePrivileges(PolicyHandle, AccountSID); } if (wrapperData->ntServicePassword) { /* Clear the password from memory (if not done yet) */ wrapperSecureFreeStrW(wrapperData->ntServicePassword); wrapperData->ntServicePassword = NULL; } if (PolicyHandle) { LsaClose(PolicyHandle); } free(AccountSID); free(binaryPath); return result; } void closeRegistryKey(HKEY hKey) { LONG result; LPSTR pBuffer = NULL; result = RegCloseKey(hKey); if (result != ERROR_SUCCESS) { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, result, 0, (LPTSTR)&pBuffer, 0, NULL); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to close the registry: %d : %s"), result, pBuffer); LocalFree(pBuffer); } } /** * Sets any environment variables stored in the system registry to the current * environment. The NT service environment only has access to the environment * variables set when the machine was last rebooted. This makes it possible * to access the latest values in registry without a reboot. * * Note that this function is always called before the configuration file has * been loaded this means that any logging that takes place will be sent to * the default log file which may be difficult for the user to locate. * * Return TRUE if there were any problems. */ int wrapperLoadEnvFromRegistryInner(HKEY baseHKey, const TCHAR *regPath, int appendPath, int source) { LONG result; LPSTR pBuffer = NULL; int envCount = 0; int ret; HKEY hKey; DWORD dwIndex; DWORD valueCount; DWORD maxValueNameLength; DWORD maxValueLength; TCHAR *valueName; TCHAR *value; DWORD thisValueNameLength; DWORD thisValueLength; DWORD thisValueType; const TCHAR *oldVal; TCHAR *newVal; BOOL expanded; /* NOTE - Any log output here will be placed in the default log file as it happens * before the wrapper.conf is loaded. */ /* Open the registry entry where the current environment variables are stored. */ result = RegOpenKeyEx(baseHKey, regPath, 0, KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, (PHKEY)&hKey); if (result == ERROR_SUCCESS) { /* Read in each of the environment variables and set them into the environment. * These values will be set as is without doing any environment variable * expansion. In order for the ExpandEnvironmentStrings function to work all * of the environment variables to be replaced must already be set. To handle * this, after we set the values as is from the registry, we need to go back * through all the ones we set and Expand them if necessary. */ /* Query the registry to find out how many values there are as well as info about how * large the values names and data are. */ result = RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL, &valueCount, &maxValueNameLength, &maxValueLength, NULL, NULL); if (result != ERROR_SUCCESS) { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, result, 0, (LPTSTR)&pBuffer, 0, NULL); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to query the registry to get the environment: %d : %s"), result, pBuffer); LocalFree(pBuffer); closeRegistryKey(hKey); return TRUE; } #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Registry contains %d variables. Longest name=%d, longest value=%d"), valueCount, maxValueNameLength, maxValueLength); #endif /* Add space for the null. */ maxValueNameLength++; maxValueLength++; /* Allocate buffers to get the value names and values from the registry. These can * be reused because we are using the setEnv function to store the values into the * environment. setEnv allocates the memory required by the environment. */ valueName = malloc(sizeof(TCHAR) * maxValueNameLength); if (!valueName) { outOfMemory(TEXT("WLEFRI"), 1); closeRegistryKey(hKey); return TRUE; } value = malloc(sizeof(TCHAR) * maxValueLength); if (!valueName) { outOfMemory(TEXT("WLEFRI"), 2); closeRegistryKey(hKey); return TRUE; } /* Loop over the values and load each of them into the local environment as is. */ dwIndex = 0; do { thisValueNameLength = maxValueNameLength; thisValueLength = maxValueLength; result = RegEnumValue(hKey, dwIndex, valueName, &thisValueNameLength, NULL, &thisValueType, (LPBYTE)value, &thisValueLength); if (result == ERROR_SUCCESS) { if ((thisValueType == REG_SZ) || (thisValueType = REG_EXPAND_SZ)) { /* Got a value. */ #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Loaded var name=\"%s\", value=\"%s\""), valueName, value); #endif if (appendPath && (strcmpIgnoreCase(TEXT("path"), valueName) == 0)) { /* The PATH variable is special, it needs to be appended to the existing value. */ oldVal = _tgetenv(TEXT("PATH")); if (oldVal) { newVal = malloc(sizeof(TCHAR) * (_tcslen(oldVal) + 1 + _tcslen(value) + 1)); if (!newVal) { outOfMemory(TEXT("WLEFRI"), 3); closeRegistryKey(hKey); return TRUE; } _sntprintf(newVal, _tcslen(oldVal) + 1 + _tcslen(value) + 1, TEXT("%s;%s"), oldVal, value); if (setEnv(valueName, newVal, source)) { /* Already reported. */ free(newVal); closeRegistryKey(hKey); return TRUE; } #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Appended to existing value: %s=%s"), valueName, newVal); #endif free(newVal); } else { /* Did not exist, set normally. */ if (setEnv(valueName, value, source)) { /* Already reported. */ closeRegistryKey(hKey); return TRUE; } } } else { if (setEnv(valueName, value, source)) { /* Already reported. */ closeRegistryKey(hKey); return TRUE; } } #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Set to local environment.")); #endif } else { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Loaded var name=\"%s\" but type is invalid: %d, skipping."), valueName, thisValueType); #endif } } else if (result = ERROR_NO_MORE_ITEMS) { /* This means we are at the end. Fall through. */ } else { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, result, 0, (LPTSTR)&pBuffer, 0, NULL); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to query the registry to get environment variable #%d: %d : %s"), dwIndex, result, getLastErrorText()); LocalFree(pBuffer); closeRegistryKey(hKey); return TRUE; } dwIndex++; } while (result != ERROR_NO_MORE_ITEMS); #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("All environment variables loaded. Loop back over them to evaluate any nested variables.")); #endif /* Go back and loop over the environment variables we just set and expand any * variables which contain % characters. Loop until we make a pass which does * not perform any replacements. */ do { expanded = FALSE; dwIndex = 0; do { thisValueNameLength = maxValueNameLength; result = RegEnumValue(hKey, dwIndex, valueName, &thisValueNameLength, NULL, &thisValueType, NULL, NULL); if (result == ERROR_SUCCESS) { /* Found an environment variable in the registry. Variables that contain references have a different type. */ if (thisValueType = REG_EXPAND_SZ) { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Get the current local value of variable \"%s\""), valueName); #endif oldVal = _tgetenv(valueName); if (oldVal == NULL) { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" The current local value of variable \"%s\" is null, meaning it was not in the registry. Skipping."), valueName); #endif } else { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" \"%s\"=\"%s\""), valueName, oldVal); #endif if (_tcschr(oldVal, TEXT('%'))) { /* This variable contains tokens which need to be expanded. */ /* Find out how much space is required to store the expanded value. */ ret = ExpandEnvironmentStrings(oldVal, NULL, 0); if (ret == 0) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { /* The ExpandEnvironmentStrings function has an internal 32k size limit. We hit it. * All we can do is skip this particular variable by leaving it unexpanded. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to expand environment variable \"%s\" because the result is larger than the system allowed 32k. Leaving unexpanded and continuing."), valueName); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to expand environment variable \"%s\": %s"), valueName, getLastErrorText()); closeRegistryKey(hKey); return TRUE; } } else { /* Allocate a buffer to hold to the expanded value. */ newVal = malloc(sizeof(TCHAR) * (ret + 2)); if (!newVal) { outOfMemory(TEXT("WLEFRI"), 4); closeRegistryKey(hKey); return TRUE; } /* Actually expand the variable. */ ret = ExpandEnvironmentStrings(oldVal, newVal, ret + 2); if (ret == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to expand environment variable \"%s\" (2): %s"), valueName, getLastErrorText()); free(newVal); closeRegistryKey(hKey); return TRUE; } /* Was anything changed? */ if (_tcscmp(oldVal, newVal) == 0) { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Value unchanged. Referenced environment variable not set.")); #endif } else { /* Set the expanded environment variable */ expanded = TRUE; #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Update local environment variable. \"%s\"=\"%s\""), valueName, newVal); #endif /* Update the environment. */ if (setEnv(valueName, newVal, source)) { /* Already reported. */ free(newVal); closeRegistryKey(hKey); return TRUE; } } free(newVal); } } } } } else if (result == ERROR_NO_MORE_ITEMS) { /* No more environment variables. */ } else { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, result, 0, (LPTSTR)&pBuffer, 0, NULL); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to read registry - %s"), getLastErrorText()); LocalFree(pBuffer); closeRegistryKey(hKey); return TRUE; } dwIndex++; } while (result != ERROR_NO_MORE_ITEMS); #ifdef _DEBUG if (expanded) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Rescan environment variables to varify that there are no more expansions necessary.")); } #endif } while (expanded); #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Done loading environment variables.")); #endif /* Close the registry entry */ closeRegistryKey(hKey); } else { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, result, 0, (LPTSTR)&pBuffer, 0, NULL); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to access registry to obtain environment variables - %s"), getLastErrorText()); LocalFree(pBuffer); return TRUE; } return FALSE; } /** * Loads the environment stored in the registry. * * (Only called for versions of Windows older than Vista or Server 2008.) * * Return TRUE if there were any problems. */ int wrapperLoadEnvFromRegistry() { /* We can't access any properties here as they are not yet loaded when called. */ /* Always load in the system wide variables. */ #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Loading System environment variables from Registry:")); #endif if (wrapperLoadEnvFromRegistryInner(HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\\"), FALSE, ENV_SOURCE_REG_SYSTEM)) { return TRUE; } /* Only load in the user specific variables if the USERNAME environment variable is set. */ if (_tgetenv(TEXT("USERNAME"))) { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Loading Account environment variables from Registry:")); #endif if (wrapperLoadEnvFromRegistryInner(HKEY_CURRENT_USER, TEXT("Environment\\"), TRUE, ENV_SOURCE_REG_ACCOUNT)){ return TRUE; } } return FALSE; } const TCHAR* getBaseKeyName(HKEY baseHKey) { if (baseHKey == HKEY_CLASSES_ROOT) { return TEXT("HKEY_CLASSES_ROOT"); } else if (baseHKey == HKEY_CURRENT_CONFIG) { return TEXT("HKEY_CURRENT_CONFIG"); } else if (baseHKey == HKEY_CURRENT_USER) { return TEXT("HKEY_CURRENT_USER"); } else if (baseHKey == HKEY_LOCAL_MACHINE) { return TEXT("HKEY_LOCAL_MACHINE"); } else if (baseHKey == HKEY_USERS) { return TEXT("HKEY_USERS"); } else { return TEXT(""); } } /** * Gets the JavaHome absolute path from the windows registry * using the location of a specific JRE and the key containing the JavaHome path. */ TCHAR* wrapperGetJavaHomeFromWindowsRegistryUsingJavaHome(HKEY baseHKey, TCHAR* subKey, TCHAR* javahome, int verbose) { HKEY openHKey = NULL; /* Will receive the handle to the opened registry key */ const TCHAR* msg; LONG result; DWORD valueType; DWORD valueSize; TCHAR *value = NULL; /* Opens the Registry Key needed to query the JavaHome */ result = RegOpenKeyEx(baseHKey, subKey, 0, KEY_QUERY_VALUE, &openHKey); if (result != ERROR_SUCCESS) { /* NOTE: on Windows 64-bit, if a 32-bit application tries to access HKLM\SOFTWARE\JavaSoft, * it's actually redirected to HKLM\SOFTWARE\Wow6432Node\JavaSoft. This can be confusing * for the user and it may be worth printing a different message for that case. */ if (verbose) { msg = getErrorText(result, NULL); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to access configured registry location \"%s\\%s\": %s"), getBaseKeyName(baseHKey), subKey, msg); } return NULL; } /* Queries for the JavaHome */ result = RegQueryValueEx(openHKey, javahome, NULL, &valueType, NULL, &valueSize); if (result != ERROR_SUCCESS) { if (verbose) { msg = getErrorText(result, NULL); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to access configured registry location \"%s\\%s\\%s\": %s"), getBaseKeyName(baseHKey), subKey, javahome, msg); } closeRegistryKey(openHKey); return NULL; } if (valueType != REG_SZ) { if (verbose) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Configured registry location \"%s\\%s\\%s\" is not of type REG_SZ."), getBaseKeyName(baseHKey), subKey, javahome); } closeRegistryKey(openHKey); return NULL; } value = malloc(sizeof(TCHAR) * valueSize); if (!value) { outOfMemory(TEXT("WGJFWRUJH"), 1); closeRegistryKey(openHKey); return NULL; } result = RegQueryValueEx(openHKey, javahome, NULL, &valueType, (LPBYTE)value, &valueSize); if (result != ERROR_SUCCESS) { if (verbose) { msg = getErrorText(result, NULL); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to access configured registry location \"%s\\%s\\%s\": %s"), getBaseKeyName(baseHKey), subKey, javahome, msg); } closeRegistryKey(openHKey); free(value); return NULL; } closeRegistryKey(openHKey); return value; } /** * Gets the JavaHome absolute path from the windows registry * using the base location for JREs and a specific version. */ TCHAR* wrapperGetJavaHomeFromWindowsRegistryUsingVersion(TCHAR* subKeyJre, TCHAR* jreversion) { TCHAR subKey[512]; /* Registry subkey that jvm creates when is installed */ _tcsncpy(subKey, subKeyJre, 512); _tcsncat(subKey, TEXT("\\"), 512); _tcsncat(subKey, jreversion, 512); return wrapperGetJavaHomeFromWindowsRegistryUsingJavaHome(HKEY_LOCAL_MACHINE, subKey, TEXT("JavaHome"), FALSE); } /** * Gets the JavaHome absolute path from the windows registry * using the base location for JREs and the 'CurrentVersion'. */ TCHAR* wrapperGetJavaHomeFromWindowsRegistryUsingCurrentVersion(TCHAR* subKeyJre) { HKEY openHKey = NULL; /* Will receive the handle to the opened registry key */ TCHAR jreversion[10]; /* Will receive a registry value that has jvm version */ LONG result; DWORD valueType; DWORD valueSize; /* Opens the Registry Key needed to query the jvm version */ result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, subKeyJre, 0, KEY_QUERY_VALUE, &openHKey); if (result != ERROR_SUCCESS) { return NULL; } /* Queries for the jvm version */ valueSize = sizeof(jreversion); result = RegQueryValueEx(openHKey, TEXT("CurrentVersion"), NULL, &valueType, (LPBYTE)jreversion, &valueSize); if (result != ERROR_SUCCESS) { closeRegistryKey(openHKey); return NULL; } closeRegistryKey(openHKey); return wrapperGetJavaHomeFromWindowsRegistryUsingVersion(subKeyJre, jreversion); } /** * Gets the JavaHome absolute path from the windows registry */ int wrapperGetJavaHomeFromWindowsRegistry(TCHAR *javaHome) { const TCHAR *prop; TCHAR *c; TCHAR subKey[512]; /* Registry subkey that jvm creates when is installed */ TCHAR *valueKey; HKEY baseHKey; TCHAR *value; prop = getStringProperty(properties, TEXT("wrapper.registry.java_home"), NULL); if (prop) { /* A registry location was specified. */ if (_tcsstr(prop, TEXT("HKEY_CLASSES_ROOT\\")) == prop) { baseHKey = HKEY_CLASSES_ROOT; _tcsncpy(subKey, prop + 18, 512); } else if (_tcsstr(prop, TEXT("HKEY_CURRENT_CONFIG\\")) == prop) { baseHKey = HKEY_CURRENT_CONFIG; _tcsncpy(subKey, prop + 20, 512); } else if (_tcsstr(prop, TEXT("HKEY_CURRENT_USER\\")) == prop) { baseHKey = HKEY_CURRENT_USER; _tcsncpy(subKey, prop + 18, 512); } else if (_tcsstr(prop, TEXT("HKEY_LOCAL_MACHINE\\")) == prop) { baseHKey = HKEY_LOCAL_MACHINE; _tcsncpy(subKey, prop + 19, 512); } else if (_tcsstr(prop, TEXT("HKEY_USERS\\")) == prop) { baseHKey = HKEY_USERS; _tcsncpy(subKey, prop + 11, 512); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("wrapper.registry.java_home does not begin with a known root key: %s"), prop); return 0; } /* log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("subKey=%s"), subKey); */ /* We need to split the value from the key. Find the last \ */ c = _tcsrchr(subKey, TEXT('\\')); if (!c) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("wrapper.registry.java_home is an invalid key: %s"), prop); return 0; } valueKey = c + 1; /* Truncate the subKey. */ *c = TEXT('\0'); /*log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("subKey=%s valueKey=%s"), subKey, valueKey); */ value = wrapperGetJavaHomeFromWindowsRegistryUsingJavaHome(baseHKey, subKey, valueKey, TRUE); } else { /* Look for the java_home in the default location (it is different for Java 9+ compared to older versions). */ /* There can be a 'CurrentVersion' in each location, but give priority to recent JREs by first trying to search in the new location. */ value = wrapperGetJavaHomeFromWindowsRegistryUsingCurrentVersion(TEXT("SOFTWARE\\JavaSoft\\JRE")); if (!value) { value = wrapperGetJavaHomeFromWindowsRegistryUsingCurrentVersion(TEXT("SOFTWARE\\JavaSoft\\Java Runtime Environment")); } } if (value) { /* Returns the JavaHome path */ _tcsncpy(javaHome, value, 512); free(value); return 1; } return 0; } TCHAR *getNTServiceStatusName(int status) { TCHAR *name; switch(status) { case SERVICE_STOPPED: name = TEXT("STOPPED"); break; case SERVICE_START_PENDING: name = TEXT("START_PENDING"); break; case SERVICE_STOP_PENDING: name = TEXT("STOP_PENDING"); break; case SERVICE_RUNNING: name = TEXT("RUNNING"); break; case SERVICE_CONTINUE_PENDING: name = TEXT("CONTINUE_PENDING"); break; case SERVICE_PAUSE_PENDING: name = TEXT("PAUSE_PENDING"); break; case SERVICE_PAUSED: name = TEXT("PAUSED"); break; default: name = TEXT("UNKNOWN"); break; } return name; } /** Starts a Wrapper instance running as an NT Service. */ int wrapperStartService() { SC_HANDLE schService; SC_HANDLE schSCManager; SERVICE_STATUS serviceStatus; const TCHAR *path; TCHAR wrapperFullPath[FILEPATHSIZE] = TEXT(""); TCHAR logFileFullPath[FILEPATHSIZE] = TEXT(""); TCHAR defaultLogFileFullPath[FILEPATHSIZE] = TEXT(""); TCHAR *status; int msgCntr; int stopping; int result; int errorCode; /* Wrapper binary. */ path = wrapperData->argBinary; result = GetFullPathName(path, FILEPATHSIZE, wrapperFullPath, NULL); if (result >= FILEPATHSIZE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("The full path of %s is too large. (%d)"), path, result); _tcsncpy(wrapperFullPath, path, FILEPATHSIZE); } else if (result == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to resolve the full path of %s : %s"), path, getLastErrorText()); _tcsncpy(wrapperFullPath, path, FILEPATHSIZE); } /* Log file path. */ path = getLogfilePath(); if (_tcslen(path) > 0) { /* The log file may have been set to an empty value to disable it. */ result = GetFullPathName(path, FILEPATHSIZE, logFileFullPath, NULL); if (result >= FILEPATHSIZE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("The full path of %s is too large. (%d)"), path, result); _tcsncpy(logFileFullPath, path, FILEPATHSIZE); } else if (result == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to resolve the full path of %s : %s"), path, getLastErrorText()); _tcsncpy(logFileFullPath, path, FILEPATHSIZE); } } /* Default Log file path. */ path = getDefaultLogfilePath(); result = GetFullPathName(path, FILEPATHSIZE, defaultLogFileFullPath, NULL); if (result >= FILEPATHSIZE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("The full path of %s is too large. (%d)"), path, result); _tcsncpy(defaultLogFileFullPath, path, FILEPATHSIZE); } else if (result == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to resolve the full path of %s : %s"), path, getLastErrorText()); _tcsncpy(defaultLogFileFullPath, path, FILEPATHSIZE); } result = 0; /* First, get a handle to the service control manager */ schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (schSCManager) { /* Next get the handle to this service... */ schService = OpenService(schSCManager, wrapperData->serviceName, WRAPPER_SERVICE_START); if (schService) { if (QueryServiceStatus(schService, &serviceStatus)) { /* Make sure that the service is not already started. */ if (serviceStatus.dwCurrentState == SERVICE_STOPPED) { /* The service is stopped, so try starting it. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Starting the %s service..."), wrapperData->serviceDisplayName); if (StartService(schService, 0, NULL)) { /* We will get here immediately if the service process was launched. * We still need to wait for it to actually start. */ msgCntr = 0; stopping = FALSE; do { if (QueryServiceStatus(schService, &serviceStatus)) { if (serviceStatus.dwCurrentState == SERVICE_STOP_PENDING) { if (!stopping) { stopping = TRUE; msgCntr = 5; /* Trigger a message */ } if (msgCntr >= 5) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Stopping...")); msgCntr = 0; } } else { if (msgCntr >= 5) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Waiting to start...")); msgCntr = 0; } } wrapperSleep(1000); msgCntr++; } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to query the status of the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); result = 1; break; } } while ((serviceStatus.dwCurrentState != SERVICE_STOPPED) && (serviceStatus.dwCurrentState != SERVICE_RUNNING) && (serviceStatus.dwCurrentState != SERVICE_PAUSED)); /* Was the service started? */ if (serviceStatus.dwCurrentState == SERVICE_RUNNING) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s service started."), wrapperData->serviceDisplayName); } else if (serviceStatus.dwCurrentState == SERVICE_PAUSED) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s service started but immediately paused.."), wrapperData->serviceDisplayName); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The %s service was launched, but failed to start."), wrapperData->serviceDisplayName); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Please check the log file for more information: %s"), logFileFullPath); result = 1; } } else { errorCode = GetLastError(); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to start the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); switch (errorCode) { case ERROR_ACCESS_DENIED: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("--------------------------------------------------------------------") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("Advice:" )); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("Usually when the Windows Service Manager does not have access to the Wrapper\nbinary, it is caused by a file permission problem preventing the user running\nthe Wrapper from accessing it. Please check the permissions on the file and\nits parent directories." )); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" Wrapper Binary : %s"), wrapperFullPath); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" Account : %s"), wrapperData->ntServiceAccount); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("--------------------------------------------------------------------") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("") ); break; case ERROR_SERVICE_REQUEST_TIMEOUT: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("--------------------------------------------------------------------") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("Advice:" )); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("Usually when the Windows Service Manager times out waiting for the Wrapper\nprocess to launch it is caused by a file permission problem preventing the\nWrapper from reading its configuration file and/or writing to its log file.\nPlease check the permissions on both files and their parent directories.\nIf there are no messages in either the configured or default log file, please\nalso check the Windows Event Log." )); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" Configuration File : %s"), wrapperData->argConfFile); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" Configured Log File : %s" ), logFileFullPath); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" Default Log File : %s" ), defaultLogFileFullPath); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("--------------------------------------------------------------------") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("") ); break; default: break; } result = 1; } } else { status = getNTServiceStatusName(serviceStatus.dwCurrentState); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The %s service is already started with status: %s"), wrapperData->serviceDisplayName, status); result = 1; } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to query the status of the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); result = 1; } /* Close this service object's handle to the service control manager */ CloseServiceHandle(schService); } else { if (GetLastError() == ERROR_ACCESS_DENIED) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to start the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); if (isVista() && !isElevated()) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Performing this action requires that you run as an elevated process.")); } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The %s service is not installed - %s"), wrapperData->serviceDisplayName, getLastErrorText()); } result = 1; } /* Finally, close the handle to the service control manager's database */ CloseServiceHandle(schSCManager); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to start the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); result = 1; } return result; } /** Stops a Wrapper instance running as an NT Service. */ int wrapperStopService(int command) { SC_HANDLE schService; SC_HANDLE schSCManager; SERVICE_STATUS serviceStatus; TCHAR *status; int msgCntr; int result = 0; /* First, get a handle to the service control manager */ schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (schSCManager) { /* Next get the handle to this service... */ schService = OpenService(schSCManager, wrapperData->serviceName, WRAPPER_SERVICE_STOP); if (schService) { /* Find out what the current status of the service is so we can decide what to do. */ if (QueryServiceStatus(schService, &serviceStatus)) { if (serviceStatus.dwCurrentState == SERVICE_STOPPED) { if (command) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s service was not started."), wrapperData->serviceDisplayName); } } else { if (serviceStatus.dwCurrentState == SERVICE_STOP_PENDING) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s service was already in the process of stopping."), wrapperData->serviceDisplayName); } else { /* Stop the service. */ if (ControlService(schService, SERVICE_CONTROL_STOP, &serviceStatus)) { if (command) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Stopping the %s service..."), wrapperData->serviceDisplayName); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Service is running. Stopping it...")); } } else { if (serviceStatus.dwCurrentState == SERVICE_START_PENDING) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s service was in the process of starting. Stopping it..."), wrapperData->serviceDisplayName); } else { status = getNTServiceStatusName(serviceStatus.dwCurrentState); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Attempt to stop the %s service failed. Status: %s"), wrapperData->serviceDisplayName, status); result = 1; } } } if (result == 0) { /* Wait for the service to stop. */ msgCntr = 0; do { if (QueryServiceStatus(schService, &serviceStatus)) { if (msgCntr >= 5) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Waiting to stop...")); msgCntr = 0; } wrapperSleep(1000); msgCntr++; } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to query the status of the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); result = 1; break; } } while (serviceStatus.dwCurrentState != SERVICE_STOPPED); if (serviceStatus.dwCurrentState == SERVICE_STOPPED) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s service stopped."), wrapperData->serviceDisplayName); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to stop the %s service."), wrapperData->serviceDisplayName); result = 1; } } } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to query the status of the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); result = 1; } /* Close this service object's handle to the service control manager */ CloseServiceHandle(schService); } else { if (GetLastError() == ERROR_ACCESS_DENIED) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to stop the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); if (isVista() && !isElevated()) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Performing this action requires that you run as an elevated process.")); } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The %s service is not installed - %s"), wrapperData->serviceDisplayName, getLastErrorText()); } result = 1; } /* Finally, close the handle to the service control manager's database */ CloseServiceHandle(schSCManager); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to stop the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); result = 1; } return result; } /** Pauses a Wrapper instance running as an NT Service. */ int wrapperPauseService() { SC_HANDLE schService; SC_HANDLE schSCManager; SERVICE_STATUS serviceStatus; TCHAR *status; int msgCntr; int result = 0; int ignore = FALSE; /* First, get a handle to the service control manager */ schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (schSCManager) { /* Next get the handle to this service... */ schService = OpenService(schSCManager, wrapperData->serviceName, WRAPPER_SERVICE_PAUSE_CONTINUE); if (schService) { /* Make sure that the service is in a state that can be paused. */ if (QueryServiceStatus(schService, &serviceStatus)) { if (serviceStatus.dwCurrentState == SERVICE_STOPPED) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s service was not started."), wrapperData->serviceDisplayName); result = 1; } else if (serviceStatus.dwCurrentState == SERVICE_STOP_PENDING) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s service was in the process of stopping."), wrapperData->serviceDisplayName); result = 1; } else if (serviceStatus.dwCurrentState == SERVICE_PAUSE_PENDING) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s service was in the process of being paused."), wrapperData->serviceDisplayName); } else if (serviceStatus.dwCurrentState == SERVICE_PAUSED) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s service was already paused."), wrapperData->serviceDisplayName); ignore = TRUE; } else { /* The service is started, starting, or resuming, so try pausing it. */ if (ControlService(schService, SERVICE_CONTROL_PAUSE, &serviceStatus)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Pausing the %s service..."), wrapperData->serviceDisplayName); } else if (!wrapperData->pausable) { status = getNTServiceStatusName(serviceStatus.dwCurrentState); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The %s service is not allowed to be paused. Status: %s"), wrapperData->serviceDisplayName, status); if (wrapperData->isAdviserEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("--------------------------------------------------------------------") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("Advice:" )); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("To be able to pause the service, please set 'wrapper.pausable=TRUE'\nand restart it." ), wrapperData->serviceDisplayName); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("--------------------------------------------------------------------") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("") ); } result = 1; } else { status = getNTServiceStatusName(serviceStatus.dwCurrentState); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Attempt to pause the %s service failed. Status: %s"), wrapperData->serviceDisplayName, status); result = 1; if (wrapperData->isAdviserEnabled && strcmpIgnoreCase(status, TEXT("running")) == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("--------------------------------------------------------------------") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("Advice:" )); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("The reason may be that the service was not restarted after setting\n'wrapper.pausable' to TRUE." ), wrapperData->serviceDisplayName); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("--------------------------------------------------------------------") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("") ); } } } if ((!ignore) && (result == 0)) { /* Wait for the service to pause. */ msgCntr = 0; do { if (QueryServiceStatus(schService, &serviceStatus)) { if (msgCntr >= 5) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Waiting to pause...")); msgCntr = 0; } wrapperSleep(1000); msgCntr++; } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to query the status of the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); result = 1; break; } } while (!((serviceStatus.dwCurrentState == SERVICE_PAUSED) || (serviceStatus.dwCurrentState == SERVICE_STOPPED))); if (serviceStatus.dwCurrentState == SERVICE_PAUSED) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s service paused."), wrapperData->serviceDisplayName); } else { status = getNTServiceStatusName(serviceStatus.dwCurrentState); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "Failed to pause %s service. Status: %s"), wrapperData->serviceDisplayName, status); result = 1; } } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to query the status of the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); result = 1; } /* Close this service object's handle to the service control manager */ CloseServiceHandle(schService); } else { if (GetLastError() == ERROR_ACCESS_DENIED) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to pause the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The %s service is not installed - %s"), wrapperData->serviceDisplayName, getLastErrorText()); } result = 1; } /* Finally, close the handle to the service control manager's database */ CloseServiceHandle(schSCManager); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to pause the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); if (isVista() && !isElevated()) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Performing this action requires that you run as an elevated process.")); } result = 1; } return result; } /** Resume a Wrapper instance running as an NT Service. */ int wrapperResumeService() { SC_HANDLE schService; SC_HANDLE schSCManager; SERVICE_STATUS serviceStatus; TCHAR *status; int msgCntr; int result = 0; int ignore = FALSE; /* First, get a handle to the service control manager */ schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (schSCManager) { /* Next get the handle to this service... */ schService = OpenService(schSCManager, wrapperData->serviceName, WRAPPER_SERVICE_PAUSE_CONTINUE); if (schService) { /* Make sure that the service is in a state that can be resumed. */ if (QueryServiceStatus(schService, &serviceStatus)) { if (serviceStatus.dwCurrentState == SERVICE_STOPPED) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s service was not started."), wrapperData->serviceDisplayName); result = 1; } else if (serviceStatus.dwCurrentState == SERVICE_STOP_PENDING) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s service was in the process of stopping."), wrapperData->serviceDisplayName); result = 1; } else if (serviceStatus.dwCurrentState == SERVICE_PAUSE_PENDING) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s service was in the process of being paused."), wrapperData->serviceDisplayName); result = 1; } else if (serviceStatus.dwCurrentState == SERVICE_CONTINUE_PENDING) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s service was in the process of being resumed."), wrapperData->serviceDisplayName); } else if (serviceStatus.dwCurrentState == SERVICE_RUNNING) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s service is already running."), wrapperData->serviceDisplayName); ignore = TRUE; } else { /* The service is paused, so try resuming it. */ if (ControlService(schService, SERVICE_CONTROL_CONTINUE, &serviceStatus)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Resuming the %s service..."), wrapperData->serviceDisplayName); } else { status = getNTServiceStatusName(serviceStatus.dwCurrentState); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Attempt to resume the %s service failed. Status: %s"), wrapperData->serviceDisplayName, status); result = 1; } } if ((!ignore) && (result == 0)) { /* Wait for the service to resume. */ msgCntr = 0; do { if (QueryServiceStatus(schService, &serviceStatus)) { if (msgCntr >= 5) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Waiting to resume...")); msgCntr = 0; } wrapperSleep(1000); msgCntr++; } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to query the status of the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); result = 1; break; } } while (!((serviceStatus.dwCurrentState == SERVICE_RUNNING) || (serviceStatus.dwCurrentState == SERVICE_STOPPED))); if (serviceStatus.dwCurrentState == SERVICE_RUNNING) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s service resumed."), wrapperData->serviceDisplayName); } else { status = getNTServiceStatusName(serviceStatus.dwCurrentState); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT( "Failed to resume %s service. Status: %s"), wrapperData->serviceDisplayName, status); result = 1; } } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to query the status of the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); result = 1; } /* Close this service object's handle to the service control manager */ CloseServiceHandle(schService); } else { if (GetLastError() == ERROR_ACCESS_DENIED) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to resume the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); if (isVista() && !isElevated()) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Performing this action requires that you run as an elevated process.")); } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The %s service is not installed - %s"), wrapperData->serviceDisplayName, getLastErrorText()); } result = 1; } /* Finally, close the handle to the service control manager's database */ CloseServiceHandle(schSCManager); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to resume the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); result = 1; } return result; } int sendServiceControlCodeInner(int controlCode) { SC_HANDLE schService; SC_HANDLE schSCManager; SERVICE_STATUS serviceStatus; TCHAR *status; int result = 0; /* First, get a handle to the service control manager */ schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (schSCManager) { /* Next get the handle to this service... */ schService = OpenService(schSCManager, wrapperData->serviceName, WRAPPER_SERVICE_CONTROL_CODE); if (schService) { /* Make sure that the service is in a state that can be resumed. */ if (QueryServiceStatus(schService, &serviceStatus)) { if (serviceStatus.dwCurrentState == SERVICE_STOPPED) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s service was not started."), wrapperData->serviceDisplayName); result = 1; } else if (serviceStatus.dwCurrentState == SERVICE_STOP_PENDING) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s service was in the process of stopping."), wrapperData->serviceDisplayName); result = 1; } else if (serviceStatus.dwCurrentState == SERVICE_PAUSED) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s service was currently paused."), wrapperData->serviceDisplayName); result = 1; } else if (serviceStatus.dwCurrentState == SERVICE_PAUSE_PENDING) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s service was in the process of being paused."), wrapperData->serviceDisplayName); result = 1; } else if (serviceStatus.dwCurrentState == SERVICE_CONTINUE_PENDING) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s service was in the process of being resumed."), wrapperData->serviceDisplayName); } else { /* The service is running, so try sending the code. */ if (ControlService(schService, controlCode, &serviceStatus)) { result = 0; } else { status = getNTServiceStatusName(serviceStatus.dwCurrentState); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Attempt to send the %s service control code %d failed. Status: %s"), wrapperData->serviceDisplayName, controlCode, status); result = 1; } } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to query the status of the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); result = 1; } /* Close this service object's handle to the service control manager */ CloseServiceHandle(schService); } else { if (GetLastError() == ERROR_ACCESS_DENIED) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to send control code to the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("OpenService failed - %s"), getLastErrorText()); if (isVista() && !isElevated()) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Performing this action requires that you run as an elevated process.")); } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The %s service is not installed - %s"), wrapperData->serviceDisplayName, getLastErrorText()); } result = 1; } /* Finally, close the handle to the service control manager's database */ CloseServiceHandle(schSCManager); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to send control code to the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); result = 1; } return result; } /** Sends a service control code to a running as an NT Service. */ int wrapperSendServiceControlCode(TCHAR **argv, TCHAR *controlCodeS) { int controlCode; int result; /* Make sure the control code is valid. */ if (controlCodeS == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Control code to send is missing.")); wrapperUsage(argv[0]); return 1; } controlCode = _ttoi(controlCodeS); if ((controlCode < 128) || (controlCode > 255)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The service control code must be in the range 128-255.")); return 1; } result = sendServiceControlCodeInner(controlCode); if (!result) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Sent the %s service control code %d."), wrapperData->serviceDisplayName, controlCode); } return result; } /** * Requests that the Wrapper perform a thread dump. */ int wrapperRequestThreadDump() { int result; if (wrapperData->threadDumpControlCode <= 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The thread dump control code is disabled.")); return 1; } result = sendServiceControlCodeInner(wrapperData->threadDumpControlCode); if (!result) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Requested that the %s service perform a thread dump."), wrapperData->serviceDisplayName); } return result; } /** * Obtains the current service status. * The returned result becomes the exitCode. The exitCode is made up of * a series of status bits: * * Bits: * 0: Service Installed. (1) * 1: Service Running. (2) * 2: Service Interactive. (4) * 3: Startup Mode: Auto. (8) * 4: Startup Mode: Manual. (16) * 5: Startup Mode: Disabled. (32) * 6: Service Running but Paused. (64) * 15: Error. (32768) */ int wrapperServiceStatus(const TCHAR* serviceName, const TCHAR* serviceDisplayName, int consoleOutput) { SC_HANDLE schService; SC_HANDLE schSCManager; SERVICE_STATUS serviceStatus; QUERY_SERVICE_CONFIG *pQueryServiceConfig; DWORD reqSize; int result = 0; schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (schSCManager) { /* Next get the handle to this service... */ schService = OpenService(schSCManager, serviceName, WRAPPER_SERVICE_QUERY_STATUS); if (schService) { /* Service is installed, so set that bit. */ if (consoleOutput) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s Service is installed."), serviceDisplayName); } result |= 1; /* Get the service configuration. */ QueryServiceConfig(schService, NULL, 0, &reqSize); pQueryServiceConfig = malloc(reqSize); if (!pQueryServiceConfig) { outOfMemory(TEXT("WSS"), 1); CloseServiceHandle(schSCManager); result |= 32768; return result; } if (QueryServiceConfig(schService, pQueryServiceConfig, reqSize, &reqSize)) { switch (pQueryServiceConfig->dwStartType) { case SERVICE_BOOT_START: /* Possible? */ case SERVICE_SYSTEM_START: /* Possible? */ case SERVICE_AUTO_START: if (consoleOutput) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Start Type: Automatic")); } result |= 8; break; case SERVICE_DEMAND_START: if (consoleOutput) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Start Type: Manual")); } result |= 16; break; case SERVICE_DISABLED: if (consoleOutput) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Start Type: Disabled")); } result |= 32; break; default: if (consoleOutput) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT(" Start Type: Unknown")); } break; } if (pQueryServiceConfig->dwServiceType & SERVICE_INTERACTIVE_PROCESS) { /* This is an interactive service, so set that bit. */ if (consoleOutput) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Interactive: Yes")); } result |= 4; } else { if (consoleOutput) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Interactive: No")); } } free(pQueryServiceConfig); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to query the configuration of the %s service - %s"), serviceDisplayName, getLastErrorText()); result |= 32768; } /* Find out what the current status of the service is so we can decide what to do. */ if (QueryServiceStatus(schService, &serviceStatus)) { if (serviceStatus.dwCurrentState == SERVICE_STOPPED) { /* The service is stopped. */ if (consoleOutput) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Running: No")); } } else { /* Any other state, it is running. Set that bit. */ if (consoleOutput) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Running: Yes")); } result |= 2; if (serviceStatus.dwCurrentState == SERVICE_PAUSED) { if (consoleOutput) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Paused: Yes")); } result |= 64; } } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to query the status of the %s service - %s"), serviceDisplayName, getLastErrorText()); result |= 32768; } /* Close this service object's handle to the service control manager */ CloseServiceHandle(schService); } else { if (GetLastError() == ERROR_ACCESS_DENIED) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to query the status of the %s service - %s"), serviceDisplayName, getLastErrorText()); if (isVista() && !isElevated()) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Performing this action requires that you run as an elevated process.")); } result |= 32768; } else { if (consoleOutput) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("The %s Service is not installed."), serviceDisplayName); } } } /* Finally, close the handle to the service control manager's database */ CloseServiceHandle(schSCManager); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to query the status of the %s service - %s"), serviceDisplayName, getLastErrorText()); result |= 32768; } return result; } /** * Uninstall the service and clean up */ int wrapperRemove() { SC_HANDLE schService; SC_HANDLE schSCManager; int result = 0; /* First attempt to stop the service if it is already running. */ result = wrapperStopService(FALSE); if (result) { /* There was a problem stopping the service. */ return result; } /* First, get a handle to the service control manager */ schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (schSCManager) { /* Next get the handle to this service... */ schService = OpenService(schSCManager, wrapperData->serviceName, SERVICE_QUERY_STATUS | DELETE); if (schService) { /* Now try to remove the service... */ if (DeleteService(schService)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("%s service removed."), wrapperData->serviceDisplayName); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to remove the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); result = 1; } /* Close this service object's handle to the service control manager */ CloseServiceHandle(schService); } else { if (GetLastError() == ERROR_ACCESS_DENIED) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to remove the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); if (isVista() && !isElevated()) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Performing this action requires that you run as an elevated process.")); } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The %s service is not installed - %s"), wrapperData->serviceDisplayName, getLastErrorText()); } result = 1; } /* Finally, close the handle to the service control manager's database */ CloseServiceHandle(schSCManager); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to remove the %s service - %s"), wrapperData->serviceDisplayName, getLastErrorText()); result = 1; } /* Remove message file registration on service remove */ if (result == 0) { /* Do this here to unregister the syslog on uninstall of a resource. */ /* unregisterSyslogMessageFile(); */ } return result; } /** * Sets the working directory to that of the current executable */ int setWorkingDir() { int size = 128; TCHAR* szPath = NULL; DWORD usedLen; int result; TCHAR* pos; /* How large a buffer is needed? The GetModuleFileName function doesn't tell us how much * is needed, only if it is too short. */ do { szPath = malloc(sizeof(TCHAR) * size); if (!szPath) { outOfMemory(TEXT("SWD"), 1); return 1; } /* Important : For win XP getLastError() is unchanged if the buffer is too small, so if we don't reset the last error first, we may actually test an old pending error. */ SetLastError(ERROR_SUCCESS); usedLen = GetModuleFileName(NULL, szPath, size); if (usedLen == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to get the path - %s"), getLastErrorText()); return 1; } else if ((usedLen == size) || (getLastError() == ERROR_INSUFFICIENT_BUFFER)) { /* Too small. */ size += 128; free(szPath); szPath = NULL; } } while (!szPath); /* The wrapperData->isDebugging flag will never be set here, so we can't really use it. */ #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Executable Name: %s"), szPath); #endif /* To get the path, strip everything off after the last '\' */ pos = _tcsrchr(szPath, TEXT('\\')); if (pos == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to extract path from: %s"), szPath); free(szPath); return 1; } else { /* Clip the path at the position of the last backslash */ pos[0] = (TCHAR)0; } /* Set a variable to the location of the binary. */ setEnv(TEXT("WRAPPER_BIN_DIR"), szPath, ENV_SOURCE_APPLICATION); result = wrapperSetWorkingDir(szPath); free(szPath); return result; } /****************************************************************************** * Main function *****************************************************************************/ /** Attempts to resolve the name of an exception. Returns null if it is unknown. */ TCHAR* getExceptionName(DWORD exCode, int nullOnUnknown) { TCHAR *exName; switch (exCode) { case EXCEPTION_ACCESS_VIOLATION: exName = TEXT("EXCEPTION_ACCESS_VIOLATION"); break; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: exName = TEXT("EXCEPTION_ARRAY_BOUNDS_EXCEEDED"); break; case EXCEPTION_BREAKPOINT: exName = TEXT("EXCEPTION_BREAKPOINT"); break; case EXCEPTION_DATATYPE_MISALIGNMENT: exName = TEXT("EXCEPTION_DATATYPE_MISALIGNMENT"); break; case EXCEPTION_FLT_DENORMAL_OPERAND: exName = TEXT("EXCEPTION_FLT_DENORMAL_OPERAND"); break; case EXCEPTION_FLT_DIVIDE_BY_ZERO: exName = TEXT("EXCEPTION_FLT_DIVIDE_BY_ZERO"); break; case EXCEPTION_FLT_INEXACT_RESULT: exName = TEXT("EXCEPTION_FLT_INEXACT_RESULT"); break; case EXCEPTION_FLT_INVALID_OPERATION: exName = TEXT("EXCEPTION_FLT_INVALID_OPERATION"); break; case EXCEPTION_FLT_OVERFLOW: exName = TEXT("EXCEPTION_FLT_OVERFLOW"); break; case EXCEPTION_FLT_STACK_CHECK: exName = TEXT("EXCEPTION_FLT_STACK_CHECK"); break; case EXCEPTION_FLT_UNDERFLOW: exName = TEXT("EXCEPTION_FLT_UNDERFLOW"); break; case EXCEPTION_ILLEGAL_INSTRUCTION: exName = TEXT("EXCEPTION_ILLEGAL_INSTRUCTION"); break; case EXCEPTION_IN_PAGE_ERROR: exName = TEXT("EXCEPTION_IN_PAGE_ERROR"); break; case EXCEPTION_INT_DIVIDE_BY_ZERO: exName = TEXT("EXCEPTION_INT_DIVIDE_BY_ZERO"); break; case EXCEPTION_INT_OVERFLOW: exName = TEXT("EXCEPTION_INT_OVERFLOW"); break; case EXCEPTION_INVALID_DISPOSITION: exName = TEXT("EXCEPTION_INVALID_DISPOSITION"); break; case EXCEPTION_NONCONTINUABLE_EXCEPTION: exName = TEXT("EXCEPTION_NONCONTINUABLE_EXCEPTION"); break; case EXCEPTION_PRIV_INSTRUCTION: exName = TEXT("EXCEPTION_PRIV_INSTRUCTION"); break; case EXCEPTION_SINGLE_STEP: exName = TEXT("EXCEPTION_SINGLE_STEP"); break; case EXCEPTION_STACK_OVERFLOW: exName = TEXT("EXCEPTION_STACK_OVERFLOW"); break; default: if (nullOnUnknown) { exName = NULL; } else { exName = TEXT("EXCEPTION_UNKNOWN"); } break; } return exName; } /** * Logs some dump information to the log output and then generate a minidump file. */ int exceptionFilterFunction(PEXCEPTION_POINTERS exceptionPointers) { DWORD exCode; int i; size_t len; TCHAR curDir[MAX_PATH]; TCHAR dumpFile[MAX_PATH]; BOOL dumpSuccessful; HANDLE hDumpFile; SYSTEMTIME stLocalTime; MINIDUMP_EXCEPTION_INFORMATION expParam; int couldLoad; FARPROC miniDumpWriteDumpDyn; HMODULE dbgHelpDll; int isCustomized = FALSE; dbgHelpDll = LoadLibrary(TEXT("Dbghelp.dll")); if (dbgHelpDll == NULL) { couldLoad = FALSE; } else { miniDumpWriteDumpDyn = GetProcAddress(dbgHelpDll, "MiniDumpWriteDump"); if(miniDumpWriteDumpDyn == NULL) { couldLoad = FALSE; } else { couldLoad = TRUE; } } /* Log any queued messages */ maintainLogger(); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("--------------------------------------------------------------------") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("encountered a fatal error in Wrapper%s"), (isCustomized ? TEXT(" (Customized)") : TEXT(""))); exCode = exceptionPointers->ExceptionRecord->ExceptionCode; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" exceptionCode = %s (%d)"), getExceptionName(exCode, FALSE), exCode); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" exceptionFlag = %s"), (exceptionPointers->ExceptionRecord->ExceptionFlags == EXCEPTION_NONCONTINUABLE ? TEXT("EXCEPTION_NONCONTINUABLE") : TEXT("EXCEPTION_NONCONTINUABLE_EXCEPTION"))); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" exceptionAddress = 0x%p"), exceptionPointers->ExceptionRecord->ExceptionAddress); if (exCode == EXCEPTION_ACCESS_VIOLATION) { switch(exceptionPointers->ExceptionRecord->ExceptionInformation[0]) { case 0: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" Read access exception from 0x%p"), exceptionPointers->ExceptionRecord->ExceptionInformation[1]); break; case 1: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" Write access exception to 0x%p"), exceptionPointers->ExceptionRecord->ExceptionInformation[1]); break; case 8: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" DEP access exception to 0x%p"), exceptionPointers->ExceptionRecord->ExceptionInformation[1]); break; default: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" Unexpected access exception to 0x%p (%d)"), exceptionPointers->ExceptionRecord->ExceptionInformation[1], exceptionPointers->ExceptionRecord->ExceptionInformation[0]); break; } } else { for (i = 0; i < (int)exceptionPointers->ExceptionRecord->NumberParameters; i++) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" exceptionInformation[%d] = %ld"), i, exceptionPointers->ExceptionRecord->ExceptionInformation[i]); } } if (wrapperData) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" Wrapper Main Loop Status:")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" Current Ticks: 0x%08x"), wrapperGetTicks()); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" Wrapper State: %s"), wrapperGetWState(wrapperData->wState)); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" Java State: %s (Timeout: 0x%08x)"), wrapperGetJState(wrapperData->jState), wrapperData->jStateTimeoutTicks); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" Exit Requested: %s"), (wrapperData->exitRequested ? TEXT("true") : TEXT("false"))); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" Restart Mode: %d"), wrapperData->restartRequested); } /* Get the current directory. */ len = GetCurrentDirectory(MAX_PATH, curDir); if (len == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" Unable to request current directory. %s"), getLastErrorText()); _sntprintf(curDir, MAX_PATH, TEXT(".")); } /* Generate the minidump. */ GetLocalTime(&stLocalTime); _sntprintf(dumpFile, MAX_PATH, TEXT("wrapper-%s-%s-%s-%s%s-%04d%02d%02d%02d%02d%02d-%ld-%ld.dmp"), wrapperOS, wrapperArch, wrapperBits, wrapperVersion, (isCustomized ? TEXT("-customized") : TEXT("")), stLocalTime.wYear, stLocalTime.wMonth, stLocalTime.wDay, stLocalTime.wHour, stLocalTime.wMinute, stLocalTime.wSecond, GetCurrentProcessId(), GetCurrentThreadId()); if (couldLoad == TRUE) { hDumpFile = CreateFile(dumpFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0); if (hDumpFile == INVALID_HANDLE_VALUE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" Failed to create dump file:\n %s\\%s : %s"), curDir, dumpFile, getLastErrorText()); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" Writing dump file: %s\\%s"), curDir, dumpFile); expParam.ThreadId = GetCurrentThreadId(); expParam.ExceptionPointers = exceptionPointers; expParam.ClientPointers = TRUE; dumpSuccessful = (BOOL)miniDumpWriteDumpDyn(GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, MiniDumpWithDataSegs, &expParam, NULL, NULL); FreeLibrary(dbgHelpDll); if (dumpSuccessful) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" Dump completed.")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" Please send the dump file to support@tanukisoftware.com along with\n your wrapper.conf and wrapper.log files.")); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" Failed to generate dump file. %s"), getLastErrorText()); } } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" Please send the log file to support@tanukisoftware.com along with\n your wrapper.conf file.")); } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("--------------------------------------------------------------------") ); return EXCEPTION_EXECUTE_HANDLER; } LPWSTR AllocateAndCopyWideString(LPCWSTR inputString) { LPWSTR outputString = NULL; outputString = (LPWSTR)LocalAlloc(LPTR, (wcslen(inputString) + 1) * sizeof(WCHAR)); if (outputString != NULL) { lstrcpyW(outputString, inputString); } return outputString; } BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo, PSPROG_PUBLISHERINFO Info) { DWORD n; BOOL fReturn = FALSE; PSPC_SP_OPUS_INFO OpusInfo = NULL; DWORD dwData; BOOL fResult; __try { /* Loop through authenticated attributes and find SPC_SP_OPUS_INFO_OBJID OID. */ for (n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++) { if (lstrcmpA(SPC_SP_OPUS_INFO_OBJID, pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0) { /* Get Size of SPC_SP_OPUS_INFO structure. */ fResult = CryptDecodeObject(ENCODING, SPC_SP_OPUS_INFO_OBJID, pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData, pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData, 0, NULL, &dwData); if (!fResult) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("CryptDecodeObject failed with %x"), GetLastError()); __leave; } /* Allocate memory for SPC_SP_OPUS_INFO structure. */ OpusInfo = (PSPC_SP_OPUS_INFO)LocalAlloc(LPTR, dwData); if (!OpusInfo) { outOfMemory(TEXT("GPAPI"), 1); __leave; } /* Decode and get SPC_SP_OPUS_INFO structure. */ fResult = CryptDecodeObject(ENCODING, SPC_SP_OPUS_INFO_OBJID, pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData, pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData, 0, OpusInfo, &dwData); if (!fResult) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("CryptDecodeObject failed with %x"), GetLastError()); __leave; } /* Fill in Program Name if present. */ if (OpusInfo->pwszProgramName) { Info->lpszProgramName = AllocateAndCopyWideString(OpusInfo->pwszProgramName); } else { Info->lpszProgramName = NULL; } /* Fill in Publisher Information if present. */ if (OpusInfo->pPublisherInfo) { switch (OpusInfo->pPublisherInfo->dwLinkChoice) { case SPC_URL_LINK_CHOICE: Info->lpszPublisherLink = AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszUrl); break; case SPC_FILE_LINK_CHOICE: Info->lpszPublisherLink = AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszFile); break; default: Info->lpszPublisherLink = NULL; break; } } else { Info->lpszPublisherLink = NULL; } /* Fill in More Info if present. */ if (OpusInfo->pMoreInfo) { switch (OpusInfo->pMoreInfo->dwLinkChoice) { case SPC_URL_LINK_CHOICE: Info->lpszMoreInfoLink = AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszUrl); break; case SPC_FILE_LINK_CHOICE: Info->lpszMoreInfoLink = AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszFile); break; default: Info->lpszMoreInfoLink = NULL; break; } } else { Info->lpszMoreInfoLink = NULL; } fReturn = TRUE; break; /* Break from for loop. */ } /* lstrcmp SPC_SP_OPUS_INFO_OBJID */ } /* for */ } __finally { if (OpusInfo != NULL) LocalFree(OpusInfo); } return fReturn; } BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo, SYSTEMTIME *st) { BOOL fResult; FILETIME lft, ft; DWORD dwData; BOOL fReturn = FALSE; DWORD n; /* Loop through authenticated attributes and find szOID_RSA_signingTime OID. */ for (n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++) { if (lstrcmpA(szOID_RSA_signingTime, pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0) { /* Decode and get FILETIME structure. */ dwData = sizeof(ft); fResult = CryptDecodeObject(ENCODING, szOID_RSA_signingTime, pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData, pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData, 0, (PVOID)&ft, &dwData); if (!fResult) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("CryptDecodeObject failed with %x"), GetLastError()); break; } /* Convert to local time. */ FileTimeToLocalFileTime(&ft, &lft); FileTimeToSystemTime(&lft, st); fReturn = TRUE; break; /* Break from for loop. */ } /* lstrcmp szOID_RSA_signingTime */ } /* for */ return fReturn; } BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo, PCMSG_SIGNER_INFO *pCounterSignerInfo) { PCCERT_CONTEXT pCertContext = NULL; BOOL fReturn = FALSE; BOOL fResult; DWORD dwSize, n; __try { *pCounterSignerInfo = NULL; /* Loop through unathenticated attributes for szOID_RSA_counterSign OID. */ for (n = 0; n < pSignerInfo->UnauthAttrs.cAttr; n++) { if (lstrcmpA(pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId, szOID_RSA_counterSign) == 0) { /* Get size of CMSG_SIGNER_INFO structure. */ fResult = CryptDecodeObject(ENCODING, PKCS7_SIGNER_INFO, pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData, pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData, 0, NULL, &dwSize); if (!fResult) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("CryptDecodeObject failed with %x"), GetLastError()); __leave; } /* Allocate memory for CMSG_SIGNER_INFO. */ *pCounterSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSize); if (!*pCounterSignerInfo) { outOfMemory(TEXT("GTSSI"), 1); __leave; } /* Decode and get CMSG_SIGNER_INFO structure for timestamp certificate. */ fResult = CryptDecodeObject(ENCODING, PKCS7_SIGNER_INFO, pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData, pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData, 0, (PVOID)*pCounterSignerInfo, &dwSize); if (!fResult) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("CryptDecodeObject failed with %x"), GetLastError()); __leave; } fReturn = TRUE; break; /* Break from for loop. */ } } } __finally { /* Clean up.*/ if (pCertContext != NULL) CertFreeCertificateContext(pCertContext); } return fReturn; } /** * Prints certificate info into a buffer. * * @param buffer Buffer to fill, or NULL to calculate the size. * @param size Size of the buffer, or 0 when calculating the size. * @param serialNr Serial number. * @param szName1 Issuer name. * @param szName2 Subject name. * * @return number of characters written to the buffer * (without the NULL termination) */ size_t printCertificateInfoInner(TCHAR* buffer, size_t size, LPTSTR serialNr, LPTSTR szName1, LPTSTR szName2) { const TCHAR* token; size_t len = 0; token = TEXT(" Serial Number:\n"); if (buffer) { _tcsncpy(buffer, token, size); } len += _tcslen(token); token = TEXT(" %s\n"); if (buffer) { _sntprintf(buffer + len, size - len, token, serialNr); } len += _tcslen(token) - 2 + _tcslen(serialNr); token = TEXT(" Issuer Name: %s\n"); if (buffer) { _sntprintf(buffer + len, size - len, token, szName1); } len += _tcslen(token) - 2 + _tcslen(szName1); token = TEXT(" Subject Name: %s"); if (buffer) { _sntprintf(buffer + len, size - len, token, szName2); buffer[size - 1] = TEXT('\0'); } len += _tcslen(token) - 2 + _tcslen(szName2); return len; } LPTSTR PrintCertificateInfo(PCCERT_CONTEXT pCertContext) { BOOL fReturn = FALSE; LPTSTR szName1 = NULL; LPTSTR szName2 = NULL; LPTSTR serialNr = NULL; DWORD dwData, serialNrLength = 0, n, i; LPTSTR buffer = NULL; size_t size = 0; __try { dwData = pCertContext->pCertInfo->SerialNumber.cbData; for (i = 0; i < 2; i++) { for (n = 0; n < dwData; n++) { if (serialNr) { _sntprintf(serialNr + (n * 3) , serialNrLength - (n * 3), TEXT("%02x "), pCertContext->pCertInfo->SerialNumber.pbData[dwData - (n + 1)]); } else { serialNrLength += 3; } } if (!serialNr) { serialNr = calloc(serialNrLength + 1, sizeof(TCHAR)); if (!serialNr) { outOfMemory(TEXT("PCI"), 1); __leave; } } } /* Get Issuer name size. */ if (!(dwData = CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, NULL, 0))) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("CertGetNameString failed.")); __leave; } /* Allocate memory for Issuer name. */ szName1 = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR)); if (!szName1) { outOfMemory(TEXT("PCI"), 2); __leave; } /* Get Issuer name. */ if (!(CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, szName1, dwData))) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("CertGetNameString failed.")); __leave; } /* Get Subject name size. */ if (!(dwData = CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 0))) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("CertGetNameString failed.")); __leave; } /* Allocate memory for subject name. */ szName2 = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR)); if (!szName2) { outOfMemory(TEXT("PCI"), 3); __leave; } /* Get subject name. */ if (!(CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, szName2, dwData))) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("CertGetNameString failed.")); __leave; } size = printCertificateInfoInner(NULL, 0, serialNr, szName1, szName2); buffer = malloc(sizeof(TCHAR) * (size + 1)); if (!buffer) { outOfMemory(TEXT("PCI"), 4); __leave; } printCertificateInfoInner(buffer, size + 1, serialNr, szName1, szName2); } __finally { if (szName1 != NULL) LocalFree(szName1); if (szName2 != NULL) LocalFree(szName2); if (serialNr != NULL) free(serialNr); } return buffer; } /** * Prints certificate info into a buffer. * * @param buffer Buffer to fill, or NULL to calculate the size. * @param size Size of the buffer, or 0 when calculating the size. * @param lpszProgramName Program name. * @param lpszPublisherLink Publisher name. * @param lpszMoreInfoLink More info link name. * @param szSignerCert Info of the signer certificate. * @param szTimeStampCert Info of the timestamp certificate. * @param st Date of the timestamp. * @param dateSet TRUE if the date of the timestamp should be printed, * FALSE otherwise. * * @return number of characters written to the buffer * (without the NULL termination) */ size_t printWholeCertificateInfoInner(TCHAR* buffer, size_t size, LPWSTR lpszProgramName, LPWSTR lpszPublisherLink, LPWSTR lpszMoreInfoLink, LPTSTR szSignerCert, LPTSTR szTimeStampCert, SYSTEMTIME st, int dateSet) { const TCHAR* token; size_t len = 0; if (buffer) { buffer[0] = 0; } if (lpszProgramName || lpszPublisherLink || lpszMoreInfoLink || szSignerCert || szTimeStampCert || dateSet) { if (buffer) { buffer[0] = TEXT('\n'); buffer[1] = 0; } len++; } if (lpszProgramName) { token = TEXT(" Program Name: %s\n"); if (buffer) { _sntprintf(buffer + len, size, token, lpszProgramName); } len += _tcslen(token) - 2 + _tcslen(lpszProgramName); } if (lpszPublisherLink) { token = TEXT(" Publisher Link: %s\n"); if (buffer) { _sntprintf(buffer + len, size - len, token, lpszPublisherLink); } len += _tcslen(token) - 2 + _tcslen(lpszPublisherLink); } if (lpszMoreInfoLink) { token = TEXT(" MoreInfo Link: %s\n"); if (buffer) { _sntprintf(buffer + len, size - len, token, lpszMoreInfoLink); } len += _tcslen(token) - 2 + _tcslen(lpszMoreInfoLink); } if (szSignerCert) { token = TEXT(" Signer Certificate:\n"); if (buffer) { _tcsncat(buffer, token, size - len); } len += _tcslen(token); token = TEXT("%s\n"); if (buffer) { _sntprintf(buffer + len, size - len, token, szSignerCert); } len += _tcslen(token) - 2 + _tcslen(szSignerCert); } if (szTimeStampCert) { token = TEXT(" TimeStamp Certificate:\n"); if (buffer) { _tcsncat(buffer, token, size - len); } len += _tcslen(token); token = TEXT("%s\n"); if (buffer) { _sntprintf(buffer + len, size - len, token, szTimeStampCert); } len += _tcslen(token) - 2 + _tcslen(szTimeStampCert); } if (dateSet) { token = TEXT(" Date of TimeStamp: %04d/%02d/%02d %02d:%02d:%02d\n"); if (buffer) { _sntprintf(buffer + len, size - len, token, st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); } len += _tcslen(token) - 10; } if (buffer) { /* For safety */ buffer[size - 1] = TEXT('\0'); } return len; } LPTSTR printWholeCertificateInfo(HCERTSTORE hStore, PCMSG_SIGNER_INFO pSignerInfo, PCCERT_CONTEXT pSignerCertContext) { PCCERT_CONTEXT pTimeStampCertContext = NULL; PCMSG_SIGNER_INFO pCounterSignerInfo = NULL; CERT_INFO CertInfo; SPROG_PUBLISHERINFO ProgPubInfo; SYSTEMTIME st; LPWSTR lpszProgramName = NULL; LPWSTR lpszPublisherLink = NULL; LPWSTR lpszMoreInfoLink = NULL; LPTSTR szSignerCert = NULL; LPTSTR szTimeStampCert = NULL; LPTSTR buffer = NULL; size_t size = 0; DWORD dateSet = FALSE; ZeroMemory(&ProgPubInfo, sizeof(ProgPubInfo)); __try { /* Get program name and publisher information from signer info structure. */ if (GetProgAndPublisherInfo(pSignerInfo, &ProgPubInfo)) { lpszProgramName = ProgPubInfo.lpszProgramName; lpszPublisherLink = ProgPubInfo.lpszPublisherLink; lpszMoreInfoLink = ProgPubInfo.lpszMoreInfoLink; } szSignerCert = PrintCertificateInfo(pSignerCertContext); /* Get the timestamp certificate signerinfo structure. */ if (GetTimeStampSignerInfo(pSignerInfo, &pCounterSignerInfo)) { /* Search for Timestamp certificate in the temporary certificate store. */ CertInfo.Issuer = pCounterSignerInfo->Issuer; CertInfo.SerialNumber = pCounterSignerInfo->SerialNumber; pTimeStampCertContext = CertFindCertificateInStore(hStore, ENCODING, 0, CERT_FIND_SUBJECT_CERT, (PVOID)&CertInfo, NULL); if (!pTimeStampCertContext) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("CertFindCertificateInStore failed with %x"), GetLastError()); __leave; } szTimeStampCert = PrintCertificateInfo(pTimeStampCertContext); /* Find Date of timestamp. */ if (GetDateOfTimeStamp(pCounterSignerInfo, &st)) { dateSet = TRUE; } } size = printWholeCertificateInfoInner(NULL, 0, lpszProgramName, lpszPublisherLink, lpszMoreInfoLink, szSignerCert, szTimeStampCert, st, dateSet); buffer = malloc(sizeof(TCHAR) * (size + 1)); if (!buffer) { outOfMemory(TEXT("PWCI"), 1); __leave; } printWholeCertificateInfoInner(buffer, size + 1, lpszProgramName, lpszPublisherLink, lpszMoreInfoLink, szSignerCert, szTimeStampCert, st, dateSet); /* Remove the last line break. */ buffer[size - 1] = 0; } __finally { /* Clean up. */ if (ProgPubInfo.lpszProgramName != NULL) { LocalFree(ProgPubInfo.lpszProgramName); } if (ProgPubInfo.lpszPublisherLink != NULL) { LocalFree(ProgPubInfo.lpszPublisherLink); } if (ProgPubInfo.lpszMoreInfoLink != NULL) { LocalFree(ProgPubInfo.lpszMoreInfoLink); } if (pCounterSignerInfo != NULL) { LocalFree(pCounterSignerInfo); } if (pTimeStampCertContext != NULL) { CertFreeCertificateContext(pTimeStampCertContext); } if (szSignerCert != NULL) { free(szSignerCert); } if (szTimeStampCert != NULL) { free(szTimeStampCert); } } return buffer; } /** * Collect pointers to the certificate store, the signer info structure and * the certificate context structure, to be later used by other functions. * * The parameters retrieved by this function call must later be freed by * freeCertificateBaseInfo(). * * @param wrapperExeName Name of the Wrapper binary. * @param phStore pointer to a handle of the certificate store. * @param ppSignerInfo the address of a pointer the signer info structure. * @param ppSignerCertContext the address of a pointer to the certificate context. */ static void getCertificateBaseInfo(LPCWSTR wrapperExeName, HCERTSTORE *phStore, PCMSG_SIGNER_INFO *ppSignerInfo, PCCERT_CONTEXT *ppSignerCertContext) { HCRYPTMSG hMsg = NULL; BOOL fResult; DWORD dwEncoding, dwContentType, dwFormatType; DWORD dwSignerInfo; CERT_INFO CertInfo; __try { /* Get message handle and store handle from the signed file. */ fResult = CryptQueryObject(CERT_QUERY_OBJECT_FILE, wrapperExeName, CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, CERT_QUERY_FORMAT_FLAG_BINARY, 0, &dwEncoding, &dwContentType, &dwFormatType, phStore, &hMsg, NULL); if (!fResult) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("No certificate found! Error: %x"), GetLastError()); __leave; } /* Get signer information size. */ fResult = CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwSignerInfo); if (!fResult) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("CryptMsgGetParam failed with %x"), GetLastError()); __leave; } /* Allocate memory for signer information. */ *ppSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSignerInfo); if (*ppSignerInfo == NULL) { outOfMemory(TEXT("GCBI"), 1); __leave; } /* Get Signer Information. */ fResult = CryptMsgGetParam(hMsg, CMSG_SIGNER_INFO_PARAM, 0, (PVOID)*ppSignerInfo, &dwSignerInfo); if (!fResult) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("CryptMsgGetParam failed with %x"), GetLastError()); __leave; } /* Search for the signer certificate in the temporary certificate store. */ CertInfo.Issuer = (*ppSignerInfo)->Issuer; CertInfo.SerialNumber = (*ppSignerInfo)->SerialNumber; *ppSignerCertContext = CertFindCertificateInStore(*phStore, ENCODING, 0, CERT_FIND_SUBJECT_CERT, (PVOID)&CertInfo, NULL); if (!*ppSignerCertContext) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("CertFindCertificateInStore failed with %x"), GetLastError()); __leave; } } __finally { /* Clean up. */ if (hMsg != NULL) { CryptMsgClose(hMsg); } } } /** * Close the certificate store, and frees the signer info / certificate context structures. * * @param hStore handle of the certificate store. * @param pSignerInfo pointer the signer info structure. * @param pSignerCertContext pointer to the certificate context. */ static void freeCertificateBaseInfo(HCERTSTORE hStore, PCMSG_SIGNER_INFO pSignerInfo, PCCERT_CONTEXT pSignerCertContext) { if (hStore != NULL) { CertCloseStore(hStore, 0); } if (pSignerInfo != NULL) { LocalFree(pSignerInfo); } if (pSignerCertContext != NULL) { CertFreeCertificateContext(pSignerCertContext); } } /** * Build and check the certificate chain. * * @param pSignerCertContext pointer to the certificate context. * @param bufferCert buffer containing the certificate info to print in case any message is logged (can be NULL). * @param loglevel Log level at which to log the messages. * * @return TRUE if the certificate chain was built successfully, FALSE otherwise. */ int checkCertificateChain(PCCERT_CONTEXT pSignerCertContext, const TCHAR* bufferCertInfo, int loglevel) { PCCERT_CHAIN_CONTEXT pChainContext = NULL; CERT_ENHKEY_USAGE EnhkeyUsage; CERT_USAGE_MATCH CertUsage; CERT_CHAIN_PARA ChainPara; DWORD dwFlags = CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT; int timeout; int loggedStatus = CERT_TRUST_NO_ERROR; const TCHAR* buffer = bufferCertInfo ? bufferCertInfo : TEXT(""); int result = FALSE; timeout = startupThreadTimeout - (wrapperGetTickAgeTicks(startupThreadStartTicks, wrapperGetTicks()) * WRAPPER_TICK_MS); if (timeout < 2000) { /* Give enough time for the revocation checking, but be short enough to feel responsive in case we are about to shutdown. */ timeout = 2000; } else if (timeout > 20000) { /* As per the API, the maximum cumulative timeout is set, by default, to 20 seconds. No need to be longer. */ timeout = 20000; } EnhkeyUsage.cUsageIdentifier = 0; EnhkeyUsage.rgpszUsageIdentifier = NULL; CertUsage.dwType = USAGE_MATCH_TYPE_AND; CertUsage.Usage = EnhkeyUsage; ZeroMemory(&ChainPara, sizeof(ChainPara)); ChainPara.cbSize = sizeof(CERT_CHAIN_PARA); ChainPara.RequestedUsage = CertUsage; ChainPara.dwUrlRetrievalTimeout = timeout; ChainPara.RequestedIssuancePolicy = CertUsage; if (pSignerCertContext) { #ifdef WRAPPER_DEBUG_CERTIFICATE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("CertGetCertificateChain() BEGIN - timeout: %dms"), timeout); #endif if (!CertGetCertificateChain(NULL, /* use the default chain engine */ pSignerCertContext, /* pointer to the end certificate */ NULL, /* use the default time */ NULL, /* search no additional stores */ &ChainPara, /* use AND logic and enhanced key usage as indicated in the ChainPara data structure */ dwFlags, /* flags: use cumulative timeout across all revocation URL retrievals */ NULL, /* reserved */ &pChainContext)) { /* pointer to the chain created */ /* CertGetCertificateChain failed. This should not happen if the binary was correctly signed. */ log_printf(WRAPPER_SOURCE_WRAPPER, loglevel, TEXT("Failed to retrieve the certificate chain. Error: %x%s"), GetLastError(), buffer); } else { if (pChainContext->TrustStatus.dwErrorStatus & (CERT_TRUST_REVOCATION_STATUS_UNKNOWN | CERT_TRUST_IS_OFFLINE_REVOCATION)) { /* Revocation checking is not performed by WinVerifyTrust(), so log a message here. */ log_printf(WRAPPER_SOURCE_WRAPPER, loglevel, TEXT("Revocation status of at least one of the certificates in the certificate chain could not be verified. Error: %x%s"), pChainContext->TrustStatus.dwErrorStatus, buffer); /* Remember that this is already logged. */ loggedStatus |= (CERT_TRUST_REVOCATION_STATUS_UNKNOWN | CERT_TRUST_IS_OFFLINE_REVOCATION); } if (pChainContext->TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN) { /* Incomplete chain. Some certificates are missing, and retrieving information from the * certificate authority over the network (such as revocation checking) timed out. */ if ((pChainContext->TrustStatus.dwErrorStatus & ~loggedStatus) == CERT_TRUST_IS_PARTIAL_CHAIN) { /* No other error. */ log_printf(WRAPPER_SOURCE_WRAPPER, loglevel, TEXT("Timed out retrieving the certificate chain.%s"), buffer); } else { /* Other error(s) not yet logged. Print the status to help support. */ log_printf(WRAPPER_SOURCE_WRAPPER, loglevel, TEXT("Timed out retrieving the certificate chain. Status: %x%s"), pChainContext->TrustStatus.dwErrorStatus, buffer); } } else { /* Certificate chain was retrieved in time and is complete. */ if ((pChainContext->TrustStatus.dwErrorStatus & ~loggedStatus) != CERT_TRUST_NO_ERROR) { /* Other errors are ignored for now, but log a debug message. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Error found in the certificate chain. Status: %x%s"), pChainContext->TrustStatus.dwErrorStatus, buffer); } } if (pChainContext->TrustStatus.dwErrorStatus == CERT_TRUST_NO_ERROR) { result = TRUE; } } #ifdef WRAPPER_DEBUG_CERTIFICATE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("CertGetCertificateChain() END")); #endif if (pChainContext) { CertFreeCertificateChain(pChainContext); } } return result; } void verifyEmbeddedSignature() { HCERTSTORE hStore = NULL; PCMSG_SIGNER_INFO pSignerInfo = NULL; PCCERT_CONTEXT pSignerCertContext = NULL; LONG lStatus; DWORD dwLastError; const TCHAR* lastErrMsg; TCHAR pwszSourceFile[_MAX_PATH]; GUID WVTPolicyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2; WINTRUST_DATA WinTrustData; WINTRUST_FILE_INFO FileData; LPTSTR buffer = NULL; int logLevel; int ret; #ifdef WRAPPER_DEBUG_CERTIFICATE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("verifyEmbeddedSignature() BEGIN")); #endif /* Get the log level of non-critical errors detected when performing trust verification. * Note: If needed, we can add more loglevel properties to target specific error types. */ logLevel = getLogLevelForName(getStringProperty(properties, TEXT("wrapper.check_certificate.default.loglevel"), TEXT("DEBUG"))); if (logLevel < getLowLogLevel()) { logLevel = LEVEL_NONE; } if (!GetModuleFileName(NULL, pwszSourceFile, _MAX_PATH)) { return; } memset(&FileData, 0, sizeof(FileData)); FileData.cbStruct = sizeof(WINTRUST_FILE_INFO); FileData.pcwszFilePath = pwszSourceFile; FileData.hFile = NULL; FileData.pgKnownSubject = NULL; memset(&WinTrustData, 0, sizeof(WinTrustData)); WinTrustData.cbStruct = sizeof(WinTrustData); WinTrustData.pPolicyCallbackData = NULL; WinTrustData.pSIPClientData = NULL; WinTrustData.dwUIChoice = WTD_UI_NONE; WinTrustData.fdwRevocationChecks = WTD_REVOKE_NONE; WinTrustData.dwUnionChoice = WTD_CHOICE_FILE; WinTrustData.dwStateAction = WTD_STATEACTION_VERIFY; WinTrustData.hWVTStateData = NULL; WinTrustData.pwszURLReference = NULL; WinTrustData.dwProvFlags = WTD_USE_DEFAULT_OSVER_CHECK | WTD_REVOCATION_CHECK_NONE; /* Do not perform revocation checking here as this can be very slow. It will be done later by CertGetCertificateChain(), which makes it possible to timeout. */ WinTrustData.dwUIContext = 0; WinTrustData.pFile = &FileData; /* On old versions of Windows (tested with 2000), the last error code is not set by WinVerifyTrust(). We will have to use lStatus instead. */ SetLastError(ERROR_SUCCESS); #ifdef WRAPPER_DEBUG_CERTIFICATE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("WinVerifyTrust() BEGIN")); #endif /* WinVerifyTrust() is used to verify the digital signature, something CertGetCertificateChain() can't do. */ lStatus = WinVerifyTrust(NULL, &WVTPolicyGUID, &WinTrustData); dwLastError = GetLastError(); if (dwLastError == ERROR_SUCCESS) { dwLastError = (DWORD)lStatus; } #ifdef WRAPPER_DEBUG_CERTIFICATE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("WinVerifyTrust() END")); #endif switch (lStatus) { case ERROR_SUCCESS: if (wrapperData->isDebugging) { getCertificateBaseInfo(pwszSourceFile, &hStore, &pSignerInfo, &pSignerCertContext); buffer = printWholeCertificateInfo(hStore, pSignerInfo, pSignerCertContext); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("The file \"%s\" is signed and the signature was verified.%s"), pwszSourceFile, buffer ? buffer : TEXT("")); free(buffer); } break; case TRUST_E_NOSIGNATURE: /* The file was not signed or had a signature that was not valid. */ if (logLevel != LEVEL_NONE) { if ((TRUST_E_SUBJECT_FORM_UNKNOWN == dwLastError) || (TRUST_E_NOSIGNATURE == dwLastError) || (TRUST_E_PROVIDER_UNKNOWN == dwLastError)) { log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("The file \"%s\" is not signed."), pwszSourceFile); } else { /* The signature was not valid or there was an error opening the file. */ lastErrMsg = getErrorText(dwLastError, NULL); log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("An unknown error occurred trying to verify the signature of the \"%s\" file: %s"), pwszSourceFile, lastErrMsg); } } break; case TRUST_E_EXPLICIT_DISTRUST: /* The hash that represents the subject or the publisher is not allowed by the admin or user. */ if (LEVEL_WARN >= getLowLogLevel()) { getCertificateBaseInfo(pwszSourceFile, &hStore, &pSignerInfo, &pSignerCertContext); buffer = printWholeCertificateInfo(hStore, pSignerInfo, pSignerCertContext); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("The signature is present, but specifically disallowed.%s\nThe Wrapper will shutdown!"), buffer ? buffer : TEXT("")); free(buffer); } /* Stop the Wrapper. */ wrapperStopProcess(wrapperData->errorExitCode, TRUE); wrapperData->wState = WRAPPER_WSTATE_STOPPING; break; case TRUST_E_SUBJECT_NOT_TRUSTED: /* The user clicked "No" when asked to install and run. */ if (logLevel != LEVEL_NONE) { getCertificateBaseInfo(pwszSourceFile, &hStore, &pSignerInfo, &pSignerCertContext); buffer = printWholeCertificateInfo(hStore, pSignerInfo, pSignerCertContext); log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("The signature is present, but not trusted.%s"), buffer ? buffer : TEXT("")); free(buffer); } break; case CRYPT_E_SECURITY_SETTINGS: /* The hash that represents the subject or the publisher was not explicitly trusted by the admin and the admin policy has disabled user trust. No signature, publisher or time stamp errors. */ if (logLevel != LEVEL_NONE) { getCertificateBaseInfo(pwszSourceFile, &hStore, &pSignerInfo, &pSignerCertContext); buffer = printWholeCertificateInfo(hStore, pSignerInfo, pSignerCertContext); log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("CRYPT_E_SECURITY_SETTINGS - The hash\nrepresenting the subject or the publisher wasn't\nexplicitly trusted by the admin and admin policy\nhas disabled user trust. No signature, publisher or timestamp errors.%s"), buffer ? buffer : TEXT("")); free(buffer); } break; default: lastErrMsg = getErrorText(dwLastError, NULL); getCertificateBaseInfo(pwszSourceFile, &hStore, &pSignerInfo, &pSignerCertContext); buffer = printWholeCertificateInfo(hStore, pSignerInfo, pSignerCertContext); if ((dwLastError == TRUST_E_BAD_DIGEST) || (dwLastError == TRUST_E_COUNTER_SIGNER) || (dwLastError == TRUST_E_CERT_SIGNATURE)) { if ((dwLastError == TRUST_E_COUNTER_SIGNER) && !isSHA2CertificateSupported(TRUE)) { /* The counter signature is always invalid on a machine that doesn't support SHA-2. Ignore. */ } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("A signature was found in \"%s\", but checksum failed: (Errorcode: 0x%x) %s%s\nThe Wrapper will shutdown!"), pwszSourceFile, lStatus, lastErrMsg, buffer ? buffer : TEXT("")); /* Stop the Wrapper. */ wrapperStopProcess(wrapperData->errorExitCode, TRUE); wrapperData->wState = WRAPPER_WSTATE_STOPPING; } } else if (dwLastError != CERT_E_CHAINING) { /* CERT_E_CHAINING will be handled below. A common error is TRUST_E_TIME_STAMP (no network?), but also print any other error here. */ log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("A signature was found in \"%s\", but checksum failed: (Errorcode: 0x%x) %s%s\nThe error is not directly related to the Wrapper's signature, therefore continue..."), pwszSourceFile, lStatus, lastErrMsg, buffer ? buffer : TEXT("")); } free(buffer); break; } /* free memory allocated by WinVerifyTrust */ WinTrustData.dwStateAction = WTD_STATEACTION_CLOSE; WinVerifyTrust(NULL, &WVTPolicyGUID, &WinTrustData); /* Check certificate chain, if 1) the file was signed, 2) the configured log level has at least one log destination, 3) the wrapper is not already stopping. */ if (lStatus != TRUST_E_NOSIGNATURE) { if (logLevel != LEVEL_NONE) { if ((wrapperData->wState != WRAPPER_WSTATE_STOPPING) && (wrapperData->wState != WRAPPER_WSTATE_STOPPED)) { buffer = NULL; if (!pSignerCertContext) { getCertificateBaseInfo(pwszSourceFile, &hStore, &pSignerInfo, &pSignerCertContext); /* The certificate info was not yet printed. Build a buffer to pass to checkCertificateChain(). */ buffer = printWholeCertificateInfo(hStore, pSignerInfo, pSignerCertContext); } /* Now check the certificate chain. The function may take some time to execute. So we call it after the verification * of the digital signature, which is more important to complete within the wrapper.startup_thread.timeout. */ ret = checkCertificateChain(pSignerCertContext, buffer, logLevel); if (ret && (dwLastError == CERT_E_CHAINING)) { /* Should not happen: WinVerifyTrust() returned an error on the chain certificate but not checkCertificateChain(). */ lastErrMsg = getErrorText(dwLastError, NULL); log_printf(WRAPPER_SOURCE_WRAPPER, logLevel, TEXT("%s (Errorcode: 0x%x)%s"), lastErrMsg, dwLastError, buffer ? buffer : TEXT("")); } free(buffer); } } } freeCertificateBaseInfo(hStore, pSignerInfo, pSignerCertContext); #ifdef WRAPPER_DEBUG_CERTIFICATE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("verifyEmbeddedSignature() END")); #endif } /** * Does some special setup for when we are running as a launcher. */ void enterLauncherMode() { /* Tell the logger to use the launcher source in place of the actual one so it is clear those entries are coming from the launcher and not the actual service. */ setLauncherSource(); } #ifndef CUNIT void _tmain(int argc, TCHAR **argv) { int localeSet = FALSE; int defaultLocaleFailed = FALSE; int silentMask; int result; #if defined(_DEBUG) int i; #endif BOOL DEPApiAvailable = FALSE; BOOL DEPStatus = FALSE; DWORD DEPError = ERROR_SUCCESS; HMODULE hk; FARPROC pfnSetDEP; /* The StartServiceCtrlDispatcher requires this table to specify * the ServiceMain function to run in the calling process. The first * member in this example is actually ignored, since we will install * our service as a SERVICE_WIN32_OWN_PROCESS service type. The NULL * members of the last entry are necessary to indicate the end of * the table; */ SERVICE_TABLE_ENTRY serviceTable[2]; /* Enable DEP as soon as possible in the main method. * - Use SetProcessDEPPolicy() instead of the /DYNAMICBASE link * option to allow DEP on WIN XP SP3 (/DYNAMICBASE is from Vista). * - Load it dynamically to allow the Wrapper running normally * (but without DEP) on older versions of Windows. * - Retain the result of the invocation to log any messages after * the logging will be initialized. */ if (_tcscmp(wrapperBits, TEXT("32")) == 0) { hk = GetModuleHandle(TEXT("KERNEL32.DLL")); if (hk) { pfnSetDEP = GetProcAddress(hk, "SetProcessDEPPolicy"); if (pfnSetDEP) { DEPApiAvailable = TRUE; DEPStatus = (BOOL)pfnSetDEP(0x00000001); /* 0x00000001 = PROCESS_DEP_ENABLE */ if (!DEPStatus) { DEPError = getLastError(); } } FreeLibrary(hk); } } if (!localeSet) { /* No need to log anything. Set the default locale so any startup error messages will have a chance of working. * We will go back and try to set the actual locale again later once it is configured. */ if (!_tsetlocale(LC_ALL, TEXT(""))) { defaultLocaleFailed = TRUE; } } if (buildSystemPath()) { appExit(1); return; /* For clarity. */ } if ((argc > 1) && (argv[1][0] == TEXT('-')) && isPromptCallCommand(&argv[1][1])) { /* This is a request from the launching script. All log output should be disabled. */ toggleLogDestinations(LOG_DESTINATION_ALL, FALSE); } else if ((argc > 3) && (_tcsstr(argv[3], TEXT("wrapper.internal.namedpipe=")) != NULL)) { /* The process was elevated, any configuration issue has already been logged by the parent process. */ silentMask = LOG_DESTINATION_ALL; toggleLogDestinations(silentMask, FALSE); } if (wrapperInitialize()) { appExit(1); return; /* For clarity. */ } /* Store the DEP status to print it after the version banner. */ wrapperData->DEPApiAvailable = DEPApiAvailable; wrapperData->DEPStatus = DEPStatus; wrapperData->DEPError = DEPError; /* Main thread initialized in wrapperInitialize. */ /* Enclose the rest of the program in a try catch block so we can * display and log useful information should the need arise. This * must be done after logging has been initialized as the catch * block makes use of the logger. */ __try { #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Wrapper DEBUG build!")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Logging initialized.")); #endif /* Get the current process. */ wrapperData->wrapperProcess = GetCurrentProcess(); wrapperData->wrapperPID = GetCurrentProcessId(); if (initializeWinSock()) { appExit(1); return; /* For clarity. */ } if (setWorkingDir()) { appExit(1); return; /* For clarity. */ } if (collectUserInfo()) { appExit(1); return; /* For clarity. */ } #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Working directory set.")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Arguments:")); for (i = 0; i < argc; i++) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" argv[%d]=%s"), i, argv[i]); } #endif /* Parse the command and configuration file from the command line. */ if (!wrapperParseArguments(argc, argv)) { appExit(1); return; /* For clarity. */ } /* Set launcher mode as soon as possible after the arguments are parsed. */ if(!strcmpIgnoreCase(wrapperData->argCommand, TEXT("su")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-setup")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("td")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-teardown")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("i")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-install")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("it")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-installstart")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("u")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-update")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("r")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-remove")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("t")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-start")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("a")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-pause")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("e")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-resume")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("p")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-stop")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("l")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-controlcode")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("d")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-dump")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("q")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-query")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("qs")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-querysilent"))) { enterLauncherMode(); } if (defaultLocaleFailed) { log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Unable to set the locale.")); } wrapperLoadHostName(); /* At this point, we have a command, confFile, and possibly additional arguments. */ if (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("?")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-help"))) { /* User asked for the usage. */ setSimpleLogLevels(); wrapperUsage(argv[0]); appExit(0); return; /* For clarity. */ } else if (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("v")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-version"))) { /* User asked for version. */ setSimpleLogLevels(); wrapperVersionBanner(); appExit(0); return; /* For clarity. */ } else if (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("h")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-hostid"))) { /* Print out a banner containing the HostId. */ setSimpleLogLevels(); wrapperVersionBanner(); showHostIds(LEVEL_STATUS, TRUE); appExit(0); return; /* For clarity. */ } if (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("s")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-service"))) { /* We are running as a Service, set a flag to remember it while loading the configuration. */ wrapperData->isConsole = FALSE; if (!isVista()) { /* When running as a service on versions of Windows older than Vista, * the environment variables must first be loaded from the registry. */ if (wrapperLoadEnvFromRegistry()) { appExit(1); return; /* For clarity. */ } } } /* Load the properties. */ /* To make the WRAPPER_LANG references in the configuration work correctly, * it is necessary to load the configuration twice. * The first time, we want to ignore the return value. Any errors will be * suppressed and will get reported again the second time through. */ /* From version 3.5.27, the community edition will also preload the configuration properties. */ wrapperLoadConfigurationProperties(TRUE); if (wrapperLoadConfigurationProperties(FALSE)) { /* Unable to load the configuration. Any errors will have already * been reported. */ if (wrapperData->argConfFileDefault && !wrapperData->argConfFileFound) { /* The config file that was being looked for was default and * it did not exist. Show the usage. */ wrapperUsage(argv[0]); } else { /* There might have been some queued messages logged on configuration load. Queue the following message to make it appear last. */ log_printf_queue(TRUE, WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" The Wrapper will stop.")); } appExit(wrapperData->errorExitCode); return; /* For clarity. */ } /* Set the default umask of the Wrapper process. */ _umask(wrapperData->umask); /* Perform the specified command */ if (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("-jvm_bits"))) { if (!wrapperInitChildPipe()) { /* Generate the command used to get the Java version but don't stop on failure. */ if (!wrapperBuildJavaVersionCommand()) { wrapperLaunchJavaVersion(); } } appExit(wrapperData->jvmBits); return; /* For compiler. */ } else if (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("-request_delta_binary_bits"))) { /* Otherwise return the binary bits */ appExit(_tcscmp(wrapperBits, TEXT("64")) == 0 ? 64 : 32); return; /* For compiler. */ } else if(!strcmpIgnoreCase(wrapperData->argCommand, TEXT("su")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-setup"))) { /* Setup the Wrapper */ /* Always auto close the log file to keep the output in synch. */ setLogfileAutoClose(TRUE); /* are we elevated ? */ if (!isElevated()) { appExit(elevateThis(argc, argv)); } else { /* are we launched secondary? */ if (isSecondary() && duplicateSTD() == FALSE) { appExit(wrapperData->errorExitCode); return; } wrapperExit(wrapperSetup(FALSE)); } return; /* For clarity. */ } else if(!strcmpIgnoreCase(wrapperData->argCommand, TEXT("td")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-teardown"))) { /* Teardown the Wrapper */ /* Always auto close the log file to keep the output in synch. */ setLogfileAutoClose(TRUE); /* are we elevated ? */ if (!isElevated()) { appExit(elevateThis(argc, argv)); } else { /* are we launched secondary? */ if (isSecondary() && duplicateSTD() == FALSE) { appExit(wrapperData->errorExitCode); return; } wrapperExit(wrapperTeardown(FALSE)); } return; /* For clarity. */ } else if(!strcmpIgnoreCase(wrapperData->argCommand, TEXT("i")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-install"))) { /* Install an NT service */ /* Always auto close the log file to keep the output in synch. */ setLogfileAutoClose(TRUE); wrapperCheckForMappedDrives(); /* are we elevated ? */ if (!isElevated()) { appExit(elevateThis(argc, argv)); } else { /* are we launched secondary? */ if (isSecondary() && duplicateSTD() == FALSE) { appExit(wrapperData->errorExitCode); return; } setupSyslogRegistration(TRUE); wrapperExit(wrapperInstall()); } return; /* For clarity. */ } else if(!strcmpIgnoreCase(wrapperData->argCommand, TEXT("it")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-installstart"))) { /* Install and Start an NT service */ /* Always auto close the log file to keep the output in synch. */ setLogfileAutoClose(TRUE); wrapperCheckForMappedDrives(); if (!isElevated()) { appExit(elevateThis(argc, argv)); } else { /* are we launched secondary? */ if (isSecondary() && duplicateSTD() == FALSE) { appExit(wrapperData->errorExitCode); return; } setupSyslogRegistration(TRUE); result = wrapperInstall(); if (!result) { result = wrapperStartService(); } wrapperExit(result); } return; /* For clarity. */ } else if (!strcmpIgnoreCase(wrapperData->argCommand, TEXT("r")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-remove"))) { /* Remove an NT service */ /* Always auto close the log file to keep the output in synch. */ setLogfileAutoClose(TRUE); if (!isElevated()) { appExit(elevateThis(argc, argv)); } else { /* are we launched secondary? */ if (isSecondary() && duplicateSTD() == FALSE) { appExit(wrapperData->errorExitCode); return; } /* don't call teardown here because it may be confusing if the user still wants to use the Wrapper as a console. */ wrapperExit(wrapperRemove()); } return; /* For clarity. */ } else if(!strcmpIgnoreCase(wrapperData->argCommand, TEXT("t")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-start"))) { /* Start an NT service */ /* Always auto close the log file to keep the output in synch. */ setLogfileAutoClose(TRUE); wrapperCheckForMappedDrives(); if (!isElevated()) { appExit(elevateThis(argc, argv)); } else { /* are we launched secondary? */ if (isSecondary() && duplicateSTD() == FALSE) { appExit(wrapperData->errorExitCode); return; } wrapperExit(wrapperStartService()); } return; /* For clarity. */ } else if(!strcmpIgnoreCase(wrapperData->argCommand, TEXT("a")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-pause"))) { /* Pause a started NT service */ /* Always auto close the log file to keep the output in synch. */ setLogfileAutoClose(TRUE); if (!isElevated()) { appExit(elevateThis(argc, argv)); } else { /* are we launched secondary? */ if (isSecondary() && duplicateSTD() == FALSE) { appExit(wrapperData->errorExitCode); return; } wrapperExit(wrapperPauseService()); } return; /* For clarity. */ } else if(!strcmpIgnoreCase(wrapperData->argCommand, TEXT("e")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-resume"))) { /* Resume a paused NT service */ /* Always auto close the log file to keep the output in synch. */ setLogfileAutoClose(TRUE); if (!isElevated()) { appExit(elevateThis(argc, argv)); } else { /* are we launched secondary? */ if (isSecondary() && duplicateSTD() == FALSE) { appExit(wrapperData->errorExitCode); return; } wrapperExit(wrapperResumeService()); } return; /* For clarity. */ } else if(!strcmpIgnoreCase(wrapperData->argCommand, TEXT("p")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-stop"))) { /* Stop an NT service */ /* Always auto close the log file to keep the output in synch. */ setLogfileAutoClose(TRUE); if (!isElevated()) { appExit(elevateThis(argc, argv)); } else { /* are we launched secondary? */ if (isSecondary() && duplicateSTD() == FALSE) { appExit(wrapperData->errorExitCode); return; } wrapperExit(wrapperStopService(TRUE)); } return; /* For clarity. */ } else if(!strcmpIgnoreCase(wrapperData->argCommand, TEXT("l")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-controlcode"))) { /* Send a control code to an NT service */ /* Always auto close the log file to keep the output in synch. */ setLogfileAutoClose(TRUE); if (!isElevated()) { appExit(elevateThis(argc, argv)); } else { /* are we launched secondary? */ if (isSecondary() && duplicateSTD() == FALSE) { appExit(wrapperData->errorExitCode); return; } wrapperExit(wrapperSendServiceControlCode(argv, wrapperData->argCommandArg)); } return; /* For clarity. */ } else if(!strcmpIgnoreCase(wrapperData->argCommand, TEXT("d")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-dump"))) { /* Request a thread dump */ /* Always auto close the log file to keep the output in synch. */ setLogfileAutoClose(TRUE); if (!isElevated()) { appExit(elevateThis(argc, argv)); } else { /* are we launched secondary? */ if (isSecondary() && duplicateSTD() == FALSE) { appExit(wrapperData->errorExitCode); return; } wrapperExit(wrapperRequestThreadDump(argv)); } return; /* For clarity. */ } else if(!strcmpIgnoreCase(wrapperData->argCommand, TEXT("q")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-query"))) { /* Return service status with console output. */ /* This command can be called without conf file IF the name of the service is specified. Validity has been verified earlier. */ /* Always auto close the log file to keep the output in synch. */ setLogfileAutoClose(TRUE); if (!isElevated()) { appExit(elevateThis(argc, argv)); } else { /* are we launched secondary? */ if (isSecondary() && duplicateSTD() == FALSE) { appExit(wrapperData->errorExitCode); return; } if (wrapperData->argCommandArg) { appExit(wrapperServiceStatus(wrapperData->argCommandArg, wrapperData->argCommandArg, TRUE)); } else { appExit(wrapperServiceStatus(wrapperData->serviceName, wrapperData->serviceDisplayName, TRUE)); } } return; /* For clarity. */ } else if(!strcmpIgnoreCase(wrapperData->argCommand, TEXT("qs")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-querysilent"))) { /* Return service status without console output. */ /* This command can be called without conf file IF the name of the service is specified. Validity has been verified earlier. */ /* Always auto close the log file to keep the output in synch. */ setLogfileAutoClose(TRUE); if (!isElevated()) { appExit(elevateThis(argc, argv)); } else { /* are we launched secondary? */ if (isSecondary() && duplicateSTD() == FALSE) { appExit(wrapperData->errorExitCode); return; } if (wrapperData->argCommandArg) { appExit(wrapperServiceStatus(wrapperData->argCommandArg, wrapperData->argCommandArg, FALSE)); } else { appExit(wrapperServiceStatus(wrapperData->serviceName, wrapperData->serviceDisplayName, FALSE)); } } return; /* For clarity. */ } else if(!strcmpIgnoreCase(wrapperData->argCommand, TEXT("c")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-console"))) { /* Run as a console application */ /* Load any dynamic functions. */ loadDLLProcs(); /* Initialize the invocation mutex as necessary, exit if it already exists. */ if (initInvocationMutex()) { appExit(wrapperData->exitCode); return; /* For clarity. */ } if (checkPidFile()) { /* The pid file exists and we are strict, so exit (cleanUpPIDFilesOnExit has not been turned on yet, so we will exit without cleaning the pid files). */ appExit(wrapperData->errorExitCode); return; /* For clarity. */ } /* From now on: * - all pid files will be cleaned when the Wrapper exits, * - any existing file will be owerwritten. */ cleanUpPIDFilesOnExit = TRUE; if (wrapperWriteStartupPidFiles()) { appExit(wrapperData->errorExitCode); return; /* For clarity. */ } appExit(wrapperRunConsole()); return; /* For clarity. */ } else if(!strcmpIgnoreCase(wrapperData->argCommand, TEXT("s")) || !strcmpIgnoreCase(wrapperData->argCommand, TEXT("-service"))) { /* Run as a service */ wrapperCheckForMappedDrives(); /* Load any dynamic functions. */ loadDLLProcs(); /* Prepare the service table */ serviceTable[0].lpServiceName = wrapperData->serviceName; serviceTable[0].lpServiceProc = (LPSERVICE_MAIN_FUNCTION)wrapperServiceMain; serviceTable[1].lpServiceName = NULL; serviceTable[1].lpServiceProc = NULL; _tprintf(TEXT("Attempting to start %s as an NT service.\n"), wrapperData->serviceDisplayName); _tprintf(TEXT("\nCalling StartServiceCtrlDispatcher...please wait.\n")); /* Start the service control dispatcher. * The ServiceControlDispatcher will call the wrapperServiceMain method. */ if (!StartServiceCtrlDispatcher(serviceTable)) { _tprintf(TEXT("\n")); _tprintf(TEXT("StartServiceControlDispatcher failed!\n")); _tprintf(TEXT("\n")); _tprintf(TEXT("The -s and --service commands should only be called by the Windows\n")); _tprintf(TEXT("ServiceManager to control the Wrapper as a service, and is not\n")); _tprintf(TEXT("designed to be run manually by the user.\n")); _tprintf(TEXT("\n")); _tprintf(TEXT("For help, type\n")); _tprintf(TEXT("%s -?\n"), argv[0]); _tprintf(TEXT("\n")); appExit(wrapperData->errorExitCode); return; /* For clarity. */ } /* We will get here when the service starts to stop */ /* As wrapperServiceMain should take care of shutdown, wait 10 sec to give some time for its shutdown * but the process should exit before the sleep completes. */ wrapperSleep(10000); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Timed out waiting for wrapperServiceMain")); appExit(wrapperData->errorExitCode); return; /* For clarity. */ } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unrecognized option: -%s"), wrapperData->argCommand); wrapperUsage(argv[0]); appExit(wrapperData->errorExitCode); return; /* For clarity. */ } } __except (exceptionFilterFunction(GetExceptionInformation())) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("<-- Wrapper Stopping due to error")); appExit(wrapperData->errorExitCode); return; /* For clarity. */ } } #endif #define IN_SIZE 1024 /* * This function will connect to the secondary/elevated wrapper process and * read all output from stdout and stderr. Furthermore it will handle input * being sent to stdin. This function won't return until the client closed all * named pipes connected. * @param in - the File HANDLE for the outbound channel to write to the 2nd process * @param out, err - the File HANDLEs for the inbound channel to read from the 2nd process * * @return TRUE if everything worked well. FALSE otherwise. */ BOOL readAndWriteNamedPipes(HANDLE in, HANDLE out, HANDLE err) { TCHAR inbuf[IN_SIZE], *secret; TCHAR *buffer = NULL; DWORD bufferSize = 0; DWORD currentBlockAvail, bytesRead, inWritten, ret; BOOL fConnected, outClosed = FALSE, errClosed = FALSE; /* the named pipes are nonblocking, so loop until an connection could * have been established with the secondary process (or an error occurred) */ do { /* ConnectNamedPipe does rather wait until a connection was established However, the inbound pipes are non-blocking, so ConnectNamedPipe immediately returns. So call it looped...*/ fConnected = ConnectNamedPipe(out, NULL); } while((fConnected == 0) && (GetLastError() == ERROR_PIPE_LISTENING)); /* check for error */ /* if ERROR_PIPE_CONNECTED it just means that while ConnectNamedPipe(..) was * called again, in the meantime the client connected. So in fact that's what we want. * WIN-7: if ERROR_NO_DATA, it means that the process has already been gone, probably an error in the start (but not in the pipe) * it might even be very likely there is data in stderr to retrieve */ if ((fConnected == 0) && (GetLastError() != ERROR_PIPE_CONNECTED) && (GetLastError() != ERROR_NO_DATA)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("The connect to stdout of the elevated process failed: %s"), getLastErrorText()); return FALSE; } /* Same as above */ do { fConnected = ConnectNamedPipe(err, NULL); } while((fConnected == 0) && (GetLastError() == ERROR_PIPE_LISTENING)); if ((fConnected == 0) && (GetLastError() != ERROR_PIPE_CONNECTED) && (GetLastError() != ERROR_NO_DATA)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("The connect to stderr of the elevated process failed: %s"), getLastErrorText()); return FALSE; } do { /* out */ if (!outClosed) { currentBlockAvail = 0; /* Check how much data is available for reading... */ ret = PeekNamedPipe(out, NULL, 0, NULL, ¤tBlockAvail, NULL); if ((ret == 0) && (GetLastError() == ERROR_BROKEN_PIPE)) { /* ERROR_BROKEN_PIPE - the client has closed the pipe. So most likely it just exited */ outClosed = TRUE; } /* currentBlockAvail is already in bytes! */ if (ret && (currentBlockAvail > 0)) { if (bufferSize < currentBlockAvail + sizeof(TCHAR)) { bufferSize = __max(512, currentBlockAvail + sizeof(TCHAR)); free(buffer); buffer = malloc(bufferSize); if (!buffer) { outOfMemory(TEXT("RAWNP"), 1); return FALSE; } } /* Clean the buffer before each read, as we don't want old stuff */ memset(buffer,0, bufferSize); if (ReadFile(out, buffer, bufferSize, &bytesRead, NULL) == TRUE) { /* if the message we just read in, doesn't have a new line, it means, that we most likely got the secondary process prompting sth. */ if (buffer[_tcslen(buffer) - 1] != TEXT('\n')) { /* To make sure, check if it is indeed waiting for input */ if (WaitForSingleObject(in, 1000) == WAIT_OBJECT_0) { /* Clean the input buffer before each read */ memset(inbuf, 0, sizeof(inbuf)); /* A prompt can have an "n" - normal at the end. So this means, that we prompt with echo */ if (buffer[_tcslen(buffer) - 1] == TEXT('n')) { /* clean the mark */ buffer[_tcslen(buffer) - 1] = TEXT('\0'); /* show the prompt */ printToConsole(buffer, stdout); /* and prompt */ _fgetts(inbuf, IN_SIZE, stdin); if (WriteFile(in, inbuf, (DWORD)(_tcslen(inbuf)) * sizeof(TCHAR), &inWritten, NULL) == FALSE) { /* something happened with the named pipe, get out */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Writing to the elevated process failed (%d): %s"), GetLastError(), getLastErrorText()); free(buffer); return FALSE; } } else if (buffer[_tcslen(buffer) - 1] == TEXT('p')) { /* A prompt can have an "p" - password at the end. So this means, that we prompt without showing the input on the screen */ buffer[_tcslen(buffer) - 1] = TEXT('\0'); /* show the prompt */ printToConsole(buffer, stdout); /* now read secret, readPassword already works with allocating a buffer (max 64(+1) character supported) */ secret = readPassword(); if (!secret) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Failed to read input from the console. (%d): %s"), GetLastError(), getLastErrorText()); free(buffer); return FALSE; } _tcsncpy(inbuf, secret, IN_SIZE); wrapperSecureFreeStrW(secret); /* "secret" does not have any delimiter we could use, so send the whole inbuf buffer */ /* this is the downside of using MESSAGE pipes */ if (WriteFile(in, inbuf, (DWORD)sizeof(inbuf), &inWritten, NULL) == FALSE) { wrapperSecureZero(inbuf, sizeof(inbuf)); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Writing to the elevated process failed (%d): %s"), GetLastError(), getLastErrorText()); free(buffer); return FALSE; } wrapperSecureZero(inbuf, sizeof(inbuf)); } else { /* no additional for the prompt was provided, but WaitForSingleObject indicates, that stdin is expecting input. Handle this as if "n" - normal was specified (without clearing the mark) /* show the prompt */ printToConsole(buffer, stdout); /* and prompt */ _fgetts(inbuf, IN_SIZE, stdin); if (WriteFile(in, inbuf, (DWORD)(_tcslen(inbuf)) * sizeof(TCHAR), &inWritten, NULL) == FALSE) { /* something happened with the named pipe, get out */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Writing to the elevated process failed (%d): %s"), GetLastError(), getLastErrorText()); free(buffer); return FALSE; } } /* this is important! for transparency writing to the elevated process works as without this layer, however, not flushing the buffer will make _getts just keep blocking until the buffer over there is full (what we don't want) */ FlushFileBuffers(in); } else { /* A timeout occurred! probably a print without a newline. */ printToConsoleLn(buffer, stdout); } } else { /* This is the normal case - just output */ /* print the message on the screen */ printToConsole(buffer, stdout); } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Reading stdout of the elevated process failed (%d): %s"), GetLastError(), getLastErrorText()); free(buffer); return FALSE; } } } /* err */ /* it works almost exactly as reading stdout, except no input will be checked */ if (!errClosed) { currentBlockAvail = 0; ret = PeekNamedPipe(err, NULL, 0, NULL, ¤tBlockAvail, NULL); if ((ret == 0) && (GetLastError() == ERROR_BROKEN_PIPE)) { errClosed = TRUE; } if (ret && (currentBlockAvail > 0)) { if (bufferSize < currentBlockAvail + sizeof(TCHAR)) { bufferSize = __max(512, currentBlockAvail + sizeof(TCHAR)); free(buffer); buffer = malloc(bufferSize); if (!buffer) { outOfMemory(TEXT("RAWNP"), 2); return FALSE; } } /* Clean the buffer before each read, as we don't want old stuff */ memset(buffer,0, bufferSize); if (ReadFile(err, buffer, bufferSize, &bytesRead, NULL) == TRUE) { printToConsole(buffer, stderr); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Reading stderr of the elevated process failed (%d): %s"), GetLastError(), getLastErrorText()); free(buffer); return FALSE; } } } } while(!errClosed || !outClosed); free(buffer); return TRUE; } /* This function needs to get called from the elevated/secondary process. * It will open the named pipes, the caller has establishes and redirects stdin, stdout, stderr * to this named pipes. * All this call is doing should be transparent and (except the stdin prompting) not interfere * the secondary process (i.e. log files will still work and logging to logfile actually will be done here) * * @return If successful this function will return TRUE, FALSE otherwise */ BOOL duplicateSTD() { TCHAR* strNamedPipeNameIn, *strNamedPipeNameOut, *strNamedPipeNameErr; const TCHAR *pipeBaseName; HANDLE pipeIn, pipeOut, pipeErr; int ret, fdOut, fdIn, fdErr; size_t len; /* Restore the configured log levels so that the elevated process resumes its logging from that point. */ toggleLogDestinations(LOG_DESTINATION_ALL, TRUE); if ((!wrapperData->isDebugging) && (getLowLogLevel() <= LEVEL_DEBUG)) { wrapperData->isDebugging = TRUE; } /* get the base name for the named pipe, each channel will append an additional extension */ pipeBaseName = getStringProperty(properties, TEXT("wrapper.internal.namedpipe"), NULL); if (!pipeBaseName) { return FALSE; } len = _tcslen(pipeBaseName) + 13; strNamedPipeNameIn = malloc(sizeof(TCHAR) * len); if (!strNamedPipeNameIn) { outOfMemory(TEXT("MSE"), 1); return FALSE; } _sntprintf(strNamedPipeNameIn, len, TEXT("\\\\.\\pipe\\%sINN"), pipeBaseName); strNamedPipeNameOut = malloc(sizeof(TCHAR) * len); if (!strNamedPipeNameOut) { free(strNamedPipeNameIn); outOfMemory(TEXT("MSE"), 2); return FALSE; } _sntprintf(strNamedPipeNameOut, len, TEXT("\\\\.\\pipe\\%sOUT"), pipeBaseName); strNamedPipeNameErr = malloc(sizeof(TCHAR) * len); if (!strNamedPipeNameErr) { free(strNamedPipeNameIn); free(strNamedPipeNameOut); outOfMemory(TEXT("MSE"), 3); return FALSE; } _sntprintf(strNamedPipeNameErr, len, TEXT("\\\\.\\pipe\\%sERR"), pipeBaseName); #ifdef _DEBUG _tprintf(TEXT("going to open %s, %s and %s\n"), strNamedPipeNameIn, strNamedPipeNameOut, strNamedPipeNameErr); #endif /* Use CreateFile to connect to the Named Pipes. */ if ((pipeIn = CreateFile(strNamedPipeNameIn, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Connect to stdin pipe failed (%d): %s"), GetLastError(), getLastErrorText()); ret = FALSE; } else { if ((pipeOut = CreateFile(strNamedPipeNameOut, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Connect to stdout pipe failed (%d): %s"), GetLastError(), getLastErrorText()); ret = FALSE; } else { if ((pipeErr = CreateFile(strNamedPipeNameErr, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Connect to stderr pipe failed (%d): %s"), GetLastError(), getLastErrorText()); ret = FALSE; } else { /* This is magic */ if (((fdIn = _open_osfhandle((long)pipeIn, 0)) != -1) && ((fdErr = _open_osfhandle((long)pipeErr, 0)) != -1) && ((fdOut = _open_osfhandle((long)pipeOut, 0)) != -1)) { if ((_dup2(fdIn, 0) == 0) && (_dup2(fdOut, 1) == 0) && (_dup2(fdErr, 2) == 0)) { ret = TRUE; #ifdef _DEBUG _ftprintf(stderr, TEXT("12345\n"));fflush(NULL); _ftprintf(stderr, TEXT("1234567890\n"));fflush(NULL); _ftprintf(stdout, TEXT("12345\n"));fflush(NULL); _ftprintf(stdout, TEXT("1234567890\n"));fflush(NULL); #endif } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("ERROR: Could not redirect the file descriptors to the client sided named pipes.")); } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("ERROR: Could not acquire the file descriptors for the client sided named pipes.")); } } } } free(strNamedPipeNameErr); free(strNamedPipeNameOut); free(strNamedPipeNameIn); return ret; } /* This function first creates 3 named pipes (2 inbound & 1 outbound) for establishing the connection. * Then it will ask the user to allow elevation for a secondary process. * And finally (if elevation granted) wait and call readAndWriteNamedPipes until the elevated process finishes. * * @param hwnd - The current window handle. * @param pszVerb - the verb defining the action ShellExecuteEx will perform * @param pszPath - the path to the executable going to be called * @param pszParameters - the parameters for the executable * @param pszDirectory - the working directory the process will have (if NULL the working direcory context will be inherited) * @param namedPipeName - the base name for the named pipes for the IPC between us and the new process. * @return the exit code of the elevated process */ int myShellExec(HWND hwnd, LPCTSTR pszVerb, LPCTSTR pszPath, LPCTSTR pszParameters, LPCTSTR pszDirectory, TCHAR* namedPipeName) { DWORD returnValue; SHELLEXECUTEINFO shex; HANDLE hNamedPipeIn, hNamedPipeOut, hNamedPipeErr; TCHAR* strNamedPipeNameIn, *strNamedPipeNameOut, *strNamedPipeNameErr; int ret = wrapperData->errorExitCode; size_t len; /* first we generate the filenames for the named pipes based on namedPipeName */ len = _tcslen(namedPipeName) + 4 + 9; strNamedPipeNameIn = malloc(sizeof(TCHAR) * len); if (!strNamedPipeNameIn) { outOfMemory(TEXT("MSE"), 1); return wrapperData->errorExitCode; } _sntprintf(strNamedPipeNameIn, len, TEXT("\\\\.\\pipe\\%sINN"), namedPipeName); strNamedPipeNameOut = malloc(sizeof(TCHAR) * len); if (!strNamedPipeNameOut) { free(strNamedPipeNameIn); outOfMemory(TEXT("MSE"), 2); return wrapperData->errorExitCode; } _sntprintf(strNamedPipeNameOut, len, TEXT("\\\\.\\pipe\\%sOUT"), namedPipeName); strNamedPipeNameErr = malloc(sizeof(TCHAR) * len); if (!strNamedPipeNameErr) { free(strNamedPipeNameIn); free(strNamedPipeNameOut); outOfMemory(TEXT("MSE"), 3); return wrapperData->errorExitCode; } _sntprintf(strNamedPipeNameErr, len, TEXT("\\\\.\\pipe\\%sERR"), namedPipeName); /* create the process information */ memset(&shex, 0, sizeof(shex)); shex.cbSize = sizeof(SHELLEXECUTEINFO); shex.fMask = SEE_MASK_NO_CONSOLE | SEE_MASK_NOCLOSEPROCESS; shex.hwnd = hwnd; shex.lpVerb = pszVerb; shex.lpFile = pszPath; shex.lpParameters = pszParameters; shex.lpDirectory = pszDirectory; #ifdef _DEBUG shex.nShow = SW_SHOWNORMAL; #else shex.nShow = SW_HIDE; #endif hNamedPipeIn = CreateNamedPipe(strNamedPipeNameIn, PIPE_ACCESS_OUTBOUND , PIPE_TYPE_BYTE | // message type pipe PIPE_READMODE_BYTE | // message-read mode PIPE_WAIT, // blocking mode 1, // max. instances 1024 * sizeof(TCHAR), // output buffer size 1024*sizeof(TCHAR), // input buffer size 0, // client time-out NULL); // default security attribute if (hNamedPipeIn == INVALID_HANDLE_VALUE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Stdin CreateNamedPipe failed (%d): %s"), GetLastError(), getLastErrorText()); ret = wrapperData->errorExitCode; } else { hNamedPipeOut = CreateNamedPipe(strNamedPipeNameOut, PIPE_ACCESS_INBOUND , PIPE_TYPE_MESSAGE | // message type pipe PIPE_READMODE_MESSAGE | // message-read mode PIPE_NOWAIT, // nonblocking mode 1, // max. instances 512 * sizeof(TCHAR), // output buffer size 512 * sizeof(TCHAR), // input buffer size 0, // client time-out NULL); // default security attribute if (hNamedPipeOut == INVALID_HANDLE_VALUE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Stdout CreateNamedPipe failed (%d): %s"), GetLastError(), getLastErrorText()); ret = wrapperData->errorExitCode; } else { hNamedPipeErr = CreateNamedPipe(strNamedPipeNameErr, PIPE_ACCESS_INBOUND , PIPE_TYPE_MESSAGE | // message type pipe PIPE_READMODE_MESSAGE | // message-read mode PIPE_NOWAIT, // nonblocking mode 1, // max. instances 512 * sizeof(TCHAR), // output buffer size 512 * sizeof(TCHAR), // input buffer size 0, // client time-out NULL); // default security attribute if (hNamedPipeErr == INVALID_HANDLE_VALUE) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Stderr CreateNamedPipe failed (%d): %s"), GetLastError(), getLastErrorText()); ret = wrapperData->errorExitCode; } else { /* Now launch the process */ if (ShellExecuteEx(&shex) == TRUE) { if (shex.hProcess != NULL) { /* now read and write the pipes */ if (readAndWriteNamedPipes(hNamedPipeIn, hNamedPipeOut, hNamedPipeErr) != TRUE) { // the error should have already been reported. } /* Wait up to 1 sec to check if the elevated process really exited */ returnValue = WaitForSingleObject(shex.hProcess, 1000); if (returnValue == WAIT_OBJECT_0) { if (!GetExitCodeProcess(shex.hProcess, &ret)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("WaitThread for Backend-Process: %s failed! (%d): %s"), TEXT("GetExitCodeProcess"), GetLastError(), getLastErrorText()); ret = wrapperData->errorExitCode; } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("The elevated Wrapper process is still alive. Trying to kill it. (%d): %s"), GetLastError(), getLastErrorText()); if (TerminateProcess(shex.hProcess, 1) == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Failed to kill the elevated Wrapper process. (%d): %s"), GetLastError(), getLastErrorText()); } ret = wrapperData->errorExitCode; } } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Failed to obtain elevated status. (%d): %s"), GetLastError(), getLastErrorText()); ret = wrapperData->errorExitCode; } CloseHandle(hNamedPipeErr); } CloseHandle(hNamedPipeOut); } CloseHandle(hNamedPipeIn); } free(strNamedPipeNameIn); free(strNamedPipeNameOut); free(strNamedPipeNameErr); return ret; } /* * This is just a wrapper function between elevateThis and myShellExec filling in the verb * For more information please refer to myShellExec */ int runElevated(__in LPCTSTR pszPath, __in_opt LPCTSTR pszParameters, __in_opt LPCTSTR pszDirectory, TCHAR* namedPipeName) { return myShellExec(NULL, TEXT("runas"), pszPath, pszParameters, pszDirectory, namedPipeName); } /* * This is the entry point on the user side for creating an elevated process. * UAC does not allow to give a running process elevated privileges, so the * wrapper has to create a copy of the current process, arm it with elevated * privileges and take care of IPC. * * @return exit code of backend process */ int elevateThis(int argc, TCHAR **argv) { int i, ret = 0; size_t len = 0; TCHAR szPath[_MAX_PATH]; TCHAR *parameter; TCHAR* strNamedPipeName; const TCHAR* commandProps = TEXT("wrapper.internal.namedpipe="); /* get the file name of the binary, we can't trust argv[0] as the working * directory might have been changed. */ if (GetModuleFileName(NULL, szPath, _MAX_PATH)) { /* seed the pseudo-random generator */ srand((unsigned)time(NULL)); strNamedPipeName = malloc(sizeof(TCHAR) * 11); if (!strNamedPipeName) { outOfMemory(TEXT("MSE"), 1); return wrapperData->errorExitCode; } /* create a pseudo-random 10 digit string */ _sntprintf(strNamedPipeName, 11, TEXT("%05d%05d"), rand() % 100000, rand() % 100000); /* ShellExecuteEx is expecting the parameter in a single string */ for (i = 1; i < argc; i++) { /* if '--' was specified, wrapperParseArguments has replaced this parameter with NULL */ if (argv[i] == NULL) { len += 3; } else { /* insert a space and quotes */ len += _tcslen(argv[i]) + 3; } } if (wrapperData->argCommandArg) { len += _tcslen(wrapperData->argCommandArg) + 1; } len += _tcslen(commandProps) + _tcslen(strNamedPipeName) + 1; parameter = calloc(len, sizeof(TCHAR)); if (!parameter) { outOfMemory(TEXT("ET"), 1); return wrapperData->errorExitCode; } /* append the command, conf file and any parameter before the command line properties and java additionals */ for (i = 1; i < argc; i++) { if ((argv[i] == NULL) || ((wrapperData->argCount > 0) && (_tcscmp(wrapperData->argValues[0], argv[i]) == 0))) { /* hit '--' or a command line property */ break; } _tcsncat(parameter, TEXT("\""), len); _tcsncat(parameter, argv[i], len); if ((i == 1) && wrapperData->argCommandArg) { _tcsncat(parameter, TEXT("="), len); _tcsncat(parameter, wrapperData->argCommandArg, len); } _tcsncat(parameter, TEXT("\""), len); _tcsncat(parameter, TEXT(" "), len); } /* the following properties must always be in first position as we will not allow to override them. */ _tcsncat(parameter, commandProps, len); _tcsncat(parameter, strNamedPipeName, len); /* continue and fill the command line properties and java additionals */ for (; i < argc; i++) { _tcsncat(parameter, TEXT(" "), len); if (argv[i] == NULL) { _tcsncat(parameter, TEXT("--"), len); } else { _tcsncat(parameter, TEXT("\""), len); _tcsncat(parameter, argv[i], len); _tcsncat(parameter, TEXT("\""), len); } } ret = runElevated(szPath, parameter, NULL, strNamedPipeName); free(strNamedPipeName); free(parameter); return ret; } return wrapperData->errorExitCode; } #endif /* ifdef WIN32 */ wrapper_3.5.51_src/src/c/wrappereventloop.c100644 0 0 372372 14333053652 16220 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ /** * This file contains the main event loop and state engine for * the Java Service Wrapper. * * Author: * Leif Mortenson * Ryan Shaw */ #include #include #include #include #ifdef WIN32 #include /* MS Visual Studio 8 went and deprecated the POXIX names for functions. * Fixing them all would be a big headache for UNIX versions. */ #pragma warning(disable : 4996) #else /* UNIX */ #include #include #endif #include "wrapper.h" #include "logger.h" #ifndef WIN32 #include "wrapper_ulimit.h" #endif #include "wrapper_encoding.h" #include "wrapper_i18n.h" #ifndef MAX #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #endif /** * Returns a constant text representation of the specified Wrapper State. * * @param wState The Wrapper State whose name is being requested. * * @return The requested Wrapper State. */ const TCHAR *wrapperGetWState(int wState) { const TCHAR *name; switch(wState) { case WRAPPER_WSTATE_STARTING: name = TEXT("STARTING"); break; case WRAPPER_WSTATE_STARTED: name = TEXT("STARTED"); break; case WRAPPER_WSTATE_PAUSING: name = TEXT("PAUSING"); break; case WRAPPER_WSTATE_PAUSED: name = TEXT("PAUSED"); break; case WRAPPER_WSTATE_RESUMING: name = TEXT("RESUMING"); break; case WRAPPER_WSTATE_STOPPING: name = TEXT("STOPPING"); break; case WRAPPER_WSTATE_STOPPED: name = TEXT("STOPPED"); break; default: name = TEXT("UNKNOWN"); break; } return name; } /** * Returns a constant text representation of the specified Java State. * * @param jState The Java State whose name is being requested. * * @return The requested Java State. */ const TCHAR *wrapperGetJState(int jState) { const TCHAR *name; switch(jState) { case WRAPPER_JSTATE_DOWN_CLEAN: name = TEXT("DOWN_CLEAN"); break; case WRAPPER_JSTATE_LAUNCH_DELAY: name = TEXT("LAUNCH(DELAY)"); break; case WRAPPER_JSTATE_RESTART: name = TEXT("RESTART"); break; case WRAPPER_JSTATE_LAUNCH: name = TEXT("LAUNCH"); break; case WRAPPER_JSTATE_LAUNCHING: name = TEXT("LAUNCHING"); break; case WRAPPER_JSTATE_LAUNCHED: name = TEXT("LAUNCHED"); break; case WRAPPER_JSTATE_STARTING: name = TEXT("STARTING"); break; case WRAPPER_JSTATE_STARTED: name = TEXT("STARTED"); break; case WRAPPER_JSTATE_STOP: name = TEXT("STOP"); break; case WRAPPER_JSTATE_STOPPING: name = TEXT("STOPPING"); break; case WRAPPER_JSTATE_STOPPED: name = TEXT("STOPPED"); break; case WRAPPER_JSTATE_KILLING: name = TEXT("KILLING"); break; case WRAPPER_JSTATE_KILL: name = TEXT("KILL"); break; case WRAPPER_JSTATE_DOWN_CHECK: name = TEXT("DOWN_CHECK"); break; case WRAPPER_JSTATE_DOWN_FLUSH_STDIN: name = TEXT("DOWN_FLUSH_STDIN"); break; case WRAPPER_JSTATE_DOWN_FLUSH: name = TEXT("DOWN_FLUSH"); break; case WRAPPER_JSTATE_KILLED: name = TEXT("KILLED"); break; default: name = TEXT("UNKNOWN"); break; } return name; } int getStateOutputModeForName(const TCHAR *name) { if (strcmpIgnoreCase(name, TEXT("CHANGED")) == 0) { /* whenever the jState changes or the suspendTimeouts flags is flipped */ return STATE_OUTPUT_MODE_CHANGED; } else if (strcmpIgnoreCase(name, TEXT("SECONDS")) == 0) { /* alias of SECONDS_1. */ return STATE_OUTPUT_MODE_SECONDS | (1 << 8); } else if (_tcsstr(name, TEXT("SECONDS_")) == name) { /* parse of name is made simple as this is just for debugging. Store the seconds in a left shifted bit. */ return STATE_OUTPUT_MODE_SECONDS | (__max(1, __min(255, _ttoi(name + 8))) << 8); } else if (_tcsstr(name, TEXT("CHANGED_OR_SECONDS_")) == name) { return STATE_OUTPUT_MODE_CHANGED | STATE_OUTPUT_MODE_SECONDS | (__max(1, __min(255, _ttoi(name + 19))) << 8); } else { /* default */ return STATE_OUTPUT_MODE_DEFAULT; } } /** * Used to filter state output. * * @param nowTicks The current tick * @param pFirstOutput pointer to a boolean which is TRUE if this is the first output. * Only used with STATE_OUTPUT_MODE_CHANGED. * The pointer will be updated by the function. * @param pPrevJState pointer to an int which indicates the JVM state of the last output. * Only used with STATE_OUTPUT_MODE_CHANGED. * The pointer will be updated by the function. * @param pPrevTimeoutSuspended pointer to an int which indicates if the timeouts were suspended in the last output. * Only used with STATE_OUTPUT_MODE_CHANGED and standard. * The pointer will be updated by the function. * @param mask used to enable filtering certain modes. * * @return TRUE if the output should be printed, FALSE otherwise. */ int isStateOutputEnabled(TICKS nowTicks, int *pFirstOutput, int *pPrevJState, int *pPrevTimeoutSuspended, int mask) { if (wrapperData->isStateOutputEnabled) { if (wrapperData->stateOutputMode & STATE_OUTPUT_MODE_DEFAULT) { return TRUE; } if (wrapperData->stateOutputMode & STATE_OUTPUT_MODE_SECONDS) { if (mask & STATE_OUTPUT_MODE_SECONDS) { /* The mask allows to filter using this mode. */ if (nowTicks % (10 * (wrapperData->stateOutputMode >> 8)) == 0) { return TRUE; } } else { return TRUE; } } if (wrapperData->stateOutputMode & STATE_OUTPUT_MODE_CHANGED) { if (mask & STATE_OUTPUT_MODE_CHANGED) { /* The mask allows to filter using this mode. Check if the state has changed. */ if ((*pFirstOutput) || (*pPrevJState != wrapperData->jState) ) { /* Currently the mode can't be changed from the command file. * If we add a command, we need to handle correctly the change of mode. */ *pPrevJState = wrapperData->jState; *pFirstOutput = FALSE; return TRUE; } } else { return TRUE; } } } else if (wrapperData->stateOutputMode & STATE_OUTPUT_MODE_CHANGED & mask) { /* Reset the flag in case output is enabled later */ *pFirstOutput = TRUE; } return FALSE; } void writeStateFile(const TCHAR *filename, const TCHAR *state, int newUmask #ifndef WIN32 , gid_t newGroup #endif ) { FILE *fp = NULL; int old_umask; int cnt = 0; /* If other processes are reading the state file it may be locked for a moment. * Avoid problems by trying a few times before giving up. */ while (cnt < 10) { old_umask = umask(newUmask); fp = _tfopen(filename, TEXT("w")); umask(old_umask); if (fp != NULL) { #ifndef WIN32 changePidFileGroup(filename, newGroup); #endif _ftprintf(fp, TEXT("%s\n"), state); fclose(fp); return; } /* Sleep for a tenth of a second. */ wrapperSleep(100); cnt++; } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to write to the status file: %s"), filename); } /** * Changes the current Wrapper state. * * wState - The new Wrapper state. */ void wrapperSetWrapperState(int wState) { if (wrapperData->isStateOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Set Wrapper State %s -> %s"), wrapperGetWState(wrapperData->wState), wrapperGetWState(wState)); } wrapperData->wState = wState; if (wrapperData->statusFilename != NULL) { writeStateFile(wrapperData->statusFilename, wrapperGetWState(wrapperData->wState), wrapperData->statusFileUmask #ifndef WIN32 , wrapperData->statusFileGroup #endif ); } } /** * Updates the current state time out. * * nowTicks - The current tick count at the time of the call, ignored if * delay is negative. * delay - The delay in seconds, added to the nowTicks after which the state * will time out, if negative will never time out. */ void wrapperUpdateJavaStateTimeout(TICKS nowTicks, int delay) { static int firstOutput = TRUE; static int prevJState; static int prevTimeoutSuspended; TICKS newTicks; int ignore; int tickAge; if (delay >= 0) { newTicks = wrapperAddToTicks(nowTicks, delay); ignore = FALSE; if (wrapperData->jStateTimeoutTicksSet) { /* We need to make sure that the new delay is longer than the existing one. * This is complicated slightly because the tick counter can be wrapped. */ tickAge = wrapperGetTickAgeTicks(wrapperData->jStateTimeoutTicks, newTicks); if (tickAge <= 0) { ignore = TRUE; } } if (ignore) { /* The new value is meaningless. */ if (isStateOutputEnabled(nowTicks, &firstOutput, &prevJState, &prevTimeoutSuspended, STATE_OUTPUT_MASK_NOSECS)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Set Java State %s (%d) Ignored Timeout %08x"), wrapperGetJState(wrapperData->jState), delay, wrapperData->jStateTimeoutTicks); } } else { if (isStateOutputEnabled(nowTicks, &firstOutput, &prevJState, &prevTimeoutSuspended, STATE_OUTPUT_MASK_NOSECS)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Set Java State %s (%d) Timeout %08x -> %08x"), wrapperGetJState(wrapperData->jState), delay, nowTicks, newTicks); } wrapperData->jStateTimeoutTicks = newTicks; wrapperData->jStateTimeoutTicksSet = 1; } } else { wrapperData->jStateTimeoutTicks = 0; wrapperData->jStateTimeoutTicksSet = 0; } } /** * Changes the current Java state. * * jState - The new Java state. * nowTicks - The current tick count at the time of the call, ignored if * delay is negative. * delay - The delay in seconds, added to the nowTicks after which the state * will time out, if negative will never time out. */ void wrapperSetJavaState(int jState, TICKS nowTicks, int delay) { if (wrapperData->isStateOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Set Java State %s -> %s"), wrapperGetJState(wrapperData->jState), wrapperGetJState(jState)); } if (wrapperData->jState != jState) { /* If the state has changed, then the old timeout will never be used. * Clear it here so any new timeout will be used. */ wrapperData->jStateTimeoutTicks = 0; wrapperData->jStateTimeoutTicksSet = 0; } wrapperData->jState = jState; wrapperUpdateJavaStateTimeout(nowTicks, delay); if (wrapperData->javaStatusFilename != NULL) { writeStateFile(wrapperData->javaStatusFilename, wrapperGetJState(wrapperData->jState), wrapperData->javaStatusFileUmask #ifndef WIN32 , wrapperData->javaStatusFileGroup #endif ); } } /** * Prints a single line which helps debugging the Wrapper and JVM states, and timeouts. */ void printStateOutput(TICKS nowTicks) { TCHAR buff1[10]; static int firstOutput = TRUE; static int prevJState; static int prevTimeoutSuspended; if (isStateOutputEnabled(nowTicks, &firstOutput, &prevJState, &prevTimeoutSuspended, STATE_OUTPUT_MASK_ALL)) { if (wrapperData->jStateTimeoutTicksSet) { _sntprintf(buff1, 10, TEXT("%ds"), wrapperGetTickAgeSeconds(nowTicks, wrapperData->jStateTimeoutTicks)); } else { _tcsncpy(buff1, TEXT("N/A"), 10); } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Ticks=%08x, WrapperState=%s, JVMState=%s JVMStateTimeoutTicks=%08x (%s), Exit=%s, RestartMode=%d"), nowTicks, wrapperGetWState(wrapperData->wState), wrapperGetJState(wrapperData->jState), wrapperData->jStateTimeoutTicks, buff1, (wrapperData->exitRequested ? TEXT("true") : TEXT("false")), wrapperData->restartRequested); } } int wrapperCheckJstateTimeout(TICKS nowTicks, int suspendable) { if (wrapperData->jStateTimeoutTicksSet && (wrapperGetTickAgeTicks(wrapperData->jStateTimeoutTicks, nowTicks) >= 0)) { return TRUE; } return FALSE; } void displayLaunchingTimeoutMessage() { const TCHAR *mainClass; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Startup failed: Timed out waiting for a signal from the JVM.")); mainClass = getStringProperty(properties, TEXT("wrapper.java.mainclass"), TEXT("Main")); if ((_tcsstr(mainClass, TEXT("org.tanukisoftware.wrapper.WrapperSimpleApp")) != NULL) || (_tcsstr(mainClass, TEXT("org.tanukisoftware.wrapper.WrapperStartStopApp")) != NULL)) { /* The user appears to be using a valid main class, so no advice available. */ } else { if (wrapperData->isAdviserEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("------------------------------------------------------------------------") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("Advice:") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("The Wrapper consists of a native component as well as a set of classes\nwhich run within the JVM that it launches. The Java component of the\nWrapper must be initialized promptly after the JVM is launched or the\nWrapper will timeout, as just happened. Most likely the main class\nspecified in the Wrapper configuration file is not correctly initializing\nthe Wrapper classes:")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" %s"), mainClass); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("While it is possible to do so manually, the Wrapper ships with helper\nclasses to make this initialization processes automatic.\nPlease review the integration section of the Wrapper's documentation\nfor the various methods which can be employed to launch an application\nwithin the Wrapper:")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT(" http://wrapper.tanukisoftware.com/doc/english/integrate.html")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("------------------------------------------------------------------------") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ADVICE, TEXT("") ); } } } /** * Handles a timeout for a DebugJVM by showing an appropriate message and * resetting internal timeouts. */ void handleDebugJVMTimeout(TICKS nowTicks, const TCHAR *message, const TCHAR *timer) { if (!wrapperData->debugJVMTimeoutNotified) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("------------------------------------------------------------------------") ); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("%s"), message); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("The JVM was launched with debug options so this may be because the JVM\nis currently suspended by a debugger. Any future timeouts during this\nJVM invocation will be silently ignored.")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("------------------------------------------------------------------------") ); } wrapperData->debugJVMTimeoutNotified = TRUE; /* Make this individual state never timeout then continue. */ if (wrapperData->isStateOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" DebugJVM timeout. Disable current %s timeout."), timer); } wrapperUpdateJavaStateTimeout(nowTicks, -1); } /** * Tests for the existence of the anchor file. If it does not exist then * the Wrapper will begin its shutdown process. * * nowTicks: The tick counter value this time through the event loop. */ void anchorPoll(TICKS nowTicks) { #if defined(WIN32) && !defined(WIN64) struct _stat64i32 fileStat; #else struct stat fileStat; #endif int result; #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Anchor timeout=%d, now=%d"), wrapperData->anchorTimeoutTicks, nowTicks); #endif if (wrapperData->anchorFilename) { if (wrapperTickExpired(nowTicks, wrapperData->anchorTimeoutTicks)) { if (wrapperData->isLoopOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Loop: check anchor file")); } result = _tstat(wrapperData->anchorFilename, &fileStat); if (result == 0) { /* Anchor file exists. Do nothing. */ #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("The anchor file %s exists."), wrapperData->anchorFilename); #endif } else { /* Anchor file is gone. */ #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("The anchor file %s was deleted."), wrapperData->anchorFilename); #endif /* Unless we are already doing so, start the shudown process. */ if (wrapperData->exitRequested || wrapperData->restartRequested || (wrapperData->jState == WRAPPER_JSTATE_STOP) || (wrapperData->jState == WRAPPER_JSTATE_STOPPING) || (wrapperData->jState == WRAPPER_JSTATE_STOPPED) || (wrapperData->jState == WRAPPER_JSTATE_KILLING) || (wrapperData->jState == WRAPPER_JSTATE_KILL) || (wrapperData->jState == WRAPPER_JSTATE_KILLED) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_CHECK) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH_STDIN) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_CLEAN)) { /* Already shutting down, so nothing more to do. */ } else { /* Always force the shutdown as this was an external event. */ wrapperStopProcess(0, TRUE); } /* To make sure that the JVM will not be restarted for any reason, * start the Wrapper shutdown process as well. */ if ((wrapperData->wState == WRAPPER_WSTATE_STOPPING) || (wrapperData->wState == WRAPPER_WSTATE_STOPPED)) { /* Already stopping. */ } else { /* Start the shutdown process. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Anchor file deleted. Shutting down.")); wrapperSetWrapperState(WRAPPER_WSTATE_STOPPING); } } wrapperData->anchorTimeoutTicks = wrapperAddToTicks(nowTicks, wrapperData->anchorPollInterval); } } } #ifdef TEST_FORTIFY_SOURCE struct S { struct T { char buf[5]; int x; } t; char buf[20]; } var; #endif /** * Tests for the existence of the command file. If it exists then it will be * opened and any included commands will be processed. On completion, the * file will be deleted. * * nowTicks: The tick counter value this time through the event loop. */ #define MAX_COMMAND_LENGTH 80 void commandPoll(TICKS nowTicks) { #if defined(WIN32) && !defined(WIN64) struct _stat64i32 fileStat; #else struct stat fileStat; #endif int result; FILE *stream; int cnt; TCHAR buffer[MAX_COMMAND_LENGTH]; TCHAR *c; TCHAR *d; TCHAR *command; TCHAR *param1; TCHAR *param2; int exitCode; int pauseTime; int logLevel; int oldLowLogLevel; int newLowLogLevel; int flag; int accessViolation = FALSE; int bufferOverflow1 = FALSE; size_t i; #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Command timeout=%08x, now=%08x"), wrapperData->commandTimeoutTicks, nowTicks); #endif if (wrapperData->commandFilename) { if (wrapperTickExpired(nowTicks, wrapperData->commandTimeoutTicks)) { if (wrapperData->isLoopOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Loop: check command file")); } result = _tstat(wrapperData->commandFilename, &fileStat); if (result == 0) { /* Command file exists. */ #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("The command file %s exists."), wrapperData->commandFilename); #endif /* We need to be able to lock and then read the command file. Other * applications will be creating this file so we need to handle the * case where it is locked for a few moments. */ cnt = 0; do { stream = _tfopen(wrapperData->commandFilename, TEXT("r+t")); if (stream == NULL) { /* Sleep for a tenth of a second. */ wrapperSleep(100); } cnt++; } while ((cnt < 10) && (stream == NULL)); if (stream == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Unable to read the command file: %s"), wrapperData->commandFilename); } else { /* Read in each of the commands line by line. */ do { c = _fgetts(buffer, MAX_COMMAND_LENGTH, stream); if (c != NULL) { /* Always strip both ^M and ^J off the end of the line, this is done rather * than simply checking for \n so that files will work on all platforms * even if their line feeds are incorrect. */ if ((d = _tcschr(buffer, 13 /* ^M */)) != NULL) { d[0] = TEXT('\0'); } if ((d = _tcschr(buffer, 10 /* ^J */)) != NULL) { d[0] = TEXT('\0'); } command = buffer; /* Remove any leading space or tabs */ while (command[0] == TEXT(' ') || command[0] == TEXT('\t')) { command++; } if (command[0] == TEXT('\0')) { /* Empty line. Ignore it silently. */ continue; } /* Remove any tailing space or tabs */ i = _tcslen(command) - 1; while (command[i] == TEXT(' ') || command[i] == TEXT('\t')) { i--; } command[i + 1] = TEXT('\0'); /** Look for the first space, everything after it will be the parameter(s). */ /* Look for parameter 1. */ if ((param1 = _tcschr(command, ' ')) != NULL ) { param1[0] = TEXT('\0'); /* Terminate the command. */ /* Find the first non-space character. */ do { param1++; } while (param1[0] == TEXT(' ')); } if (param1 != NULL) { /* Look for parameter 2. */ if ((param2 = _tcschr(param1, ' ')) != NULL ) { param2[0] = TEXT('\0'); /* Terminate param1. */ /* Find the first non-space character. */ do { param2++; } while (param2[0] == TEXT(' ')); } if (param2 != NULL) { /* Make sure parameter 2 is terminated. */ if ((d = _tcschr(param2, ' ')) != NULL ) { d[0] = TEXT('\0'); /* Terminate param2. */ } } } else { param2 = NULL; } /* Process the command. */ if (strcmpIgnoreCase(command, TEXT("RESTART")) == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Command '%s'. %s"), command, wrapperGetRestartProcessMessage()); wrapperRestartProcess(); } else if (strcmpIgnoreCase(command, TEXT("STOP")) == 0) { if (param1 == NULL) { exitCode = 0; } else { exitCode = _ttoi(param1); } if (exitCode < 0 || exitCode > 255) { exitCode = wrapperData->errorExitCode; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("The exit code specified along with the 'STOP' command must be in the range %d to %d.\n Changing to the default error exit code %d."), 1, 255, exitCode); } log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Command '%s'. Shutting down with exit code %d."), command, exitCode); /* Always force the shutdown as this is an external event. */ wrapperStopProcess(exitCode, TRUE); /* To make sure that the JVM will not be restarted for any reason, * start the Wrapper shutdown process as well. */ if ((wrapperData->wState == WRAPPER_WSTATE_STOPPING) || (wrapperData->wState == WRAPPER_WSTATE_STOPPED)) { /* Already stopping. */ } else { wrapperSetWrapperState(WRAPPER_WSTATE_STOPPING); } } else if (strcmpIgnoreCase(command, TEXT("PAUSE")) == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Command '%s'. %s"), command, wrapperGetPauseProcessMessage()); wrapperPauseProcess(WRAPPER_ACTION_SOURCE_CODE_COMMANDFILE); } else if (strcmpIgnoreCase(command, TEXT("RESUME")) == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Command '%s'. %s"), command, wrapperGetResumeProcessMessage()); wrapperResumeProcess(WRAPPER_ACTION_SOURCE_CODE_COMMANDFILE); } else if (strcmpIgnoreCase(command, TEXT("DUMP")) == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Command '%s'. Requesting a Thread Dump."), command); wrapperRequestDumpJVMState(); } else if (strcmpIgnoreCase(command, TEXT("GC")) == 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Command '%s'. Requesting a GC."), command); wrapperRequestJVMGC(WRAPPER_ACTION_SOURCE_CODE_COMMANDFILE); } else if ((strcmpIgnoreCase(command, TEXT("CONSOLE_LOGLEVEL")) == 0) || (strcmpIgnoreCase(command, TEXT("LOGFILE_LOGLEVEL")) == 0) || (strcmpIgnoreCase(command, TEXT("SYSLOG_LOGLEVEL")) == 0)) { if (param1 == NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Command '%s' is missing its log level."), command); } else { logLevel = getLogLevelForName(param1); if (logLevel == LEVEL_UNKNOWN) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Command '%s' specified an unknown log level: '%'"), command, param1); } else { oldLowLogLevel = getLowLogLevel(); if (strcmpIgnoreCase(command, TEXT("CONSOLE_LOGLEVEL")) == 0) { setConsoleLogLevelInt(logLevel); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Command '%s'. Set console log level to '%s'."), command, param1); } else if (strcmpIgnoreCase(command, TEXT("LOGFILE_LOGLEVEL")) == 0) { setLogfileLevelInt(logLevel); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Command '%s'. Set log file log level to '%s'."), command, param1); } else if (strcmpIgnoreCase(command, TEXT("SYSLOG_LOGLEVEL")) == 0) { setSyslogLevelInt(logLevel); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Command '%s'. Set syslog log level to '%s'."), command, param1); } else { /* Shouldn't get here. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Command '%s' lead to an unexpected state."), command); } newLowLogLevel = getLowLogLevel(); if (oldLowLogLevel != newLowLogLevel) { wrapperData->isDebugging = (newLowLogLevel <= LEVEL_DEBUG); _sntprintf(buffer, MAX_COMMAND_LENGTH, TEXT("%d"), getLowLogLevel()); wrapperProtocolFunction(WRAPPER_MSG_LOW_LOG_LEVEL, buffer); } } } } else if ((strcmpIgnoreCase(command, TEXT("LOOP_OUTPUT")) == 0) || (strcmpIgnoreCase(command, TEXT("STATE_OUTPUT")) == 0) || (strcmpIgnoreCase(command, TEXT("MEMORY_OUTPUT")) == 0) || (strcmpIgnoreCase(command, TEXT("CPU_OUTPUT")) == 0) || (strcmpIgnoreCase(command, TEXT("TIMER_OUTPUT")) == 0) || (strcmpIgnoreCase(command, TEXT("SLEEP_OUTPUT")) == 0)) { flag = ((param1 != NULL) && (strcmpIgnoreCase(param1, TEXT("TRUE")) == 0)); if (strcmpIgnoreCase(command, TEXT("LOOP_OUTPUT")) == 0) { wrapperData->isLoopOutputEnabled = flag; } else if (strcmpIgnoreCase(command, TEXT("STATE_OUTPUT")) == 0) { wrapperData->isStateOutputEnabled = flag; } else if (strcmpIgnoreCase(command, TEXT("MEMORY_OUTPUT")) == 0) { wrapperData->isMemoryOutputEnabled = flag; } else if (strcmpIgnoreCase(command, TEXT("CPU_OUTPUT")) == 0) { wrapperData->isCPUOutputEnabled = flag; } else if (strcmpIgnoreCase(command, TEXT("TIMER_OUTPUT")) == 0) { wrapperData->isTickOutputEnabled = flag; } else if (strcmpIgnoreCase(command, TEXT("SLEEP_OUTPUT")) == 0) { wrapperData->isSleepOutputEnabled = flag; } if (flag) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Command '%s'. Enable %s."), command, command); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Command '%s'. Disable %s."), command, command); } } else if ((strcmpIgnoreCase(command, TEXT("CLOSE_SOCKET")) == 0) || (strcmpIgnoreCase(command, TEXT("CLOSE_BACKEND")) == 0)) { if (wrapperData->commandFileTests) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Command '%s'. Closing backend socket to JVM..."), command); wrapperProtocolClose(); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Command '%s'. Tests disabled."), command); } } else if (strcmpIgnoreCase(command, TEXT("PAUSE_THREAD")) == 0) { if (wrapperData->commandFileTests) { if (param2 == NULL) { pauseTime = -1; } else { pauseTime = __max(0, __min(3600, _ttoi(param2))); } if (strcmpIgnoreCase(param1, TEXT("MAIN")) == 0) { wrapperData->pauseThreadMain = pauseTime; } else if (strcmpIgnoreCase(param1, TEXT("TIMER")) == 0) { wrapperData->pauseThreadTimer = pauseTime; } else if (strcmpIgnoreCase(param1, TEXT("JAVAIO")) == 0) { wrapperData->pauseThreadJavaIO = pauseTime; } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Command '%s'. Enqueue request to pause unknown thread."), command); pauseTime = 0; } if (pauseTime > 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Command '%s'. Enqueue request to pause %s thread for %d seconds..."), command, param1, pauseTime); } else if (pauseTime < 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Command '%s'. Enqueue request to pause %s thread indefinitely..."), command, param1); } } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Command '%s'. Tests disabled."), command); } } else if (strcmpIgnoreCase(command, TEXT("PAUSE_LOGGER")) == 0) { if (wrapperData->commandFileTests) { if (param1 == NULL) { pauseTime = -1; } else { pauseTime = __max(0, __min(3600, _ttoi(param1))); } if (pauseTime > 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Command '%s'. Enqueue request to pause logger for %d seconds..."), command, pauseTime); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Command '%s'. Enqueue request to pause logger indefinitely..."), command); } setPauseTime(pauseTime); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Command '%s'. Tests disabled."), command); } } else if (strcmpIgnoreCase(command, TEXT("ACCESS_VIOLATION")) == 0) { if (wrapperData->commandFileTests) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Command '%s'. Intentionally causing an Access Violation in Wrapper..."), command); /* We can't do the access violation here because we want to make sure the * file is deleted first, otherwise it be executed again when the Wrapper is restarted. */ accessViolation = TRUE; } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Command '%s'. Tests disabled."), command); } #ifdef TEST_FORTIFY_SOURCE } else if (strcmpIgnoreCase(command, TEXT("BUFFER_OVERFLOW1")) == 0) { if (wrapperData->commandFileTests) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Command '%s'. Intentionally causing a Buffer Overflow in Wrapper..."), command); bufferOverflow1 = TRUE; } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Command '%s'. Tests disabled."), command); } #endif } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Command '%s' is unknown, ignoring."), command); } } } while (c != NULL); /* Close the file. */ fclose(stream); /* Delete the file. */ if (_tremove(wrapperData->commandFilename) == -1) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Unable to delete the command file, %s: %s"), wrapperData->commandFilename, getLastErrorText()); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Command file has been processed and deleted.")); } if (accessViolation || bufferOverflow1) { /* Make sure that everything is logged before the crash. */ flushLogfile(); if (accessViolation) { /* Actually cause the access violation. */ c = NULL; c[0] = TEXT('\0'); /* Should never get here. */ } #ifdef TEST_FORTIFY_SOURCE if (bufferOverflow1) { /* Actually cause the buffer overflow. */ strcpy (&var.t.buf[1], "abcdefg"); /* Should never get here. */ } #endif } } } else { /* Command file does not exist. */ #ifdef _DEBUG log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("The command file %s does not exist."), wrapperData->commandFilename); #endif } wrapperData->commandTimeoutTicks = wrapperAddToTicks(nowTicks, wrapperData->commandPollInterval); } } } /******************************************************************** * Wrapper States *******************************************************************/ /** * WRAPPER_WSTATE_STARTING * The Wrapper process is being started. It will remain in this state * until a JVM and its application has been successfully started. * * nowTicks: The tick counter value this time through the event loop. */ void wStateStarting(TICKS nowTicks) { /* While the wrapper is starting up, we need to ping the service */ /* manager to reasure it that we are still alive. */ #ifdef WIN32 /* Tell the service manager that we are starting */ wrapperReportStatus(FALSE, WRAPPER_WSTATE_STARTING, 0, wrapperData->ntStartupWaitHint * 1000); #endif /* If we are supposed to pause on startup, we need to jump to that state now, and report that we are started. */ if (wrapperData->initiallyPaused && wrapperData->pausable) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Initially Paused.")); wrapperSetWrapperState(WRAPPER_WSTATE_PAUSED); #ifdef WIN32 /* Tell the service manager that we started */ wrapperReportStatus(FALSE, WRAPPER_WSTATE_PAUSED, 0, 0); #endif } else { /* If the JVM state is now STARTED, then change the wrapper state */ /* to be STARTED as well. */ if (wrapperData->jState == WRAPPER_JSTATE_STARTED) { wrapperSetWrapperState(WRAPPER_WSTATE_STARTED); #ifdef WIN32 /* Tell the service manager that we started */ wrapperReportStatus(FALSE, WRAPPER_WSTATE_STARTED, 0, 0); #endif } } } /** * WRAPPER_WSTATE_STARTED * The Wrapper process is started. It will remain in this state until * the Wrapper is ready to start shutting down. The JVM process may * be restarted one or more times while the Wrapper is in this state. * * nowTicks: The tick counter value this time through the event loop. */ void wStateStarted(TICKS nowTicks) { /* Just keep running. Nothing to do here. */ } /** * WRAPPER_WSTATE_PAUSING * The Wrapper process is being paused. If stopping the JVM is enabled * then it will remain in this state until the JVM has been stopped. * Otherwise it will immediately go to the WRAPPER_WSTATE_PAUSED state. * * nowTicks: The tick counter value this time through the event loop. */ void wStatePausing(TICKS nowTicks) { /* While the wrapper is pausing, we need to ping the service */ /* manager to reasure it that we are still alive. */ /* If we are configured to do so, stop the JVM */ if (wrapperData->pausableStopJVM) { /* If it has not already been set, set the exit request flag. */ if (wrapperData->jState == WRAPPER_JSTATE_DOWN_CLEAN) { /* JVM is now down. We are now paused. */ wrapperSetWrapperState(WRAPPER_WSTATE_PAUSED); #ifdef WIN32 /* Tell the service manager that we are paused */ wrapperReportStatus(FALSE, WRAPPER_WSTATE_PAUSED, 0, 0); #endif } else { #ifdef WIN32 /* Tell the service manager that we are pausing */ wrapperReportStatus(FALSE, WRAPPER_WSTATE_PAUSING, 0, wrapperData->ntShutdownWaitHint * 1000); #endif if (wrapperData->exitRequested || (wrapperData->jState == WRAPPER_JSTATE_STOP) || (wrapperData->jState == WRAPPER_JSTATE_STOPPING) || (wrapperData->jState == WRAPPER_JSTATE_STOPPED) || (wrapperData->jState == WRAPPER_JSTATE_KILLING) || (wrapperData->jState == WRAPPER_JSTATE_KILL) || (wrapperData->jState == WRAPPER_JSTATE_KILLED) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_CHECK) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH_STDIN) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH)) { /* In the process of stopping the JVM. */ } else { /* The JVM needs to be stopped, start that process. */ wrapperData->exitRequested = TRUE; /* Make sure the JVM will be restarted. */ wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_CONFIGURED; } } } else { /* We want to leave the JVM process as is. We are now paused. */ wrapperSetWrapperState(WRAPPER_WSTATE_PAUSED); #ifdef WIN32 /* Tell the service manager that we are paused */ wrapperReportStatus(FALSE, WRAPPER_WSTATE_PAUSED, 0, 0); #endif } } /** * WRAPPER_WSTATE_PAUSED * The Wrapper process is paused. It will remain in this state until * the Wrapper is resumed or is ready to start shutting down. The * JVM may be stopped or will remain stopped while the Wrapper is in * this state. * * nowTicks: The tick counter value this time through the event loop. */ void wStatePaused(TICKS nowTicks) { /* Just keep running. Nothing to do here. */ } /** * WRAPPER_WSTATE_RESUMING * The Wrapper process is being resumed. We will remain in this state * until the JVM enters the running state. It may or may not be initially * started. * * nowTicks: The tick counter value this time through the event loop. */ void wStateResuming(TICKS nowTicks) { /* While the wrapper is resuming, we need to ping the service */ /* manager to reasure it that we are still alive. */ /* If the JVM state is now STARTED, then change the wrapper state */ /* to be STARTED as well. */ if (wrapperData->jState == WRAPPER_JSTATE_STARTED) { wrapperSetWrapperState(WRAPPER_WSTATE_STARTED); #ifdef WIN32 /* Tell the service manager that we started */ wrapperReportStatus(FALSE, WRAPPER_WSTATE_STARTED, 0, 0); #endif } else { /* JVM is down and so it needs to be started. */ #ifdef WIN32 /* Tell the service manager that we are resuming */ wrapperReportStatus(FALSE, WRAPPER_WSTATE_RESUMING, 0, wrapperData->ntStartupWaitHint * 1000); #endif } } /** * WRAPPER_WSTATE_STOPPING * The Wrapper process has started its shutdown process. It will * remain in this state until it is confirmed that the JVM has been * stopped. * * nowTicks: The tick counter value this time through the event loop. */ void wStateStopping(TICKS nowTicks) { /* The wrapper is stopping, we need to ping the service manager * to reasure it that we are still alive. */ #ifdef WIN32 /* Tell the service manager that we are stopping */ wrapperReportStatus(FALSE, WRAPPER_WSTATE_STOPPING, wrapperData->exitCode, wrapperData->ntShutdownWaitHint * 1000); #endif /* If the JVM state is now DOWN_CLEAN, then change the wrapper state * to be STOPPED as well. */ if (wrapperData->jState == WRAPPER_JSTATE_DOWN_CLEAN) { wrapperSetWrapperState(WRAPPER_WSTATE_STOPPED); /* Don't tell the service manager that we stopped here. That * will be done when the application actually quits. */ } } /** * WRAPPER_WSTATE_STOPPED * The Wrapper process is now ready to exit. The event loop will complete * and the Wrapper process will exit. * * nowTicks: The tick counter value this time through the event loop. */ void wStateStopped(TICKS nowTicks) { /* The wrapper is ready to stop. Nothing to be done here. This */ /* state will exit the event loop below. */ } /******************************************************************** * JVM States *******************************************************************/ /** * WRAPPER_JSTATE_DOWN_CLEAN * The JVM process currently does not exist and we are clean. Depending * on the Wrapper state and other factors, we will either stay in this * state or switch to the LAUNCH state causing a JVM to be launched * after a delay set in this function. * * nowTicks: The tick counter value this time through the event loop. * nextSleep: Flag which is used to determine whether or not the state engine * will be sleeping before then next time through the loop. It * may make sense to avoid certain actions if it is known that the * function will be called again immediately. */ void jStateDownClean(TICKS nowTicks, int nextSleep) { TCHAR onExitParamBuffer[16 + 10 + 1]; const TCHAR *onExitAction; int startupDelay; int restartMode; /* The JVM can be down for one of 4 reasons. The first is that the * wrapper is just starting. The second is that the JVM is being * restarted for some reason, the 3rd is that the wrapper is paused, * and the 4th is that the wrapper is trying to shut down. */ if ((wrapperData->wState == WRAPPER_WSTATE_STARTING) || (wrapperData->wState == WRAPPER_WSTATE_STARTED) || (wrapperData->wState == WRAPPER_WSTATE_RESUMING)) { if (wrapperData->restartRequested) { /* A JVM needs to be launched. */ restartMode = wrapperData->restartRequested; wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_NO; wrapperData->stopPacketReceived = FALSE; wrapperData->stoppedPacketReceived = FALSE; wrapperData->restartPacketReceived = FALSE; /* Depending on the number of restarts to date, decide how to handle the (re)start. */ if (wrapperData->jvmRestarts > 0) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Preparing to restart with mode %d."), restartMode); } /* This is not the first JVM, so make sure that we still want to launch. */ if ((wrapperData->wState == WRAPPER_WSTATE_RESUMING) && wrapperData->pausableStopJVM) { /* We are resuming and the JVM was expected to be stopped. Always launch * immediately and reset the failed invocation count. * This mode of restarts works even if restarts have been disabled. */ wrapperData->failedInvocationCount = 0; wrapperSetJavaState(WRAPPER_JSTATE_LAUNCH_DELAY, nowTicks, 0); } else if ((restartMode == WRAPPER_RESTART_REQUESTED_AUTOMATIC) && wrapperData->isAutoRestartDisabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Automatic JVM Restarts disabled. Shutting down.")); wrapperSetWrapperState(WRAPPER_WSTATE_STOPPING); } else if (wrapperData->isRestartDisabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("JVM Restarts disabled. Shutting down.")); wrapperSetWrapperState(WRAPPER_WSTATE_STOPPING); } else if (wrapperGetTickAgeSeconds(wrapperData->jvmLaunchTicks, nowTicks) >= wrapperData->successfulInvocationTime) { /* The previous JVM invocation was running long enough that its invocation */ /* should be considered a success. Reset the failedInvocationStart to */ /* start the count fresh. */ wrapperData->failedInvocationCount = 0; /* Set the state to launch after the restart delay. */ wrapperSetJavaState(WRAPPER_JSTATE_LAUNCH_DELAY, nowTicks, wrapperData->restartDelay); if (wrapperData->restartDelay > 0) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Waiting %d seconds before launching another JVM."), wrapperData->restartDelay); } } } else { /* The last JVM invocation died quickly and was considered to have */ /* been a faulty launch. Increase the failed count. */ wrapperData->failedInvocationCount++; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("JVM was running for %d seconds (less than the successful invocation time of %d seconds).\n Incrementing failed invocation count (currently %d)."), wrapperGetTickAgeSeconds(wrapperData->jvmLaunchTicks, nowTicks), wrapperData->successfulInvocationTime, wrapperData->failedInvocationCount); /* See if we are allowed to try restarting the JVM again. */ if (wrapperData->failedInvocationCount < wrapperData->maxFailedInvocations) { /* Try reslaunching the JVM */ /* Set the state to launch after the restart delay. */ wrapperSetJavaState(WRAPPER_JSTATE_LAUNCH_DELAY, nowTicks, wrapperData->restartDelay); if (wrapperData->restartDelay > 0) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Waiting %d seconds before launching another JVM."), wrapperData->restartDelay); } } } else { /* Unable to launch another JVM. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("There were %d failed launches in a row, each lasting less than %d seconds. Giving up."), wrapperData->failedInvocationCount, wrapperData->successfulInvocationTime); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT(" There may be a configuration problem: please check the logs.")); wrapperSetWrapperState(WRAPPER_WSTATE_STOPPING); } } } else { /* This will be the first invocation. */ wrapperData->failedInvocationCount = 0; /* Set the state to launch after the startup delay. */ if (wrapperData->isConsole) { startupDelay = wrapperData->startupDelayConsole; } else { startupDelay = wrapperData->startupDelayService; } wrapperSetJavaState(WRAPPER_JSTATE_LAUNCH_DELAY, nowTicks, startupDelay); if (startupDelay > 0) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Waiting %d seconds before launching the first JVM."), startupDelay); } } } } else { /* The JVM is down, but a restart has not yet been requested. * See if the user has registered any events for the exit code. */ _sntprintf(onExitParamBuffer, 16 + 10 + 1, TEXT("wrapper.on_exit.%d"), wrapperData->exitCode); onExitAction = getStringProperty(properties, onExitParamBuffer, getStringProperty(properties, TEXT("wrapper.on_exit.default"), TEXT("shutdown"))); if (wrapperData->shutdownActionTriggered && ((strcmpIgnoreCase(onExitAction, TEXT("restart")) == 0) || (strcmpIgnoreCase(onExitAction, TEXT("pause")) == 0))) { onExitAction = TEXT("shutdown"); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Ignoring the action specified with %s.\n A shutdown configured with %s was already initiated."), isGeneratedProperty(properties, onExitParamBuffer) == FALSE ? onExitParamBuffer : TEXT("wrapper.on_exit.default"), wrapperData->shutdownActionPropertyName); } if (strcmpIgnoreCase(onExitAction, TEXT("restart")) == 0) { /* We want to restart the JVM. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("on_exit trigger matched. Restarting the JVM. (Exit code: %d)"), wrapperData->exitCode); wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_CONFIGURED; /* Fall through, the restart will take place on the next loop. */ } else if (strcmpIgnoreCase(onExitAction, TEXT("pause")) == 0) { /* We want to pause the JVM. */ if (wrapperData->pausable) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("on_exit trigger matched. Pausing the Wrapper. (Exit code: %d)"), wrapperData->exitCode); wrapperPauseProcess(WRAPPER_ACTION_SOURCE_CODE_ON_EXIT); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("on_exit trigger matched. Pausing not enabled. Restarting the JVM. (Exit code: %d)"), wrapperData->exitCode); wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_CONFIGURED; } } else { /* We want to stop the Wrapper. */ if (strcmpIgnoreCase(onExitAction, TEXT("shutdown")) != 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Encountered an unexpected value for configuration property %s=%s. Resolving to %s."), onExitParamBuffer, onExitAction, TEXT("SHUTDOWN")); } wrapperSetWrapperState(WRAPPER_WSTATE_STOPPING); } } } else if (wrapperData->wState == WRAPPER_WSTATE_PAUSED) { /* The wrapper is paused. */ if (wrapperData->pausableStopJVM) { /* The stop state is expected. */ /* Make sure we are setup to restart when the Wrapper is resumed later. */ wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_CONFIGURED; } else { /* The JVM should still be running, but it is not. Try to figure out why. */ if (wrapperData->restartRequested) { /* The JVM must have crashed. The restart will be honored when the service * is resumed. Do nothing for now. */ } else { /* No restart was requested. So the JVM must have requested a stop. * Normally, this would result in the service stopping from the paused * state, but it is possible that an exit code is registered. Check them. */ /* No need to check wrapperData->shutdownActionTriggered here. Even though the PAUSE would be * originated from some event, it wouldn't be the direct cause of the JVM being down. */ _sntprintf(onExitParamBuffer, 16 + 10 + 1, TEXT("wrapper.on_exit.%d"), wrapperData->exitCode); onExitAction = getStringProperty(properties, onExitParamBuffer, getStringProperty(properties, TEXT("wrapper.on_exit.default"), TEXT("shutdown"))); if (strcmpIgnoreCase(onExitAction, TEXT("restart")) == 0) { /* We want to restart the JVM. But not now. Let the user know. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("on_exit trigger matched. Service is paused, will restart the JVM when resumed. (Exit code: %d)"), wrapperData->exitCode); /* Make sure we are setup to restart when the Wrapper is resumed later. */ wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_CONFIGURED; /* Fall through, the restart will take place once the service is resumed. */ } else if (strcmpIgnoreCase(onExitAction, TEXT("pause")) == 0) { /* We are paused as expected. */ /* Make sure we are setup to restart when the Wrapper is resumed later. */ wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_CONFIGURED; } else { /* We want to stop the Wrapper. */ if (strcmpIgnoreCase(onExitAction, TEXT("shutdown")) != 0) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_WARN, TEXT("Encountered an unexpected value for configuration property %s=%s. Resolving to %d."), onExitParamBuffer, onExitAction, TEXT("SHUTDOWN")); } wrapperSetWrapperState(WRAPPER_WSTATE_STOPPING); } } } } else { /* The wrapper is shutting down or pausing. Do nothing. */ } /* Reset the last ping time */ wrapperData->lastPingTicks = nowTicks; wrapperData->lastLoggedPingTicks = nowTicks; } /** * WRAPPER_JSTATE_LAUNCH_DELAY * Waiting to launch a JVM. When the state timeout has expired, a JVM * will be launched. * * nowTicks: The tick counter value this time through the event loop. * nextSleep: Flag which is used to determine whether or not the state engine * will be sleeping before then next time through the loop. It * may make sense to avoid certain actions if it is known that the * function will be called again immediately. */ void jStateLaunchDelay(TICKS nowTicks, int nextSleep) { const TCHAR *mainClass; int ret; /* The Waiting state is set from the DOWN_CLEAN state if a JVM had * previously been launched the Wrapper will wait in this state * until the restart delay has expired. If this was the first * invocation, then the state timeout will be set to the current * time causing the new JVM to be launced immediately. */ if ((wrapperData->wState == WRAPPER_WSTATE_STARTING) || (wrapperData->wState == WRAPPER_WSTATE_STARTED) || (wrapperData->wState == WRAPPER_WSTATE_RESUMING)) { /* Is it time to proceed? */ if (wrapperCheckJstateTimeout(nowTicks, FALSE)) { /* Launch the new JVM */ if (wrapperData->jvmRestarts > 0) { /* See if the logs should be rolled on Wrapper startup. */ if (getLogfileRollMode() & ROLL_MODE_JVM) { rollLogs(NULL); } /* Unless this is the first JVM invocation, make it possible to reload the * Wrapper configuration file. */ if (wrapperData->restartReloadConf) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Reloading Wrapper configuration...")); /* If the working dir has been changed then we need to restore it before * the configuration can be reloaded. This is needed to support relative * references to include files. * The working directory will then be restored by wrapperLoadConfigurationProperties() just below. */ if (wrapperData->workingDir && wrapperData->originalWorkingDir) { if (wrapperSetWorkingDir(wrapperData->originalWorkingDir)) { /* Failed to restore the working dir. Shutdown the Wrapper */ goto stop; } } if (wrapperLoadConfigurationProperties(FALSE)) { /* Failed to reload the configuration. This is bad. * The JVM is already down. Shutdown the Wrapper. */ goto stop; } #ifndef WIN32 /* Reset the group of the pid files in case the properties were changed. * All PID files related to the Java process will be recreated so it is not necessary to reset their group. */ changePidFileGroup(wrapperData->pidFilename, wrapperData->pidFileGroup); changePidFileGroup(wrapperData->lockFilename, wrapperData->lockFileGroup); changePidFileGroup(wrapperData->statusFilename, wrapperData->statusFileGroup); changePidFileGroup(wrapperData->anchorFilename, wrapperData->anchorFileGroup); #endif wrapperSetConsoleTitle(); /* Dump the reloaded properties */ dumpProperties(properties); /* Dump the environment variables */ dumpEnvironment(); #ifndef WIN32 showResourceslimits(); #endif } if (wrapperData->restartJavaVersionFailed) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Requesting again the Java version...")); wrapperData->restartJavaVersionFailed = FALSE; } } /* Make sure user is not trying to use the old removed SilverEgg package names. */ mainClass = getStringProperty(properties, TEXT("wrapper.java.mainclass"), TEXT("Main")); if (_tcsstr(mainClass, TEXT("com.silveregg.wrapper.WrapperSimpleApp")) != NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The %s class is no longer supported." ), TEXT("com.silveregg.wrapper.WrapperSimpleApp")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Please use the %s class instead." ), TEXT("com.silveregg.wrapper.WrapperSimpleApp")); goto stop; } else if (_tcsstr(mainClass, TEXT("com.silveregg.wrapper.WrapperStartStopApp")) != NULL) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The %s class is no longer supported." ), TEXT("com.silveregg.wrapper.WrapperStartStopApp")); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Please use the %s class instead." ), TEXT("com.silveregg.wrapper.WrapperStartStopApp")); goto stop; } /* Set the launch time to the curent time */ wrapperData->jvmLaunchTicks = nowTicks; /* Generate a unique key to use when communicating with the JVM */ wrapperBuildKey(); /* Check the backend server to make sure it has been initialized. * This is needed so we can pass its port as part of the java command. */ if (!wrapperCheckServerBackend(TRUE)) { /* The backend is not up. An error should have been reported. But this means we * are unable to continue. */ goto stop; } /* Generate the command used to get the Java version. */ if (wrapperBuildJavaVersionCommand()) { /* There was either an out of memory error or we failed to get the Java command. No need to continue. */ goto stop; } /* Get the Java version. */ ret = wrapperLaunchJavaVersion(); /* There may be some queued message. */ maintainLogger(); switch(ret) { case JAVA_VERSION_LAUNCH_FAILED: /* The same will also happen with a real JVM instance, so stop. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Failed to launch the Java command.")); goto stop; case JAVA_VERSION_WAIT_FAILED: /* The system might currently be too busy. Count this as a failed invocation and try restarting. * We already logged the details of the error, so just update the state and return. */ wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_AUTOMATIC; wrapperData->restartJavaVersionFailed = TRUE; wrapperData->jvmRestarts++; wrapperSetJavaState(WRAPPER_JSTATE_DOWN_CLEAN, nowTicks, -1); break; case JAVA_VERSION_KILL_FAILED: /* We don't want to accumulate unkilled JVMs, so stop. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Failed to forcibly terminate the JVM process, unable to continue.")); if (wrapperData->restartRequested != WRAPPER_RESTART_REQUESTED_NO) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT(" The scheduled restart of the JVM has been cancelled.")); wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_NO; wrapperData->isRestartDisabled = TRUE; } goto stop; default: if (!wrapperData->javaVersion) { /* We failed to get the Java Version in logParseJavaVersionOutput(). */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Failed to resolve the version of Java.")); /* Failed. Wrapper shutdown. */ goto stop; } /* Make sure that the Java version is in the range in which the Wrapper is allowed to run. */ if (!wrapperConfirmJavaVersion()) { /* Failed. Wrapper shutdown. */ goto stop; } /* Resolve the encoding to be used for reading the JVM output (this needs to be done before building the Java command line). */ if (resolveJvmEncoding(wrapperData->javaVersion->major, wrapperData->jvmVendor)) { /* Failed to get the encoding of the JVM output. * Stop here because won't be able to display output correctly. */ goto stop; } /* Generate the command used to launch the Java process */ if (wrapperBuildJavaCommand()) { /* Failed. Wrapper shutdown. */ goto stop; } /* Log a few comments that will explain the JVM behavior. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("%s wrapper.startup.timeout=%d, wrapper.startup.delay.console=%d, wrapper.startup.delay.service=%d, wrapper.restart.delay=%d"), TEXT("Startup Timeouts:"), wrapperData->startupTimeout, wrapperData->startupDelayConsole, wrapperData->startupDelayService, wrapperData->restartDelay); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("%s wrapper.ping.interval=%d, wrapper.ping.interval.logged=%d, wrapper.ping.timeout=%d, wrapper.ping.alert.threshold=%d"), TEXT("Ping settings:"), wrapperData->pingInterval, wrapperData->pingIntervalLogged, wrapperData->pingTimeout, wrapperData->pingAlertThreshold); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("%s wrapper.shutdown.timeout=%d, wrapper.jvm_exit.timeout=%d, wrapper.jvm_cleanup.timeout=%d, wrapper.jvm_terminate.timeout=%d"), TEXT("Shutdown Timeouts:"), wrapperData->shutdownTimeout, wrapperData->jvmExitTimeout, wrapperData->jvmCleanupTimeout, wrapperData->jvmTerminateTimeout); } if (wrapperData->jvmRestarts > 0) { wrapperSetJavaState(WRAPPER_JSTATE_RESTART, nowTicks, -1); } else { /* Increment the JVM restart Id to keep track of how many JVMs we have launched. */ wrapperData->jvmRestarts++; wrapperSetJavaState(WRAPPER_JSTATE_LAUNCH, nowTicks, -1); } } } } else { /* The wrapper is shutting down, pausing or paused. Switch to the * down clean state because the JVM was never launched. */ wrapperSetJavaState(WRAPPER_JSTATE_DOWN_CLEAN, nowTicks, -1); } return; stop: wrapperSetWrapperState(WRAPPER_WSTATE_STOPPING); wrapperData->exitCode = wrapperData->errorExitCode; } /** * WRAPPER_JSTATE_RESTART * The Wrapper is ready to restart a JVM. * * nowTicks: The tick counter value this time through the event loop. * nextSleep: Flag which is used to determine whether or not the state engine * will be sleeping before then next time through the loop. It * may make sense to avoid certain actions if it is known that the * function will be called again immediately. */ void jStateRestart(TICKS nowTicks, int nextSleep) { if ((wrapperData->wState == WRAPPER_WSTATE_STARTING) || (wrapperData->wState == WRAPPER_WSTATE_STARTED) || (wrapperData->wState == WRAPPER_WSTATE_RESUMING)) { /* Increment the JVM restart Id to keep track of how many JVMs we have launched. */ wrapperData->jvmRestarts++; wrapperSetJavaState(WRAPPER_JSTATE_LAUNCH, nowTicks, -1); } else { /* The wrapper is shutting down, pausing or paused. Switch to the * down clean state because the JVM was never launched. */ wrapperSetJavaState(WRAPPER_JSTATE_DOWN_CLEAN, nowTicks, -1); } } /** * WRAPPER_JSTATE_LAUNCH * The Wrapper is ready to launch a JVM. * * nowTicks: The tick counter value this time through the event loop. * nextSleep: Flag which is used to determine whether or not the state engine * will be sleeping before then next time through the loop. It * may make sense to avoid certain actions if it is known that the * function will be called again immediately. */ void jStateLaunch(TICKS nowTicks, int nextSleep) { if ((wrapperData->wState == WRAPPER_WSTATE_STARTING) || (wrapperData->wState == WRAPPER_WSTATE_STARTED) || (wrapperData->wState == WRAPPER_WSTATE_RESUMING)) { if (!wrapperData->runWithoutJVM) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Launching a JVM...")); } if (wrapperLaunchJavaApp()) { /* We know that there was a problem launching the JVM process. * If we fail at this level, assume it is a critical problem and don't bother trying to restart later. * A message should have already been logged. */ wrapperSetJavaState(WRAPPER_JSTATE_DOWN_CLEAN, nowTicks, -1); } else { /* The JVM was launched. We still do not know whether the * launch will be successful. Allow seconds before giving up. * This can take quite a while if the system is heavily loaded. * (At startup for example) */ if (wrapperData->startupTimeout > 0) { wrapperSetJavaState(WRAPPER_JSTATE_LAUNCHING, nowTicks, wrapperData->startupTimeout); } else { wrapperSetJavaState(WRAPPER_JSTATE_LAUNCHING, nowTicks, -1); } } } else { /* The wrapper is shutting down, pausing or paused. Switch to the down clean state because the JVM was never launched. */ wrapperSetJavaState(WRAPPER_JSTATE_DOWN_CLEAN, nowTicks, -1); } } /** * WRAPPER_JSTATE_LAUNCHING * The JVM process has been launched, but there has been no confirmation that * the JVM and its application have started. We remain in this state until * the state times out or the WrapperManager class in the JVM has sent a * message that it is initialized. * * nowTicks: The tick counter value this time through the event loop. * nextSleep: Flag which is used to determine whether or not the state engine * will be sleeping before then next time through the loop. It * may make sense to avoid certain actions if it is known that the * function will be called again immediately. */ void jStateLaunching(TICKS nowTicks, int nextSleep) { /* Make sure that the JVM process is still up and running */ if (nextSleep && (wrapperGetProcessStatus(nowTicks, FALSE) == WRAPPER_PROCESS_DOWN)) { /* The process is gone. Restart it. (Handled and logged) */ } else { /* The process is up and running. * We are waiting in this state until we receive a KEY packet * from the JVM attempting to register. * Have we waited too long already */ if (wrapperCheckJstateTimeout(nowTicks, TRUE)) { if (wrapperData->debugJVM) { handleDebugJVMTimeout(nowTicks, TEXT("Startup: Timed out waiting for a signal from the JVM."), TEXT("startup")); } else { displayLaunchingTimeoutMessage(); /* Give up on the JVM and start trying to kill it. */ wrapperKillProcess(FALSE); /* Restart the JVM. */ wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_AUTOMATIC; } } } } /** * WRAPPER_JSTATE_LAUNCHED * The WrapperManager class in the JVM has been initialized. We are now * ready to request that the application in the JVM be started. * * nowTicks: The tick counter value this time through the event loop. * nextSleep: Flag which is used to determine whether or not the state engine * will be sleeping before then next time through the loop. It * may make sense to avoid certain actions if it is known that the * function will be called again immediately. */ void jStateLaunched(TICKS nowTicks, int nextSleep) { int ret; /* The Java side of the wrapper code has responded to a ping. * Tell the Java wrapper to start the Java application. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Start Application.")); } ret = wrapperProtocolFunction(WRAPPER_MSG_START, TEXT("start")); if (ret) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unable to send the start command to the JVM.")); /* Give up on the JVM and start trying to kill it. */ wrapperKillProcess(FALSE); /* Restart the JVM. */ wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_AUTOMATIC; } else { /* Start command send. Start waiting for the app to signal * that it has started. Allow seconds before * giving up. A good application will send starting signals back * much sooner than this as a way to extend this time if necessary. */ if (wrapperData->startupTimeout > 0) { wrapperSetJavaState(WRAPPER_JSTATE_STARTING, nowTicks, wrapperData->startupTimeout); } else { wrapperSetJavaState(WRAPPER_JSTATE_STARTING, nowTicks, -1); } } } /** * WRAPPER_JSTATE_STARTING * The JVM is up and the application has been asked to start. We * stay in this state until we receive confirmation that the * application has been started or the state times out. * * nowTicks: The tick counter value this time through the event loop. * nextSleep: Flag which is used to determine whether or not the state engine * will be sleeping before then next time through the loop. It * may make sense to avoid certain actions if it is known that the * function will be called again immediately. */ void jStateStarting(TICKS nowTicks, int nextSleep) { /* Make sure that the JVM process is still up and running */ if (nextSleep && (wrapperGetProcessStatus(nowTicks, FALSE) == WRAPPER_PROCESS_DOWN)) { /* The process is gone. Restart it. (Handled and logged) */ } else { /* Have we waited too long already */ if (wrapperCheckJstateTimeout(nowTicks, TRUE)) { if (wrapperData->debugJVM) { handleDebugJVMTimeout(nowTicks, TEXT("Startup: Timed out waiting for a signal from the JVM."), TEXT("startup")); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Startup failed: Timed out waiting for signal from JVM.")); /* Give up on the JVM and start trying to kill it. */ wrapperKillProcess(FALSE); /* Restart the JVM. */ wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_AUTOMATIC; } } else { /* Keep waiting. */ } } } /** * WRAPPER_JSTATE_STARTED * The application in the JVM has confirmed that it is started. We will * stay in this state, sending pings to the JVM at regular intervals, * until the JVM fails to respond to a ping, or the JVM is ready to be * shutdown. * The pings are sent to make sure that the JVM does not die or hang. * * nowTicks: The tick counter value this time through the event loop. * nextSleep: Flag which is used to determine whether or not the state engine * will be sleeping before then next time through the loop. It * may make sense to avoid certain actions if it is known that the * function will be called again immediately. */ #define JSTATESTARTED_MESSAGE_MAXLEN (7 + 8 + 1) /* "silent ffffffff\0" */ void jStateStarted(TICKS nowTicks, int nextSleep) { int ret; TCHAR protocolMessage[JSTATESTARTED_MESSAGE_MAXLEN]; PPendingPing pendingPing; /* Make sure that the JVM process is still up and running */ if (nextSleep && (wrapperGetProcessStatus(nowTicks, FALSE) == WRAPPER_PROCESS_DOWN)) { /* The process is gone. Restart it. (Handled and logged) */ } else { /* Look for any PendingPings which are slow but that we have not yet made a note of. * Don't worry about the posibility of finding more than one in a single pass as that should only happen if the Wrapper process was without CPU for a while. We will quickly catchup on the following cycles. */ if (wrapperData->firstUnwarnedPendingPing != NULL) { if ((wrapperData->pingAlertThreshold > 0) && (wrapperGetTickAgeTicks(wrapperData->firstUnwarnedPendingPing->slowTicks, nowTicks) >= 0)) { wrapperPingSlow(); /* Remove the PendingPing so it won't be warned again. It still exists in the main list, so it should not be cleaned up here. */ wrapperData->firstUnwarnedPendingPing = wrapperData->firstUnwarnedPendingPing->nextPendingPing; } } if (wrapperData->pingTimedOut && wrapperData->jStateTimeoutTicksSet && (wrapperGetTickAgeTicks(wrapperData->jStateTimeoutTicks, nowTicks) < 0)) { /* No longer in a timeout state. Lets reset the flag to allow for further actions if the JVM happens to hang again. */ wrapperData->pingTimedOut = FALSE; } if (wrapperCheckJstateTimeout(nowTicks, TRUE)) { /* Have we waited too long already. The jStateTimeoutTicks is reset each time a ping * response is received from the JVM. */ if (wrapperData->debugJVM) { handleDebugJVMTimeout(nowTicks, TEXT("Ping: Timed out waiting for signal from JVM."), TEXT("ping")); } else { if (wrapperData->pingTimedOut == FALSE) { wrapperPingTimeoutResponded(); /* This is to ensure only one call to wrapperPingTimeoutResponded() will be done even if the state of the JVM remains started after processing the actions. */ wrapperData->pingTimedOut = TRUE; } } } else if (wrapperGetTickAgeTicks(wrapperAddToTicks(wrapperData->lastPingTicks, wrapperData->pingInterval), nowTicks) >= 0) { /* It is time to send another ping to the JVM */ if (wrapperGetTickAgeTicks(wrapperAddToTicks(wrapperData->lastLoggedPingTicks, wrapperData->pingIntervalLogged), nowTicks) >= 0) { if (wrapperData->isLoopOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Loop: Sending a ping packet.")); } _sntprintf(protocolMessage, JSTATESTARTED_MESSAGE_MAXLEN, TEXT("ping %08x"), nowTicks); ret = wrapperProtocolFunction(WRAPPER_MSG_PING, protocolMessage); wrapperData->lastLoggedPingTicks = nowTicks; } else { if (wrapperData->isLoopOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Loop: Sending a silent ping packet.")); } _sntprintf(protocolMessage, JSTATESTARTED_MESSAGE_MAXLEN, TEXT("silent %08x"), nowTicks); ret = wrapperProtocolFunction(WRAPPER_MSG_PING, protocolMessage); } if (ret) { /* Failed to send the ping. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("JVM Ping Failed.")); } } else { /* Ping sent successfully. */ if (wrapperData->pendingPingQueueOverflow && (!wrapperData->pendingPingQueueOverflowEmptied)) { /* We don't want to create any more PendingPing objects until the JVM has caught up. */ } else if (wrapperData->pendingPingCount >= WRAPPER_MAX_PENDING_PINGS) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Too many Pending Pings. Disabling some ping checks until the JVM has caught up.")); } wrapperData->pendingPingQueueOverflow = TRUE; wrapperData->pendingPingQueueOverflowEmptied = FALSE; #ifdef DEBUG_PING_QUEUE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" PING QUEUE Set Overflow")); #endif } else { pendingPing = malloc(sizeof(PendingPing)); if (!pendingPing) { outOfMemory(TEXT("JSS"), 1); } else { memset(pendingPing, 0, sizeof(PendingPing)); pendingPing->sentTicks = nowTicks; pendingPing->slowTicks = wrapperAddToTicks(nowTicks, wrapperData->pingAlertThreshold); /* Add it to the PendingPing queue. */ if (wrapperData->firstPendingPing == NULL) { /* The queue was empty. */ wrapperData->pendingPingCount = 1; wrapperData->firstUnwarnedPendingPing = pendingPing; wrapperData->firstPendingPing = pendingPing; wrapperData->lastPendingPing = pendingPing; } else { /* Add to the end of an existing queue. */ wrapperData->pendingPingCount++; if (wrapperData->firstUnwarnedPendingPing == NULL) { wrapperData->firstUnwarnedPendingPing = pendingPing; } wrapperData->lastPendingPing->nextPendingPing = pendingPing; wrapperData->lastPendingPing = pendingPing; } #ifdef DEBUG_PING_QUEUE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("+++ PING QUEUE Size: %d"), wrapperData->pendingPingCount); #endif if ((wrapperData->pendingPingCount > 1) && wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Pending Pings %d"), wrapperData->pendingPingCount); } } } } if (wrapperData->isLoopOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Loop: Sent a ping packet.")); } wrapperData->lastPingTicks = nowTicks; } else { /* Do nothing. Keep waiting. */ } } } /** * WRAPPER_JSTATE_STOP * The application in the JVM should be asked to stop but is still running. * * nowTicks: The tick counter value this time through the event loop. * nextSleep: Flag which is used to determine whether or not the state engine * will be sleeping before then next time through the loop. It * may make sense to avoid certain actions if it is known that the * function will be called again immediately. */ void jStateStop(TICKS nowTicks, int nextSleep) { /* Make sure that the JVM process is still up and running */ if (nextSleep && (wrapperGetProcessStatus(nowTicks, FALSE) == WRAPPER_PROCESS_DOWN)) { /* The process is gone. (Handled and logged)*/ } else { /* Ask the JVM to shutdown. */ wrapperProtocolFunction(WRAPPER_MSG_STOP, NULL); /* Allow up to 5 + seconds for the application to stop itself. */ if (wrapperData->shutdownTimeout > 0) { wrapperSetJavaState(WRAPPER_JSTATE_STOPPING, nowTicks, 5 + wrapperData->shutdownTimeout); } else { wrapperSetJavaState(WRAPPER_JSTATE_STOPPING, nowTicks, -1); } } } /** * WRAPPER_JSTATE_STOPPING * The application in the JVM has been asked to stop but we are still * waiting for a signal that it is stopped. * * nowTicks: The tick counter value this time through the event loop. * nextSleep: Flag which is used to determine whether or not the state engine * will be sleeping before then next time through the loop. It * may make sense to avoid certain actions if it is known that the * function will be called again immediately. */ void jStateStopping(TICKS nowTicks, int nextSleep) { /* Make sure that the JVM process is still up and running */ if (nextSleep && (wrapperGetProcessStatus(nowTicks, FALSE) == WRAPPER_PROCESS_DOWN)) { /* The process is gone. (Handled and logged)*/ } else { /* Have we waited too long already */ if (wrapperCheckJstateTimeout(nowTicks, TRUE)) { if (wrapperData->debugJVM) { handleDebugJVMTimeout(nowTicks, TEXT("Shutdown: Timed out waiting for a signal from the JVM."), TEXT("shutdown")); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Shutdown failed: Timed out waiting for signal from JVM.")); /* Give up on the JVM and start trying to kill it. */ wrapperData->exitCode = wrapperData->errorExitCode; wrapperKillProcess(FALSE); } } else { /* Keep waiting. */ } } } /** * WRAPPER_JSTATE_STOPPED * The application in the JVM has signaled that it has stopped. We are now * waiting for the JVM process to exit. A good application will do this on * its own, but if it fails to exit in a timely manner then the JVM will be * killed. * Once the JVM process is gone we go back to the DOWN state. * * nowTicks: The tick counter value this time through the event loop. * nextSleep: Flag which is used to determine whether or not the state engine * will be sleeping before then next time through the loop. It * may make sense to avoid certain actions if it is known that the * function will be called again immediately. */ void jStateStopped(TICKS nowTicks, int nextSleep) { if (nextSleep && (wrapperGetProcessStatus(nowTicks, FALSE) == WRAPPER_PROCESS_DOWN)) { /* The process is gone. This is what we were waiting for. */ } else { /* Have we waited too long already */ if (wrapperCheckJstateTimeout(nowTicks, TRUE)) { if (wrapperData->debugJVM) { handleDebugJVMTimeout(nowTicks, TEXT("Shutdown: Timed out waiting for the JVM to terminate."), TEXT("JVM exit")); } else { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Shutdown failed: Timed out waiting for the JVM to terminate.")); /* Give up on the JVM and start trying to kill it. */ wrapperData->exitCode = wrapperData->errorExitCode; wrapperKillProcess(FALSE); } } else { /* Keep waiting. */ } } } /** * WRAPPER_JSTATE_KILLING * The Wrapper is about to kill the JVM. If thread dumps on exit is enabled * then the Wrapper must wait a few moments between telling the JVM to do * a thread dump and actually killing it. The Wrapper will sit in this state * while it is waiting. * * nowTicks: The tick counter value this time through the event loop. * nextSleep: Flag which is used to determine whether or not the state engine * will be sleeping before then next time through the loop. It * may make sense to avoid certain actions if it is known that the * function will be called again immediately. */ void jStateKilling(TICKS nowTicks, int nextSleep) { /* Make sure that the JVM process is still up and running */ if (nextSleep && (wrapperGetProcessStatus(nowTicks, FALSE) == WRAPPER_PROCESS_DOWN)) { /* The process is gone. (Handled and logged) */ } else { /* Have we waited long enough */ if (wrapperCheckJstateTimeout(nowTicks, FALSE)) { /* It is time to actually kill the JVM. */ wrapperSetJavaState(WRAPPER_JSTATE_KILL, nowTicks, 0); } else { /* Keep waiting. */ } } } /** * WRAPPER_JSTATE_KILL * The Wrapper is ready to kill the JVM. * * nowTicks: The tick counter value this time through the event loop. * nextSleep: Flag which is used to determine whether or not the state engine * will be sleeping before then next time through the loop. It * may make sense to avoid certain actions if it is known that the * function will be called again immediately. */ void jStateKill(TICKS nowTicks, int nextSleep) { if (nextSleep && (wrapperGetProcessStatus(nowTicks, FALSE) == WRAPPER_PROCESS_DOWN)) { /* The process is gone. (Handled and logged) */ } else { /* Have we waited long enough */ if (wrapperCheckJstateTimeout(nowTicks, FALSE)) { /* It is time to actually kill the JVM. */ if (wrapperKillProcessNow()) { /* The attempt to forcibly kill the JVM process failed. There is no way for us to continue from this point as another JVM can not be safely launched. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Failed to forcibly terminate the JVM process, unable to continue.")); if (wrapperData->restartRequested != WRAPPER_RESTART_REQUESTED_NO) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT(" The scheduled restart of the JVM has been cancelled.")); wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_NO; wrapperData->isRestartDisabled = TRUE; } } else { /* The request to kill the JVM was successful, but we do not yet know if it is actually gone. * There was a problem on Windows where the OS did not actually follow through will killing the JVM. * The WRAPPER_JSTATE_KILLED state is used to confirm that the JVM is actually gone. */ if (wrapperData->jvmTerminateTimeout > 0) { wrapperSetJavaState(WRAPPER_JSTATE_KILLED, nowTicks, 5 + wrapperData->jvmTerminateTimeout); } else { wrapperSetJavaState(WRAPPER_JSTATE_KILLED, nowTicks, -1); } } } else { /* Keep waiting. */ } } } /** * WRAPPER_JSTATE_KILLED * The Wrapper has asked the OS to forcibly kill the JVM and is now waiting to confirm that is actually gone. * * nowTicks: The tick counter value this time through the event loop. * nextSleep: Flag which is used to determine whether or not the state engine * will be sleeping before then next time through the loop. It * may make sense to avoid certain actions if it is known that the * function will be called again immediately. */ void jStateKillConfirm(TICKS nowTicks, int nextSleep) { if (nextSleep && (wrapperGetProcessStatus(nowTicks, FALSE) == WRAPPER_PROCESS_DOWN)) { /* The process is gone. (Handled and logged) */ } else { if (wrapperCheckJstateTimeout(nowTicks, FALSE)) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Timed out waiting for the OS to forcibly terminate the JVM process, unable to continue.")); if (wrapperData->restartRequested != WRAPPER_RESTART_REQUESTED_NO) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT(" The scheduled restart of the JVM has been cancelled.")); wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_NO; wrapperData->isRestartDisabled = TRUE; } wrapperSetJavaState(WRAPPER_JSTATE_DOWN_CHECK, nowTicks, -1); wrapperStopProcess(wrapperData->errorExitCode, TRUE); } else { /* Keep waiting. */ } } } /** * WRAPPER_JSTATE_DOWN_CHECK * The JVM process currently does not exist but we still need to clean up. * Once we have cleaned up, we will switch to the DOWN_FLUSH state. * * nowTicks: The tick counter value this time through the event loop. * nextSleep: Flag which is used to determine whether or not the state engine * will be sleeping before then next time through the loop. It * may make sense to avoid certain actions if it is known that the * function will be called again immediately. */ void jStateDownCheck(TICKS nowTicks, int nextSleep) { wrapperSetJavaState(WRAPPER_JSTATE_DOWN_FLUSH_STDIN, nowTicks, -1); } /** * WRAPPER_JSTATE_DOWN_FLUSH_STDIN * The JVM process and all its children are confirmed terminated. We now need * to empty STDIN in order to cleanly start with a new series of bytes for the * next JVM. This is only needed if a restart is requested. * * NOTES: - This state never times out, but it starts printing a message at regular * intervals if the flushing takes too long. * TODO: If we want to implement a timeout, we should think of a way to * inform the process piping in that we are no longer accepting new data. * - This state will not be called if we go directly to the down clean state * before the JVM was even launched. This is intended. The stdin is reset * only if a JVM was launched. * * nowTicks: The tick counter value this time through the event loop. * nextSleep: Flag which is used to determine whether or not the state engine * will be sleeping before then next time through the loop. It * may make sense to avoid certain actions if it is known that the * function will be called again immediately. */ void jStateDownFlushStdin(TICKS nowTicks, int nextSleep) { #ifndef WIN32 static int tick_counter = 0; int wait = FALSE; if ((wrapperData->javaINFlush) && (wrapperData->javaNewProcessGroup) && !wrapperData->disableConsoleInput && (wrapperData->restartRequested) && (wrapperData->wState != WRAPPER_WSTATE_STOPPING) && (wrapperData->wState != WRAPPER_WSTATE_STOPPED)) { if (tick_counter == 0) { if (wrapperData->isStateOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Waiting for stdin to be flushed.")); } /* JVM is gone. The read end of pipeind was closed when forking in the parent, and the write-end was set to auto-close on JVM exit. * In case any of the above failed, just make sure that the pipe is closed as this is needed for JavaIN thread to start flushing stdin data. */ closeStdinPipe(); } if (!wrapperData->javaINFlushed) { if (!wrapperData->javaINFlushing) { /* Normally we should go into flushing mode pretty quickly as select() should not block longer than one tick * and write() should fail immediately once Java is down. However, if the JavaIN thread gets blocked on read() * for some reason, we don't want that to prevent Java from restarting. */ if (tick_counter == (900 / WRAPPER_TICK_MS)) { /* The JavaIN thread is blocked and lost. Disable stdin. */ wrapperData->disableConsoleInputPermanent = TRUE; wrapperData->disableConsoleInput = TRUE; log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("The thread reading stdin is not responding. Stop reading console input.")); } else { wait = TRUE; } } else { /* Flushing... */ if (tick_counter == (1000 / WRAPPER_TICK_MS)) { /* Print a first message after 1 sec. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Flushing stdin before starting a new JVM...")); } else if (tick_counter == (11000 / WRAPPER_TICK_MS)) { /* Print a new message every 10 secs. */ log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Flushing stdin before starting a new JVM...")); tick_counter = (1000 / WRAPPER_TICK_MS); } wait = TRUE; } if (wait) { tick_counter++; /* Do no go to next state. */ return; } } } /* reset counter */ tick_counter = 0; #endif wrapperSetJavaState(WRAPPER_JSTATE_DOWN_FLUSH, nowTicks, -1); } /** * WRAPPER_JSTATE_DOWN_FLUSH * The JVM process currently does not exist but we still need to confirm that * the backend is closed and the ping queue is flushed. * Once we have processed everything, we will switch to the DOWN_CLEAN state. * * nowTicks: The tick counter value this time through the event loop. * nextSleep: Flag which is used to determine whether or not the state engine * will be sleeping before then next time through the loop. It * may make sense to avoid certain actions if it is known that the * function will be called again immediately. */ void jStateDownFlush(TICKS nowTicks, int nextSleep) { PPendingPing pendingPing; /* Always proceed after a single cycle. */ /* TODO - Look into ways of reliably detecting when the backend and stdout piles are closed. */ /* Always close the backend here to make sure we are ready for the next JVM. * In normal cases, the backend will have already been closed, but if the JVM * crashed or the Wrapper thread was delayed, then it is possible that it is * still open at this point. */ wrapperProtocolClose(); /* Make sure that the PendingPing pool is empty so they don't cause strange behavior with the next JVM invocation. */ if (wrapperData->firstPendingPing != NULL) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("%d pings were not replied to when the JVM process exited."), wrapperData->pendingPingCount); } while (wrapperData->firstPendingPing != NULL) { pendingPing = wrapperData->firstPendingPing; if (pendingPing->nextPendingPing != NULL) { /* This was the first PendingPing of several in the queue. */ wrapperData->pendingPingCount--; if (wrapperData->firstUnwarnedPendingPing == wrapperData->firstPendingPing) { wrapperData->firstUnwarnedPendingPing = pendingPing->nextPendingPing; } wrapperData->firstPendingPing = pendingPing->nextPendingPing; pendingPing->nextPendingPing = NULL; #ifdef DEBUG_PING_QUEUE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("--- PING QUEUE Size: %d"), wrapperData->pendingPingCount); #endif } else { /* This was the only PendingPing in the queue. */ wrapperData->pendingPingCount = 0; wrapperData->firstUnwarnedPendingPing = NULL; wrapperData->firstPendingPing = NULL; wrapperData->lastPendingPing = NULL; #ifdef DEBUG_PING_QUEUE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("--- PING QUEUE Empty.") ); #endif } /* Free up the pendingPing object. */ if (pendingPing != NULL) { free(pendingPing); pendingPing = NULL; } } } if (wrapperData->pendingPingQueueOverflow) { wrapperData->pendingPingQueueOverflow = FALSE; wrapperData->pendingPingQueueOverflowEmptied = FALSE; #ifdef DEBUG_PING_QUEUE log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("--- PING QUEUE Reset Overflow.") ); #endif } /* We are now down and clean. */ wrapperSetJavaState(WRAPPER_JSTATE_DOWN_CLEAN, nowTicks, -1); } /******************************************************************** * Event Loop / State Engine *******************************************************************/ void logTickTimerStats() { struct tm when; time_t now, overflowTime; TICKS sysTicks; TICKS ticks; time(&now); sysTicks = wrapperGetSystemTicks(); overflowTime = (time_t)(now - (sysTicks / (1000 / WRAPPER_TICK_MS))); when = *localtime(&overflowTime); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Last system time tick overflow at: %04d/%02d/%02d %02d:%02d:%02d"), when.tm_year + 1900, when.tm_mon + 1, when.tm_mday, when.tm_hour, when.tm_min, when.tm_sec); overflowTime = (time_t)(now + ((0xffffffffUL - sysTicks) / (1000 / WRAPPER_TICK_MS))); when = *localtime(&overflowTime); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Next system time tick overflow at: %04d/%02d/%02d %02d:%02d:%02d"), when.tm_year + 1900, when.tm_mon + 1, when.tm_mday, when.tm_hour, when.tm_min, when.tm_sec); if (!wrapperData->useSystemTime) { ticks = wrapperGetTicks(); overflowTime = (time_t)(now - (ticks / (1000 / WRAPPER_TICK_MS))); when = *localtime(&overflowTime); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Last tick overflow at: %04d/%02d/%02d %02d:%02d:%02d"), when.tm_year + 1900, when.tm_mon + 1, when.tm_mday, when.tm_hour, when.tm_min, when.tm_sec); overflowTime = (time_t)(now + ((0xffffffffUL - ticks) / (1000 / WRAPPER_TICK_MS))); when = *localtime(&overflowTime); log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Next tick overflow at: %04d/%02d/%02d %02d:%02d:%02d"), when.tm_year + 1900, when.tm_mon + 1, when.tm_mday, when.tm_hour, when.tm_min, when.tm_sec); } } /** * The main event loop for the wrapper. Handles all state changes and events. */ void wrapperEventLoop() { TICKS nowTicks; int uptimeSeconds; TICKS lastCycleTicks = wrapperGetTicks(); int nextSleep; int prevWState; int prevJState; /* Initialize the tick timeouts. */ wrapperData->anchorTimeoutTicks = lastCycleTicks; wrapperData->commandTimeoutTicks = lastCycleTicks; wrapperData->memoryOutputTimeoutTicks = lastCycleTicks; wrapperData->cpuOutputTimeoutTicks = lastCycleTicks; wrapperData->pageFaultOutputTimeoutTicks = lastCycleTicks; wrapperData->logfileCloseTimeoutTicks = lastCycleTicks; wrapperData->logfileCloseTimeoutTicksSet = FALSE; wrapperData->logfileFlushTimeoutTicks = lastCycleTicks; wrapperData->logfileFlushTimeoutTicksSet = FALSE; /* Always auto-flush untils the main loop is reached. This guaranties us all log outputs even if the Wrapper * stops suddenly or get blocked before this point. (had problems when waiting for network interfaces to be up). */ setLogfileAutoFlush(wrapperData->logfileFlushTimeout == 0); if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Use tick timer mutex=%s"), wrapperData->useTickMutex ? TEXT("TRUE") : TEXT("FALSE")); } if (wrapperData->isTickOutputEnabled) { logTickTimerStats(); } if (wrapperData->isLoopOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Event loop started.")); } if (wrapperData->isMemoryOutputEnabled) { wrapperDumpMemoryBanner(); } nextSleep = TRUE; do { if (wrapperData->isLoopOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Loop: %ssleep"), (nextSleep ? TEXT("") : TEXT("no "))); } if (nextSleep) { /* Sleep for a tenth of a second. */ wrapperSleep(100); } nextSleep = TRUE; prevWState = wrapperData->wState; prevJState = wrapperData->jState; /* Before doing anything else, always maintain the logger to make sure * that any queued messages are logged before doing anything else. * Called a second time after socket and child output to make sure * that all messages appropriate for the state changes have been * logged. Failure to do so can result in a confusing sequence of * output. */ if (wrapperData->isLoopOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Loop: maintain logger")); } maintainLogger(); if (wrapperData->pauseThreadMain) { wrapperPauseThread(wrapperData->pauseThreadMain, TEXT("main")); wrapperData->pauseThreadMain = 0; } /* After we maintain the logger, see if there were any signals trapped. */ #ifdef WIN32 wrapperMaintainControlCodes(); #else wrapperMaintainSignals(); #endif #ifdef WIN32 /* Check to make sure the Wrapper or Java console windows are hidden. * This is done here to make sure they go away even in cases where they can't be hidden right away. * Users have also reported that the console can be redisplayed when a user logs back in or switches users. */ wrapperCheckConsoleWindows(); #endif if (!wrapperData->useJavaIOThread) { /* Check the stout pipe of the child process. */ if (wrapperData->isLoopOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Loop: process JVM output")); } /* Request that the processing of child output not take more than 250ms. */ if (wrapperReadChildOutput(250)) { if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Pause reading child process output to share cycles.")); } nextSleep = FALSE; } } /* Check for incoming data packets. */ if (wrapperData->isLoopOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Loop: process socket")); } /* Don't bother processing the socket if we are shutting down and the JVM is down. */ if (((wrapperData->jState == WRAPPER_JSTATE_DOWN_CHECK) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH_STDIN) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_CLEAN)) && ((wrapperData->wState == WRAPPER_WSTATE_STOPPING) || (wrapperData->wState == WRAPPER_WSTATE_STOPPED))) { /* Skip socket processing. */ } else { if (wrapperProtocolRead()) { /* There was more data waiting to be read, but we broke out. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Pause reading socket data to share cycles.")); } nextSleep = FALSE; } } /* See comment for first call above. */ if (wrapperData->isLoopOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Loop: maintain logger(2)")); } maintainLogger(); /* Get the current time for use in this cycle. */ nowTicks = wrapperGetTicks(); /* Tell the logging code what to use for the uptime. */ if (!wrapperData->uptimeFlipped) { uptimeSeconds = wrapperGetTickAgeSeconds(WRAPPER_TICK_INITIAL, nowTicks); if (uptimeSeconds > WRAPPER_MAX_UPTIME_SECONDS) { wrapperData->uptimeFlipped = TRUE; setUptime(0, TRUE); } else { setUptime(uptimeSeconds, FALSE); } } /* Log memory usage. */ if (wrapperData->isMemoryOutputEnabled) { if (wrapperTickExpired(nowTicks, wrapperData->memoryOutputTimeoutTicks)) { wrapperDumpMemory(); wrapperData->memoryOutputTimeoutTicks = wrapperAddToTicks(nowTicks, wrapperData->memoryOutputInterval); } } /* Log CPU usage. */ if (wrapperData->isCPUOutputEnabled) { if (wrapperTickExpired(nowTicks, wrapperData->cpuOutputTimeoutTicks)) { wrapperDumpCPUUsage(); wrapperData->cpuOutputTimeoutTicks = wrapperAddToTicks(nowTicks, wrapperData->cpuOutputInterval); } } #ifdef WIN32 /* Log PageFault info. */ if (wrapperData->isPageFaultOutputEnabled) { if (wrapperTickExpired(nowTicks, wrapperData->pageFaultOutputTimeoutTicks)) { wrapperDumpPageFaultUsage(); wrapperData->pageFaultOutputTimeoutTicks = wrapperAddToTicks(nowTicks, wrapperData->pageFaultOutputInterval); } } #endif /* Test the activity of the logfile. */ if (getLogfileActivity() != 0) { /* There was log output since the last pass. */ /* Set the close timeout if enabled. This is based on inactivity, so we always want to extend it from the current time when there was output. */ if (wrapperData->logfileCloseTimeout > 0) { wrapperData->logfileCloseTimeoutTicks = wrapperAddToTicks(nowTicks, wrapperData->logfileCloseTimeout); wrapperData->logfileCloseTimeoutTicksSet = TRUE; } /* Set the flush timeout if enabled, and it is not already set. */ if (wrapperData->logfileFlushTimeout > 0) { if (!wrapperData->logfileFlushTimeoutTicksSet) { wrapperData->logfileFlushTimeoutTicks = wrapperAddToTicks(nowTicks, wrapperData->logfileFlushTimeout); wrapperData->logfileFlushTimeoutTicksSet = TRUE; } } } else if (wrapperData->logfileCloseTimeoutTicksSet && (wrapperTickExpired(nowTicks, wrapperData->logfileCloseTimeoutTicks))) { /* If the inactivity timeout has expired then we want to close the logfile, otherwise simply flush it. */ closeLogfile(); /* Reset the timeout ticks so we don't start another timeout until something has been logged. */ wrapperData->logfileCloseTimeoutTicksSet = FALSE; /* If we close the file, it is automatically flushed. */ wrapperData->logfileFlushTimeoutTicksSet = FALSE; } /* Is it is time to flush the logfile? */ if (wrapperData->logfileFlushTimeoutTicksSet && (wrapperTickExpired(nowTicks, wrapperData->logfileFlushTimeoutTicks))) { /* Time to flush the output. */ flushLogfile(); /* Reset the timeout until more output is logged. */ wrapperData->logfileFlushTimeoutTicksSet = FALSE; } /* Has the process been getting CPU? This check will only detect a lag * if the useSystemTime flag is set. */ if (wrapperGetTickAgeSeconds(lastCycleTicks, nowTicks) > wrapperData->cpuTimeout) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_INFO, TEXT("Wrapper Process has not received any CPU time for %d seconds. Extending timeouts."), wrapperGetTickAgeSeconds(lastCycleTicks, nowTicks)); if (wrapperData->jStateTimeoutTicksSet) { wrapperData->jStateTimeoutTicks = wrapperAddToTicks(wrapperData->jStateTimeoutTicks, wrapperGetTickAgeSeconds(lastCycleTicks, nowTicks)); } } lastCycleTicks = nowTicks; printStateOutput(nowTicks); /* If we are configured to do so, confirm that the anchor file still exists. */ anchorPoll(nowTicks); /* If we are configured to do so, look for a command file and perform any * requested operations. */ commandPoll(nowTicks); if (wrapperData->exitRequested) { /* A new request for the JVM to be stopped has been made. */ if (wrapperData->isLoopOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Loop: exit requested")); } /* Acknowledge that we have seen the exit request so we don't get here again. */ wrapperData->exitRequested = FALSE; if (wrapperData->jState == WRAPPER_JSTATE_DOWN_CLEAN) { /* A JVM is not currently running. Nothing to do.*/ } else if ((wrapperData->jState == WRAPPER_JSTATE_LAUNCH_DELAY) || (wrapperData->jState == WRAPPER_JSTATE_RESTART) || (wrapperData->jState == WRAPPER_JSTATE_LAUNCH)) { /* A JVM is not yet running go back to the DOWN_CLEAN state. */ wrapperSetJavaState(WRAPPER_JSTATE_DOWN_CLEAN, nowTicks, -1); } else if ((wrapperData->jState == WRAPPER_JSTATE_STOP) || (wrapperData->jState == WRAPPER_JSTATE_STOPPING) || (wrapperData->jState == WRAPPER_JSTATE_STOPPED) || (wrapperData->jState == WRAPPER_JSTATE_KILLING) || (wrapperData->jState == WRAPPER_JSTATE_KILL) || (wrapperData->jState == WRAPPER_JSTATE_KILLED) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_CHECK) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH_STDIN) || (wrapperData->jState == WRAPPER_JSTATE_DOWN_FLUSH)) { /* The JVM is already being stopped, so nothing else needs to be done. */ } else { /* The JVM should be running or is in the process of launching, so it needs to be stopped. */ if (wrapperGetProcessStatus(nowTicks, FALSE) == WRAPPER_PROCESS_DOWN) { /* The process is gone. (Handled and logged) */ if (wrapperData->restartPacketReceived) { /* The restart packet was received. If we are here then it was delayed, * but it means that we do want to restart. */ } else { /* We never want to restart here. */ wrapperData->restartRequested = WRAPPER_RESTART_REQUESTED_NO; if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Reset the restart flag.")); } } } else { /* JVM is still up. Try asking it to shutdown nicely. */ if (wrapperData->isDebugging) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_DEBUG, TEXT("Sending stop signal to JVM")); } wrapperSetJavaState(WRAPPER_JSTATE_STOP, nowTicks, -1); } } } /* Do something depending on the wrapper state */ if (wrapperData->isLoopOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Loop: handle wrapper state: %s"), wrapperGetWState(wrapperData->wState)); } switch(wrapperData->wState) { case WRAPPER_WSTATE_STARTING: wStateStarting(nowTicks); break; case WRAPPER_WSTATE_STARTED: wStateStarted(nowTicks); break; case WRAPPER_WSTATE_PAUSING: wStatePausing(nowTicks); break; case WRAPPER_WSTATE_PAUSED: wStatePaused(nowTicks); break; case WRAPPER_WSTATE_RESUMING: wStateResuming(nowTicks); break; case WRAPPER_WSTATE_STOPPING: wStateStopping(nowTicks); break; case WRAPPER_WSTATE_STOPPED: wStateStopped(nowTicks); break; default: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unknown wState=%d"), wrapperData->wState); break; } /* Do something depending on the JVM state */ if (wrapperData->isLoopOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT(" Loop: handle JVM state: %s"), wrapperGetJState(wrapperData->jState)); } switch(wrapperData->jState) { case WRAPPER_JSTATE_DOWN_CLEAN: jStateDownClean(nowTicks, nextSleep); break; case WRAPPER_JSTATE_LAUNCH_DELAY: jStateLaunchDelay(nowTicks, nextSleep); break; case WRAPPER_JSTATE_RESTART: jStateRestart(nowTicks, nextSleep); break; case WRAPPER_JSTATE_LAUNCH: jStateLaunch(nowTicks, nextSleep); break; case WRAPPER_JSTATE_LAUNCHING: jStateLaunching(nowTicks, nextSleep); break; case WRAPPER_JSTATE_LAUNCHED: jStateLaunched(nowTicks, nextSleep); break; case WRAPPER_JSTATE_STARTING: jStateStarting(nowTicks, nextSleep); break; case WRAPPER_JSTATE_STARTED: jStateStarted(nowTicks, nextSleep); break; case WRAPPER_JSTATE_STOP: jStateStop(nowTicks, nextSleep); break; case WRAPPER_JSTATE_STOPPING: jStateStopping(nowTicks, nextSleep); break; case WRAPPER_JSTATE_STOPPED: jStateStopped(nowTicks, nextSleep); break; case WRAPPER_JSTATE_KILLING: jStateKilling(nowTicks, nextSleep); break; case WRAPPER_JSTATE_KILL: jStateKill(nowTicks, nextSleep); break; case WRAPPER_JSTATE_KILLED: jStateKillConfirm(nowTicks, nextSleep); break; case WRAPPER_JSTATE_DOWN_CHECK: jStateDownCheck(nowTicks, nextSleep); break; case WRAPPER_JSTATE_DOWN_FLUSH_STDIN: jStateDownFlushStdin(nowTicks, nextSleep); break; case WRAPPER_JSTATE_DOWN_FLUSH: jStateDownFlush(nowTicks, nextSleep); break; default: log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_ERROR, TEXT("Unknown jState=%d"), wrapperData->jState); break; } if ((prevWState != wrapperData->wState) || (prevJState != wrapperData->jState)) { nextSleep = FALSE; } } while (wrapperData->wState != WRAPPER_WSTATE_STOPPED); /* Assertion check of Java State. */ if (wrapperData->jState != WRAPPER_JSTATE_DOWN_CLEAN) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_FATAL, TEXT("Wrapper shutting down while java state still %s."), wrapperGetJState(wrapperData->jState)); } if (wrapperData->isLoopOutputEnabled) { log_printf(WRAPPER_SOURCE_WRAPPER, LEVEL_STATUS, TEXT("Event loop stopped.")); } } wrapper_3.5.51_src/src/c/wrapperinfo.c.in100644 0 0 2265 14333053652 15474 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.org/doc/english/licenseOverview.html */ #ifdef WIN32 #include #include #endif #include "wrapper_i18n.h" /** * wrapperinfo.c is built as part of the build process. Ant creates this * file by making a copy of wrapperinfo.c.in, replacing tokens as it does * so. If you need to make modifications to this file, the changes should * always be made to wrapperinfo.c.in. */ TCHAR *wrapperVersionRoot = TEXT("@version.root@"); TCHAR *wrapperVersion = TEXT("@version@"); TCHAR *wrapperBits = TEXT("@bits@"); TCHAR *wrapperArch = TEXT("@dist.arch@"); TCHAR *wrapperOS = TEXT("@dist.os@"); TCHAR *wrapperReleaseDate = TEXT("20221111"); TCHAR *wrapperReleaseTime = TEXT("0000"); TCHAR *wrapperBuildDate = TEXT("@build.date@"); TCHAR *wrapperBuildTime = TEXT("@build.time@"); TCHAR *wrapperJavacTargetVersion = TEXT("@javac.target.version@"); wrapper_3.5.51_src/src/c/wrapperinfo.h100644 0 0 1425 14333053652 15071 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ #ifndef _WRAPPERINFO_H #define _WRAPPERINFO_H #ifdef WIN32 #include #endif extern TCHAR *wrapperVersionRoot; extern TCHAR *wrapperVersion; extern TCHAR *wrapperBits; extern TCHAR *wrapperArch; extern TCHAR *wrapperOS; extern TCHAR *wrapperReleaseDate; extern TCHAR *wrapperReleaseTime; extern TCHAR *wrapperBuildDate; extern TCHAR *wrapperBuildTime; extern TCHAR *wrapperJavacTargetVersion; #endif wrapper_3.5.51_src/src/c/wrapperjni.c100644 0 0 102375 14333053652 14757 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ #include #include #include #include #include #ifdef WIN32 #include #include #include #define dup2 _dup2 #endif #include "wrapper_i18n.h" #ifndef WIN32 #include #endif #include "loggerjni.h" #include "wrapperjni.h" #include "wrapperinfo.h" /* The largest possible "name+'='+value" property pair length on Windows. */ #define MAX_ENV_PAIR_LEN 32767 int wrapperJNIDebugging = JNI_FALSE; #define CONTROL_EVENT_QUEUE_SIZE 10 int controlEventQueue[CONTROL_EVENT_QUEUE_SIZE]; int controlEventQueueLastReadIndex = 0; int controlEventQueueLastWriteIndex = 0; /** Flag to keep track of whether StdOut has been redirected. */ int redirectedStdOut = FALSE; /** Flag to keep track of whether StdErr has been redirected. */ int redirectedStdErr = FALSE; /* Special symbols that need to be defined manually as part of the bootstrap process. */ const char utf8ClassJavaLangString[] = {106, 97,118, 97, 47, /* java/ */ 108, 97,110,103, 47, /* lang/ */ 83,116,114,105,110,103, 0}; /* "java/lang/String" */ const char utf8MethodInit[] = {60,105,110,105,116, 62, 0}; /* "" */ const char utf8Sig_BrV[] = {40, 91, 66, 41, 86, 0}; /* "([B)V" */ const char utf8ClassJavaLangOutOfMemoryError[] = {106, 97,118, 97, 47, /* java/ */ 108, 97,110, 103, 47, /* lang/ */ 79, 117, 116, 79, 102, 77, 101, 109, 111, 114, 121, 69, 114, 114, 111, 114, 0}; /* OutOfMemoryError */ const char utf8ClassOrgTanukisoftwareWrapperWrapperJNIError[] = {111, 114, 103, 47, /* org/ */ 116, 97, 110, 117, 107, 105, 115, 111, 102, 116, 119, 97, 114, 101, 47, /* tanukisoftware/ */ 119, 114, 97, 112, 112, 101, 114, 47, /* wrapper/ */ 87, 114, 97, 112, 112, 101, 114, 74, 78, 73, 69, 114, 114, 111, 114, 0}; /* "WrapperJNIError" */ /* * For UTF8 constants, '_' in the name means an array, 'r' preceeds the return * portion of a method declaration, 'V' is Void. The rest is like the * Java format. */ char *utf8SigLjavaLangStringrV; char *utf8ClassJavaLangSystem; char *utf8MethodGetProperties; char *utf8SigVrLjavaUtilProperties; char *utf8MethodGetProperty; char *utf8SigLjavaLangStringrLjavaLangString; char *utf8javaIOIOException; /* "java/io/IOException" */ #ifdef WIN32 #else char *utf8ClassOrgTanukisoftwareWrapperWrapperUNIXUser; char *utf8MethodSetGroup; char *utf8MethodAddGroup; char *utf8SigIIStringStringStringStringrV; char *utf8SigIStringrV; #endif /** * Cause the current thread to sleep for the specified number of milliseconds. * * @param ms Number of milliseconds to wait for. * @param interrupt TRUE to return when nanosleep was interrupted by a signal, * FALSE to continue sleeping the remaining time. * This parameter is ignored on Windows or when using usleep. * * @return the number of remaining ms to sleep if interrupted, -1 if there was an error, 0 otherwise. */ int wrapperSleepInterrupt(int ms, int interrupt) { int result; #ifdef WIN32 Sleep(ms); result = 0; /* On Windows Sleep returns void. */ #else /* We want to use nanosleep if it is available, but make it possible for the user to build a version that uses usleep if they want. usleep does not behave nicely with signals thrown while sleeping. This was the believed cause of a hang experienced on one Solaris system. */ #ifdef USE_USLEEP result = wrapperUsleep(ms * 1000); /* microseconds */ #else struct timespec ts; struct timespec tsRemaining; if (ms >= 1000) { ts.tv_sec = ms / 1000; ts.tv_nsec = (ms % 1000) * 1000000; /* nanoseconds */ } else { ts.tv_sec = 0; ts.tv_nsec = ms * 1000000; /* nanoseconds */ } while (((result = nanosleep(&ts, &tsRemaining)) == -1) && (errno == EINTR)) { if (interrupt) { return (tsRemaining.tv_sec * 1000) + (tsRemaining.tv_nsec / 1000000); } ts.tv_sec = tsRemaining.tv_sec; ts.tv_nsec = tsRemaining.tv_nsec; } #endif #endif return result; } void wrapperSleep(int ms) { wrapperSleepInterrupt(ms, FALSE); } /** * Create a jstring from a MultiBytes Char string. The jstring must be freed up by caller. * * @param env The current JNIEnv. * @param str The MultiBytes string to convert. * * @return The new jstring or NULL if there were any exceptions thrown. */ jstring JNU_NewStringFromNativeMB(JNIEnv *env, const char *str) { jstring result; size_t len; char *strOut; TCHAR* errorW; #ifdef WIN32 int encodingFrom = CP_OEMCP; #else char* encodingFrom = nl_langinfo(CODESET); #ifdef MACOSX if (strlen(encodingFrom) == 0) { encodingFrom = "UTF-8"; } #endif #endif len = strlen(str); if (len > 0) { if (converterMBToMB(str, encodingFrom, &strOut, __UTF8) < 0) { if (strOut) { /* An error message is stored in strOut (we need to convert it to wide chars to display it). */ #ifdef WIN32 if (multiByteToWideChar(strOut, __UTF8, &errorW, FALSE)) { #else if (converterMBToWide(strOut, __UTF8, &errorW, FALSE)) { #endif _tprintf(TEXT("WrapperJNI Warn: Unexpected conversion error: %s\n"), getLastErrorText()); fflush(NULL); } else { _tprintf(TEXT("%s\n"), errorW); fflush(NULL); } if (errorW) { free(errorW); } free(strOut); } else { throwOutOfMemoryError(env, TEXT("JNSFNC1")); } return NULL; } result = (*env)->NewStringUTF(env, strOut); free(strOut); } else { result = (*env)->NewStringUTF(env, str); } return result; } /** * Create a jstring from a Wide Char string. The jstring must be freed up by caller. * * @param env The current JNIEnv. * @param strW The Wide string to convert. * * @return The new jstring or NULL if there were any exceptions thrown. */ jstring JNU_NewStringFromNativeW(JNIEnv *env, const TCHAR *strW) { jstring result; size_t len; char* msgMB; #ifdef WIN32 int size; #else TCHAR* errorW; #endif len = _tcslen(strW); if (len > 0) { #ifdef WIN32 size = WideCharToMultiByte(CP_UTF8, 0, strW, -1, NULL, 0, NULL, NULL); if (size <= 0) { _tprintf(TEXT("WrapperJNI Warn: Failed to convert string \"%s\": %s\n"), strW, getLastErrorText()); fflush(NULL); return NULL; } msgMB = malloc(sizeof(char) * size); if (!msgMB) { throwOutOfMemoryError(env, TEXT("JNSN1")); return NULL; } WideCharToMultiByte(CP_UTF8, 0, strW, -1, msgMB, size, NULL, NULL); #else if (converterWideToMB(strW, &msgMB, MB_UTF8) < 0) { if (msgMB) { /* An error message is stored in msgMB (we need to convert it to wide chars to display it). */ if (converterMBToWide(msgMB, NULL, &errorW, FALSE)) { _tprintf(TEXT("WrapperJNI Warn: Failed to convert string \"%s\": %s\n"), strW, getLastErrorText()); fflush(NULL); } else { _tprintf(TEXT("%s\n"), errorW); fflush(NULL); } if (errorW) { free(errorW); } free(msgMB); } else { throwOutOfMemoryError(env, TEXT("JNSN2")); } return NULL; } #endif } else { /* We need to special case empty strings as some of the functions don't work correctly for them. */ msgMB = malloc(sizeof(char) * 1); if (!msgMB) { throwOutOfMemoryError(env, TEXT("JNSN3")); return NULL; } msgMB[0] = '\0'; } result = (*env)->NewStringUTF(env, msgMB); free(msgMB); return result; } /** * Converts a jstring into a newly malloced TCHAR array. * * @param end The JNIEnv. * @param jstr The jstring. * * @return The requested Wide String, or NULL if there was a problem. It is * the responsibility of the caller to free up the returned string. */ TCHAR *JNU_GetNativeWFromString(JNIEnv *env, jstring jstr) { TCHAR* tresult = NULL; const char *result; #ifdef WIN32 int size; #endif result = (*env)->GetStringUTFChars(env, jstr, NULL); #ifdef WIN32 size = MultiByteToWideChar(CP_UTF8, 0, result, -1, NULL, 0); if (size <= 0) { _tprintf(TEXT("WrapperJNI Warn: Unexpected conversion error: %s\n"), getLastErrorText()); fflush(NULL); } else { tresult = malloc(sizeof(TCHAR) * (size + 1)); if (!tresult) { throwOutOfMemoryError(env, TEXT("GSNC1")); } else { MultiByteToWideChar(CP_UTF8, 0, result,-1, tresult, size + 1); } } #else if (converterMBToWide(result, MB_UTF8, &tresult, TRUE)) { if (tresult) { _tprintf(tresult); fflush(NULL); free(tresult); tresult = NULL; } else { throwOutOfMemoryError(env, TEXT("GSNC2")); } } #endif (*env)->ReleaseStringUTFChars(env, jstr, (const char *)result); return tresult; } #ifdef WIN32 /* So far this function is only used by windows. if we want to use it for unix as well, first provide correct wchar handling... */ void JNU_SetByteArrayRegion(JNIEnv *env, jbyteArray* jarray, jsize start, jsize len, const TCHAR *buffer) { char* msg; #if defined(UNICODE) && defined(WIN32) int size; size = WideCharToMultiByte(CP_OEMCP, 0, buffer, -1, NULL, 0, NULL, NULL); msg = malloc(size); if (!msg) { throwOutOfMemoryError(env, TEXT("JSBAR1")); return; } WideCharToMultiByte(CP_OEMCP,0, buffer,-1, msg, size, NULL, NULL); #else msg = (TCHAR*) buffer; #endif (*env)->SetByteArrayRegion(env, *jarray, start, len, (jbyte*) msg); #if defined(UNICODE) && defined(WIN32) free(msg); #endif } #endif /** * Returns a new buffer containing the UTF8 characters for the specified native string. * Note: this function is ok. Just not sure we need to need to pass through a jstring. * We could handle the conversion in native code only. * * It is the responsibility of the caller to free the returned buffer. */ char *getUTF8Chars(JNIEnv *env, const char *nativeChars) { jstring js; jsize jlen; const char *stringChars; jboolean isCopy; char *utf8Chars = NULL; js = JNU_NewStringFromNativeMB(env, nativeChars); if (js != NULL) { jlen = (*env)->GetStringUTFLength(env, js); utf8Chars = malloc(jlen + 1); if (!utf8Chars) { throwOutOfMemoryError(env, TEXT("GUTFC1")); } else { stringChars = ((*env)->GetStringUTFChars(env, js, &isCopy)); if (stringChars != NULL) { memcpy(utf8Chars, stringChars, jlen); utf8Chars[jlen] = '\0'; (*env)->ReleaseStringUTFChars(env, js, stringChars); } else { throwOutOfMemoryError(env, TEXT("GUTFC2")); free(utf8Chars); utf8Chars = NULL; } } (*env)->DeleteLocalRef(env, js); } return utf8Chars; } void initUTF8Strings(JNIEnv *env) { /* Now initialize all of the strings using our helper function. */ utf8SigLjavaLangStringrV = getUTF8Chars(env, "(Ljava/lang/String;)V"); utf8ClassJavaLangSystem = getUTF8Chars(env, "java/lang/System"); utf8MethodGetProperties = getUTF8Chars(env, "getProperties"); utf8SigVrLjavaUtilProperties = getUTF8Chars(env, "()Ljava/util/Properties;"); utf8MethodGetProperty = getUTF8Chars(env, "getProperty"); utf8SigLjavaLangStringrLjavaLangString = getUTF8Chars(env, "(Ljava/lang/String;)Ljava/lang/String;"); utf8javaIOIOException = getUTF8Chars(env, "java/io/IOException"); #ifdef WIN32 #else utf8ClassOrgTanukisoftwareWrapperWrapperUNIXUser = getUTF8Chars(env, "org/tanukisoftware/wrapper/WrapperUNIXUser"); utf8MethodSetGroup = getUTF8Chars(env, "setGroup"); utf8MethodAddGroup = getUTF8Chars(env, "addGroup"); utf8SigIIStringStringStringStringrV = getUTF8Chars(env, "(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); utf8SigIStringrV = getUTF8Chars(env, "(ILjava/lang/String;)V"); #endif } /** * Looks up a System property and sets its value in the propertyValue parameter. * * It is the responsibility of the caller to free up the propertyValue buffer if it is non-NULL. * * @param env Current JNIEnv. * @param propertyName Name of the property. * @param propertyValue Value of the property, or NULL if it was not set. * * @return TRUE if there were any problems, FALSE if successful. NULL values will still be successful. */ int getSystemProperty(JNIEnv *env, const TCHAR *propertyName, TCHAR **propertyValue, int encodeNative) { int result; jclass jClassSystem; jmethodID jMethodIdGetProperty; jstring jStringKeyPropName; jstring jStringKeyValue; TCHAR *keyChars; /* Initialize the propertyValue to point to NULL in case we fail. */ *propertyValue = NULL; if ((jClassSystem = (*env)->FindClass(env, utf8ClassJavaLangSystem)) != NULL) { if ((jMethodIdGetProperty = (*env)->GetStaticMethodID(env, jClassSystem, utf8MethodGetProperty, utf8SigLjavaLangStringrLjavaLangString)) != NULL) { if ((jStringKeyPropName = JNU_NewStringFromNativeW(env, propertyName)) != NULL) { if ((jStringKeyValue = (jstring)(*env)->CallStaticObjectMethod(env, jClassSystem, jMethodIdGetProperty, jStringKeyPropName)) != NULL) { /* Collect the value. */ if (!encodeNative) { if ((keyChars = JNU_GetNativeWFromString(env, jStringKeyValue)) != NULL) { *propertyValue = malloc(sizeof(TCHAR) * (_tcslen(keyChars) + 1)); if (!*propertyValue) { throwOutOfMemoryError(env, TEXT("GSP1")); result = TRUE; } else { _tcsncpy(*propertyValue, keyChars, _tcslen(keyChars) + 1); result = FALSE; } free(keyChars); } else { /* Exception Thrown */ result = TRUE; } } else { if ((keyChars = (TCHAR*)(*env)->GetStringUTFChars(env, jStringKeyValue, NULL)) != NULL) { *propertyValue = malloc(strlen((char*)keyChars) + 1); if (!*propertyValue) { throwOutOfMemoryError(env, TEXT("GSP2")); result = TRUE; } else { strncpy((char*)*propertyValue, (char*)keyChars, strlen((char*)keyChars) + 1); result = FALSE; } (*env)->ReleaseStringUTFChars(env, jStringKeyValue, (const char *)keyChars); } else { /* Exception Thrown */ result = TRUE; } } (*env)->DeleteLocalRef(env, jStringKeyValue); } else { /* Property was not set. */ result = FALSE; } (*env)->DeleteLocalRef(env, jStringKeyPropName); } else { result = TRUE; } } else { result = TRUE; } (*env)->DeleteLocalRef(env, jClassSystem); } else { result = TRUE; } return result; } static JavaVM *jvm = NULL; static jobject outGlobalRef = NULL; static jmethodID printlnMethodId = NULL; int printMessageCallback(const TCHAR* message) { JNIEnv** envPtr; JNIEnv* env = NULL; jstring jMsg; /* Do not print directly to the standard output because the JVM will interpret it with its own encoding (file.encoding) * which may differ from the locale encoding (especially on Windows or when setting file.encoding from the java additionals). * Instead we will create a JString and let the WrapperResources print it. * * Another possibility would be to change the locale so that its encoding matches with the JVM encoding, or to convert the log * messages to MB using the JVM encoding (we should however use the equivalent code page or iconv syntax). To do this, we would * need to either include wrapper_jvminfo.c to the native libary (but it would make it heavier), or pass the equivalent encoding * in a system property (the conversion would be the opposite as when the Wrapper catches JVM outputs). */ if (jvm && outGlobalRef && printlnMethodId) { /* envPtr is to avoid a warning "dereferencing type-punned pointer will break strict-aliasing rules" happening on certain platforms. */ envPtr = &env; if ((*jvm)->AttachCurrentThread(jvm, (void**)envPtr, NULL) == 0) { if ((jMsg = JNU_NewStringFromNativeW(env, message)) != NULL) { (*env)->CallVoidMethod(env, outGlobalRef, printlnMethodId, jMsg); return FALSE; } } } return TRUE; } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeDispose * Signature: (Z)V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeDispose(JNIEnv *env, jclass jClassWrapperManager, jboolean debugging) { if (wrapperJNIDebugging) { /* This is useful for making sure that this is the last JNI call. */ log_printf(TEXT("WrapperJNI Debug: Disposing WrapperManager native library.")); } (*env)->DeleteGlobalRef(env, outGlobalRef); } /** * Get a pointer to a Java method used to print native messages. * * @return TRUE if there were any problems. */ int initLog(JNIEnv *env) { jobject outField; jclass systemClass, printStreamClass; jfieldID outFieldId; /* Get system class */ if ((systemClass = (*env)->FindClass(env, getUTF8Chars(env, "java/lang/System"))) != NULL) { /* Lookup the "out" field */ if ((outFieldId = (*env)->GetStaticFieldID(env, systemClass, getUTF8Chars(env, "out"), getUTF8Chars(env, "Ljava/io/PrintStream;"))) != NULL) { /* Get "out" PrintStream instance */ if ((outField = (*env)->GetStaticObjectField(env, systemClass, outFieldId)) != NULL) { /* Get PrintStream class */ if ((printStreamClass = (*env)->FindClass(env, getUTF8Chars(env, "java/io/PrintStream"))) != NULL) { /* Lookup println() */ if ((printlnMethodId = (*env)->GetMethodID(env, printStreamClass, getUTF8Chars(env, "println"), getUTF8Chars(env, "(Ljava/lang/String;)V"))) != NULL) { /* Save a JavaVM* instance to get an environment in our callback method */ if ((*env)->GetJavaVM(env, &jvm) == 0) { /* Keep a global reference to the out stream for faster reuse. */ if ((outGlobalRef = (*env)->NewGlobalRef(env, outField)) != NULL) { /* Register a callback method to print our messages */ setPrintMessageCallback(printMessageCallback); return FALSE; } } } (*env)->DeleteLocalRef(env, printStreamClass); } (*env)->DeleteLocalRef(env, outField); } } (*env)->DeleteLocalRef(env, systemClass); } return TRUE; } /** * Do common initializaion. * * @return TRUE if there were any problems. */ int initCommon(JNIEnv *env, jclass jClassWrapperManager) { TCHAR* outfile; TCHAR* errfile; int outfd; int errfd; int mode; int options; #ifdef HPUX TCHAR* fixIconvHpuxPropValue = NULL; #endif #ifdef WIN32 mode = _S_IWRITE; options = _O_WRONLY | _O_APPEND | _O_CREAT; #else mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; options = O_WRONLY | O_APPEND | O_CREAT; #endif initUTF8Strings(env); #ifdef HPUX if (!getSystemProperty(env, TEXT("wrapper.fix_iconv_hpux"), &fixIconvHpuxPropValue, FALSE)) { if (fixIconvHpuxPropValue && (strcmpIgnoreCase(fixIconvHpuxPropValue, TEXT("ALWAYS")) == 0)) { toggleIconvHpuxFix(TRUE); } free(fixIconvHpuxPropValue); } #endif if (getSystemProperty(env, TEXT("wrapper.java.errfile"), &errfile, FALSE)) { /* Failed */ return TRUE; } if (errfile) { _ftprintf(stderr, TEXT("WrapperJNI: Redirecting %s to file %s...\n"), TEXT("StdErr"), errfile); fflush(NULL); if (((errfd = _topen(errfile, options, mode)) == -1) || (dup2(errfd, STDERR_FILENO) == -1)) { throwThrowable(env, utf8javaIOIOException, TEXT("Failed to redirect %s to file %s (Err: %s)"), TEXT("StdErr"), errfile, getLastErrorText()); return TRUE; } else { redirectedStdErr = TRUE; } free(errfile); } if (getSystemProperty(env, TEXT("wrapper.java.outfile"), &outfile, FALSE)) { /* Failed */ return TRUE; } if (outfile) { log_printf(TEXT("WrapperJNI: Redirecting %s to file %s..."), TEXT("StdOut"), outfile); if (((outfd = _topen(outfile, options, mode)) == -1) || (dup2(outfd, STDOUT_FILENO) == -1)) { throwThrowable(env, utf8javaIOIOException, TEXT("Failed to redirect %s to file %s (Err: %s)"), TEXT("StdOut"), outfile, getLastErrorText()); return TRUE; } else { redirectedStdOut = TRUE; } free(outfile); } return FALSE; } void throwThrowable(JNIEnv *env, const char *throwableClassName, const TCHAR *lpszFmt, ...) { va_list vargs; int messageBufferSize = 0; TCHAR *messageBuffer = NULL; int count; jclass jThrowableClass; jmethodID constructor; jstring jMessageBuffer; jobject jThrowable; #if defined(UNICODE) && !defined(WIN32) TCHAR *msg = NULL; int i; int flag; #endif #if defined(UNICODE) && !defined(WIN32) if (wcsstr(lpszFmt, TEXT("%s")) != NULL) { msg = malloc(sizeof(wchar_t) * (wcslen(lpszFmt) + 1)); if (msg) { /* Loop over the format and convert all '%s' patterns to %S' so the UNICODE displays correctly. */ if (wcslen(lpszFmt) > 0) { for (i = 0; i < _tcslen(lpszFmt); i++){ msg[i] = lpszFmt[i]; if ((lpszFmt[i] == TEXT('%')) && (i < _tcslen(lpszFmt)) && (lpszFmt[i + 1] == TEXT('s')) && ((i == 0) || (lpszFmt[i - 1] != TEXT('%')))){ msg[i+1] = TEXT('S'); i++; } } } msg[wcslen(lpszFmt)] = TEXT('\0'); } else { throwOutOfMemoryError(env, TEXT("TT0")); return; } flag = TRUE; } else { msg = (TCHAR*) lpszFmt; flag = FALSE; } #endif do { if (messageBufferSize == 0) { /* No buffer yet. Allocate one to get started. */ messageBufferSize = 100; #if defined(HPUX) /* Due to a bug in the HPUX libc (version < 1403), the length of the buffer passed to _vsntprintf must have a length of 1 + N, where N is a multiple of 8. Adjust it as necessary. */ messageBufferSize = messageBufferSize + (((messageBufferSize - 1) % 8) == 0 ? 0 : 8 - ((messageBufferSize - 1) % 8)); #endif messageBuffer = (TCHAR*)malloc( messageBufferSize * sizeof(TCHAR)); if (!messageBuffer) { throwOutOfMemoryError(env, TEXT("TT1")); #if defined(UNICODE) && !defined(WIN32) if (flag == TRUE) { free(msg); } #endif return; } } /* Try writing to the buffer. */ va_start(vargs, lpszFmt); #if defined(UNICODE) && !defined(WIN32) count = _vsntprintf(messageBuffer, messageBufferSize, msg, vargs); #else count = _vsntprintf(messageBuffer, messageBufferSize, lpszFmt, vargs); #endif va_end(vargs); if ((count < 0) || (count >= (int)messageBufferSize)) { /* If the count is exactly equal to the buffer size then a null TCHAR was not written. * It must be larger. * Windows will return -1 if the buffer is too small. If the number is * exact however, we still need to expand it to have room for the null. * UNIX will return the required size. */ /* Free the old buffer for starters. */ free(messageBuffer); /* Decide on a new buffer size. */ if (count + 1 <= (int)messageBufferSize + 50) { messageBufferSize += 50; } else { messageBufferSize = count + 1; } #if defined(HPUX) /* Due to a bug in the HPUX libc (version < 1403), the length of the buffer passed to _vsntprintf must have a length of 1 + N, where N is a multiple of 8. Adjust it as necessary. */ messageBufferSize = messageBufferSize + (((messageBufferSize - 1) % 8) == 0 ? 0 : 8 - ((messageBufferSize - 1) % 8)); #endif messageBuffer = (TCHAR*)malloc(messageBufferSize * sizeof(TCHAR)); if (!messageBuffer) { throwOutOfMemoryError(env, TEXT("TT2")); #if defined(UNICODE) && !defined(WIN32) if (flag == TRUE) { free(msg); } #endif return; } /* Always set the count to -1 so we will loop again. */ count = -1; } } while (count < 0); #if defined(UNICODE) && !defined(WIN32) if (flag == TRUE) { free(msg); } #endif /* We have the messageBuffer */ if ((jThrowableClass = (*env)->FindClass(env, throwableClassName)) != NULL) { if ((constructor = (*env)->GetMethodID(env, jThrowableClass, utf8MethodInit, utf8SigLjavaLangStringrV)) != NULL) { if ((jMessageBuffer = JNU_NewStringFromNativeW(env, messageBuffer)) != NULL) { if ((jThrowable = (*env)->NewObject(env, jThrowableClass, constructor, jMessageBuffer)) != NULL) { if ((*env)->Throw(env, jThrowable)) { log_printf(TEXT("WrapperJNI Error: Unable to throw %s with message: %s"), throwableClassName, messageBuffer); } (*env)->DeleteLocalRef(env, jThrowable); } (*env)->DeleteLocalRef(env, jMessageBuffer); } } (*env)->DeleteLocalRef(env, jThrowableClass); } free(messageBuffer); } /** * Throws an OutOfMemoryError. * * @param env The current JNIEnv. * @param locationCode The locationCode to help tell where the error happened. */ void throwOutOfMemoryError(JNIEnv *env, const TCHAR* locationCode) { throwThrowable(env, utf8ClassJavaLangOutOfMemoryError, TEXT("Out of memory (%s)"), locationCode); log_printf(TEXT("WrapperJNI Error: Out of memory (%s)"), locationCode); } void throwJNIError(JNIEnv *env, const TCHAR *message) { jclass exceptionClass; jmethodID constructor; jstring jMessage; jobject exception; if ((exceptionClass = (*env)->FindClass(env, utf8ClassOrgTanukisoftwareWrapperWrapperJNIError)) != NULL) { /* Look for the constructor. Ignore failures. */ if ((constructor = (*env)->GetMethodID(env, exceptionClass, utf8MethodInit, utf8Sig_BrV)) != NULL) { if ((jMessage = JNU_NewStringFromNativeW(env, message)) != NULL) { if ((exception = (*env)->NewObject(env, exceptionClass, constructor, jMessage)) != NULL) { if ((*env)->Throw(env, exception)) { log_printf(TEXT("WrapperJNI Error: Unable to throw WrapperJNIError with message: %s"), message); } (*env)->DeleteLocalRef(env, exception); } (*env)->DeleteLocalRef(env, jMessage); } } (*env)->DeleteLocalRef(env, exceptionClass); } } void wrapperJNIHandleSignal(int signal) { if (wrapperLockControlEventQueue()) { /* Failed. Should have been reported. */ log_printf(TEXT("WrapperJNI Error: Signal %d trapped, but ignored."), signal); return; } #ifdef _DEBUG log_printf(TEXT(" Queue Write 1 R:%d W:%d E:%d"), controlEventQueueLastReadIndex, controlEventQueueLastWriteIndex, signal); #endif controlEventQueueLastWriteIndex++; if (controlEventQueueLastWriteIndex >= CONTROL_EVENT_QUEUE_SIZE) { controlEventQueueLastWriteIndex = 0; } controlEventQueue[controlEventQueueLastWriteIndex] = signal; #ifdef _DEBUG log_printf(TEXT(" Queue Write 2 R:%d W:%d"), controlEventQueueLastReadIndex, controlEventQueueLastWriteIndex); #endif if (wrapperReleaseControlEventQueue()) { /* Failed. Should have been reported. */ return; } } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetLibraryVersion * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetLibraryVersion(JNIEnv *env, jclass clazz) { jstring version; version = JNU_NewStringFromNativeW(env, wrapperVersion); return version; } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeIsProfessionalEdition * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeIsProfessionalEdition(JNIEnv *env, jclass clazz) { return JNI_FALSE; } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeIsStandardEdition * Signature: ()Z */ JNIEXPORT jboolean JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeIsStandardEdition(JNIEnv *env, jclass clazz) { return JNI_FALSE; } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetControlEvent * Signature: (V)I */ JNIEXPORT jint JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetControlEvent(JNIEnv *env, jclass clazz) { int event = 0; if (wrapperLockControlEventQueue()) { /* Failed. Should have been reported. */ return 0; } if (controlEventQueueLastWriteIndex != controlEventQueueLastReadIndex) { #ifdef _DEBUG _tprintf(TEXT(" Queue Read 1 R:%d W:%d\n"), controlEventQueueLastReadIndex, controlEventQueueLastWriteIndex); fflush(NULL); #endif controlEventQueueLastReadIndex++; if (controlEventQueueLastReadIndex >= CONTROL_EVENT_QUEUE_SIZE) { controlEventQueueLastReadIndex = 0; } event = controlEventQueue[controlEventQueueLastReadIndex]; #ifdef _DEBUG _tprintf(TEXT(" Queue Read 2 R:%d W:%d E:%d\n"), controlEventQueueLastReadIndex, controlEventQueueLastWriteIndex, event); fflush(NULL); #endif } if (wrapperReleaseControlEventQueue()) { /* Failed. Should have been reported. */ return event; } return event; } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: accessViolationInner * Signature: (V)V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_accessViolationInner(JNIEnv *env, jclass clazz) { TCHAR *ptr; log_printf(TEXT("WrapperJNI Warn: Causing access violation...")); /* Cause access violation */ ptr = NULL; ptr[0] = L'\n'; } JNIEXPORT jobject JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeExec(JNIEnv *env, jclass jWrapperManagerClass, jobjectArray jCmdArray, jstring jCmdLine, jobject jWrapperProcessConfig, jboolean spawnChDir) { throwThrowable(env, "org/tanukisoftware/wrapper/WrapperLicenseError", TEXT("This function is only available in the Professional Edition of the Java Service Wrapper.")); return NULL; } wrapper_3.5.51_src/src/c/wrapperjni.h100644 0 0 11271 14333053652 14736 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ #ifndef _WRAPPERJNI_H #define _WRAPPERJNI_H #include "org_tanukisoftware_wrapper_WrapperManager.h" #ifndef TRUE #define TRUE -1 #endif #undef gettext #ifdef WIN32 #include #endif #include #include "wrapper_i18n.h" #ifndef FALSE #define FALSE 0 #endif #ifdef WIN32 #define STDOUT_FILENO 1 #define STDERR_FILENO 2 #endif /** Flag to keep track of whether StdOut has been redirected. */ extern int redirectedStdOut; /** Flag to keep track of whether StdErr has been redirected. */ extern int redirectedStdErr; extern void throwJNIError(JNIEnv *env, const TCHAR *message); /* Special symbols that need to be defined manually as part of the bootstrap process. */ extern const char utf8ClassJavaLangString[]; extern const char utf8MethodInit[]; extern const char utf8Sig_BrV[]; extern const char utf8ClassJavaLangOutOfMemoryError[]; /* Symbols which need to be defined. */ extern char *utf8SigLjavaLangStringrV; extern char *utf8ClassJavaLangSystem; extern char *utf8MethodGetProperties; extern char *utf8SigVrLjavaUtilProperties; extern char *utf8MethodGetProperty; extern char *utf8SigLjavaLangStringrLjavaLangString; extern char *utf8javaIOIOException; /* "java/io/IOException" */ #ifdef WIN32 #else /* UNIX specific symbols. */ extern char *utf8ClassOrgTanukisoftwareWrapperWrapperUNIXUser; extern char *utf8MethodSetGroup; extern char *utf8MethodAddGroup; extern char *utf8SigIIStringStringStringStringrV; extern char *utf8SigIStringrV; #endif extern int initLog(JNIEnv *env); extern int initCommon(JNIEnv *env, jclass jClassWrapperManager); extern void throwOutOfMemoryError(JNIEnv *env, const TCHAR* locationCode); extern void wrapperSleep(int ms); extern int wrapperJNIDebugging; extern int wrapperLockControlEventQueue(); extern int wrapperReleaseControlEventQueue(); extern void wrapperJNIHandleSignal(int signal); extern void throwThrowable(JNIEnv *env, const char *throwableClassName, const TCHAR *lpszFmt, ...); #ifdef WIN32 extern void JNU_SetByteArrayRegion(JNIEnv *env, jbyteArray *jarray, jsize start, jsize len, const TCHAR *buffer); #else /** * Create a jstring from a MultiBytes Char string. The jstring must be freed up by caller. * * @param env The current JNIEnv. * @param str The MultiBytes string to convert. * * @return The new jstring or NULL if there were any exceptions thrown. */ extern jstring JNU_NewStringFromNativeMB(JNIEnv *env, const char *str); #endif /** * Create a jstring from a Wide Char string. The jstring must be freed up by caller. * * @param env The current JNIEnv. * @param strW The Wide string to convert. * * @return The new jstring or NULL if there were any exceptions thrown. */ extern jstring JNU_NewStringFromNativeW(JNIEnv *env, const TCHAR *strW); /** * Create a jstring from a Wide Char string. The jstring must be freed up by caller. * * @param env The current JNIEnv. * @param strW The Wide string to convert. * * @return The new jstring or NULL if there were any exceptions thrown. */ extern TCHAR* JNU_GetNativeWFromString(JNIEnv *env, jstring jstr); /** * Looks up a System property and sets its value in the propertyValue parameter. * * It is the responsibility of the caller to free up the propertyValue buffer if it is non-NULL. * * @param env Current JNIEnv. * @param propertyName Name of the property. * @param propertyValue Value of the property, or NULL if it was not set. * * @return TRUE if there were any problems, FALSE if successful. NULL values will still be successful. */ extern int getSystemProperty(JNIEnv *env, const TCHAR *propertyName, TCHAR **propertyValue, int encodeNative); #endif wrapper_3.5.51_src/src/c/wrapperjni_unix.c100644 0 0 44264 14333053652 16004 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ #ifndef WIN32 #include #include #include #include #include #include #include #include #include #include #include #include #include "loggerjni.h" #include "wrapperjni.h" pid_t wrapperProcessId = -1; pthread_mutex_t controlEventQueueMutex = PTHREAD_MUTEX_INITIALIZER; int wrapperLockControlEventQueue() { int count = 0; /* Only wait for up to 30 seconds to make sure we don't get into a deadlock situation. * This could happen if a signal is encountered while locked. */ while (pthread_mutex_trylock(&controlEventQueueMutex) == EBUSY) { if (count >= 3000) { log_printf(TEXT("WrapperJNI Error: Timed out waiting for internal lock (%s)."), TEXT("control event queue")); return -1; } wrapperSleep(10); count++; } if (count > 0) { if (wrapperJNIDebugging) { /* This is useful for making sure that the JNI call is working. */ log_printf(TEXT("WrapperJNI Debug: Looped %d times before lock (%s)."), count, TEXT("control event queue")); } } return 0; } int wrapperReleaseControlEventQueue() { int ret = pthread_mutex_unlock(&controlEventQueueMutex); if (ret != 0) { log_printf(TEXT("WrapperJNI Error: Failed to release internal lock (%s, 0x%x)."), TEXT("control event queue"), ret); } return ret; } /** * Handle interrupt signals (i.e. Crtl-C). */ void handleInterrupt(int sig_num) { wrapperJNIHandleSignal(org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_C_EVENT); signal(SIGINT, handleInterrupt); } /** * Handle termination signals (i.e. machine is shutting down). */ void handleTermination(int sig_num) { wrapperJNIHandleSignal(org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_TERM_EVENT); signal(SIGTERM, handleTermination); } /** * Handle hangup signals. */ void handleHangup(int sig_num) { wrapperJNIHandleSignal(org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_HUP_EVENT); signal(SIGHUP, handleHangup); } /** * Handle usr1 signals. * * SIGUSR1 & SIGUSR2 are used by the JVM for internal garbage collection sweeps. * These signals MUST be passed on to the JVM or the JVM will hang. */ /* void handleUsr1(int sig_num) { wrapperJNIHandleSignal(org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_USR1_EVENT); signal(SIGUSR1, handleUsr1); } */ /** * Handle usr2 signals. * * SIGUSR1 & SIGUSR2 are used by the JVM for internal garbage collection sweeps. * These signals MUST be passed on to the JVM or the JVM will hang. */ /* void handleUsr2(int sig_num) { wrapperJNIHandleSignal(org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_USR2_EVENT); signal(SIGUSR2, handleUsr2); } */ /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeInit * Signature: (Z)V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeInit(JNIEnv *env, jclass jClassWrapperManager, jboolean debugging) { TCHAR *retLocale; wrapperJNIDebugging = debugging; #ifdef FREEBSD if (loadIconvLibrary()) { /* Already reported. */ return; } #endif /* Set the locale so we can display MultiByte characters. */ retLocale = _tsetlocale(LC_ALL, TEXT("")); #if defined(UNICODE) if (retLocale) { free(retLocale); } #endif initLog(env); if (wrapperJNIDebugging) { /* This is useful for making sure that the JNI call is working. */ log_printf(TEXT("WrapperJNI Debug: Inside native WrapperManager initialization method")); } /* Set handlers for signals */ signal(SIGINT, handleInterrupt); signal(SIGTERM, handleTermination); signal(SIGHUP, handleHangup); /* signal(SIGUSR1, handleUsr1); signal(SIGUSR2, handleUsr2); */ if (initCommon(env, jClassWrapperManager)) { /* Failed. An exception will have been thrown. */ return; } /* Store the current process Id */ wrapperProcessId = getpid(); } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeLoadWrapperProperties * Signature: ()V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeLoadWrapperProperties(JNIEnv *env, jclass clazz) { } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeRaiseExceptionInner * Signature: (I)V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeRaiseExceptionInner(JNIEnv *env, jclass clazz, jint code) { log_printf(TEXT("WrapperJNI: Not Windows. RaiseException ignored.")); } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeRaiseFailFastExceptionInner * Signature: ()V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeRaiseFailFastExceptionInner(JNIEnv *env, jclass clazz) { log_printf(TEXT("WrapperJNI: Not Windows. FailFastException ignored.")); } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeRedirectPipes * Signature: ()I */ JNIEXPORT jint JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeRedirectPipes(JNIEnv *env, jclass clazz) { int fd; fd = _topen(TEXT("/dev/null"), O_RDWR, 0); if (fd != -1) { if (!redirectedStdErr) { _ftprintf(stderr, TEXT("WrapperJNI: Redirecting %s to /dev/null\n"), TEXT("StdErr")); fflush(NULL); if (dup2(fd, STDERR_FILENO) == -1) { _ftprintf(stderr, TEXT("WrapperJNI: Failed to redirect %s to /dev/null (Err: %s)\n"), TEXT("StdErr"), getLastErrorText()); fflush(NULL); } else { redirectedStdErr = TRUE; } } if (!redirectedStdOut) { log_printf(TEXT("WrapperJNI: Redirecting %s to /dev/null"), TEXT("StdOut")); if (dup2(fd, STDOUT_FILENO) == -1) { log_printf(TEXT("WrapperJNI: Failed to redirect %s to /dev/null (Err: %s)"), TEXT("StdOut"), getLastErrorText()); } else { redirectedStdOut = TRUE; } } } else { _ftprintf(stderr, TEXT("WrapperJNI: Failed to open /dev/null (Err: %s)\n"), getLastErrorText()); fflush(NULL); } return 0; } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetJavaPID * Signature: ()I */ JNIEXPORT jint JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetJavaPID(JNIEnv *env, jclass clazz) { return (int) getpid(); } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeRequestThreadGroup * Signature: ()V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeRequestThreadDump( JNIEnv *env, jclass clazz) { if (wrapperJNIDebugging) { log_printf(TEXT("WrapperJNI Debug: Sending SIGQUIT event to process group %d."), (int)wrapperProcessId); } if (kill(wrapperProcessId, SIGQUIT) < 0) { log_printf(TEXT("WrapperJNI Error: Unable to send SIGQUIT to JVM process: %s"), getLastErrorText()); } } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeSetConsoleTitle * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeSetConsoleTitle(JNIEnv *env, jclass clazz, jstring jstringTitle) { if (wrapperJNIDebugging) { log_printf(TEXT("WrapperJNI Debug: Setting the console title not supported on UNIX platforms.")); } } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetUser * Signature: (Z)Lorg/tanukisoftware/wrapper/WrapperUser; */ /*#define UVERBOSE*/ JNIEXPORT jobject JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetUser(JNIEnv *env, jclass clazz, jboolean groups) { jclass wrapperUserClass; jmethodID constructor; jmethodID setGroup; jmethodID addGroup; uid_t uid; struct passwd *pw; gid_t ugid; jstring jstringUser; jstring jstringRealName; jstring jstringHome; jstring jstringShell; jobject wrapperUser = NULL; struct group *aGroup; int member; int i; gid_t ggid; jstring jstringGroupName; /* Look for the WrapperUser class. Ignore failures as JNI throws an exception. */ if ((wrapperUserClass = (*env)->FindClass(env, utf8ClassOrgTanukisoftwareWrapperWrapperUNIXUser)) != NULL) { /* Look for the constructor. Ignore failures. */ if ((constructor = (*env)->GetMethodID(env, wrapperUserClass, utf8MethodInit, utf8SigIIStringStringStringStringrV)) != NULL) { uid = geteuid(); pw = getpwuid(uid); ugid = pw->pw_gid; /* Create the arguments to the constructor as java objects */ /* User */ jstringUser = JNU_NewStringFromNativeMB(env, pw->pw_name); if (jstringUser) { /* Real Name */ jstringRealName = JNU_NewStringFromNativeMB(env, pw->pw_gecos); if (jstringRealName) { /* Home */ jstringHome = JNU_NewStringFromNativeMB(env, pw->pw_dir); if (jstringHome) { /* Shell */ jstringShell = JNU_NewStringFromNativeMB(env, pw->pw_shell); if (jstringShell) { /* Now create the new wrapperUser using the constructor arguments collected above. */ wrapperUser = (*env)->NewObject(env, wrapperUserClass, constructor, uid, ugid, jstringUser, jstringRealName, jstringHome, jstringShell); /* If the caller requested the user's groups then look them up. */ if (groups) { /* Set the user group. */ if ((setGroup = (*env)->GetMethodID(env, wrapperUserClass, utf8MethodSetGroup, utf8SigIStringrV)) != NULL) { if ((aGroup = getgrgid(ugid)) != NULL) { ggid = aGroup->gr_gid; /* Group name */ jstringGroupName = JNU_NewStringFromNativeMB(env, aGroup->gr_name); if (jstringGroupName) { /* Add the group to the user. */ (*env)->CallVoidMethod(env, wrapperUser, setGroup, ggid, jstringGroupName); (*env)->DeleteLocalRef(env, jstringGroupName); } else { /* Exception Thrown */ } } } else { /* Exception Thrown */ } /* Look for the addGroup method. Ignore failures. */ if ((addGroup = (*env)->GetMethodID(env, wrapperUserClass, utf8MethodAddGroup, utf8SigIStringrV)) != NULL) { setgrent(); while ((aGroup = getgrent()) != NULL) { /* Search the member list to decide whether or not the user is a member. */ member = 0; i = 0; while ((member == 0) && aGroup->gr_mem[i]) { if (strcmp(aGroup->gr_mem[i], pw->pw_name) == 0) { member = 1; } i++; } if (member) { ggid = aGroup->gr_gid; /* Group name */ jstringGroupName = JNU_NewStringFromNativeMB(env, aGroup->gr_name); if (jstringGroupName) { /* Add the group to the user. */ (*env)->CallVoidMethod(env, wrapperUser, addGroup, ggid, jstringGroupName); (*env)->DeleteLocalRef(env, jstringGroupName); } else { /* Exception Thrown */ } } } endgrent(); } else { /* Exception Thrown */ } } (*env)->DeleteLocalRef(env, jstringShell); } else { /* Exception Thrown */ } (*env)->DeleteLocalRef(env, jstringHome); } else { /* Exception Thrown */ } (*env)->DeleteLocalRef(env, jstringRealName); } else { /* Exception Thrown */ } (*env)->DeleteLocalRef(env, jstringUser); } else { /* Exception Thrown */ } } else { /* Exception Thrown */ } (*env)->DeleteLocalRef(env, wrapperUserClass); } return wrapperUser; } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetInteractiveUser * Signature: (Z)Lorg/tanukisoftware/wrapper/WrapperUser; */ JNIEXPORT jobject JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetInteractiveUser(JNIEnv *env, jclass clazz, jboolean groups) { /* If the DISPLAY environment variable is set then assume that this user * has access to an X display, in which case we will return the same thing * as nativeGetUser. */ if (getenv("DISPLAY")) { /* This is an interactive JVM since it has access to a display. */ return Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetUser(env, clazz, groups); } else { /* There is no DISPLAY variable, so assume that this JVM is non-interactive. */ return NULL; } } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeListServices * Signature: ()[Lorg/tanukisoftware/wrapper/WrapperWin32Service; */ JNIEXPORT jobjectArray JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeListServices(JNIEnv *env, jclass clazz) { /** Not supported on UNIX platforms. */ return NULL; } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeSendServiceControlCode * Signature: (Ljava/lang/String;I)Lorg/tanukisoftware/wrapper/WrapperWin32Service; */ JNIEXPORT jobject JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeSendServiceControlCode(JNIEnv *env, jclass clazz, jbyteArray serviceName, jint controlCode) { /** Not supported on UNIX platforms. */ return NULL; } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetPortStatus * Signature: (ILjava/lang/String;I)I * * @param port Port is the port whose status is requested. * @param protocol The protocol of the port, 0=tcpv4, 1=tcpv6 * * @return The status, -1=error, 0=closed, >0=in use. */ JNIEXPORT jint JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetPortStatus(JNIEnv *env, jclass clazz, jint port, jstring jAddress, jint protocol) { /* Not implemented on UNIX as it is not needed for now. May implement if ever made publicly available. */ return 0; } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeSetDpiAwareness * Signature: ()I * * @param awareness The DPI awareness value to set. */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeSetDpiAwareness(JNIEnv *env, jclass clazz, jint awareness) { /* Not implemented on UNIX. */ return; } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetDpiAwareness * Signature: ()I * * @return The dpi Awareness of the current process, or -1 if there was an error. */ JNIEXPORT jint JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetDpiAwareness(JNIEnv *env, jclass clazz) { /* Not implemented on UNIX. */ return -1; } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetDpiScale * Signature: ()I * * @return The dpi scale (should be devided by 96 to get the scale factor). */ JNIEXPORT jint JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetDpiScale(JNIEnv *env, jclass clazz) { /* Not implemented on UNIX: always return 96 as it is the default value. */ return 96; } #endif wrapper_3.5.51_src/src/c/wrapperjni_win.c100644 0 0 213012 14333053652 15623 0ustar 0 0 /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ #ifndef WIN32 /* For some reason this is not defined sometimes when I build on MFVC 6.0 $%$%$@@!! * This causes a compiler error to let me know about the problem. Anyone with any * ideas as to why this sometimes happens or how to fix it, please let me know. */ barf #endif #ifdef WIN32 #define WIN32_NO_STATUS #include #undef WIN32_NO_STATUS #include #include #include #include #include #include #include #include #include "loggerjni.h" #include "wrapperjni.h" /* The largest possible command line length on Windows. */ #define MAX_COMMAND_LINE_LEN 32766 /* MS Visual Studio 8 went and deprecated the POXIX names for functions. * Fixing them all would be a big headache for UNIX versions. */ #pragma warning(disable : 4996) /* Reference to HINSTANCE of this DLL */ EXTERN_C IMAGE_DOS_HEADER __ImageBase; static DWORD javaProcessId = 0; HANDLE controlEventQueueMutexHandle = NULL; FARPROC OptionalProcess32First = NULL; FARPROC OptionalProcess32Next = NULL; FARPROC OptionalThread32First = NULL; FARPROC OptionalThread32Next = NULL; FARPROC OptionalCreateToolhelp32Snapshot = NULL; FARPROC OptionalRaiseFailFastException = NULL; int wrapperLockControlEventQueue() { #ifdef _DEBUG _tprintf(TEXT(" wrapperLockControlEventQueue()\n")); fflush(NULL); #endif if (!controlEventQueueMutexHandle) { /* Not initialized so fail quietly. A message was shown on startup. */ return -1; } /* Only wait for up to 30 seconds to make sure we don't get into a deadlock situation. * This could happen if a signal is encountered while locked. */ switch (WaitForSingleObject(controlEventQueueMutexHandle, 30000)) { case WAIT_ABANDONED: log_printf(TEXT("WrapperJNI Error: Control Event mutex was abandoned.")); return -1; case WAIT_FAILED: log_printf(TEXT("WrapperJNI Error: Control Event mutex wait failed.")); return -1; case WAIT_TIMEOUT: log_printf(TEXT("WrapperJNI Error: Control Event mutex wait timed out.")); return -1; default: /* Ok */ break; } return 0; } int wrapperReleaseControlEventQueue() { #ifdef _DEBUG _tprintf(TEXT(" wrapperReleaseControlEventQueue()\n")); fflush(NULL); #endif if (!ReleaseMutex(controlEventQueueMutexHandle)) { log_printf(TEXT( "WrapperJNI Error: Failed to release internal lock (%s, 0x%x)."), TEXT("control event queue"), GetLastError()); return -1; } return 0; } /** * Handler to take care of the case where the user hits CTRL-C when the wrapper * is being run as a console. If this is not done, then the Java process * would exit due to a CTRL_LOGOFF_EVENT when a user logs off even if the * application is installed as a service. * * Handlers are called in the reverse order that they are registered until one * returns TRUE. So last registered is called first until the default handler * is called. This means that if we return FALSE, the JVM'S handler will then * be called. */ int wrapperConsoleHandler(int key) { int event; /* Call the control callback in the java code */ switch(key) { case CTRL_C_EVENT: event = org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_C_EVENT; break; case CTRL_BREAK_EVENT: /* This is a request to do a thread dump. Let the JVM handle this. */ return FALSE; case CTRL_CLOSE_EVENT: event = org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_CLOSE_EVENT; break; case CTRL_LOGOFF_EVENT: event = org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_LOGOFF_EVENT; break; case CTRL_SHUTDOWN_EVENT: event = org_tanukisoftware_wrapper_WrapperManager_WRAPPER_CTRL_SHUTDOWN_EVENT; break; default: event = key; } if (wrapperJNIDebugging) { log_printf(TEXT("WrapperJNI Debug: Got Control Signal %d->%d"), key, event); } wrapperJNIHandleSignal(event); if (wrapperJNIDebugging) { log_printf(TEXT("WrapperJNI Debug: Handled signal")); } return TRUE; /* We handled the event. */ } /** * Looks up the name of the explorer.exe file in the registry. It may change * in a future version of windows, so this is the safe thing to do. */ TCHAR explorerExe[1024]; void initExplorerExeName() { /* Location: "\\HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Shell" */ _sntprintf(explorerExe, 1024, TEXT("Explorer.exe")); } void throwException(JNIEnv *env, const char *className, int jErrorCode, const TCHAR *message) { jclass exceptionClass; jmethodID constructor; jbyteArray jMessage; jobject exception; if (exceptionClass = (*env)->FindClass(env, className)) { /* Look for the constructor. Ignore failures. */ if (constructor = (*env)->GetMethodID(env, exceptionClass, "", "(I[B)V")) { jMessage = (*env)->NewByteArray(env, (jsize)_tcslen(message) * sizeof(TCHAR)); /* The 1.3.1 jni.h file does not specify the message as const. The cast is to * avoid compiler warnings trying to pass a (const TCHAR *) as a (TCHAR *). */ JNU_SetByteArrayRegion(env, &jMessage, 0, (jsize)_tcslen(message) * sizeof(TCHAR), message); exception = (*env)->NewObject(env, exceptionClass, constructor, jErrorCode, jMessage); if ((*env)->Throw(env, exception)) { log_printf(TEXT("WrapperJNI Error: Unable to throw exception of class '%s' with message: %s"), className, message); } (*env)->DeleteLocalRef(env, jMessage); (*env)->DeleteLocalRef(env, exception); } (*env)->DeleteLocalRef(env, exceptionClass); } else { log_printf(TEXT("WrapperJNI Error: Unable to load class, '%s' to report exception: %s"), className, message); } } void throwServiceException(JNIEnv *env, int errorCode, const TCHAR *message) { throwException(env, "org/tanukisoftware/wrapper/WrapperServiceException", errorCode, message); } /** * Converts a FILETIME to a time_t structure. */ time_t fileTimeToTimeT(FILETIME *filetime) { SYSTEMTIME utc; SYSTEMTIME local; TIME_ZONE_INFORMATION timeZoneInfo; struct tm tm; FileTimeToSystemTime(filetime, &utc); GetTimeZoneInformation(&timeZoneInfo); SystemTimeToTzSpecificLocalTime(&timeZoneInfo, &utc, &local); tm.tm_sec = local.wSecond; tm.tm_min = local.wMinute; tm.tm_hour = local.wHour; tm.tm_mday = local.wDay; tm.tm_mon = local.wMonth - 1; tm.tm_year = local.wYear - 1900; tm.tm_wday = local.wDayOfWeek; tm.tm_yday = -1; tm.tm_isdst = -1; return mktime(&tm); } /** * Looks for the login time given a user SID. The login time is found by looking * up the SID in the registry. */ time_t getUserLoginTime(TCHAR *sidText) { LONG result; LPSTR pBuffer = NULL; HKEY userKey; int i; TCHAR userKeyName[MAX_PATH]; DWORD userKeyNameSize; FILETIME lastTime; time_t loginTime; loginTime = 0; /* Open a key to the HKRY_USERS registry. */ result = RegOpenKey(HKEY_USERS, NULL, &userKey); if (result != ERROR_SUCCESS) { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, result, 0, (LPTSTR)&pBuffer, 0, NULL); log_printf(TEXT("WrapperJNI Error: Error opening registry for HKEY_USERS: %s"), getLastErrorText()); LocalFree(pBuffer); return loginTime; } /* Loop over the users */ i = 0; userKeyNameSize = sizeof(userKeyName); while ((result = RegEnumKeyEx(userKey, i, userKeyName, &userKeyNameSize, NULL, NULL, NULL, &lastTime)) == ERROR_SUCCESS) { if (_tcsicmp(sidText, userKeyName) == 0) { /* We found the SID! */ /* Convert the FILETIME to UNIX time. */ loginTime = fileTimeToTimeT(&lastTime); break; } userKeyNameSize = sizeof(userKeyName); i++; } if (result != ERROR_SUCCESS) { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, result, 0, (LPTSTR)&pBuffer, 0, NULL); log_printf(TEXT("WrapperJNI Error: Unable to enumerate the registry: %d : %s"), result, pBuffer); LocalFree(pBuffer); } /* Always close the userKey. */ result = RegCloseKey(userKey); if (result != ERROR_SUCCESS) { FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL, result, 0, (LPTSTR)&pBuffer, 0, NULL); log_printf(TEXT("WrapperJNI Error: Unable to close the registry: %d : %s"), result, pBuffer); LocalFree(pBuffer); } return loginTime; } /** * Sets group information in a user object. * * Returns TRUE if there were any problems. */ int setUserGroups(JNIEnv *env, jclass wrapperUserClass, jobject wrapperUser, HANDLE hProcessToken) { jmethodID addGroup; TOKEN_GROUPS *tokenGroups; DWORD tokenGroupsSize; DWORD i; TCHAR *sidText; TCHAR *groupName; DWORD groupNameSize; TCHAR *domainName; DWORD domainNameSize; SID_NAME_USE sidType; jstring jstringSID; jstring jstringGroupName; jstring jstringDomainName; int result = FALSE; /* Look for the method used to add groups to the user. */ if (addGroup = (*env)->GetMethodID(env, wrapperUserClass, "addGroup", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V")) { /* Get the TokenGroups info from the token. */ GetTokenInformation(hProcessToken, TokenGroups, NULL, 0, &tokenGroupsSize); tokenGroups = (TOKEN_GROUPS *)malloc(tokenGroupsSize); if (!tokenGroups) { throwOutOfMemoryError(env, TEXT("SUG1")); result = TRUE; } else { if (GetTokenInformation(hProcessToken, TokenGroups, tokenGroups, tokenGroupsSize, &tokenGroupsSize)) { /* Loop over each of the groups and add each one to the user. */ for (i = 0; i < tokenGroups->GroupCount; i++) { /* Get the text representation of the sid. */ if (ConvertSidToStringSid(tokenGroups->Groups[i].Sid, &sidText) == 0) { log_printf(TEXT("WrapperJNI Error: Failed to convert SId to String: %s"), getLastErrorText()); result = TRUE; } else { /* We now have an SID, use it to lookup the account. */ groupNameSize = 0; domainNameSize = 0; LookupAccountSid(NULL, tokenGroups->Groups[i].Sid, NULL, &groupNameSize, NULL, &domainNameSize, &sidType); groupName = (TCHAR*)malloc(sizeof(TCHAR) * groupNameSize); if (!groupName) { throwOutOfMemoryError(env, TEXT("SUG3")); result = TRUE; } else { domainName = (TCHAR*)malloc(sizeof(TCHAR) * domainNameSize); if (!domainName) { throwOutOfMemoryError(env, TEXT("SUG4")); result = TRUE; } else { if (LookupAccountSid(NULL, tokenGroups->Groups[i].Sid, groupName, &groupNameSize, domainName, &domainNameSize, &sidType)) { /* Create the arguments to the constructor as java objects */ /* SID byte array */ jstringSID = JNU_NewStringFromNativeW(env, sidText); if (jstringSID) { /* GroupName byte array */ jstringGroupName = JNU_NewStringFromNativeW(env, groupName); if (jstringGroupName) { /* DomainName byte array */ jstringDomainName = JNU_NewStringFromNativeW(env, domainName); if (jstringDomainName) { /* Now actually add the group to the user. */ (*env)->CallVoidMethod(env, wrapperUser, addGroup, jstringSID, jstringGroupName, jstringDomainName); (*env)->DeleteLocalRef(env, jstringDomainName); } else { /* Exception Thrown */ break; } (*env)->DeleteLocalRef(env, jstringGroupName); } else { /* Exception Thrown */ break; } (*env)->DeleteLocalRef(env, jstringSID); } else { /* Exception Thrown */ break; } } else { /* This is normal as some accounts do not seem to be mappable. */ /* log_printf(TEXT("WrapperJNI Debug: Unable to locate account for Sid, %s: %s"), sidText, getLastErrorText()); */ } free(domainName); } free(groupName); } LocalFree(sidText); } } } else { log_printf(TEXT("WrapperJNI Error: Unable to get token information: %s"), getLastErrorText()); } free(tokenGroups); } } else { /* Exception Thrown */ } return result; } /** * Creates and returns a WrapperUser instance to represent the user who owns * the specified process Id. */ jobject createWrapperUserForProcess(JNIEnv *env, DWORD processId, jboolean groups) { HANDLE hProcess; HANDLE hProcessToken; TOKEN_USER *tokenUser; DWORD tokenUserSize; TCHAR *sidText; TCHAR *userName; DWORD userNameSize; TCHAR *domainName; DWORD domainNameSize; SID_NAME_USE sidType; time_t loginTime; jclass wrapperUserClass; jmethodID constructor; jstring jstringSID; jstring jstringUserName; jstring jstringDomainName; jobject wrapperUser = NULL; if (hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, processId)) { if (OpenProcessToken(hProcess, TOKEN_QUERY, &hProcessToken)) { GetTokenInformation(hProcessToken, TokenUser, NULL, 0, &tokenUserSize); tokenUser = (TOKEN_USER *)malloc(tokenUserSize); if (!tokenUser) { throwOutOfMemoryError(env, TEXT("CWUFP1")); } else { if (GetTokenInformation(hProcessToken, TokenUser, tokenUser, tokenUserSize, &tokenUserSize)) { /* Get the text representation of the sid. */ if (ConvertSidToStringSid(tokenUser->User.Sid, &sidText) == 0) { log_printf(TEXT("Failed to convert SId to String: %s"), getLastErrorText()); } else { /* We now have an SID, use it to lookup the account. */ userNameSize = 0; domainNameSize = 0; LookupAccountSid(NULL, tokenUser->User.Sid, NULL, &userNameSize, NULL, &domainNameSize, &sidType); userName = (TCHAR*)malloc(sizeof(TCHAR) * userNameSize); if (!userName) { throwOutOfMemoryError(env, TEXT("CWUFP3")); } else { domainName = (TCHAR*)malloc(sizeof(TCHAR) * domainNameSize); if (!domainName) { throwOutOfMemoryError(env, TEXT("CWUFP4")); } else { if (LookupAccountSid(NULL, tokenUser->User.Sid, userName, &userNameSize, domainName, &domainNameSize, &sidType)) { /* Get the time that this user logged in. */ loginTime = getUserLoginTime(sidText); /* Look for the WrapperUser class. Ignore failures as JNI throws an exception. */ if (wrapperUserClass = (*env)->FindClass(env, "org/tanukisoftware/wrapper/WrapperWin32User")) { /* Look for the constructor. Ignore failures. */ if (constructor = (*env)->GetMethodID(env, wrapperUserClass, "", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V")) { /* Create the arguments to the constructor as java objects */ /* SID */ jstringSID = JNU_NewStringFromNativeW(env, sidText); if (jstringSID) { /* UserName */ jstringUserName = JNU_NewStringFromNativeW(env, userName); if (jstringUserName) { /* DomainName */ jstringDomainName = JNU_NewStringFromNativeW(env, domainName); if (jstringDomainName) { /* Now create the new wrapperUser using the constructor arguments collected above. */ wrapperUser = (*env)->NewObject(env, wrapperUserClass, constructor, jstringSID, jstringUserName, jstringDomainName, loginTime); /* If the caller requested the user's groups then look them up. */ if (groups) { if (setUserGroups(env, wrapperUserClass, wrapperUser, hProcessToken)) { /* Failed. Just continue without groups. */ } } (*env)->DeleteLocalRef(env, jstringDomainName); } else { /* Exception Thrown */ } (*env)->DeleteLocalRef(env, jstringUserName); } else { /* Exception Thrown */ } (*env)->DeleteLocalRef(env, jstringSID); } else { /* Exception Thrown */ } } else { /* Exception Thrown */ } (*env)->DeleteLocalRef(env, wrapperUserClass); } else { /* Exception Thrown */ } } else { /* This is normal as some accounts do not seem to be mappable. */ } free(domainName); } free(userName); } LocalFree(sidText); } } else { log_printf(TEXT("WrapperJNI Error: Unable to get token information: %s"), getLastErrorText()); } free(tokenUser); } CloseHandle(hProcessToken); } else { log_printf(TEXT("WrapperJNI Error: Unable to open process token: %s"), getLastErrorText()); } CloseHandle(hProcess); } else { log_printf(TEXT("WrapperJNI Error: Unable to open process: %s"), getLastErrorText()); } return wrapperUser; } HMODULE kernel32Mod; void loadDLLProcs() { if ((kernel32Mod = GetModuleHandle(TEXT("KERNEL32.DLL"))) == NULL) { log_printf(TEXT("WrapperJNI Error: Unable to load %s: %s"), TEXT("KERNEL32.DLL"), getLastErrorText()); return; } #ifdef UNICODE if ((OptionalProcess32First = GetProcAddress(kernel32Mod, "Process32FirstW")) == NULL) { if (wrapperJNIDebugging) { log_printf(TEXT("WrapperJNI Debug: The %s function is not available on this version of Windows."), TEXT("Process32FirstW")); } } #else if ((OptionalProcess32First = GetProcAddress(kernel32Mod, "Process32First")) == NULL) { if (wrapperJNIDebugging) { log_printf(TEXT("WrapperJNI Debug: The %s function is not available on this version of Windows."), TEXT("Process32First")); } } #endif #ifdef UNICODE if ((OptionalProcess32Next = GetProcAddress(kernel32Mod, "Process32NextW")) == NULL) { if (wrapperJNIDebugging) { log_printf(TEXT("WrapperJNI Debug: The %s function is not available on this version of Windows."), TEXT("Process32NextW")); } } #else if ((OptionalProcess32Next = GetProcAddress(kernel32Mod, "Process32Next")) == NULL) { if (wrapperJNIDebugging) { log_printf(TEXT("WrapperJNI Debug: The %s function is not available on this version of Windows."), TEXT("Process32Next")); } } #endif if ((OptionalThread32First = GetProcAddress(kernel32Mod, "Thread32First")) == NULL) { if (wrapperJNIDebugging) { log_printf(TEXT("WrapperJNI Debug: The %s function is not available on this version of Windows."), TEXT("Thread32First")); } } if ((OptionalThread32Next = GetProcAddress(kernel32Mod, "Thread32Next")) == NULL) { if (wrapperJNIDebugging) { log_printf(TEXT("WrapperJNI Debug: The %s function is not available on this version of Windows."), TEXT("Thread32Next")); } } if ((OptionalCreateToolhelp32Snapshot = GetProcAddress(kernel32Mod, "CreateToolhelp32Snapshot")) == NULL) { if (wrapperJNIDebugging) { log_printf(TEXT("WrapperJNI Debug: The %s function is not available on this version of Windows."), TEXT("CreateToolhelp32Snapshot")); } } if ((OptionalRaiseFailFastException = GetProcAddress(kernel32Mod, "RaiseFailFastException")) == NULL) { if (wrapperJNIDebugging) { log_printf(TEXT("WrapperJNI Debug: The %s function is not available on this version of Windows."), TEXT("RaiseFailFastException")); } } } const TCHAR* getExceptionName(DWORD exCode) { TCHAR *exName; switch (exCode) { case EXCEPTION_ACCESS_VIOLATION: exName = TEXT("EXCEPTION_ACCESS_VIOLATION"); break; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: exName = TEXT("EXCEPTION_ARRAY_BOUNDS_EXCEEDED"); break; case EXCEPTION_BREAKPOINT: exName = TEXT("EXCEPTION_BREAKPOINT"); break; case EXCEPTION_DATATYPE_MISALIGNMENT: exName = TEXT("EXCEPTION_DATATYPE_MISALIGNMENT"); break; case EXCEPTION_FLT_DENORMAL_OPERAND: exName = TEXT("EXCEPTION_FLT_DENORMAL_OPERAND"); break; case EXCEPTION_FLT_DIVIDE_BY_ZERO: exName = TEXT("EXCEPTION_FLT_DIVIDE_BY_ZERO"); break; case EXCEPTION_FLT_INEXACT_RESULT: exName = TEXT("EXCEPTION_FLT_INEXACT_RESULT"); break; case EXCEPTION_FLT_INVALID_OPERATION: exName = TEXT("EXCEPTION_FLT_INVALID_OPERATION"); break; case EXCEPTION_FLT_OVERFLOW: exName = TEXT("EXCEPTION_FLT_OVERFLOW"); break; case EXCEPTION_FLT_STACK_CHECK: exName = TEXT("EXCEPTION_FLT_STACK_CHECK"); break; case EXCEPTION_FLT_UNDERFLOW: exName = TEXT("EXCEPTION_FLT_UNDERFLOW"); break; case EXCEPTION_ILLEGAL_INSTRUCTION: exName = TEXT("EXCEPTION_ILLEGAL_INSTRUCTION"); break; case EXCEPTION_IN_PAGE_ERROR: exName = TEXT("EXCEPTION_IN_PAGE_ERROR"); break; case EXCEPTION_INT_DIVIDE_BY_ZERO: exName = TEXT("EXCEPTION_INT_DIVIDE_BY_ZERO"); break; case EXCEPTION_INT_OVERFLOW: exName = TEXT("EXCEPTION_INT_OVERFLOW"); break; case EXCEPTION_INVALID_DISPOSITION: exName = TEXT("EXCEPTION_INVALID_DISPOSITION"); break; case EXCEPTION_NONCONTINUABLE_EXCEPTION: exName = TEXT("EXCEPTION_NONCONTINUABLE_EXCEPTION"); break; case EXCEPTION_PRIV_INSTRUCTION: exName = TEXT("EXCEPTION_PRIV_INSTRUCTION"); break; case EXCEPTION_SINGLE_STEP: exName = TEXT("EXCEPTION_SINGLE_STEP"); break; case EXCEPTION_STACK_OVERFLOW: exName = TEXT("EXCEPTION_STACK_OVERFLOW"); break; default: exName = TEXT("EXCEPTION_UNKNOWN"); break; } return exName; } void commonExceptionInfo(PEXCEPTION_POINTERS pExceptionInfo) { int i; log_printf(TEXT("WrapperJNI Error: ExceptionCode: %s (0x%x)"), getExceptionName(pExceptionInfo->ExceptionRecord->ExceptionCode), pExceptionInfo->ExceptionRecord->ExceptionCode); switch (pExceptionInfo->ExceptionRecord->ExceptionCode) { case EXCEPTION_ACCESS_VIOLATION: case EXCEPTION_IN_PAGE_ERROR: if (pExceptionInfo->ExceptionRecord->NumberParameters >= 2) { switch (pExceptionInfo->ExceptionRecord->ExceptionInformation[0]) { case 0: log_printf(TEXT("WrapperJNI Error: Read access exception from 0x%p"), pExceptionInfo->ExceptionRecord->ExceptionInformation[1]); break; case 1: log_printf(TEXT("WrapperJNI Error: Write access exception to 0x%p"), pExceptionInfo->ExceptionRecord->ExceptionInformation[1]); break; case 8: log_printf(TEXT("WrapperJNI Error: DEP access exception to 0x%p"), pExceptionInfo->ExceptionRecord->ExceptionInformation[1]); break; default: log_printf(TEXT("WrapperJNI Error: Unexpected access exception to 0x%p (%d)"), pExceptionInfo->ExceptionRecord->ExceptionInformation[1], pExceptionInfo->ExceptionRecord->ExceptionInformation[0]); break; } } if ((pExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_IN_PAGE_ERROR) && (pExceptionInfo->ExceptionRecord->NumberParameters >= 3)) { log_printf(TEXT("WrapperJNI Error: Status Code: %d"), pExceptionInfo->ExceptionRecord->ExceptionInformation[2]); } break; default: for (i = 0; i < (int)pExceptionInfo->ExceptionRecord->NumberParameters; i++) { log_printf(TEXT("WrapperJNI Error: ExceptionInformation[%d] = %ld"), i, pExceptionInfo->ExceptionRecord->ExceptionInformation[i]); } break; } log_printf(TEXT("WrapperJNI Error: ExceptionFlags: %s (0x%x)"), (pExceptionInfo->ExceptionRecord->ExceptionFlags & EXCEPTION_NONCONTINUABLE_EXCEPTION ? TEXT("EXCEPTION_NONCONTINUABLE_EXCEPTION") : TEXT("")), pExceptionInfo->ExceptionRecord->ExceptionFlags); } LONG WINAPI wrapperVectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) { log_printf(TEXT("WrapperJNI Error: ============================================================")); log_printf(TEXT("WrapperJNI Error: Detected an exception in the Java process.")); commonExceptionInfo(pExceptionInfo); log_printf(TEXT("WrapperJNI Error: ============================================================")); return EXCEPTION_CONTINUE_SEARCH; } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeInit * Signature: (Z)V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeInit(JNIEnv *env, jclass jClassWrapperManager, jboolean debugging) { TCHAR szPath[_MAX_PATH]; DWORD usedLen; OSVERSIONINFO osVer; TCHAR *registerNativeHandlerValue; DWORD shutdownlevel; DWORD shutdownflags; wrapperJNIDebugging = debugging; /* Set the locale so we can display MultiByte characters. */ _tsetlocale(LC_ALL, TEXT("")); initLog(env); if (wrapperJNIDebugging) { /* This is useful for making sure that the JNI call is working. */ log_printf(TEXT("WrapperJNI Debug: Initializing WrapperManager native library.")); /* Important : For win XP getLastError() is unchanged if the buffer is too small, so if we don't reset the last error first, we may actually test an old pending error. */ SetLastError(ERROR_SUCCESS); usedLen = GetModuleFileName(NULL, szPath, _MAX_PATH); if (usedLen == 0) { log_printf(TEXT("WrapperJNI Debug: Unable to retrieve the Java process file name. %s"), getLastErrorText()); } else if ((usedLen == _MAX_PATH) || (getLastError() == ERROR_INSUFFICIENT_BUFFER)) { log_printf(TEXT("WrapperJNI Debug: Unable to retrieve the Java process file name. %s"), TEXT("Path too long.")); } else { log_printf(TEXT("WrapperJNI Debug: Java Executable: %s"), szPath); } /* Important : For win XP getLastError() is unchanged if the buffer is too small, so if we don't reset the last error first, we may actually test an old pending error. */ SetLastError(ERROR_SUCCESS); usedLen = GetModuleFileName((HINSTANCE)&__ImageBase, szPath, _MAX_PATH); if (usedLen == 0) { log_printf(TEXT("WrapperJNI Debug: Unable to retrieve the native library file name. %s"), getLastErrorText()); } else if ((usedLen == _MAX_PATH) || (getLastError() == ERROR_INSUFFICIENT_BUFFER)) { log_printf(TEXT("WrapperJNI Debug: Unable to retrieve the native library file name. %s"), TEXT("Path too long.")); } else { log_printf(TEXT("WrapperJNI Debug: Native Library: %s"), szPath); } } /* Lets define the shutdown priority lower than wrapper.exe, meaning the system will wait that wrapper.exe shuts down completely before killing the Java process. * By default, the shutdown level inherits from the parent process. */ if (!GetProcessShutdownParameters(&shutdownlevel, &shutdownflags)) { log_printf(TEXT("WrapperJNI Error: Failed to retrieve the shutdown parameters.")); log_printf(TEXT("WrapperJNI Error: If the system shuts down while the application is still running, the Java process will be forcibly killed.")); } else if (!SetProcessShutdownParameters(shutdownlevel-1, SHUTDOWN_NORETRY)) { log_printf(TEXT("WrapperJNI Error: Failed to set the shutdown parameters.")); log_printf(TEXT("WrapperJNI Error: If the system shuts down while the application is still running, the Java process will be forcibly killed.")); } if (initCommon(env, jClassWrapperManager)) { /* Failed. An exception will have been thrown. */ return; } /* Register exception handlers. */ if (!getSystemProperty(env, TEXT("wrapper.register_native_handler"), ®isterNativeHandlerValue, FALSE)) { /* Default to no registered handlers. */ if ((registerNativeHandlerValue != NULL) && (strcmpIgnoreCase(registerNativeHandlerValue, TEXT("TRUE")) == 0)) { /* VectoredExceptionHander is called whenever any Exception is thrown, regardless of whether or not it is caught by its surounding code. */ AddVectoredExceptionHandler(1, wrapperVectoredExceptionHandler); } if (registerNativeHandlerValue != NULL) { free(registerNativeHandlerValue); registerNativeHandlerValue = NULL; } } osVer.dwOSVersionInfoSize = sizeof(osVer); #pragma warning(push) #pragma warning(disable : 4996) /* Visual Studio 2013 deprecates GetVersionEx but we still want to use it. */ if (GetVersionEx(&osVer)) { if (wrapperJNIDebugging) { log_printf(TEXT("WrapperJNI Debug: Windows version: %ld.%ld.%ld"), osVer.dwMajorVersion, osVer.dwMinorVersion, osVer.dwBuildNumber); } } else { log_printf(TEXT("WrapperJNI Error: Unable to retrieve the Windows version information.")); } #pragma warning(pop) loadDLLProcs(); if (!(controlEventQueueMutexHandle = CreateMutex(NULL, FALSE, NULL))) { log_printf(TEXT("WrapperJNI Error: Failed to create control event queue mutex. Signals will be ignored. %s"), getLastErrorText()); controlEventQueueMutexHandle = NULL; } /* Make sure that the handling of CTRL-C signals is enabled for this process. */ if (!SetConsoleCtrlHandler(NULL, FALSE)) { log_printf(TEXT("WrapperJNI Error: Attempt to reset control signal handlers failed. %s"), getLastErrorText()); } /* Initialize the CTRL-C handler */ if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)wrapperConsoleHandler, TRUE)) { log_printf(TEXT("WrapperJNI Error: Attempt to register a control signal handler failed. %s"), getLastErrorText()); } /* Store the current process Id */ javaProcessId = GetCurrentProcessId(); /* Initialize the explorer.exe name. */ initExplorerExeName(); } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeLoadWrapperProperties * Signature: ()V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeLoadWrapperProperties(JNIEnv *env, jclass clazz) { } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeRaiseExceptionInner * Signature: (I)V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeRaiseExceptionInner(JNIEnv *env, jclass clazz, jint code) { log_printf(TEXT("WrapperJNI Warn: Raising Exception 0x%08x..."), code); RaiseException(code, EXCEPTION_NONCONTINUABLE, 0, NULL); } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeRaiseFailFastExceptionInner * Signature: ()V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeRaiseFailFastExceptionInner(JNIEnv *env, jclass clazz) { if (OptionalRaiseFailFastException != NULL) { log_printf(TEXT("WrapperJNI Warn: Raising FailFastException...")); OptionalRaiseFailFastException(NULL, NULL, 0x1/*FAIL_FAST_GENERATE_EXCEPTION_ADDRESS*/); } else { log_printf(TEXT("WrapperJNI: FailFastException not available on this version of Windows.")); } } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeRedirectPipes * Signature: ()I */ JNIEXPORT jint JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeRedirectPipes(JNIEnv *env, jclass clazz) { /* We don't need to do anything on Windows. */ return 0; } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetJavaPID * Signature: ()I */ JNIEXPORT jint JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetJavaPID(JNIEnv *env, jclass clazz) { return GetCurrentProcessId(); } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeRequestThreadDump * Signature: ()V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeRequestThreadDump(JNIEnv *env, jclass clazz) { if (wrapperJNIDebugging) { log_printf(TEXT("WrapperJNI Debug: Sending BREAK event to process group %ld."), javaProcessId); } if (GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, javaProcessId) == 0) { if (getLastError() == 6) { log_printf(TEXT("WrapperJNI Error: Unable to send BREAK event to JVM process because it does not have a console.")); } else { log_printf(TEXT("WrapperJNI Error: Unable to send BREAK event to JVM process: %s"), getLastErrorText()); } } } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeSetConsoleTitle * Signature: (Ljava/lang/String;)V */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeSetConsoleTitle(JNIEnv *env, jclass clazz, jstring jstringTitle) { TCHAR *title; title = JNU_GetNativeWFromString(env, jstringTitle); if (!title) { throwOutOfMemoryError(env, TEXT("NSCT1")); } else { if (wrapperJNIDebugging) { log_printf(TEXT("WrapperJNI Debug: Setting the console title to: %s"), title); } SetConsoleTitle(title); free(title); } } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetUser * Signature: (Z)Lorg/tanukisoftware/wrapper/WrapperUser; */ /*#define UVERBOSE*/ JNIEXPORT jobject JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetUser(JNIEnv *env, jclass clazz, jboolean groups) { DWORD processId; #ifdef UVERBOSE log_printf(TEXT("WrapperJNI Debug: nativeGetUser()")); #endif /* Get the current processId. */ processId = GetCurrentProcessId(); return createWrapperUserForProcess(env, processId, groups); } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetInteractiveUser * Signature: (Z)Lorg/tanukisoftware/wrapper/WrapperUser; */ /*#define IUVERBOSE*/ JNIEXPORT jobject JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetInteractiveUser(JNIEnv *env, jclass clazz, jboolean groups) { HANDLE snapshot; PROCESSENTRY32 processEntry; THREADENTRY32 threadEntry; BOOL foundThread; HDESK desktop; jobject wrapperUser = NULL; #ifdef IUVERBOSE log_printf(TEXT("WrapperJNI Debug: nativeGetInteractiveUser()")); #endif /* This function will only work if all required optional functions existed. */ if ((OptionalProcess32First == NULL) || (OptionalProcess32Next == NULL) || (OptionalThread32First == NULL) || (OptionalThread32Next == NULL) || (OptionalCreateToolhelp32Snapshot == NULL)) { if (wrapperJNIDebugging) { log_printf(TEXT("WrapperJNI Debug: getInteractiveUser not supported on this platform.")); } return NULL; } /* In order to be able to return the interactive user, we first need to locate the * logged on user whose desktop we are able to open. On XP systems, there will be * more than one user with a desktop, but only the first one to log on will allow * us to open its desktop. On all NT systems, there will be additional logged on * users if there are other services running. */ if ((snapshot = (HANDLE)OptionalCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0)) >= 0) { processEntry.dwSize = sizeof(processEntry); if (OptionalProcess32First(snapshot, &processEntry)) { do { /* We are only interrested in the Explorer processes. */ if (_tcsicmp(explorerExe, processEntry.szExeFile) == 0) { #ifdef IUVERBOSE log_printf(TEXT("WrapperJNI Debug: Process size=%ld, cnt=%ld, id=%ld, parentId=%ld, moduleId=%ld, threads=%ld, exe=%s"), processEntry.dwSize, processEntry.cntUsage, processEntry.th32ProcessID, processEntry.th32ParentProcessID, processEntry.th32ModuleID, processEntry.cntThreads, processEntry.szExeFile); #endif /* Now look for a thread which is owned by the explorer process. */ threadEntry.dwSize = sizeof(threadEntry); if (OptionalThread32First(snapshot, &threadEntry)) { foundThread = FALSE; do { /* We are only interrested in threads that belong to the current Explorer process. */ if (threadEntry.th32OwnerProcessID == processEntry.th32ProcessID) { #ifdef IUVERBOSE log_printf(TEXT("WrapperJNI Debug: Thread Id=%ld"), threadEntry.th32ThreadID); #endif /* We have a thread, now see if we can gain access to its desktop */ if (desktop = GetThreadDesktop(threadEntry.th32ThreadID)) { /* We got the desktop! We now know that this is the thread and thus * process that we have been looking for. Unfortunately it does not * appear that we can get the Sid of the account directly from this * desktop. I tried using GetUserObjectInformation, but the Sid * returned does not seem to map to a valid account. */ wrapperUser = createWrapperUserForProcess(env, processEntry.th32ProcessID, groups); } else { #ifdef IUVERBOSE log_printf(TEXT("WrapperJNI Debug: GetThreadDesktop failed: %s"), getLastErrorText()); #endif } /* We only need the first thread, so break */ foundThread = TRUE; break; } } while (OptionalThread32Next(snapshot, &threadEntry)); if (!foundThread && (GetLastError() != ERROR_NO_MORE_FILES)) { #ifdef IUVERBOSE log_printf(TEXT("WrapperJNI Debug: Unable to get next thread entry: %s"), getLastErrorText()); #endif } } else if (GetLastError() != ERROR_NO_MORE_FILES) { log_printf(TEXT("WrapperJNI Debug: Unable to get first thread entry: %s"), getLastErrorText()); } } } while (OptionalProcess32Next(snapshot, &processEntry)); #ifdef IUVERBOSE if (GetLastError() != ERROR_NO_MORE_FILES) { log_printf(TEXT("WrapperJNI Debug: Unable to get next process entry: %s"), getLastErrorText()); } #endif } else if (GetLastError() != ERROR_NO_MORE_FILES) { log_printf(TEXT("WrapperJNI Error: Unable to get first process entry: %s"), getLastErrorText()); } CloseHandle(snapshot); } else { log_printf(TEXT("WrapperJNI Error: Toolhelp snapshot failed: %s"), getLastErrorText()); } return wrapperUser; } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeListServices * Signature: ()[Lorg/tanukisoftware/wrapper/WrapperWin32Service; */ JNIEXPORT jobjectArray JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeListServices(JNIEnv *env, jclass clazz) { TCHAR buffer[512]; SC_HANDLE hSCManager; DWORD size, sizeNeeded, servicesReturned, resumeHandle; DWORD err; ENUM_SERVICE_STATUS *services = NULL; BOOL threwError = FALSE; DWORD i; jobjectArray serviceArray = NULL; jclass serviceClass; jmethodID constructor; jstring jStringName; jstring jStringDisplayName; DWORD state; DWORD exitCode; jobject service; hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE); if (hSCManager) { /* Before we can get the list of services, we need to know how much memory it will take. */ resumeHandle = 0; if (!EnumServicesStatus(hSCManager, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &sizeNeeded, &servicesReturned, &resumeHandle)) { err = GetLastError(); if ((err == ERROR_MORE_DATA) || (err == ERROR_INSUFFICIENT_BUFFER)) { /* Allocate the needed memory and call again. */ size = sizeNeeded; services = malloc(size); if (!services) { throwOutOfMemoryError(env, TEXT("NLS1")); } else { if (!EnumServicesStatus(hSCManager, SERVICE_WIN32, SERVICE_STATE_ALL, services, size, &sizeNeeded, &servicesReturned, &resumeHandle)) { /* Failed to get the services. */ _sntprintf(buffer, 512, TEXT("Unable to enumerate the system services: %s"), getLastErrorText()); throwServiceException(env, GetLastError(), buffer); threwError = TRUE; } else { /* Success. */ } /* free(services) is done below. */ } } else { _sntprintf(buffer, 512, TEXT("Unable to enumerate the system services: %s"), getLastErrorText()); throwServiceException(env, GetLastError(), buffer); threwError = TRUE; } } else { /* Success which means that no services were found. */ } if (!threwError) { if (serviceClass = (*env)->FindClass(env, "org/tanukisoftware/wrapper/WrapperWin32Service")) { /* Look for the constructor. Ignore failures. */ if (constructor = (*env)->GetMethodID(env, serviceClass, "", "(Ljava/lang/String;Ljava/lang/String;II)V")) { serviceArray = (*env)->NewObjectArray(env, servicesReturned, serviceClass, NULL); for (i = 0; i < servicesReturned; i++) { jStringName = JNU_NewStringFromNativeW(env, services[i].lpServiceName); if (jStringName) { jStringDisplayName = JNU_NewStringFromNativeW(env, services[i].lpDisplayName); if (jStringDisplayName) { state = services[i].ServiceStatus.dwCurrentState; exitCode = services[i].ServiceStatus.dwWin32ExitCode; if (exitCode == ERROR_SERVICE_SPECIFIC_ERROR) { exitCode = services[i].ServiceStatus.dwServiceSpecificExitCode; } service = (*env)->NewObject(env, serviceClass, constructor, jStringName, jStringDisplayName, state, exitCode); (*env)->SetObjectArrayElement(env, serviceArray, i, service); (*env)->DeleteLocalRef(env, service); (*env)->DeleteLocalRef(env, jStringDisplayName); } else { /* Exception Thrown */ break; } (*env)->DeleteLocalRef(env, jStringName); } else { /* Exception Thrown */ break; } } } (*env)->DeleteLocalRef(env, serviceClass); } else { /* Unable to load the service class. */ _sntprintf(buffer, 512, TEXT("Unable to locate class org.tanukisoftware.wrapper.WrapperWin32Service")); throwServiceException(env, 1, buffer); } } if (services != NULL) { free(services); } /* Close the handle to the service control manager database */ CloseServiceHandle(hSCManager); } else { /* Unable to open the service manager. */ _sntprintf(buffer, 512, TEXT("Unable to open the Windows service control manager database: %s"), getLastErrorText()); throwServiceException(env, GetLastError(), buffer); } return serviceArray; } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeSendServiceControlCode * Signature: (Ljava/lang/String;I)Lorg/tanukisoftware/wrapper/WrapperWin32Service; */ JNIEXPORT jobject JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeSendServiceControlCode(JNIEnv *env, jclass clazz, jstring jStringServiceName, jint controlCode) { jobject service = NULL; TCHAR *serviceName; size_t bufferSize = 2048; TCHAR buffer[2048]; SC_HANDLE hSCManager; SC_HANDLE hService; int serviceAccess; DWORD wControlCode; BOOL threwError = FALSE; SERVICE_STATUS serviceStatus; jclass serviceClass; jmethodID constructor; DWORD displayNameSize; TCHAR *displayName; jstring jStringDisplayName; DWORD state; DWORD exitCode; if ((serviceName = JNU_GetNativeWFromString(env, jStringServiceName))) { hSCManager = OpenSCManager(NULL, NULL, GENERIC_READ); if (hSCManager) { /* Decide on the access needed when opening the service. */ if (controlCode == org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_START) { serviceAccess = SERVICE_START | SERVICE_INTERROGATE | SERVICE_QUERY_STATUS; wControlCode = SERVICE_CONTROL_INTERROGATE; } else if (controlCode == org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_STOP) { serviceAccess = SERVICE_STOP | SERVICE_QUERY_STATUS; wControlCode = SERVICE_CONTROL_STOP; } else if (controlCode == org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_INTERROGATE) { serviceAccess = SERVICE_INTERROGATE | SERVICE_QUERY_STATUS; wControlCode = SERVICE_CONTROL_INTERROGATE; } else if (controlCode == org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_PAUSE) { serviceAccess = SERVICE_PAUSE_CONTINUE | SERVICE_QUERY_STATUS; wControlCode = SERVICE_CONTROL_PAUSE; } else if (controlCode == org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_CONTINUE) { serviceAccess = SERVICE_PAUSE_CONTINUE | SERVICE_QUERY_STATUS; wControlCode = SERVICE_CONTROL_CONTINUE; } else if ((controlCode >= 128) || (controlCode <= 255)) { serviceAccess = SERVICE_USER_DEFINED_CONTROL | SERVICE_QUERY_STATUS; wControlCode = controlCode; } else { /* Illegal control code. */ _sntprintf(buffer, 512, TEXT("Illegal Control code specified: %d"), controlCode); throwServiceException(env, 1, buffer); threwError = TRUE; } if (!threwError) { hService = OpenService(hSCManager, serviceName, serviceAccess); if (hService) { /* If we are trying to start a service, it needs to be handled specially. */ if (controlCode == org_tanukisoftware_wrapper_WrapperManager_SERVICE_CONTROL_CODE_START) { if (StartService(hService, 0, NULL)) { /* Started the service. Continue on and interrogate the service. */ } else { /* Failed. */ _sntprintf(buffer, bufferSize, TEXT("Unable to start service \"%s\": %s"), serviceName, getLastErrorText()); throwServiceException(env, GetLastError(), buffer); threwError = TRUE; } } if (!threwError) { if (ControlService(hService, wControlCode, &serviceStatus)) { /* Success. fall through. */ } else { /* Failed to send the control code. See if the service is running. */ if (GetLastError() == ERROR_SERVICE_NOT_ACTIVE) { /* Service is not running, so get its status information. */ if (QueryServiceStatus(hService, &serviceStatus)) { /* We got the status. fall through. */ } else { /* Actual failure. */ _sntprintf(buffer, bufferSize, TEXT("Unable to query status of service \"%s\": %s"), serviceName, getLastErrorText()); throwServiceException(env, GetLastError(), buffer); threwError = TRUE; } } else { /* Actual failure. */ _sntprintf(buffer, bufferSize, TEXT("Unable to query status of service \"%s\": %s"), serviceName, getLastErrorText()); throwServiceException(env, GetLastError(), buffer); threwError = TRUE; } } if (!threwError) { /* Build up a service object to return. */ if (serviceClass = (*env)->FindClass(env, "org/tanukisoftware/wrapper/WrapperWin32Service")) { /* Look for the constructor. Ignore failures. */ if (constructor = (*env)->GetMethodID(env, serviceClass, "", "(Ljava/lang/String;Ljava/lang/String;II)V")) { /* Look up the display name of the service. First need to figure out how big it is. */ displayNameSize = 0; GetServiceDisplayName(hSCManager, serviceName, NULL, &displayNameSize); if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { _sntprintf(buffer, bufferSize, TEXT("Unable to obtain the display name of service \"%s\": %s"), serviceName, getLastErrorText()); throwServiceException(env, GetLastError(), buffer); threwError = TRUE; } else { displayNameSize++; /* Add room for the '\0' . */ displayName = malloc(sizeof(TCHAR) * displayNameSize); if (!displayName) { throwOutOfMemoryError(env, TEXT("NSSCC1")); threwError = TRUE; } else { /* Now get the display name for real. */ if ((GetServiceDisplayName(hSCManager, serviceName, displayName, &displayNameSize) == 0) && GetLastError()) { _sntprintf(buffer, bufferSize, TEXT("Unable to obtain the display name of service \"%s\": %s"), serviceName, getLastErrorText()); throwServiceException(env, GetLastError(), buffer); threwError = TRUE; } else { /* Convert the display name to a jstring. */ jStringDisplayName = JNU_NewStringFromNativeW(env, displayName); if (jStringDisplayName) { state = serviceStatus.dwCurrentState; exitCode = serviceStatus.dwWin32ExitCode; if (exitCode == ERROR_SERVICE_SPECIFIC_ERROR) { exitCode = serviceStatus.dwServiceSpecificExitCode; } service = (*env)->NewObject(env, serviceClass, constructor, jStringServiceName, jStringDisplayName, state, exitCode); (*env)->DeleteLocalRef(env, jStringDisplayName); } } free(displayName); } } } else { /* Exception Thrown */ threwError = TRUE; } (*env)->DeleteLocalRef(env, serviceClass); } else { /* Exception Thrown */ threwError = TRUE; } } } CloseServiceHandle(hService); } else { /* Unable to open service. */ _sntprintf(buffer, bufferSize, TEXT("Unable to open the service '%s': %s"), serviceName, getLastErrorText()); throwServiceException(env, GetLastError(), buffer); threwError = TRUE; } } /* Close the handle to the service control manager database */ CloseServiceHandle(hSCManager); } else { /* Unable to open the service manager. */ _sntprintf(buffer, bufferSize, TEXT("Unable to open the Windows service control manager database: %s"), getLastErrorText()); throwServiceException(env, GetLastError(), buffer); threwError = TRUE; } free(serviceName); } else { /* Exception Thrown */ } return service; } /*#define GETPORTSTATUS_DEBUG*/ int getPortStatusTCPv4(JNIEnv *env, int port, jstring jAddress) { PMIB_TCPTABLE tcpTable; DWORD tcpTableSize; DWORD tcpTableSizeNew; DWORD retVal; int tries; int i; int result; int haveTable; const char *nativeAddress; struct in_addr addr; char localAddr[128]; /* This will only be IPV4 address, so the maximum possible length should be 15+1 */ int localPort; #ifdef GETPORTSTATUS_DEBUG char remoteAddr[128]; int remotePort; TCHAR *stateName; #endif /* We don't know how big the table needs to be, and it can change with timing, so loop. */ tcpTableSize = 0; tcpTable = NULL; tries = 0; result = 0; haveTable = FALSE; while (!haveTable) { tries++; if (tries > 10) { /* Too many attempts, may happen if the TCP table is changing a lot? */ free(tcpTable); return -101; } /* When we retry after errors other than 'ERROR_INSUFFICIENT_BUFFER', not sure that the table size variable is unchanged. * Keep track of the last known tcpTable size as we reuse it to be safe. */ tcpTableSizeNew = tcpTableSize; retVal = GetTcpTable(tcpTable, &tcpTableSizeNew, TRUE); switch (retVal) { case NO_ERROR: /* Got our table. */ haveTable = TRUE; break; case ERROR_NOT_SUPPORTED: /* No need to retry */ if (wrapperJNIDebugging) { log_printf(TEXT("WrapperJNI Error: Cannot check port status (GetTcpTable not supported on this system).")); } free(tcpTable); return -100; case ERROR_INSUFFICIENT_BUFFER: /* Initial call or too small, need to retry. */ #ifdef GETPORTSTATUS_DEBUG log_printf(TEXT("WrapperJNI Debug: GetTcpTable buffer too small < %d"), tcpTableSizeNew); #endif if (tcpTable) { free(tcpTable); } tcpTableSize = tcpTableSizeNew; tcpTable = malloc(tcpTableSize); if (!tcpTable) { throwOutOfMemoryError(env, TEXT("GPSVF1")); return -100; } break; case ERROR_INVALID_PARAMETER: /* Should not happen, but retry. */ #ifdef GETPORTSTATUS_DEBUG log_printf(TEXT("WrapperJNI Debug: GetTcpTable invalid parameter. Try again.")); #endif break; case STATUS_UNSUCCESSFUL: /* Can happen when the table is receiving lots of changes and is normal. */ #ifdef GETPORTSTATUS_DEBUG log_printf(TEXT("WrapperJNI Debug: GetTcpTable busy. Try again.")); #endif break; default: /* Unexpected. */ #ifdef GETPORTSTATUS_DEBUG log_printf(TEXT("WrapperJNI Debug: GetTcpTable error(%d)"), retVal); #endif break; } } nativeAddress = (*env)->GetStringUTFChars(env, jAddress, 0); if (nativeAddress) { #ifdef GETPORTSTATUS_DEBUG log_printf(TEXT("WrapperJNI Debug: nativeAddress=%S port=%d"), nativeAddress, port); #endif for (i = 0; i < (int)tcpTable->dwNumEntries; i++) { addr.S_un.S_addr = (u_long)tcpTable->table[i].dwLocalAddr; strncpy(localAddr, inet_ntoa(addr), sizeof(localAddr)); localPort = ntohs((u_short)tcpTable->table[i].dwLocalPort); #ifdef GETPORTSTATUS_DEBUG addr.S_un.S_addr = (u_long)tcpTable->table[i].dwRemoteAddr; strncpy(remoteAddr, inet_ntoa(addr), sizeof(remoteAddr)); remotePort = ntohs((u_short)tcpTable->table[i].dwRemotePort); switch (tcpTable->table[i].dwState) { case MIB_TCP_STATE_CLOSED: stateName = TEXT("CLOSED"); break; case MIB_TCP_STATE_LISTEN: stateName = TEXT("LISTEN"); break; case MIB_TCP_STATE_SYN_SENT: stateName = TEXT("SYN-SENT"); break; case MIB_TCP_STATE_SYN_RCVD: stateName = TEXT("SYN-RECEIVED"); break; case MIB_TCP_STATE_ESTAB: stateName = TEXT("ESTABLISHED"); break; case MIB_TCP_STATE_FIN_WAIT1: stateName = TEXT("FIN-WAIT-1"); break; case MIB_TCP_STATE_FIN_WAIT2: stateName = TEXT("FIN-WAIT-2"); break; case MIB_TCP_STATE_CLOSE_WAIT: stateName = TEXT("CLOSE-WAIT"); break; case MIB_TCP_STATE_CLOSING: stateName = TEXT("CLOSING"); break; case MIB_TCP_STATE_LAST_ACK: stateName = TEXT("LAST-ACK"); break; case MIB_TCP_STATE_TIME_WAIT: stateName = TEXT("TIME-WAIT"); break; case MIB_TCP_STATE_DELETE_TCB: stateName = TEXT("DELETE-TCB"); break; default: stateName = TEXT("UNKNOWN"); break; } log_printf(TEXT("WrapperJNI Debug: TcpTable[%d] State: %d %s Local: %S:%d Remote: %S:%d"), i, tcpTable->table[i].dwState, stateName, localAddr, localPort, remoteAddr, remotePort); #endif if ((port == localPort) && (strcmp(nativeAddress, localAddr) == 0)) { /* Matched. */ result = tcpTable->table[i].dwState; #ifdef GETPORTSTATUS_DEBUG log_printf(TEXT("WrapperJNI Debug: MATCHED! Port In Use. state=%d"), result); #endif /* No need to keep looping. */ break; } } (*env)->ReleaseStringUTFChars(env, jAddress, nativeAddress); } else { free(tcpTable); throwOutOfMemoryError(env, TEXT("GPSVF2")); return -100; } free(tcpTable); return result; } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetPortStatus * Signature: (ILjava/lang/String;I)I * * @param port Port is the port whose status is requested. * @param protocol The protocol of the port, 0=tcpv4, 1=tcpv6 * * @return The status, -1=error, 0=closed, >0=in use. */ JNIEXPORT jint JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetPortStatus(JNIEnv *env, jclass clazz, jint port, jstring jAddress, jint protocol) { switch (protocol) { case 0: return getPortStatusTCPv4(env, port, jAddress); case 1: /* GetTcp6Table required Windows Vista/2008 Server. Is it needed now? */ /*return getPortStatusTCPv6(env, port, jAddress);*/ return 0; default: return -99; } } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeSetDpiAwareness * Signature: ()I * * @param awareness The DPI awareness value to set (0 = not aware, 1 = system DPI aware, 2 = per monitor DPI aware). */ JNIEXPORT void JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeSetDpiAwareness(JNIEnv *env, jclass clazz, jint awareness) { HMODULE scalingAPI; FARPROC DynSetProcessDpiAwareness = NULL; HRESULT ret; /* The ShellScalingAPI was added in windows 8.1 */ if ((scalingAPI = LoadLibrary(TEXT("Shcore.dll"))) == NULL) { return; } else if ((DynSetProcessDpiAwareness = GetProcAddress(scalingAPI, "SetProcessDpiAwareness")) == NULL) { return; } ret = (HRESULT)DynSetProcessDpiAwareness(awareness); if (ret == E_INVALIDARG) { throwThrowable(env, "java/lang/IllegalArgumentException", TEXT("")); } else if (ret == E_ACCESSDENIED) { throwThrowable(env, "java/io/IOException", TEXT("The DPI awareness is already set.")); } } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetDpiAwareness * Signature: ()I * * @return The dpi Awareness of the current process (0 = not aware, 1 = system DPI aware, 2 = per monitor DPI aware), or -1 if there was an error. */ JNIEXPORT jint JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetDpiAwareness(JNIEnv *env, jclass clazz) { HMODULE scalingAPI; FARPROC DynGetProcessDpiAwareness = NULL; HRESULT ret; int awareness; /* PROCESS_DPI_AWARENESS */ /* The ShellScalingAPI was added in windows 8.1 */ if ((scalingAPI = LoadLibrary(TEXT("Shcore.dll"))) == NULL) { return -1; } else if ((DynGetProcessDpiAwareness = GetProcAddress(scalingAPI, "GetProcessDpiAwareness")) == NULL) { return -1; } ret = (HRESULT)DynGetProcessDpiAwareness(NULL, &awareness); if (ret == E_INVALIDARG) { /* should not happen. */ throwThrowable(env, "java/lang/IllegalArgumentException", TEXT("")); return -1; } else if (ret == E_ACCESSDENIED) { throwThrowable(env, "java/io/IOException", TEXT("The application does not have sufficient privileges.")); return -1; } return (jint)awareness; } /* * Class: org_tanukisoftware_wrapper_WrapperManager * Method: nativeGetDpiScale * Signature: ()I * * @return The dpi scale (should be devided by 96 to get the scale factor). */ JNIEXPORT jint JNICALL Java_org_tanukisoftware_wrapper_WrapperManager_nativeGetDpiScale(JNIEnv *env, jclass clazz) { const POINT ptZero = { 0, 0 }; int dpiX; int dpiY; HMONITOR hMonitor; int awareness; /* PROCESS_DPI_AWARENESS */ HMODULE scalingAPI; FARPROC DynGetProcessDpiAwareness = NULL; FARPROC DynGetDpiForMonitor = NULL; jint result; /* The ShellScalingAPI was added in windows 8.1 */ if ((scalingAPI = LoadLibrary(TEXT("Shcore.dll"))) == NULL) { return 96; /* no scaling */ } else if ((DynGetProcessDpiAwareness = GetProcAddress(scalingAPI, "GetProcessDpiAwareness")) == NULL || (DynGetDpiForMonitor = GetProcAddress(scalingAPI, "GetDpiForMonitor")) == NULL) { return 96; /* no scaling */ } /* get DPI awareness */ DynGetProcessDpiAwareness(NULL, &awareness); /* if awareness is PROCESS_SYSTEM_DPI_AWARE or PROCESS_PER_MONITOR_DPI_AWARE, * we can get the scale factor, else it will return 96 anyway so stop here. */ if (awareness == 0) { return 96; } /* get the primary monitor */ hMonitor = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY); /* get DPI scale of the monitor */ DynGetDpiForMonitor(hMonitor, 0, &dpiX, &dpiY); result = (dpiX + dpiY) / 2; /* do some control on the returned value */ if (result > 4 * 96) { result = 4 * 96; } else if (result < 96) { result = 96; } return result; } #endif wrapper_3.5.51_src/src/conf/wrapper.conf.in100644 0 0 20273 14333053652 16045 0ustar 0 0 #encoding=UTF-8 # Configuration files must begin with a line specifying the encoding # of the the file. #******************************************************************** # Wrapper License Properties (Ignored by Community Edition) #******************************************************************** # Professional and Standard Editions of the Wrapper require a valid # License Key to start. Licenses can be purchased or a trial license # requested on the following pages: # http://wrapper.tanukisoftware.com/purchase # http://wrapper.tanukisoftware.com/trial # Include file problems can be debugged by leaving only one '#' # at the beginning of the following line: ##include.debug # The Wrapper will look for either of the following optional files for a # valid License Key. License Key properties can optionally be included # directly in this configuration file. #include ../conf/wrapper-license.conf #include ../conf/wrapper-license-%WRAPPER_HOST_NAME%.conf # The following property will output information about which License Key(s) # are being found, and can aid in resolving any licensing problems. #wrapper.license.debug=TRUE #******************************************************************** # Wrapper Localization #******************************************************************** # Specify the language and locale which the Wrapper should use. #wrapper.lang=en_US # en_US or ja_JP # Specify the location of the language resource files (*.mo). wrapper.lang.folder=../lang #******************************************************************** # Wrapper Java Properties #******************************************************************** # Java Application # Locate the java binary on the system PATH: wrapper.java.command=java # Specify a specific java binary: #set.JAVA_HOME=/java/path #wrapper.java.command=%JAVA_HOME%/bin/java # Tell the Wrapper to log the full generated Java command line. #wrapper.java.command.loglevel=INFO # Java Main class. This class must implement the WrapperListener interface # or guarantee that the WrapperManager class is initialized. Helper # classes are provided to do this for you. # See the following page for details: # http://wrapper.tanukisoftware.com/doc/english/integrate.html wrapper.java.mainclass=org.tanukisoftware.wrapper.WrapperSimpleApp # Log level for notices about missing Java Classpath entries. wrapper.java.classpath.missing.loglevel=WARN # Java Classpath (include wrapper.jar) Add class path elements as # needed starting from 1 wrapper.java.classpath.1=../lib/wrapper.jar # Java Library Path (location of Wrapper.DLL or libwrapper.so) wrapper.java.library.path.1=../lib # Java Bits. On applicable platforms, tells the JVM to run in 32 or 64-bit mode. wrapper.java.additional.auto_bits=TRUE # Java Additional Parameters wrapper.java.additional.1= # Initial Java Heap Size (in MB) #wrapper.java.initmemory=3 # Maximum Java Heap Size (in MB) #wrapper.java.maxmemory=64 # Application parameters. Add parameters as needed starting from 1 wrapper.app.parameter.1= #******************************************************************** # Wrapper Logging Properties #******************************************************************** # Enables Debug output from the Wrapper. # wrapper.debug=TRUE # Format of output for the console. (See docs for formats) wrapper.console.format=PM # Log Level for console output. (See docs for log levels) wrapper.console.loglevel=INFO # Log file to use for wrapper output logging. wrapper.logfile=../logs/wrapper.log # Format of output for the log file. (See docs for formats) wrapper.logfile.format=LPTM # Log Level for log file output. (See docs for log levels) wrapper.logfile.loglevel=INFO # Maximum size that the log file will be allowed to grow to before # the log is rolled. Size is specified in bytes. The default value # of 0, disables log rolling. May abbreviate with the 'k' (kb) or # 'm' (mb) suffix. For example: 10m = 10 megabytes. wrapper.logfile.maxsize=0 # Maximum number of rolled log files which will be allowed before old # files are deleted. The default value of 0 implies no limit. wrapper.logfile.maxfiles=0 # Log Level for sys/event log output. (See docs for log levels) wrapper.syslog.loglevel=NONE #******************************************************************** # Wrapper General Properties #******************************************************************** # Allow for the use of non-contiguous numbered properties wrapper.ignore_sequence_gaps=TRUE # Do not start if the pid file already exists. wrapper.pidfile.strict=TRUE # Title to use when running as a console wrapper.console.title=@app.long.name@ #******************************************************************** # Wrapper JVM Checks #******************************************************************** # Detect DeadLocked Threads in the JVM. (Requires Standard Edition) wrapper.check.deadlock=TRUE wrapper.check.deadlock.interval=60 wrapper.check.deadlock.action=RESTART wrapper.check.deadlock.output=FULL # Out Of Memory detection. # Ignore -verbose:class output to avoid false positives. wrapper.filter.trigger.1000=[Loaded java.lang.OutOfMemoryError wrapper.filter.action.1000=NONE # (Simple match) wrapper.filter.trigger.1001=java.lang.OutOfMemoryError # (Only match text in stack traces if -XX:+PrintClassHistogram is being used.) #wrapper.filter.trigger.1001=Exception in thread "*" java.lang.OutOfMemoryError #wrapper.filter.allow_wildcards.1001=TRUE wrapper.filter.action.1001=RESTART wrapper.filter.message.1001=The JVM has run out of memory. #******************************************************************** # Wrapper Email Notifications. (Requires Professional Edition) #******************************************************************** # Common Event Email settings. #wrapper.event.default.email.debug=TRUE #wrapper.event.default.email.smtp.host= #wrapper.event.default.email.smtp.port=25 #wrapper.event.default.email.subject=[%WRAPPER_HOSTNAME%:%WRAPPER_NAME%:%WRAPPER_EVENT_NAME%] Event Notification #wrapper.event.default.email.sender= #wrapper.event.default.email.recipient= # Configure the log attached to event emails. #wrapper.event.default.email.maillog=ATTACHMENT #wrapper.event.default.email.maillog.lines=50 #wrapper.event.default.email.maillog.format=LPTM #wrapper.event.default.email.maillog.loglevel=INFO # Enable specific event emails. #wrapper.event.wrapper_start.email=TRUE #wrapper.event.jvm_prelaunch.email=TRUE #wrapper.event.jvm_start.email=TRUE #wrapper.event.jvm_started.email=TRUE #wrapper.event.jvm_deadlock.email=TRUE #wrapper.event.jvm_stop.email=TRUE #wrapper.event.jvm_stopped.email=TRUE #wrapper.event.jvm_restart.email=TRUE #wrapper.event.jvm_failed_invocation.email=TRUE #wrapper.event.jvm_max_failed_invocations.email=TRUE #wrapper.event.jvm_kill.email=TRUE #wrapper.event.jvm_killed.email=TRUE #wrapper.event.jvm_unexpected_exit.email=TRUE #wrapper.event.wrapper_stop.email=TRUE # Specify custom mail content wrapper.event.jvm_restart.email.body=The JVM was restarted.\n\nPlease check on its status.\n #******************************************************************** # Wrapper Windows Service Properties #******************************************************************** # WARNING - Do not modify any of these properties when an application # using this configuration file has been installed as a service. # Please uninstall the service before modifying this section. The # service can then be reinstalled. # Name of the service wrapper.name=@app.name@ # Display name of the service wrapper.displayname=@app.long.name@ # Description of the service wrapper.description=@app.description@ # Service dependencies. Add dependencies as needed starting from 1 wrapper.ntservice.dependency.1= # Mode in which the service is installed. AUTO_START, DELAY_START or DEMAND_START wrapper.ntservice.starttype=AUTO_START # Allow the service to interact with the desktop (Windows NT/2000/XP only). wrapper.ntservice.interactive=FALSE # Allow the current user to perform certain actions without being prompted for # administrator credentials. (Requires Professional Edition) #wrapper.ntservice.permissions.1.account=CURRENT_USER #wrapper.ntservice.permissions.1.allow=START, STOP, PAUSE_RESUME wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperActionServer.java100644 0 0 44654 14333053652 25243 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.io.IOException; import java.io.InterruptedIOException; import java.io.UnsupportedEncodingException; import java.net.InetAddress; import java.net.Socket; import java.net.SocketException; import java.net.ServerSocket; import java.util.Hashtable; import org.tanukisoftware.wrapper.WrapperManager; /** * If an application instantiates an instance of this class, the JVM will * listen on the specified port for connections. When a connection is * detected, the first byte of input will be read from the socket and * then the connection will be immediately closed. An action will then * be performed based on the byte read from the stream. *

* The easiest way to invoke an action manually is to telnet to the specified * port and then type the single command key. * telnet localhost 9999, for example. *

* Valid commands include: *

    *
  • S : Shutdown cleanly.
  • *
  • H : Immediate forced shutdown.
  • *
  • R : Restart
  • *
  • D : Perform a Thread Dump
  • *
  • U : Unexpected shutdown. (Simulate a crash for testing)
  • *
  • V : Cause an access violation. (For testing)
  • *
  • G : Make the JVM appear to be hung. (For testing)
  • *
*

* Additional user defined actions can be defined by calling the * {@link #registerAction( byte command, Runnable action )} method. * The Wrapper project reserves the right to define any upper case * commands in the future. To avoid future conflicts, please use lower * case for user defined commands. *

* This application will work even in most deadlock situations because the * thread is in issolation from the rest of the application. If the JVM * is truely hung, this class will fail to accept connections but the * Wrapper itself will detect the hang and restart the JVM externally. *

* The following code can be used in your application to start up the * WrapperActionServer with all default actions enabled: *

 *  int port = 9999;
 *  WrapperActionServer server = new WrapperActionServer( port );
 *  server.enableShutdownAction( true );
 *  server.enableHaltExpectedAction( true );
 *  server.enableRestartAction( true );
 *  server.enableThreadDumpAction( true );
 *  server.enableHaltUnexpectedAction( true );
 *  server.enableAccessViolationAction( true );
 *  server.enableAppearHungAction( true );
 *  server.start();
 * 
* Then remember to stop the server when your application shuts down: *
 *  server.stop();
 * 
* * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperActionServer implements Runnable { /** Command to invoke a shutdown action. */ public final static byte COMMAND_SHUTDOWN = (byte)'S'; /** Command to invoke an expected halt action. */ public final static byte COMMAND_HALT_EXPECTED = (byte)'H'; /** Command to invoke a restart action. */ public final static byte COMMAND_RESTART = (byte)'R'; /** Command to invoke a thread dump action. */ public final static byte COMMAND_DUMP = (byte)'D'; /** Command to invoke an unexpected halt action. */ public final static byte COMMAND_HALT_UNEXPECTED = (byte)'U'; /** Command to invoke an access violation. */ public final static byte COMMAND_ACCESS_VIOLATION = (byte)'V'; /** Command to invoke an appear hung action. */ public final static byte COMMAND_APPEAR_HUNG = (byte)'G'; /** The address to bind the port server to. Null for any address. */ private InetAddress m_bindAddr; /** The port to listen on for connections. */ private int m_port; /** Reference to the worker thread. */ private Thread m_runner; /** Flag set when the m_runner thread has been asked to stop. */ private boolean m_runnerStop = false; /** Reference to the ServerSocket. */ private ServerSocket m_serverSocket; /** Table of all the registered actions. */ private Hashtable m_actions = new Hashtable(); /** Log channel */ private static WrapperPrintStream m_out; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates and starts WrapperActionServer instance bound to the * specified port and address. * * @param port Port on which to listen for connections. * @param bindAddress Address to bind to. */ public WrapperActionServer( int port, InetAddress bindAddress ) { m_port = port; m_bindAddr = bindAddress; boolean streamSet = false; if ( "true".equals( System.getProperty( "wrapper.use_sun_encoding" ) ) ) { String sunStdoutEncoding = System.getProperty( "sun.stdout.encoding" ); if ( ( sunStdoutEncoding != null ) && !sunStdoutEncoding.equals( System.getProperty( "file.encoding" ) ) ) { /* We need to create the stream using the same encoding as the one used for stdout, else this will lead to encoding issues. */ try { m_out = new WrapperPrintStream( System.out, false, sunStdoutEncoding, "WrapperActionServer: " ); streamSet = true; } catch ( UnsupportedEncodingException e ) { /* This should not happen because we always make sure the encoding exists before launching a JVM. */ System.out.println( WrapperManager.getRes().getString( "Failed to set the encoding ''{0}'' when creating a WrapperPrintStream.\n Make sure the value of sun.stdout.encoding is correct.", sunStdoutEncoding ) ); } } } if ( !streamSet ) { m_out = new WrapperPrintStream( System.out, "WrapperActionServer: " ); } } /** * Creates and starts WrapperActionServer instance bound to the * specified port. The socket will bind to all addresses and * should be concidered a security risk. * * @param port Port on which to listen for connections. */ public WrapperActionServer( int port ) { this( port, null ); } /*--------------------------------------------------------------- * Runnable Methods *-------------------------------------------------------------*/ /** * Thread which will listen for connections on the socket. */ public void run() { if ( Thread.currentThread() != m_runner ) { throw new IllegalStateException(WrapperManager.getRes().getString( "Private method." ) ); } try { while ( !m_runnerStop ) { try { int command; Socket socket = m_serverSocket.accept(); try { // Set a short timeout of 15 seconds, // so connections will be promptly closed if left idle. socket.setSoTimeout( 15000 ); // Read a single byte. command = socket.getInputStream().read(); } finally { socket.close(); } if ( command >= 0 ) { Runnable action; synchronized( m_actions ) { action = (Runnable)m_actions.get( new Integer( command ) ); } if ( action != null ) { try { action.run(); } catch ( Throwable t ) { m_out.println( WrapperManager.getRes().getString( "Error processing action." ) ); t.printStackTrace( m_out ); } } } } catch ( Throwable t ) { // Check for throwable type this way rather than with seperate catches // to work around a problem where InterruptedException can be thrown // when the compiler gives an error saying that it can't. if ( m_runnerStop && ( ( t instanceof InterruptedException ) || ( t instanceof SocketException ) || ( t instanceof InterruptedIOException ) || ( ( t instanceof IOException ) && ( t.getMessage() != null ) && ( t.getMessage().indexOf( "Bad file descriptor" ) >= 0 ) ) // Happens on shutdown on IA64 Linux. ) ) { // This is expected, the service is being stopped. } else { m_out.println( WrapperManager.getRes().getString( "Unexpected error." ) ); t.printStackTrace( m_out ); // Avoid tight thrashing try { Thread.sleep( 5000 ); } catch ( InterruptedException e ) { // Ignore } } } } } finally { synchronized( this ) { m_runner = null; // Wake up the stop method if it is waiting for the runner to stop. this.notify(); } } } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Starts the runner thread. * * @throws IOException If the server socket is unable to bind to the * specified port or there are any other problems * opening a socket. */ public void start() throws IOException { // Create the server socket. m_serverSocket = new ServerSocket( m_port, 5, m_bindAddr ); m_runner = new Thread( this, "WrapperActionServer_runner" ); m_runner.setDaemon( true ); m_runner.start(); } /** * Stops the runner thread, blocking until it has stopped. * @throws Exception If the thread is unable to interrupt. */ public void stop() throws Exception { Thread runner = m_runner; m_runnerStop = true; runner.interrupt(); // Close the server socket so it stops blocking for new connections. ServerSocket serverSocket = m_serverSocket; if ( serverSocket != null ) { try { serverSocket.close(); } catch ( IOException e ) { // Ignore. } } synchronized( this ) { while( m_runner != null ) { try { // Wait to be notified that the thread has exited. this.wait(); } catch ( InterruptedException e ) { // Ignore } } } } /** * Registers an action with the action server. The server will not accept * any new connections until an action has returned, so keep that in mind * when writing them. Also be aware than any uncaught exceptions will be * dumped to the console if uncaught by the action. To avoid this, wrap * the code in a try { ... } catch (Throwable t) { ... } * block. * * @param command Command to be registered. Will override any exiting * action already registered with the same command. * @param action Action to be registered. */ public void registerAction( byte command, Runnable action ) { synchronized( m_actions ) { m_actions.put( new Integer( command ), action ); } } /** * Unregisters an action with the given command. If no action exists with * the specified command, the method will quietly ignore the call. * * @param command Command to be unregistered. */ public void unregisterAction( byte command ) { synchronized( m_actions ) { m_actions.remove( new Integer( command ) ); } } /** * Enable or disable the shutdown command. Disabled by default. * * @param enable True to enable to action, false to disable it. */ public void enableShutdownAction( boolean enable ) { if ( enable ) { registerAction( COMMAND_SHUTDOWN, new Runnable() { public void run() { WrapperManager.stopAndReturn( 0 ); } } ); } else { unregisterAction( COMMAND_SHUTDOWN ); } } /** * Enable or disable the expected halt command. Disabled by default. * This will shutdown the JVM, but will do so immediately without going * through the clean shutdown process. * * @param enable True to enable to action, false to disable it. */ public void enableHaltExpectedAction( boolean enable ) { if ( enable ) { registerAction( COMMAND_HALT_EXPECTED, new Runnable() { public void run() { WrapperManager.stopImmediate( 0 ); } } ); } else { unregisterAction( COMMAND_HALT_EXPECTED ); } } /** * Enable or disable the restart command. Disabled by default. * * @param enable True to enable to action, false to disable it. */ public void enableRestartAction( boolean enable ) { if ( enable ) { registerAction( COMMAND_RESTART, new Runnable() { public void run() { WrapperManager.restartAndReturn(); } } ); } else { unregisterAction( COMMAND_RESTART ); } } /** * Enable or disable the thread dump command. Disabled by default. * * @param enable True to enable to action, false to disable it. */ public void enableThreadDumpAction( boolean enable ) { if ( enable ) { registerAction( COMMAND_DUMP, new Runnable() { public void run() { WrapperManager.requestThreadDump(); } } ); } else { unregisterAction( COMMAND_DUMP ); } } /** * Enable or disable the unexpected halt command. Disabled by default. * If this command is executed, the Wrapper will think the JVM crashed * and restart it. * * @param enable True to enable to action, false to disable it. */ public void enableHaltUnexpectedAction( boolean enable ) { if ( enable ) { registerAction( COMMAND_HALT_UNEXPECTED, new Runnable() { public void run() { Runtime.getRuntime().halt( 0 ); } } ); } else { unregisterAction( COMMAND_HALT_UNEXPECTED ); } } /** * Enable or disable the access violation command. Disabled by default. * This command is useful for testing how an application handles the worst * case situation where the JVM suddenly crashed. When this happens, the * the JVM will simply die and there will be absolutely no chance for any * shutdown or cleanup work to be done by the JVM. * * @param enable True to enable to action, false to disable it. */ public void enableAccessViolationAction( boolean enable ) { if ( enable ) { registerAction( COMMAND_ACCESS_VIOLATION, new Runnable() { public void run() { WrapperManager.accessViolationNative(); } } ); } else { unregisterAction( COMMAND_ACCESS_VIOLATION ); } } /** * Enable or disable the appear hung command. Disabled by default. * This command is useful for testing how an application handles the * situation where the JVM stops responding to the Wrapper's ping * requests. This can happen if the JVM hangs or some piece of code * deadlocks. When this happens, the Wrapper will give up after the * ping timeout has expired and kill the JVM process. The JVM will * not have a chance to clean up and shudown gracefully. * * @param enable True to enable to action, false to disable it. */ public void enableAppearHungAction( boolean enable ) { if ( enable ) { registerAction( COMMAND_APPEAR_HUNG, new Runnable() { public void run() { WrapperManager.appearHung(); } } ); } else { unregisterAction( COMMAND_APPEAR_HUNG ); } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperGroup.java100644 0 0 2521 14333053652 23676 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * A WrapperGroup contains information about a group which a user * belongs to. A WrapperGroup is obtained via a WrapperUser. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public abstract class WrapperGroup { /* The name of the group. */ private String m_group; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ WrapperGroup( String group ) { // Decode the parameters using the default system encoding. m_group = group; } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns the name of the group. * * @return The name of the group. */ public String getGroup() { return m_group; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperInfo.java100644 0 0 6540 14333053652 23502 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.org/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ import java.util.Calendar; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.text.ParseException; /** * WrapperInfo.java is build as part of the build process and should not be * modified. Any changes to this class should be made to WrapperInfo.java.in * * @author Leif Mortenson */ final class WrapperInfo { /** Version of the Wrapper. */ private static final String m_version = "3.5.51"; /** Date that the Wrapper was built. */ private static final Calendar m_build = Calendar.getInstance(); /** Static initializer to create the build calendar from info hardcoded * during the build. */ static { Calendar buildDate = Calendar.getInstance(); Calendar buildTime = Calendar.getInstance(); try { buildDate.setTime( new SimpleDateFormat( "yyyyMMdd" ).parse( "20221110" ) ); buildTime.setTime( new SimpleDateFormat( "HHmm" ).parse( "1044" ) ); m_build.set( buildDate.get( Calendar.YEAR ), buildDate.get( Calendar.MONTH ), buildDate.get( Calendar.DATE ), buildTime.get( Calendar.HOUR_OF_DAY ), buildTime.get( Calendar.MINUTE ) ); } catch ( ParseException e ) { System.out.println( "WrapperInfo: Can not parse build date: " + e.getMessage() ); } } /** * Returns the version of the Wrapper. * * @return the version of the Wrapper. */ static String getVersion() { return m_version; } /** * Returns the time that the Wrapper was built. * * @return The time that the Wrapper was built. */ static String getBuildTime() { DateFormat df = new SimpleDateFormat( "HH:mm zz MMM d, yyyy" ); return df.format( m_build.getTime() ); } /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Can not be instantiated. */ private WrapperInfo() { } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperInfo.java.in100644 0 0 6736 14333053647 24122 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.org/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ import java.util.Calendar; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.text.ParseException; /** * WrapperInfo.java is build as part of the build process and should not be * modified. Any changes to this class should be made to WrapperInfo.java.in * * @author Leif Mortenson */ final class WrapperInfo { /** Version of the Wrapper. */ private static final String m_version = "@version.root@"; /** Date that the Wrapper was built. */ private static final Calendar m_build = Calendar.getInstance(); /** Static initializer to create the build calendar from info hardcoded * during the build. */ static { Calendar buildDate = Calendar.getInstance(); Calendar buildTime = Calendar.getInstance(); try { buildDate.setTime( new SimpleDateFormat( "yyyyMMdd" ).parse( "@build.date@" ) ); buildTime.setTime( new SimpleDateFormat( "HHmm" ).parse( "@build.time@" ) ); m_build.set( buildDate.get( Calendar.YEAR ), buildDate.get( Calendar.MONTH ), buildDate.get( Calendar.DATE ), buildTime.get( Calendar.HOUR_OF_DAY ), buildTime.get( Calendar.MINUTE ) ); } catch ( ParseException e ) { System.out.println( "WrapperInfo: Can not parse build date: " + e.getMessage() ); } } /** * Returns the version of the Wrapper. * * @return the version of the Wrapper. */ static String getVersion() { return m_version; } /** * Returns the time that the Wrapper was built. * * @return The time that the Wrapper was built. */ static String getBuildTime() { DateFormat df = new SimpleDateFormat( "HH:mm zz MMM d, yyyy" ); return df.format( m_build.getTime() ); } /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Can not be instantiated. */ private WrapperInfo() { } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperJNIError.java100644 0 0 3275 14333053652 24243 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * WrapperJNIErrors are thrown when user code encounters problems accessing * native Wrapper features. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperJNIError extends Error { /** * Serial Version UID. */ private static final long serialVersionUID = 4163224795268336447L; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperJNIError. * * @param message Message describing the exception. */ WrapperJNIError( String message ) { super( message ); } /** * Creates a new WrapperJNIError. * * @param message Message describing the exception. */ WrapperJNIError( byte[] message ) { this( new String( message ) ); } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Return string representation of the Error. * * @return String representation of the Error. */ public String toString() { return this.getClass().getName() + " " + getMessage(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperJarApp.java100644 0 0 77375 14333053652 24022 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.io.File; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.StringTokenizer; import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; /** * By default the WrapperJarApp will only wait for 2 seconds for the main * method of the start class to complete. This was done because the main * methods of many applications never return. It is possible to force the * class to wait for the startup main method to complete by defining the * following system property when launching the JVM (defaults to FALSE): * -Dorg.tanukisoftware.wrapper.WrapperJarApp.waitForStartMain=TRUE *

* Using the waitForStartMain property will cause the startup to wait * indefinitely. This is fine if the main method will always return * within a predefined period of time. But if there is any chance that * it could hang, then the maxStartMainWait property may be a better * option. It allows the 2 second wait time to be overridden. To wait * for up to 5 minutes for the startup main method to complete, set * the property to 300 as follows (defaults to 2 seconds): * -Dorg.tanukisoftware.wrapper.WrapperJarApp.maxStartMainWait=300 *

* By default, the WrapperJarApp will tell the Wrapper to exit with an * exit code of 1 if any uncaught exceptions are thrown in the configured * main method. This is good in most cases, but is a little different than * the way Java works on its own. Java will stay up and running if it has * launched any other non-daemon threads even if the main method ends because * of an uncaught exception. To get this same behavior, it is possible to * specify the following system property when launching the JVM (defaults to * FALSE): * -Dorg.tanukisoftware.wrapper.WrapperJarApp.ignoreMainExceptions=TRUE *

* It is possible to extend this class but make absolutely sure that any * overridden methods call their super method or the class will fail to * function correctly. Most users will have no need to override this * class. Remember that if overridden, the main method will also need to * be recreated in the child class to make sure that the correct instance * is created. *

* NOTE - The main methods of many applications are designed not to * return. In these cases, you must either stick with the default 2 second * startup timeout or specify a slightly longer timeout, using the * maxStartMainWait property, to simulate the amount of time your application * takes to start up. *

* WARNING - If the waitForStartMain is specified for an application * whose start method never returns, the Wrapper will appear at first to be * functioning correctly. However the Wrapper will never enter a running * state, this means that the Windows Service Manager and several of the * Wrapper's error recovery mechanisms will not function correctly. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperJarApp implements WrapperListener, Runnable { /** Info level log channel */ private static WrapperPrintStream m_outInfo; /** Error level log channel */ private static WrapperPrintStream m_outError; /** Debug level log channel */ private static WrapperPrintStream m_outDebug; /** * Application's main method */ private Method m_mainMethod; /** * Command line arguments to be passed on to the application */ private String[] m_appArgs; /** * Gets set to true when the thread used to launch the application * actuially starts. */ private boolean m_mainStarted; /** * Gets set to true when the thread used to launch the application * completes. */ private boolean m_mainComplete; /** * Exit code to be returned if the application fails to start. */ private Integer m_mainExitCode; /** * True if uncaught exceptions in the user app's main method should be ignored. */ private boolean m_ignoreMainExceptions; /** * Flag used to signify that the start method has completed. */ private boolean m_startComplete; /** * Flag that is set if there were any initialization problems. */ private boolean m_initFailed; /** * Error message which should be shown if initialization Failed. */ private String m_initError; /** * True if usage should be shown as part of an initialization error. */ private boolean m_initShowUsage; /** * The exception which caused the error. Only needs to be set if the stacktrace is required. */ private Throwable m_initException; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates an instance of a WrapperJarApp. * * @param args The full list of arguments passed to the JVM. */ protected WrapperJarApp( String args[] ) { // Initialize the WrapperManager class on startup by referencing it. Class wmClass = WrapperManager.class; // Set up some log channels boolean streamsSet = false; if ( "true".equals( System.getProperty( "wrapper.use_sun_encoding" ) ) ) { String sunStdoutEncoding = System.getProperty( "sun.stdout.encoding" ); if ( ( sunStdoutEncoding != null ) && !sunStdoutEncoding.equals( System.getProperty( "file.encoding" ) ) ) { /* We need to create the stream using the same encoding as the one used for stdout, else this will lead to encoding issues. */ try { m_outInfo = new WrapperPrintStream( System.out, false, sunStdoutEncoding, "WrapperJarApp: " ); m_outError = new WrapperPrintStream( System.out, false, sunStdoutEncoding, "WrapperJarApp Error: " ); m_outDebug = new WrapperPrintStream( System.out, false, sunStdoutEncoding, "WrapperJarApp Debug: " ); streamsSet = true; } catch ( UnsupportedEncodingException e ) { /* This should not happen because we always make sure the encoding exists before launching a JVM. * If any of the above streams failed, we want to fall back to streams that use the same encoding. */ System.out.println( WrapperManager.getRes().getString( "Failed to set the encoding ''{0}'' when creating a WrapperPrintStream.\n Make sure the value of sun.stdout.encoding is correct.", sunStdoutEncoding ) ); } } } if ( !streamsSet ) { m_outInfo = new WrapperPrintStream( System.out, "WrapperJarApp: " ); m_outError = new WrapperPrintStream( System.out, "WrapperJarApp Error: " ); m_outDebug = new WrapperPrintStream( System.out, "WrapperJarApp Debug: " ); } // Do all of our initialization here so the modified array list which is passed // to the WrapperListener.start method can remain unchanged. Ideally we would // want to handle this within the start method, but that would be an API change // that could effect users. // appArgs will be an args array with the jar file name stripped off. String[] appArgs; // Get the jar file name of the application if ( args.length < 1 ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Not enough arguments. Minimum {0} required.", "1" ); m_initShowUsage = true; // No jar file name, do the best we can for now. appArgs = new String[0]; } else { // Look for the specified jar file. File file = new File( args[0] ); if ( !file.exists() ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Unable to locate the jar file {0}", args[0] ); m_initShowUsage = true; } else { File parent = file.getParentFile(); JarFile jarFile; try { jarFile = new JarFile( file ); } catch ( IOException e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Unable to open the jar file {0} : {1}", args[0], e ); jarFile = null; } if ( !m_initFailed ) { Manifest manifest; try { manifest = jarFile.getManifest(); } catch ( IOException e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Unable to access the jar''s manifest file {0} : {1}", args[0], e ); manifest = null; } if ( !m_initFailed ) { Attributes attributes = manifest.getMainAttributes(); String mainClassName = attributes.getValue( "Main-Class" ); if ( mainClassName == null ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "The Main-Class was not specified correctly in the jar file''s manifest file. Please make sure all required meta information is being set." ); /* no main class, no chance this will ever do something sensible, so stop here now */ } else { String classPath = attributes.getValue( "Class-Path" ); if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( "Jar Main-Class: " + mainClassName ); } URL[] classURLs; if ( ( classPath != null ) && ( !classPath.equals( "" ) ) ) { if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString("Jar Classpath: {0}", classPath ) ); } StringTokenizer st = new StringTokenizer( classPath, " \n\r" ); classURLs = new URL[st.countTokens() + 1]; // Store the main jar in the classpath. try { classURLs[0] = new URL( "file:" + file.getAbsolutePath() ); } catch ( MalformedURLException e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Unable to add jar to classpath: {0}", e ); } if ( !m_initFailed ) { if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString(" Classpath[0]=") + classURLs[0] ); } // Add any other jars in the manifest classpath relative to the location of the main jar. for ( int i = 1; st.hasMoreTokens(); i++ ) { String classEntry = st.nextToken(); try { classURLs[i] = new URL( "file:" + new File( parent, classEntry).getAbsolutePath() ); } catch ( MalformedURLException e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Malformed classpath in the jar''s manifest file {0} : {1}", args[0], e ); } if ( !m_initFailed ) { if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString(" Classpath[{0}]=", new Integer( i ) ) + classURLs[i] ); } } } } } else { if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "Jar Classpath: Not specified." ) ); } classURLs = new URL[1]; // Store the main jar in the classpath. try { classURLs[0] = new URL( "file:" + file.getAbsolutePath() ); } catch ( MalformedURLException e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Unable to add jar to classpath: {0}", e ); } if ( !m_initFailed ) { if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( " Classpath[0]=" ) + classURLs[0] ); } } } if ( !m_initFailed ) { URLClassLoader cl = URLClassLoader.newInstance( classURLs, this.getClass().getClassLoader() ); // Look for the specified class by name Class mainClass; try { mainClass = Class.forName( mainClassName, true, cl ); } catch ( ClassNotFoundException e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Unable to locate the class {0} : {1}", mainClassName, e ); mainClass = null; } catch ( ExceptionInInitializerError e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Class {0} found but could not be initialized due to:", mainClassName ); m_initException = e; mainClass = null; } catch ( LinkageError e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Class {0} found but could not be initialized: {1}", mainClassName, e ); mainClass = null; } if ( !m_initFailed ) { // Look for the main method try { // getDeclaredMethod will return any method named main in the specified class, // while getMethod will only return public methods, but it will search up the // inheritance path. m_mainMethod = mainClass.getMethod( "main", new Class[] { String[].class } ); } catch ( NoSuchMethodException e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Unable to locate a public static main method in class {0} : {1}", args[0] , e ); } catch ( SecurityException e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Unable to locate a public static main method in class {0} : {1}", args[0], e ); } if ( !m_initFailed ) { // Make sure that the method is public and static int modifiers = m_mainMethod.getModifiers(); if ( !( Modifier.isPublic( modifiers ) && Modifier.isStatic( modifiers ) ) ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "The main method in class {0} must be declared public and static.", args[0] ); } } } } } } } } // Strip the jar file name off of the args list. // This is assuming the jar file name was valid for now. appArgs = new String[args.length - 1]; System.arraycopy( args, 1, appArgs, 0, appArgs.length ); } // Start the application. If the JVM was launched from the native // Wrapper then the application will wait for the native Wrapper to // call the application's start method. Otherwise the start method // will be called immediately. WrapperManager.start( this, appArgs ); // This thread ends, the WrapperManager will start the application after the Wrapper has // been properly initialized by calling the start method above. } /*--------------------------------------------------------------- * Runnable Methods *-------------------------------------------------------------*/ /** * Used to launch the application in a separate thread. */ public void run() { // Notify the start method that the thread has been started by the JVM. synchronized( this ) { m_mainStarted = true; notifyAll(); } Throwable t = null; try { if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString("invoking main method" ) ); } try { m_mainMethod.invoke( null, new Object[] { m_appArgs } ); } finally { // Make sure the rest of this thread does not fall behind the application. Thread.currentThread().setPriority( Thread.MAX_PRIORITY ); } if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString("main method completed" ) ); } synchronized(this) { // Let the start() method know that the main method returned, in case it is // still waiting. m_mainComplete = true; this.notifyAll(); } return; } catch ( IllegalAccessException e ) { t = e; } catch ( IllegalArgumentException e ) { t = e; } catch ( InvocationTargetException e ) { t = e.getTargetException(); if ( t == null ) { t = e; } } // If we get here, then an error was thrown. If this happened quickly // enough, the start method should be allowed to shut things down. m_outInfo.println(); m_outError.println( WrapperManager.getRes().getString( "Encountered an error running main:" ) ); // We should print a stack trace here, because in the case of an // InvocationTargetException, the user needs to know what exception // their app threw. t.printStackTrace( m_outError ); synchronized( this ) { if ( m_ignoreMainExceptions ) { if ( !m_startComplete ) { // An exception was thrown, but we want to let the application continue. m_mainComplete = true; this.notifyAll(); } return; } else { if ( m_startComplete ) { // Shut down here. WrapperManager.stop( 1 ); return; // Will not get here. } else { // Let start method handle shutdown. m_mainComplete = true; m_mainExitCode = new Integer( 1 ); this.notifyAll(); return; } } } } /*--------------------------------------------------------------- * WrapperListener Methods *-------------------------------------------------------------*/ /** * The start method is called when the WrapperManager is signaled by the * native wrapper code that it can start its application. This * method call is expected to return, so a new thread should be launched * if necessary. * If there are any problems, then an Integer should be returned, set to * the desired exit code. If the application should continue, * return null. */ public Integer start( String[] args ) { // See if there were any startup problems. if ( m_initFailed ) { if ( m_initError != null ) { m_outError.println( m_initError ); } if ( m_initException != null ) { m_initException.printStackTrace( m_outError ); } if ( m_initShowUsage ) { showUsage(); } return new Integer( 1 ); } // Decide whether or not to wait for the start main method to complete before returning. boolean waitForStartMain = WrapperSystemPropertyUtil.getBooleanProperty( WrapperJarApp.class.getName() + ".waitForStartMain", false ); m_ignoreMainExceptions = WrapperSystemPropertyUtil.getBooleanProperty( WrapperJarApp.class.getName() + ".ignoreMainExceptions", false ); int maxStartMainWait = WrapperSystemPropertyUtil.getIntProperty( WrapperJarApp.class.getName() + ".maxStartMainWait", 2 ); maxStartMainWait = Math.max( 1, maxStartMainWait ); // Decide the maximum number of times to loop waiting for the main start method. int maxLoops; if ( waitForStartMain ) { maxLoops = Integer.MAX_VALUE; if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "start(args) Will wait indefinitely for the main method to complete." ) ); } } else { maxLoops = maxStartMainWait; // 1s loops. if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "start(args) Will wait up to {0} seconds for the main method to complete.", new Integer( maxLoops ) ) ); } } Thread mainThread = new Thread( this, "WrapperJarAppMain" ); synchronized(this) { m_appArgs = args; mainThread.start(); // Make sure the rest of this thread does not fall behind the application. Thread.currentThread().setPriority( Thread.MAX_PRIORITY ); // To avoid problems with the main thread starting slowly on heavily loaded systems, // do not continue until the thread has actually started. while ( !m_mainStarted ) { try { this.wait( 1000 ); } catch ( InterruptedException e ) { // Continue. } } // Wait for startup main method to complete. int loops = 0; while ( ( loops < maxLoops ) && ( !m_mainComplete ) ) { try { this.wait( 1000 ); } catch ( InterruptedException e ) { // Continue. } if ( !m_mainComplete ) { // If maxLoops is large then this could take a while. Notify the // WrapperManager that we are still starting so it doesn't give up. WrapperManager.signalStarting( 5000 ); } loops++; } // Always set the flag stating that the start method completed. This is needed // so the run method can decide whether or not it needs to be responsible for // shutting down the JVM in the event of an exception thrown by the start main // method. m_startComplete = true; // The main exit code will be null unless an error was thrown by the start // main method. if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "start(args) end. Main Completed={0}, exitCode={1}", new Boolean( m_mainComplete ), m_mainExitCode ) ); } return m_mainExitCode; } } /** * Called when the application is shutting down. */ public int stop( int exitCode ) { if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( "stop(" + exitCode + ")" ); } // Normally an application will be asked to shutdown here. Standard Java applications do // not have shutdown hooks, so do nothing here. It will be as if the user hit CTRL-C to // kill the application. return exitCode; } /** * Called whenever the native wrapper code traps a system control signal * against the Java process. It is up to the callback to take any actions * necessary. Possible values are: WrapperManager.WRAPPER_CTRL_C_EVENT, * WRAPPER_CTRL_CLOSE_EVENT, WRAPPER_CTRL_LOGOFF_EVENT, or * WRAPPER_CTRL_SHUTDOWN_EVENT */ public void controlEvent( int event ) { if ( ( event == WrapperManager.WRAPPER_CTRL_LOGOFF_EVENT ) && ( WrapperManager.isLaunchedAsService() || WrapperManager.isIgnoreUserLogoffs() ) ) { // Ignore m_outInfo.println( WrapperManager.getRes().getString("User logged out. Ignored." ) ); } else { if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println(WrapperManager.getRes().getString( "controlEvent({0}) Stopping", new Integer( event ) ) ); } WrapperManager.stop( 0 ); // Will not get here. } } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Displays application usage */ protected void showUsage() { // Show this output without headers. System.out.println(); System.out.println( WrapperManager.getRes().getString( "WrapperJarApp Usage:" ) ); System.out.println( WrapperManager.getRes().getString( " java org.tanukisoftware.wrapper.WrapperJarApp '{'jar_file'}' [app_arguments]" ) ); System.out.println(); System.out.println( WrapperManager.getRes().getString( "Where:" ) ); System.out.println( WrapperManager.getRes().getString( " jar_file: The jar file to run." ) ); System.out.println( WrapperManager.getRes().getString( " app_arguments: The arguments that would normally be passed to the" ) ); System.out.println( WrapperManager.getRes().getString( " application." ) ); } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ /** * Used to Wrapper enable a standard Java application. This main * expects the first argument to be the class name of the application * to launch. All remaining arguments will be wrapped into a new * argument list and passed to the main method of the specified * application. * * @param args Arguments passed to the application. */ public static void main( String args[] ) { new WrapperJarApp( args ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperLicenseError.java100644 0 0 1312 14333053652 25173 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ public class WrapperLicenseError extends Error { /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ WrapperLicenseError( String message ) { super( message ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperListener.java100644 0 0 17410 14333053652 24412 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ /** * Applications which need to be controlled directly as a service can implement * the WrapperListener interface and then register themselves with the * WrapperManager on instantiation. The WrapperManager will then control the * the class as a service for its entire life-cycle. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public interface WrapperListener { /** * The start method is called when the WrapperManager is signaled by the * native wrapper code that it can start its application. This * method call is expected to return, so a new thread should be launched * if necessary. *

* If this method throws an exception the Wrapper will shutdown the current * JVM in an error state and then relaunch a new JVM. It is the * responsibility of the user code to catch any exceptions and return an * appropriate exit code if the exception should result in the Wrapper * stopping. * * @param args List of arguments used to initialize the application. * * @return Any error code if the application should exit on completion * of the start method. If there were no problems then this * method should return null. */ Integer start( String[] args ); /** * Called when the application is shutting down. The Wrapper assumes that * this method will return fairly quickly. If the shutdown code * could potentially take a long time, then WrapperManager.signalStopping() * should be called to extend the timeout period. If for some reason, * the stop method can not return, then it must call * WrapperManager.stopped() to avoid warning messages from the Wrapper. *

* By default, the stop() method will only be called if the start() method * has completed and returned null. There are however cases in which the * stop method should be called on shutdown even if the start method has * not returned or returned an exit code. This functionality can be * enabled by setting the following property in the Wrapper configuration * file: wrapper.listener.force_stop=TRUE. *

* WARNING - Directly calling System.exit in this method will result in * a deadlock in cases where this method is called from within a shutdown * hook. This method will be invoked by a shutdown hook if the JVM * shutdown was originally initiated by a call to System.exit. * * @param exitCode The suggested exit code that will be returned to the OS * when the JVM exits. If WrapperManager.stop was called * to stop the JVM then this exit code will reflect that * value. However, if System.exit or Runtime.halt were * used then this exitCode will always be 0. In these * cases, the Wrapper process will be able to detect the * actual JVM exit code and handle it correctly. * * @return The exit code to actually return to the OS. In most cases, this * should just be the value of exitCode, however the user code has * the option of changing the exit code if there are any problems * during shutdown. */ int stop( int exitCode ); /** * Called whenever the native wrapper code traps a system control signal * against the Java process. It is up to the callback to take any actions * necessary. Possible values are: WrapperManager.WRAPPER_CTRL_C_EVENT, * WRAPPER_CTRL_CLOSE_EVENT, WRAPPER_CTRL_LOGOFF_EVENT, * WRAPPER_CTRL_SHUTDOWN_EVENT, WRAPPER_CTRL_TERM_EVENT, or * WRAPPER_CTRL_HUP_EVENT. *

* The WRAPPER_CTRL_C_EVENT will be called whether or not the JVM is * controlled by the Wrapper. If controlled by the Wrapper, it is * undetermined as to whether the Wrapper or the JVM will receive this * signal first, but the Wrapper will always initiate a shutdown. In * most cases, the implementation of this method should call * WrapperManager.stop() to initiate a shutdown from within the JVM. * The WrapperManager will always handle the shutdown correctly whether * shutdown is initiated from the Wrapper, within the JVM or both. * By calling stop here, it will ensure that the application will behave * correctly when run standalone, without the Wrapper. *

* WRAPPER_CTRL_CLOSE_EVENT, WRAPPER_CTRL_LOGOFF_EVENT, and * WRAPPER_CTRL_SHUTDOWN_EVENT events will only be encountered on Windows * systems. Like the WRAPPER_CTRL_C_EVENT event, it is undetermined as to * whether the Wrapper or JVM will receive the signal first. All signals * will be triggered by the OS whether the JVM is being run as an NT * service or as a console application. If the JVM is running as a * console application, the Application must respond to the CLOSE and * LOGOFF events by calling WrapperManager.stop() in a timely manner. * In these cases, Windows will wait for the JVM process to exit before * moving on to signal the next process. If the JVM process does not exit * within a reasonable amount of time, Windows will pop up a message box * for the user asking if they wish to wait for the process or exit or * forcibly close it. The JVM must call stop() in response to the * SHUTDOWN method whether running as a console or NT service. Usually, * the LOGOFF event should be ignored when the Wrapper is running as an * NT service. *

* WRAPPER_CTRL_TERM_EVENT events will only be encountered on UNIX systems. *

* If the wrapper.ignore_signals property is set to TRUE then any * WRAPPER_CTRL_C_EVENT, WRAPPER_CTRL_CLOSE_EVENT, * WRAPPER_CTRL_TERM_EVENT, or WRAPPER_CTRL_HUP_EVENT * events will be blocked prior to this method being called. *

* Unless you know what you are doing, it is suggested that the body of * this method contain the following code, or its functional equivalent. *

     *   public void controlEvent( int event )
     *   {
     *       if ( ( event == WrapperManager.WRAPPER_CTRL_LOGOFF_EVENT )
     *           && ( WrapperManager.isLaunchedAsService() || WrapperManager.isIgnoreUserLogoffs() ) )
     *       {
     *           // Ignore
     *       }
     *       else
     *       {
     *           WrapperManager.stop( 0 );
     *       }
     *   }
     * 
* * @param event The system control signal. */ void controlEvent( int event ); } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperManager.java100644 0 0 1004112 14333053652 24233 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.InterruptedIOException; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.BindException; import java.net.ConnectException; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketException; import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.net.URL; import java.security.CodeSource; import java.security.AccessControlException; import java.security.AccessController; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivilegedAction; import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.StringTokenizer; import java.util.ResourceBundle; import java.util.Locale; import java.util.MissingResourceException; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.tanukisoftware.wrapper.event.WrapperControlEvent; import org.tanukisoftware.wrapper.event.WrapperEvent; import org.tanukisoftware.wrapper.event.WrapperEventListener; import org.tanukisoftware.wrapper.event.WrapperLogFileChangedEvent; import org.tanukisoftware.wrapper.event.WrapperSecondInvocationEvent; import org.tanukisoftware.wrapper.event.WrapperPingEvent; import org.tanukisoftware.wrapper.event.WrapperServiceActionEvent; import org.tanukisoftware.wrapper.event.WrapperServiceControlEvent; import org.tanukisoftware.wrapper.event.WrapperServicePauseEvent; import org.tanukisoftware.wrapper.event.WrapperServiceResumeEvent; import org.tanukisoftware.wrapper.event.WrapperTickEvent; import org.tanukisoftware.wrapper.security.WrapperEventPermission; import org.tanukisoftware.wrapper.security.WrapperPermission; import org.tanukisoftware.wrapper.security.WrapperServicePermission; import org.tanukisoftware.wrapper.security.WrapperUserEventPermission; /** * Handles all communication with the native portion of the Wrapper code. * Communication takes place either over a Pipe, or Socket. In either * case, the Wrapper code will initializate its end of the communication * and then launch Java in a separate process. * * In the case of a socket, the Wrapper will set up a server socket which * the Java code is expected to open a socket to on startup. When the * server socket is created, a port will be chosen depending on what is * available to the system. This port will then be passed to the Java * process as property named "wrapper.port". * * In the case of a pipe, the Wrapper will set up a pair of named ports * and then pass in a property named "wrapper.backend=pipe". The Wrapper * and JVM will communicate via the pipes. * * For security reasons, the native code will only allow connections from * localhost and will expect to receive the key specified in a property * named "wrapper.key". * * When the backend connection is a socket, it does not have any timeout * set by default. For testing purposes it is possible to set a timeout * in seconds using the following system property when launching the JVM. * This will only be noticeable if it is shorter than the ping interval, * and debug output is enabled: * -Dwrapper.backend.so_timeout=300 * * This class is implemented as a singleton class. * * Generate JNI Headers with the following command in the build/classes * directory: * javah -jni -classpath ./ org.tanukisoftware.wrapper.WrapperManager * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public final class WrapperManager implements Runnable { private static final String WRAPPER_CONNECTION_THREAD_NAME = "Wrapper-Connection"; private static final int DEFAULT_PORT = 15003; private static final int DEFAULT_CPU_TIMEOUT = 10000; /** The number of milliseconds in one tick. Used for internal system * time independent time keeping. */ private static final int TICK_MS = 100; private static final int TIMER_FAST_THRESHOLD = 2 * 24 * 3600 * 1000 / TICK_MS; // 2 days. private static final int TIMER_SLOW_THRESHOLD = 2 * 24 * 3600 * 1000 / TICK_MS; // 2 days. /** * Backend server can be of 3 types: socket IPv4, socket IPv6 or pipe */ private static final int BACKEND_TYPE_UNKNOWN = 0; private static final int BACKEND_TYPE_SOCKET_V4 = 0x01; private static final int BACKEND_TYPE_SOCKET_V6 = 0x02; private static final int BACKEND_TYPE_PIPE = 0x04; private static final byte WRAPPER_MSG_START = (byte)100; private static final byte WRAPPER_MSG_STOP = (byte)101; private static final byte WRAPPER_MSG_RESTART = (byte)102; private static final byte WRAPPER_MSG_PING = (byte)103; private static final byte WRAPPER_MSG_STOP_PENDING = (byte)104; private static final byte WRAPPER_MSG_START_PENDING = (byte)105; private static final byte WRAPPER_MSG_STARTED = (byte)106; private static final byte WRAPPER_MSG_STOPPED = (byte)107; private static final byte WRAPPER_MSG_JAVA_PID = (byte)108; private static final byte WRAPPER_MSG_KEY = (byte)110; private static final byte WRAPPER_MSG_BADKEY = (byte)111; private static final byte WRAPPER_MSG_LOW_LOG_LEVEL = (byte)112; private static final byte WRAPPER_MSG_PING_TIMEOUT = (byte)113; /* No longer used. */ private static final byte WRAPPER_MSG_SERVICE_CONTROL_CODE = (byte)114; private static final byte WRAPPER_MSG_PROPERTIES = (byte)115; /** Log commands are actually 116 + the LOG LEVEL. */ private static final byte WRAPPER_MSG_LOG = (byte)116; private static final byte WRAPPER_MSG_CHILD_LAUNCH = (byte)132; private static final byte WRAPPER_MSG_CHILD_TERM = (byte)133; private static final byte WRAPPER_MSG_LOGFILE = (byte)134; private static final byte WRAPPER_MSG_CHECK_DEADLOCK = (byte)135; private static final byte WRAPPER_MSG_DEADLOCK = (byte)136; private static final byte WRAPPER_MSG_APPEAR_ORPHAN = (byte)137; /* No longer used. */ private static final byte WRAPPER_MSG_PAUSE = (byte)138; private static final byte WRAPPER_MSG_RESUME = (byte)139; private static final byte WRAPPER_MSG_GC = (byte)140; private static final byte WRAPPER_MSG_FIRE_USER_EVENT= (byte)141; private static final byte WRAPPER_MSG_SECOND_INVOCATION_EVENT = (byte)142; private static final byte WRAPPER_MSG_FIRE_CTRL_EVENT = (byte)143; private static final byte WRAPPER_MSG_SUSPEND_TIMEOUTS= (byte)144; private static final byte WRAPPER_MSG_RESUME_TIMEOUTS = (byte)145; /** Received when the user presses CTRL-C in the console on Windows or UNIX platforms. */ public static final int WRAPPER_CTRL_C_EVENT = 200; /** Received when the user clicks on the close button of a Console on Windows. */ public static final int WRAPPER_CTRL_CLOSE_EVENT = 201; /** Received when the user logs off of a Windows system. */ public static final int WRAPPER_CTRL_LOGOFF_EVENT = 202; /** Received when a Windows system is shutting down. */ public static final int WRAPPER_CTRL_SHUTDOWN_EVENT = 203; /** Received when a SIG TERM is received on a UNIX system. */ public static final int WRAPPER_CTRL_TERM_EVENT = 204; /** Received when a SIG HUP is received on a UNIX system. */ public static final int WRAPPER_CTRL_HUP_EVENT = 205; /** Received when a SIG USR1 is received on a UNIX system. */ public static final int WRAPPER_CTRL_USR1_EVENT = 206; /** Received when a SIG USR2 is received on a UNIX system. */ public static final int WRAPPER_CTRL_USR2_EVENT = 207; /** Log message at debug log level. */ public static final int WRAPPER_LOG_LEVEL_DEBUG = 1; /** Log message at info log level. */ public static final int WRAPPER_LOG_LEVEL_INFO = 2; /** Log message at status log level. */ public static final int WRAPPER_LOG_LEVEL_STATUS = 3; /** Log message at warn log level. */ public static final int WRAPPER_LOG_LEVEL_WARN = 4; /** Log message at error log level. */ public static final int WRAPPER_LOG_LEVEL_ERROR = 5; /** Log message at fatal log level. */ public static final int WRAPPER_LOG_LEVEL_FATAL = 6; /** Log message at advice log level. */ public static final int WRAPPER_LOG_LEVEL_ADVICE = 7; /** Log message at notice log level. */ public static final int WRAPPER_LOG_LEVEL_NOTICE = 8; /** Service Control code which can be sent to start a service. */ public static final int SERVICE_CONTROL_CODE_START = 0x10000; /** Service Control code which can be sent or received to stop a service. */ public static final int SERVICE_CONTROL_CODE_STOP = 1; /** Service Control code which can be sent to pause a service. */ public static final int SERVICE_CONTROL_CODE_PAUSE = 2; /** Service Control code which can be sent to resume a paused service. */ public static final int SERVICE_CONTROL_CODE_CONTINUE = 3; /** Service Control code which can be sent to or received interrogate the status of a service. */ public static final int SERVICE_CONTROL_CODE_INTERROGATE = 4; /** Service Control code which can be received when the system is shutting down. */ public static final int SERVICE_CONTROL_CODE_SHUTDOWN = 5; /** Service Control code which is received when the system being suspended. */ public static final int SERVICE_CONTROL_CODE_POWEREVENT_QUERYSUSPEND = 0x0D00; /** Service Control code which is received when permission to suspend the * computer was denied by a process. Support for this event was removed * from the Windows OS starting with Vista.*/ public static final int SERVICE_CONTROL_CODE_POWEREVENT_QUERYSUSPENDFAILED = 0x0D02; /** Service Control code which is received when the computer is about to * enter a suspended state. */ public static final int SERVICE_CONTROL_CODE_POWEREVENT_SUSPEND = 0x0D04; /** Service Control code which is received when the system has resumed * operation. This event can indicate that some or all applications did * not receive a SERVICE_CONTROL_CODE_POWEREVENT_SUSPEND event. * Support for this event was removed from the Windows OS starting with * Vista. See SERVICE_CONTROL_CODE_POWEREVENT_RESUMEAUTOMATIC. */ public static final int SERVICE_CONTROL_CODE_POWEREVENT_RESUMECRITICAL = 0x0D06; /** Service Control code which is received when the system has resumed * operation after being suspended. */ public static final int SERVICE_CONTROL_CODE_POWEREVENT_RESUMESUSPEND = 0x0D07; /** Service Control code which is received when the battery power is low. * Support for this event was removed from the Windows OS starting with * Vista. See SERVICE_CONTROL_CODE_POWEREVENT_POWERSTATUSCHANGE. */ public static final int SERVICE_CONTROL_CODE_POWEREVENT_BATTERYLOW = 0x0D09; /** Service Control code which is received when there is a change in the * power status of the computer, such as a switch from battery power to * A/C. The system also broadcasts this event when remaining battery * power slips below the threshold specified by the user or if the * battery power changes by a specified percentage. */ public static final int SERVICE_CONTROL_CODE_POWEREVENT_POWERSTATUSCHANGE = 0x0D0A; /** Service Control code which is received when the APM BIOS has signaled * an APM OEM event. Support for this event was removed from the Windows * OS starting with Vista. */ public static final int SERVICE_CONTROL_CODE_POWEREVENT_OEMEVENT = 0x0D0B; /** Service Control code which is received when the computer has woken up * automatically to handle an event. */ public static final int SERVICE_CONTROL_CODE_POWEREVENT_RESUMEAUTOMATIC = 0x0D12; /** Reference to the original value of System.out. */ private static PrintStream m_out; /** Reference to the original value of System.err. */ private static PrintStream m_err; /** Info level log channel */ private static WrapperPrintStream m_outInfo; /** Error level log channel */ private static WrapperPrintStream m_outError; /** Debug level log channel */ private static WrapperPrintStream m_outDebug; /** Stores the resolved OS used in naming of native library. */ private static String m_os; /** Stores the resolved Architecture used in naming of native library. */ private static String m_arch; /** Flag to remember whether or not this is Windows. */ private static boolean m_windows = false; /** Flag to remember whether or not this is MacOSX. */ private static boolean m_macosx = false; /** Flag to remember whether or not this is AIX. */ private static boolean m_aix = false; /** Flag to remember whether or not this is z/OS. */ private static boolean m_zos = false; /** Flag that will be set to true once a SecurityManager has been detected and tested. */ private static boolean m_securityManagerChecked = false; private static boolean m_disposed = false; /** The starting flag is set when the Application has been asked to start. */ private static boolean m_starting = false; /** The started flag is set when the Application has completed its startup. */ private static boolean m_started = false; private static WrapperManager m_instance = null; private static Thread m_hook = null; private static boolean m_hookTriggered = false; private static boolean m_hookRemoveFailed = false; /* Flag which records when the shutdownJVM method has completed. */ private static boolean m_shutdownJVMComplete = false; /** Map which stores shutdown locks for each thread. */ private static Map m_shutdownLockMap = new HashMap(); /** Tracks the total number of outstanding shutdown locks. */ private static int m_shutdownLocks = 0; /** Tracks the number of threads which are attempting to launch child processes. */ private static int m_runningExecs = 0; private static String[] m_args; private static int m_backendType = BACKEND_TYPE_UNKNOWN; private static boolean m_backendConnected = false; private static OutputStream m_backendOS = null; private static InputStream m_backendIS = null; private static int m_port = DEFAULT_PORT; private static int m_jvmPort; private static int m_jvmPortMin; private static int m_jvmPortMax; private static String m_wrapperPortAddress = null; private static String m_key; private static int m_soTimeout = -1; private static long m_cpuTimeout = DEFAULT_CPU_TIMEOUT; /** Tick count when the start method completed. */ private static int m_startedTicks; /** The lowest configured log level in the Wrapper's configuration. This * is set to a high value by default to disable all logging if the * Wrapper does not register its low level or is not present. */ private static int m_lowLogLevel = WRAPPER_LOG_LEVEL_NOTICE + 1; /** Flag, set when the JVM is launched that is used to remember whether * or not system signals are supposed to be ignored. */ private static boolean m_ignoreSignals = false; /** Flag which controls whether the Wrapper process is expected to close the * connection after the STARTED packet is sent. */ private static boolean m_detachStarted = false; /** Thread which processes all communications with the native code. */ private static Thread m_commRunner; private static boolean m_commRunnerStarted = false; private static Thread m_eventRunner; private static int m_eventRunnerTicks; private static Thread m_startupRunner; /** True if the system time should be used for internal timeouts. */ private static boolean m_useSystemTime; /** The threashold of how many ticks the timer can be fast before a * warning is displayed. */ private static int m_timerFastThreshold; /** The threashold of how many ticks the timer can be slow before a * warning is displayed. */ private static int m_timerSlowThreshold; /** Flag which controls whether or not the test methods are disabled. */ private static boolean m_disableTests; /** Flag which controls whether or not the WrapperListener.stop method will * be called on shutdown when the WrapperListener.start method has not * returned or returned an exit code. */ private static boolean m_listenerForceStop; /** * Bit depth of the currently running JVM. Will be 32 or 64. * A 64-bit JVM means that the system is also 64-bit, but a 32-bit JVM * can be run either on a 32 or 64-bit system. */ private static int m_jvmBits; /** An integer which stores the number of ticks since the * JVM was launched. Using an int rather than a long allows the value * to be used without requiring any synchronization. This is only * used if the m_useSystemTime flag is false. */ private static volatile int m_ticks; private static WrapperListener m_listener; private static int m_lastPingTicks; private static Socket m_backendSocket; private static boolean m_appearHung = false; private static int m_slowSeconds = 0; private static boolean m_ignoreUserLogoffs = false; private static boolean m_service = false; private static boolean m_debug = false; private static boolean m_logFinalizer = false; private static int m_jvmId = 0; /** Flag set when any thread initiates a stop or restart. */ private static boolean m_stoppingInit = false; /** Flag set when the thread that will be in charge of actually stopping has been fixed. */ private static boolean m_stopping = false; /** Thread that is in charge of stopping. */ private static Thread m_stoppingThread; /** Flag set when the the thread which is charge of stopping has completed. The Wrapper JNI code could then become invalid at any time and should no longer be trusted. */ private static boolean m_stopped = false; /** If set then this message will be sent as a STOP message as soon as we connect to the Wrapper. */ private static String m_pendingStopMessage = null; private static int m_exitCode; private static boolean m_libraryOK = false; private static boolean m_libraryLoaded = false; private static boolean m_libraryVersionOk = false; private static boolean m_wrapperVersionOk = false; private static byte[] m_commandBuffer = new byte[512]; private static File m_logFile = null; /** The contents of the wrapper configuration. */ private static WrapperProperties m_properties; /** List of registered WrapperEventListeners and their registered masks. */ private static List m_wrapperEventListenerMaskList = new ArrayList(); /** Array of registered WrapperEventListeners and their registered masks. * Should not be referenced directly. Access by calling * getWrapperEventListenerMasks(). */ private static WrapperEventListenerMask[] m_wrapperEventListenerMasks = null; /** Flag used to tell whether or not WrapperCoreEvents should be produced. */ private static boolean m_produceCoreEvents = false; // message resources: eventually these will be split up private static WrapperResources m_res; /** Set on startup to show whether or not a Professional edition library is being used. * Overriding this makes no difference as the underlying library also contains these checks. * Caching this value makes it possible to reduce the number of JNI calls and avoid confusing * errors on shutdown. */ private static boolean m_professionalEdition; /** Set on startup to show whether or not a Standard edition library is being used. * Overriding this makes no difference as the underlying library also contains these checks. * Caching this value makes it possible to reduce the number of JNI calls and avoid confusing * errors on shutdown. */ private static boolean m_standardEdition; /** * Returns the WrapperResources object which is used to manage all resources for * the Java Service Wrapper. * * @return the Wrapper's resouces. */ public static WrapperResources getRes() { return m_res; } /*--------------------------------------------------------------- * Class Initializer *-------------------------------------------------------------*/ /** * When the WrapperManager class is first loaded, it attempts to load the * configuration file specified using the 'wrapper.config' system property. * When the JVM is launched from the Wrapper native code, the * 'wrapper.config' and 'wrapper.key' parameters are specified. * The 'wrapper.key' parameter is a password which is used to verify that * connections are only coming from the native Wrapper which launched the * current JVM. */ static { // The wraper.jar must be given AllPermissions if a security manager // has been configured. This is not a problem if one of the standard // Wrapper helper classes is used to launch the JVM. // If however a custom WrapperListener is being implemented then this // class will most likely be loaded by code that is neither part of // the system, nor part of the Wrapper code base. To avoid having // to also give those classes AllPermissions as well, we do all of // initialization in a Privileged block. This means that the code // only requires that the wrapper.jar has been given the required // permissions. AccessController.doPrivileged( new PrivilegedAction() { public Object run() { privilegedClassInit(); return null; } } ); } /** * Logs information about the package of the specified class. * * @param clazz Class to log. */ private static void logPackageInfo( Class clazz ) { if ( m_debug ) { Package pkg = WrapperManager.class.getPackage(); if ( pkg == null ) { m_outDebug.println( getRes().getString( "{0} package not found.", clazz.getName() ) ); } else { m_outDebug.println( getRes().getString( "{0} package information:", clazz.getName() ) ); m_outDebug.println( getRes().getString( " Implementation Title: {0}", pkg.getImplementationTitle() ) ); m_outDebug.println( getRes().getString( " Implementation Vendor: {0}", pkg.getImplementationVendor() ) ); m_outDebug.println( getRes().getString( " Implementation Version: {0}", pkg.getImplementationVersion() ) ); m_outDebug.println( getRes().getString( " Is Sealed?: {0}", pkg.isSealed() ? getRes().getString( "True" ) : getRes().getString( "False" ) ) ); } ProtectionDomain proDom = clazz.getProtectionDomain(); m_outDebug.println( getRes().getString( "{0} protection domain:", clazz.getName() ) ); CodeSource codeSource = proDom.getCodeSource(); URL jarLocation = codeSource.getLocation(); m_outDebug.println( getRes().getString( " Location: {0}", jarLocation ) ); // Try reading in the full jar so we can calculate its size and MD5 hash. try { InputStream is = jarLocation.openStream(); try { int jarSize = 0; MessageDigest md = MessageDigest.getInstance( "MD5" ); int data; while ( ( data = is.read() ) >= 0 ) { jarSize++; md.update( (byte)( data & 0xff ) ); } m_outDebug.println( getRes().getString( " Size: {0}", new Integer( jarSize ) ) ); byte[] bytes = md.digest(); StringBuffer sb = new StringBuffer(); for ( int i = 0; i < bytes.length; i++ ) { String val = Integer.toString( bytes[i] & 0xff, 16 ).toLowerCase(); if ( val.length() == 1 ) { sb.append( "0" ); } sb.append( val ); } m_outDebug.println( getRes().getString( " MD5: {0}" , sb ) ); } finally { is.close(); } } catch ( NoSuchAlgorithmException e ) { m_outDebug.println( getRes().getString( " Unable to calculate MD5: {0}", e ) ); } catch ( IOException e ) { m_outDebug.println( getRes().getString( " Unable to access location: {0}", e ) ); } } } /** * The body of the static initializer is moved into a seperate method so * it can be run as a PrivilegedAction. */ private static void privilegedClassInit() { // Store references to the original System.out and System.err // PrintStreams. The WrapperManager will always output to the // original streams so its output will always end up in the // wrapper.log file even if the end user code redirects the // output to another log file. // This is also important to be protect the Wrapper's functionality // from the case where the user PrintStream enters a deadlock state. m_out = System.out; m_err = System.err; // Set up some log channels boolean streamsSet = false; if ( "true".equals( System.getProperty( "wrapper.use_sun_encoding" ) ) ) { String sunStdoutEncoding = System.getProperty( "sun.stdout.encoding" ); if ( ( sunStdoutEncoding != null ) && !sunStdoutEncoding.equals( System.getProperty( "file.encoding" ) ) ) { // We need to create the stream using the same encoding as the one used for stdout, else this will lead to encoding issues. try { m_outInfo = new WrapperPrintStream( m_out, false, sunStdoutEncoding, "WrapperManager: " ); m_outError = new WrapperPrintStream( m_out, false, sunStdoutEncoding, "WrapperManager Error: " ); m_outDebug = new WrapperPrintStream( m_out, false, sunStdoutEncoding, "WrapperManager Debug: " ); streamsSet = true; } catch ( UnsupportedEncodingException e ) { // This should not happen because we always make sure the encoding exists before launching a JVM. // If any of the above streams failed, we want to fall back to streams that use the same encoding. System.out.println( "Failed to set the encoding '" + sunStdoutEncoding + "' when creating a WrapperPrintStream.\n Make sure the value of sun.stdout.encoding is correct." ); } } } if ( !streamsSet ) { m_outInfo = new WrapperPrintStream( m_out, "WrapperManager: " ); m_outError = new WrapperPrintStream( m_out, "WrapperManager Error: " ); m_outDebug = new WrapperPrintStream( m_out, "WrapperManager Debug: " ); } // Always create an empty properties object in case we are not running // in the Wrapper or the properties are never sent. m_properties = new WrapperProperties(); m_properties.lock(); // Create a dummy resources file so initial localization will work until the native library is loaded. m_res = new WrapperResources(); // This must be done before attempting to access any System Properties // as that could cause a SecurityException if it is too strict. checkSecurityManager(); // Check for the debug flag m_debug = WrapperSystemPropertyUtil.getBooleanProperty( "wrapper.debug", false ); if ( m_debug ) { // This message is logged when localization is not yet initialized. m_outDebug.println( "WrapperManager class initialized by thread: " + Thread.currentThread().getName() + " Using classloader: " + WrapperManager.class.getClassLoader().toString() ); } // The copyright banner was moved into the wrapper binary. In order to // aid in the debugging of user integrations, some kind of a known message // needs to be displayed on startup so it is obvious whether or not the // WrapperManager class is being initialized. // This message is logged when localization is not yet initialized. m_outInfo.println( "Initializing..." ); // We need to get the key before the version can be verified. m_key = System.getProperty( "wrapper.key" ); // Store the log finalizer flag. m_logFinalizer = WrapperSystemPropertyUtil.getBooleanProperty( "wrapper.logFinalizers", false ); // Check for the jvmID m_jvmId = WrapperSystemPropertyUtil.getIntProperty( "wrapper.jvmid", 1 ); if ( m_debug ) { // This message is logged when localization is not yet initialized. m_outDebug.println( "JVM #" + m_jvmId ); } // Decide whether this is a 32 or 64 bit version of Java. m_jvmBits = WrapperSystemPropertyUtil.getIntProperty( "sun.arch.data.model", -1 ); if ( m_jvmBits == -1 ) { m_jvmBits = WrapperSystemPropertyUtil.getIntProperty( "com.ibm.vm.bitmode", -1 ); } if ( m_debug ) { if ( m_jvmBits > 0 ) { // This message is logged when localization is not yet initialized. m_outDebug.println( "Running a " + m_jvmBits + "-bit JVM." ); } else { // This message is logged when localization is not yet initialized. m_outDebug.println( "The bit depth of this JVM could not be determined." ); } } // Log information about the Wrapper's package. logPackageInfo( WrapperManager.class ); // Get the detachStarted flag. m_detachStarted = WrapperSystemPropertyUtil.getBooleanProperty( "wrapper.detachStarted", false ); // Initialize the timerTicks to a very high value. This means that we will // always encounter the first rollover (200 * WRAPPER_MS / 1000) seconds // after the Wrapper the starts, which means the rollover will be well // tested. m_ticks = Integer.MAX_VALUE - 200; m_useSystemTime = WrapperSystemPropertyUtil.getBooleanProperty( "wrapper.use_system_time", false ); m_timerFastThreshold = WrapperSystemPropertyUtil.getIntProperty( "wrapper.timer_fast_threshold", TIMER_FAST_THRESHOLD ) * 1000 / TICK_MS; m_timerSlowThreshold = WrapperSystemPropertyUtil.getIntProperty( "wrapper.timer_slow_threshold", TIMER_SLOW_THRESHOLD ) * 1000 / TICK_MS; // Check to see if we should disable test methods m_disableTests = WrapperSystemPropertyUtil.getBooleanProperty( "wrapper.disable_tests", false ); // Check to see if we should register a shutdown hook boolean disableShutdownHook = WrapperSystemPropertyUtil.getBooleanProperty( "wrapper.disable_shutdown_hook", false ); // Check to see if the listener stop method should always be called. m_listenerForceStop = WrapperSystemPropertyUtil.getBooleanProperty( "wrapper.listener.force_stop", false ); // Make it possible for a user to set the SO_TIMEOUT of the backend. Mainly for testing. m_soTimeout = WrapperSystemPropertyUtil.getIntProperty( "wrapper.backend.so_timeout", -1 ) * 1000; // If the shutdown hook is not disabled, then register it. if ( !disableShutdownHook ) { if ( m_debug ) { // This message is logged when localization is not yet initialized. m_outDebug.println( "Registering shutdown hook" ); } m_hook = new Thread( "Wrapper-Shutdown-Hook" ) { /** * Run the shutdown hook. (Triggered by the JVM when it is about to shutdown) */ public void run() { // Stop the Wrapper cleanly. m_hookTriggered = true; if ( m_debug ) { m_outDebug.println( getRes().getString( "ShutdownHook started" ) ); } // Let the startup thread die since the shutdown hook is running. m_startupRunner = null; // If we are not already stopping, then do so. WrapperManager.stop( 0 ); if ( m_debug ) { m_outDebug.println( getRes().getString( "ShutdownHook complete" ) ); } // Really all done. if ( m_debug ) { m_outDebug.println( getRes().getString( "WrapperManager stopped due to {0}", getRes().getString( "Shutdown Hook" ) ) ); } m_stopped = true; } }; // Register the shutdown hook. Runtime.getRuntime().addShutdownHook( m_hook ); } // Initialize connection values. m_backendType = BACKEND_TYPE_UNKNOWN; m_port = 0; m_jvmPort = 0; m_jvmPortMin = 0; m_jvmPortMax = 0; // A key is required for the wrapper to work correctly. If it is not // present, then assume that we are not being controlled by the native // wrapper. if ( m_key == null ) { if ( m_debug ) { // This message is logged when localization is not yet initialized. m_outDebug.println( "Not using the Wrapper. (key not specified)" ); } // The wrapper will not be used, so other values will not be used. m_service = false; m_cpuTimeout = 31557600000L; // One Year. Effectively never. } else { if ( m_debug ) { m_outDebug.println( getRes().getString( "Using the Wrapper" ) ); } if ( WrapperSystemPropertyUtil.getBooleanProperty( "wrapper.disable_console_input", false ) ) { // Replace the System.in stream with one of our own to disable it. System.setIn( new WrapperInputStream() ); } // Decide what the backend connection type is String backendType = WrapperSystemPropertyUtil.getStringProperty( "wrapper.backend", "SOCKET" ); if ( backendType.equalsIgnoreCase( "PIPE" ) ) { // Pipe based communication m_backendType = BACKEND_TYPE_PIPE; } else { // Socket based communication if (backendType.equalsIgnoreCase( "SOCKET" ) ) { m_backendType = BACKEND_TYPE_SOCKET_V4; } else{ m_backendType = BACKEND_TYPE_SOCKET_V6; } // An address may not have been specified. if ( ( m_wrapperPortAddress = System.getProperty( "wrapper.port.address" ) ) == null ) { /* Use the default loopback */ if (m_backendType == BACKEND_TYPE_SOCKET_V4) { m_wrapperPortAddress = "127.0.0.1"; } else { m_wrapperPortAddress = "::1"; } } String sPort; if ( ( sPort = System.getProperty( "wrapper.port" ) ) == null ) { // This message is logged when localization is not yet initialized. String msg = "The 'wrapper.port' system property was not set."; m_outError.println( msg ); throw new ExceptionInInitializerError( msg ); } try { m_port = Integer.parseInt( sPort ); } catch ( NumberFormatException e ) { // This message is logged when localization is not yet initialized. String msg = "'" + sPort + "' is not a valid value for 'wrapper.port'."; m_outError.println( msg ); throw new ExceptionInInitializerError( msg ); } m_jvmPort = WrapperSystemPropertyUtil.getIntProperty( "wrapper.jvm.port", -1 ); m_jvmPortMin = WrapperSystemPropertyUtil.getIntProperty( "wrapper.jvm.port.min", 31000 ); m_jvmPortMax = WrapperSystemPropertyUtil.getIntProperty( "wrapper.jvm.port.max", 31999 ); } // Check for the ignore signals flag m_ignoreSignals = WrapperSystemPropertyUtil.getBooleanProperty( "wrapper.ignore_signals", false ); // If this is being run as a headless server, then a flag would have been set m_service = WrapperSystemPropertyUtil.getBooleanProperty( "wrapper.service", false ); // Get the cpuTimeout String sCPUTimeout = System.getProperty( "wrapper.cpu.timeout" ); if ( sCPUTimeout == null ) { m_cpuTimeout = DEFAULT_CPU_TIMEOUT; } else { try { m_cpuTimeout = Integer.parseInt( sCPUTimeout ) * 1000L; } catch ( NumberFormatException e ) { // This message is logged when localization is not yet initialized. String msg = "'" + sCPUTimeout + "' is not a valid value for 'wrapper.cpu.timeout'."; m_outError.println( msg ); throw new ExceptionInInitializerError( msg ); } } } // Register the MBeans if configured to do so. if ( WrapperSystemPropertyUtil.getBooleanProperty( WrapperManager.class.getName() + ".mbean", true ) ) { registerMBean( new org.tanukisoftware.wrapper.jmx.WrapperManager(), "org.tanukisoftware.wrapper:type=WrapperManager" ); } if ( WrapperSystemPropertyUtil.getBooleanProperty( WrapperManager.class.getName() + ".mbean.testing", false ) ) { registerMBean( new org.tanukisoftware.wrapper.jmx.WrapperManagerTesting(), "org.tanukisoftware.wrapper:type=WrapperManagerTesting" ); } // Initialize the native code to trap system signals initializeNativeLibrary(); if ( isNativeLibraryOk() ) { // Get the PID of the current JVM from the native library. Be careful as the method // will not exist if the library is old. try { System.setProperty( "wrapper.java.pid", Integer.toString( nativeGetJavaPID() ) ); } catch ( Throwable e ) { if ( m_debug ) { m_outDebug.println( getRes().getString( "Call to nativeGetJavaPID() failed: {0}", e ) ); } } } // Start a thread which looks for control events sent to the // process. The thread is also used to keep track of whether // the VM has been getting CPU to avoid invalid timeouts and // to maintain the number of ticks since the JVM was launched. m_eventRunnerTicks = getTicks(); m_eventRunner = new Thread( "Wrapper-Control-Event-Monitor" ) { public void run() { if ( m_debug ) { m_outDebug.println( getRes().getString( "Control event monitor thread started." ) ); } try { WrapperTickEventImpl tickEvent = new WrapperTickEventImpl(); int lastTickOffset = 0; boolean first = true; boolean stoppingLogged = false; // This loop should not exit until the WrapperManager is completely stopped. while ( !m_stopped ) { int offsetDiff; if ( !m_useSystemTime ) { // Get the tick count based on the system time. int sysTicks = getSystemTicks(); // Increment the tick counter by 1. This loop takes just slightly // more than the length of a "tick" but it is a good enough // approximation for our purposes. The accuracy of the tick length // falls sharply when the system is under heavly load, but this // has the desired effect as the Wrapper is also much less likely // to encounter false timeouts due to the heavy load. // The ticks field is volatile and a single integer, so it is not // necessary to synchronize this. // When the ticks count reaches the upper limit of the int range, // it is ok to just let it overflow and wrap. m_ticks++; // Calculate the offset between the two tick counts. // This will always work due to overflow. int tickOffset = sysTicks - m_ticks; // The number we really want is the difference between this tickOffset // and the previous one. offsetDiff = tickOffset - lastTickOffset; if ( first ) { first = false; } else { if ( offsetDiff > m_timerSlowThreshold ) { m_outInfo.println( getRes().getString( "The timer fell behind the system clock by {0} ms." , new Integer( offsetDiff * TICK_MS ) ) ); } else if ( offsetDiff < - m_timerFastThreshold ) { m_outInfo.println( getRes().getString( "The system clock fell behind the timer by {0} ms." , new Integer( -1 * offsetDiff * TICK_MS ) ) ); } } // Store this tick offset for the net time through the loop. lastTickOffset = tickOffset; } else { offsetDiff = 0; } //m_outInfo.println( " UNIX Time: " // + Long.toHexString( System.currentTimeMillis() ) // + ", ticks=" + Integer.toHexString( getTicks() ) + ", sysTicks=" // + Integer.toHexString( getSystemTicks() ) ); // Attempt to detect whether or not we are being starved of CPU. // This will only have any effect if the m_useSystemTime flag is // set. // The tick timer will always result in an age of exactly one // because it is incremented each time through this loop. int nowTicks = getTicks(); long age = getTickAge( m_eventRunnerTicks, nowTicks ); if ( ( m_cpuTimeout > 0 ) && ( age > m_cpuTimeout ) ) { m_outInfo.println( getRes().getString( "JVM Process has not received any CPU time for {0} seconds. Extending timeouts." , new Long( age / 1000 ) ) ); // Make sure that we don't get any ping timeouts in this event m_lastPingTicks = nowTicks; } m_eventRunnerTicks = nowTicks; // If there are any listeners interrested in core events then fire // off a tick event. if ( m_produceCoreEvents ) { tickEvent.m_ticks = nowTicks; tickEvent.m_tickOffset = offsetDiff; fireWrapperEvent( tickEvent ); } if ( isNativeLibraryOk() ) { // To avoid the JVM shutting down while we are in the middle of a JNI call, if ( !isShuttingDown() ) { // Look for control events in the wrapper library. // There may be more than one. int event = 0; do { event = WrapperManager.nativeGetControlEvent(); if ( event != 0 ) { WrapperManager.controlEvent( event ); } } while ( event != 0 ); } else if ( !stoppingLogged ) { stoppingLogged = true; if ( m_debug ) { m_outDebug.println( getRes().getString( "Stopped checking for control events." ) ); } } } // Wait before checking for another control event. try { Thread.sleep( TICK_MS ); } catch ( InterruptedException e ) { } } } finally { if ( m_debug ) { m_outDebug.println( getRes().getString( "Control event monitor thread stopped." ) ); } } } }; m_eventRunner.setDaemon( true ); m_eventRunner.start(); // Resolve the system thread count based on the Java Version String fullVersion = System.getProperty( "java.fullversion" ); String vendor = System.getProperty( "java.vm.vendor", "" ); String os = System.getProperty( "os.name", "" ).toLowerCase(); if ( fullVersion == null ) { fullVersion = System.getProperty( "java.runtime.version" ) + " " + System.getProperty( "java.vm.name" ); } if ( m_debug ) { // Display more JVM info right after the call initialization of the // library. m_outDebug.println( getRes().getString( "Java PID : {0}", Integer.toString( getJavaPID() ) ) ); m_outDebug.println( getRes().getString( "Java Version : {0}", fullVersion ) ); m_outDebug.println( getRes().getString( "Java VM Vendor : {0}", vendor ) ); m_outDebug.println( getRes().getString( "OS Name : {0}", System.getProperty( "os.name", "" ) ) ); m_outDebug.println( getRes().getString( "OS Arch : {0}", System.getProperty( "os.arch", "" ) ) ); m_outDebug.println(); } // This thread will most likely be the thread which launches the JVM. // Once this method returns however, the main thread will likely // quit. There will be a slight delay before the Wrapper binary // has a chance to send a command to start the application. // During this lag, the JVM may not have any non-daemon threads // running and would exit. To keep it from doing so, start a // simple non-daemon thread which will run until the // WrapperListener.start() method returns or the Wrapper's // shutdown thread has started. m_startupRunner = new Thread( "Wrapper-Startup-Runner" ) { public void run() { // Make sure the rest of this thread does not fall behind the application. Thread.currentThread().setPriority( Thread.MAX_PRIORITY ); if ( m_debug ) { m_outDebug.println( getRes().getString( "Startup runner thread started." ) ); } try { while ( m_startupRunner != null ) { try { Thread.sleep( 100 ); } catch ( InterruptedException e ) { // Ignore. } } } finally { if ( m_debug ) { m_outDebug.println( getRes().getString( "Startup runner thread stopped." ) ); } } } }; // This thread must not be a daemon thread. m_startupRunner.setDaemon( false ); m_startupRunner.start(); // Create the singleton m_instance = new WrapperManager(); } /*--------------------------------------------------------------- * Finalizers *-------------------------------------------------------------*/ protected void finalize() throws Throwable { try { // This should only happen if the class gets unloaded. if ( isLoggingFinalizers() ) { System.out.println( "WrapperManager.finalize" ); } } finally { super.finalize(); } } /*--------------------------------------------------------------- * Native Methods *-------------------------------------------------------------*/ private static native void nativeInit( boolean debug ); private static native void nativeLoadWrapperProperties( ); private static native void nativeDispose( boolean debug ); private static native String nativeGetLibraryVersion(); private static native int nativeGetJavaPID(); private static native boolean nativeIsProfessionalEdition(); private static native boolean nativeIsStandardEdition(); private static native int nativeGetControlEvent(); private static native int nativeRedirectPipes(); private static native void nativeRequestThreadDump(); private static native void accessViolationInner(); // Should start with native, but need to preserve for compatibility. private static native void nativeRaiseExceptionInner( int code ); private static native void nativeRaiseFailFastExceptionInner(); private static native void nativeSetConsoleTitle( String titleBytes ); private static native WrapperUser nativeGetUser( boolean groups ); private static native WrapperUser nativeGetInteractiveUser( boolean groups ); private static native WrapperWin32Service[] nativeListServices(); private static native WrapperWin32Service nativeSendServiceControlCode( String serviceName, int controlCode ); private static native WrapperProcess nativeExec( String[] cmdArray, String cmdLine, WrapperProcessConfig config, boolean allowCWDOnSpawn ); private static native String nativeWrapperGetEnv( String val ) throws NullPointerException; private static native WrapperResources nativeLoadWrapperResources(String domain, String folder, boolean makeActive); private static native boolean nativeCheckDeadLocks(); private static native int nativeGetPortStatus(int port, String address, int protocol); public static native int nativeGetDpiScale(); public static native int nativeGetDpiAwareness(); public static native void nativeSetDpiAwareness(int awareness); /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns a tick count calculated from the system clock. */ private static int getSystemTicks() { // Calculate a tick count using the current system time. The // conversion from a long in ms, to an int in TICK_MS increments // will result in data loss, but the loss of bits and resulting // overflow is expected and Ok. return (int)( System.currentTimeMillis() / TICK_MS ); } /** * Returns the number of ticks since the JVM was launched. This * count is not good enough to be used where accuracy is required but * it allows us to implement timeouts in environments where the system * time is modified while the JVM is running. *

* An int is used rather than a long so the counter can be implemented * without requiring any synchronization. At the tick resolution, the * tick counter will overflow and wrap (every 6.8 years for 100ms ticks). * This behavior is expected. The getTickAge method should be used * in cases where the difference between two ticks is required. * * Returns the tick count. */ private static int getTicks() { if ( m_useSystemTime ) { return getSystemTicks(); } else { return m_ticks; } } /** * Returns the number of milliseconds that have elapsed between the * start and end counters. This method assumes that both tick counts * were obtained by calling getTicks(). This method will correctly * handle cases where the tick counter has overflowed and reset. * * @param start A base tick count. * @param end An end tick count. * * @return The number of milliseconds that are represented by the * difference between the two specified tick counts. */ private static long getTickAge( int start, int end ) { // Important to cast the first value so that negative values are correctly // cast to negative long values. return (long)( end - start ) * TICK_MS; } /** * Attempts to load the a native library file. * * @param name Name of the library to load. * @param file Name of the actual library file. * * @return null if the library was successfully loaded, an error message * otherwise. */ private static String loadNativeLibrary( String name, String file ) { try { checkOldLibraryOnAix(file); System.loadLibrary( name ); if ( m_debug ) { m_outDebug.println( getRes().getString( " Attempt to load native library with name: {0} Result: {1}" , file, getRes().getString( "Success!" ) ) ); } return null; } catch ( UnsatisfiedLinkError e ) { if ( m_debug ) { m_outDebug.println( getRes().getString( " Attempt to load native library with name: {0} Result: {1}" , file, e.getMessage() ) ); } String error = e.getMessage(); if ( error == null ) { error = e.toString(); } return error; } catch ( Throwable e ) { if ( m_debug ) { m_outDebug.println( getRes().getString( " Attempt to load native library with name: {0} Result: {1}" , file, e.getMessage() ) ); } String error = e.toString(); return error; } } /** * Java 1.5 and above supports the ability to register the WrapperManager * MBean internally. */ private static void registerMBean( Object mbean, String name ) { Class classManagementFactory; Class classMBeanServer; Class classObjectName; try { classManagementFactory = Class.forName( "java.lang.management.ManagementFactory" ); classMBeanServer = Class.forName( "javax.management.MBeanServer" ); classObjectName = Class.forName( "javax.management.ObjectName" ); } catch ( ClassNotFoundException e ) { if ( m_debug ) { m_outDebug.println(getRes().getString( "Registering MBeans not supported by current JVM: {0}" , name ) ); } return; } try { // This code uses reflection so it combiles on older JVMs. // The original code is as follows: // javax.management.MBeanServer mbs = // java.lang.management.ManagementFactory.getPlatformMBeanServer(); // javax.management.ObjectName oName = new javax.management.ObjectName( name ); // mbs.registerMBean( mbean, oName ); // The version of the above code using reflection follows. Method methodGetPlatformMBeanServer = classManagementFactory.getMethod( "getPlatformMBeanServer", (Class[])null ); Constructor constructorObjectName = classObjectName.getConstructor( new Class[] {String.class} ); Method methodRegisterMBean = classMBeanServer.getMethod( "registerMBean", new Class[] {Object.class, classObjectName} ); Object mbs = methodGetPlatformMBeanServer.invoke( (Object)null, (Object[])null ); Object oName = constructorObjectName.newInstance( new Object[] {name} ); methodRegisterMBean.invoke( mbs, new Object[] {mbean, oName} ); if ( m_debug ) { m_outDebug.println( getRes().getString( "Registered MBean with Platform MBean Server: {0}", name ) ); } } catch ( Throwable t ) { if ( t instanceof ClassNotFoundException ) { m_outError.println( "Using MBean requires at least a JVM version 1.5." ); } m_outError.println( "Unable to register the " + name + " MBean." ); t.printStackTrace( m_outError ); } } /** * Searches for a file on a path. * * @param file File to look for. * @param path Path to be searched. * * @return Reference to the file object if found, otherwise null. */ private static File locateFileOnPath( String file, String path ) { // A library path exists but the library was not found on it. String pathSep = System.getProperty( "path.separator" ); // Search for the file on the library path to verify that it does not // exist, it could be some other problem StringTokenizer st = new StringTokenizer( path, pathSep ); while( st.hasMoreTokens() ) { File libFile = new File( new File( st.nextToken() ), file ); if ( libFile.exists() ) { return libFile; } } return null; } /** * Generates a detailed native library base name which is made up of the * base name, the os name, architecture, and the bits of the current JVM, * not the platform. * * @return A detailed native library base name. */ private static String generateDetailedNativeLibraryBaseName( String baseName, int jvmBits ) { // Generate an os name. Most names are used as is, but some are modified. String os = System.getProperty( "os.name", "" ).toLowerCase(); if ( os.startsWith( "windows" ) ) { os = "windows"; m_windows = true; } else if ( os.equals( "sunos" ) ) { os = "solaris"; } else if ( os.equals( "hp-ux" ) || os.equals( "hp-ux64" ) ) { os = "hpux"; } else if ( os.equals( "mac os x" ) ) { os = "macosx"; m_macosx = true; } else if ( os.equals( "unix_sv" ) ) { os = "unixware"; } else if ( os.equals( "os/400" ) ) { os = "os400"; } else if ( os.equals( "z/os" ) ) { os = "zos"; m_zos = true; } else if ( os.indexOf("aix") > -1 ) { m_aix = true; } m_os = os; // Generate an architecture name. String arch = System.getProperty( "os.arch", "" ).toLowerCase(); if ( m_macosx ) { if ( arch.startsWith( "arm" ) || arch.startsWith( "aarch" ) ) { arch = "arm"; } else if ( arch.equals( "ppc" ) || arch.equals( "ppc64" ) || arch.equals( "x86" ) || arch.equals( "x86_64" ) ) { arch = "universal"; } } else { if ( arch.equals( "amd64" ) || arch.equals( "athlon" ) || arch.equals( "x86_64" ) || arch.equals( "i686" ) || arch.equals( "i586" ) || arch.equals( "i486" ) || arch.equals( "i386" ) ) { arch = "x86"; } else if ( arch.startsWith( "ia32" ) || arch.startsWith( "ia64" ) ) { arch = "ia"; } else if ( arch.startsWith( "sparc" ) ) { arch = "sparc"; } else if ( arch.startsWith( "ppc64le" ) ) { arch = "ppcle"; } else if ( arch.equals( "power" ) || arch.equals( "powerpc" ) || arch.equals( "ppc64" ) ) { if ( m_aix ) { arch = "ppc"; } else { arch = "ppcbe"; } } else if ( arch.startsWith( "pa_risc" ) || arch.startsWith( "pa-risc" ) ) { arch = "parisc"; } else if ( arch.startsWith( "arm" ) || arch.startsWith( "aarch" ) ) { if ( jvmBits == 64 ) { arch = "arm"; } else { arch = System.getProperty( "wrapper.arch" ); } } else if ( arch.equals( "s390" ) || arch.equals( "s390x" ) ) { arch = "390"; } } m_arch = arch; return baseName + "-" + os + "-" + arch + "-" + jvmBits; } /** * Generates a filename which includes the current os name, architecture, and jvm bits. * The format returned will be "<baseName>-<os>-<arch>-<bits>". * This is useful by applications which need to load resource files based on the current platform. * * @param baseName The base file name. * * @return The native base file name appropriate for the current environment. */ public static String generateDetailedNativeBaseName( String baseName ) { return generateDetailedNativeLibraryBaseName( baseName, m_jvmBits ); } /** * On AIX, mapLibraryName() will return "lib*.a" with the IBM SDK, and "lib*.so" with the openJDK. * Shared libraries can either have .so or .a suffix on AIX, but System.loadLibrary() in openJDK only accepts '.so' file. * Note : After calling this function, we will always log the libraries names with a '.so' suffix even though the loaded lib was '.a'. * This may not be perfect, but there is no way to control whether System.loadLibrary() loaded a '.a' or a '.so' file. * We could use System.load() to control the path & suffix, but we don't want to make a special implementation for AIX. * We could also loop through the lib paths and check if libraries with same names & different suffix coexist, but it assumes loadLibrary() always load '.a' in priority, which is not guaranteed in future versions of Java. */ private static String mapSharedLibraryName(String name) { String result = System.mapLibraryName( name ); if (isAIX() && result.endsWith(".a")) { result = result.substring(0, result.length() - 2).concat(".so"); } return result; } /** * On AIX, '.a' libraries are loaded prior to '.so' libraries if both coexist on the same folder. * To help the user, lets warn if any '.a' file are found in the lib folders. */ private static void checkOldLibraryOnAix(String libName) { if (isAIX()) { if (libName.endsWith(".so")) { libName = libName.substring(0, libName.length() - 3).concat(".a"); } // We want to log the exact path of the library, so we don't pass libPaths with separators but rather loop on each path String pathSep = System.getProperty( "path.separator" ); String[] libPaths = System.getProperty( "java.library.path" ).split(pathSep); for ( int j = 0; j < libPaths.length; j++ ) { File libFile = locateFileOnPath( libName, libPaths[j] ); if ( libFile != null ) { m_outInfo.println( getRes().getString( "WARNING - {0} was found in {1}.", libName, libPaths[j] )); m_outInfo.println( getRes().getString( " Recent Wrapper''s native libraries have a ''.so'' suffix." )); m_outInfo.println( getRes().getString( " Depending on the version of Java that is used, {0}", libName )); m_outInfo.println( getRes().getString( " may be loaded instead of a more recent library." )); m_outInfo.println( getRes().getString( " Please remove {0} and make sure that the latest version", libName )); m_outInfo.println( getRes().getString( " of the Wrapper''s native library is in the lib folder." )); } } } } /** * Searches for and then loads the native library. This method will attempt * locate the wrapper library using one of the following 3 naming */ private static void initializeNativeLibrary() { // Look for the base name of the library. String baseName = System.getProperty( "wrapper.native_library" ); if ( baseName == null ) { // This should only happen if an old version of the Wrapper binary is being used. // This message is logged when localization is not yet initialized. m_outInfo.println( "WARNING - The wrapper.native_library system property was not" ); m_outInfo.println( " set. Using the default value, 'wrapper'." ); baseName = "wrapper"; } String[] detailedNames = new String[4]; if ( m_jvmBits > 0 ) { detailedNames[0] = generateDetailedNativeLibraryBaseName( baseName, m_jvmBits ); } else { detailedNames[0] = generateDetailedNativeLibraryBaseName( baseName, 32 ); detailedNames[1] = generateDetailedNativeLibraryBaseName( baseName, 64 ); } // Construct brief and detailed native library file names. String file = mapSharedLibraryName( baseName ); String[] detailedFiles = new String[detailedNames.length]; for ( int i = 0; i < detailedNames.length; i++ ) { if ( detailedNames[i] != null ) { detailedFiles[i] = mapSharedLibraryName( detailedNames[i] ); } } String[] detailedErrors = new String[detailedNames.length]; String baseError = null; // Try loading the native library using the detailed name first. If that fails, use // the brief name. if ( m_debug ) { // This message is logged when localization is not yet initialized. m_outDebug.println( "Load native library. There are multiple possible file names and the first to be found will be used. Errors loading non-existing files is normal and is only a problem if they all fail." ); } m_libraryOK = false; for ( int i = 0; i < detailedNames.length; i++ ) { if ( detailedNames[i] != null ) { detailedErrors[i] = loadNativeLibrary( detailedNames[i], detailedFiles[i] ); if ( detailedErrors[i] == null ) { m_libraryOK = true; break; } } } if ( ( !m_libraryOK ) && ( ( baseError = loadNativeLibrary( baseName, file ) ) == null ) ) { m_libraryOK = true; } if ( m_libraryOK ) { m_libraryLoaded = true; if ( m_debug ) { // This message is logged when localization is not yet initialized. m_outDebug.println( " Successfully loaded native library." ); } // Cache the values of the professional and standard edition flags. try { m_professionalEdition = nativeIsProfessionalEdition(); } catch ( Throwable e ) { if ( m_debug ) { // This message is logged when localization is not yet initialized. m_outDebug.println( "Call to nativeIsProfessionalEdition() failed: " + e ); } m_professionalEdition = false; } try { m_standardEdition = nativeIsStandardEdition(); } catch ( Throwable e ) { if ( m_debug ) { // This message is logged when localization is not yet initialized. m_outDebug.println( "Call to nativeIsStandardEdition() failed: " + e ); } m_standardEdition = false; } // Try reloading the resources once the library is initialized so we get actual localized content. // We do this before trying to initialize the native library intentionally so any messages will be // localized correctly. This one call is designed to handle this state. m_res = loadWrapperResourcesInner( System.getProperty( "wrapper.lang.domain") + "jni", WrapperSystemPropertyUtil.getStringProperty( "wrapper.lang.folder", "../lang" ), true ); if ( m_debug ) { m_outDebug.println( getRes().getString( "Loaded localized resources." ) ); } // The library was loaded correctly, so initialize it. if ( m_debug ) { m_outDebug.println( getRes().getString( "Calling native initialization method." ) ); } nativeInit( m_debug ); if ( m_stoppingInit ) { // Certain checks in the nativeInit call can result in the JVM starting to shutdown. // Avoid further JNI related messages that would be confusing. m_libraryOK = false; } } else { m_libraryLoaded = false; // The library could not be loaded, so we want to give the user a useful // clue as to why not. String libPath = System.getProperty( "java.library.path" ); m_outInfo.println(); if ( libPath.equals( "" ) ) { // No library path // This message is logged when localization is unavailable. m_outInfo.println( "ERROR - Unable to load the Wrapper's native library because the" ); m_outInfo.println( " java.library.path was set to ''. Please see the" ); m_outInfo.println( " documentation for the wrapper.java.library.path" ); m_outInfo.println( " configuration property." ); } else { // Attempt to locate the actual files on the path. String error = null; File libFile = null; for ( int i = 0; i < detailedNames.length; i++ ) { if ( detailedFiles[i] != null ) { libFile = locateFileOnPath( detailedFiles[i], libPath ); if ( libFile != null ) { error = detailedErrors[i]; break; } } } if ( libFile == null ) { libFile = locateFileOnPath( file, libPath ); if ( libFile != null ) { error = baseError; } } if ( libFile == null ) { // The library could not be located on the library path. // This message is logged when localization is unavailable. m_outInfo.println( "ERROR - Unable to load the Wrapper's native library because none of the" ); m_outInfo.println( " following files:" ); for ( int i = 0; i < detailedNames.length; i++ ) { if ( detailedFiles[i] != null ) { m_outInfo.println( " " + detailedFiles[i] ); } } m_outInfo.println( " " + file ); m_outInfo.println( " could be located on the following java.library.path:" ); String pathSep = System.getProperty( "path.separator" ); StringTokenizer st = new StringTokenizer( libPath, pathSep ); while ( st.hasMoreTokens() ) { File pathElement = new File( st.nextToken() ); m_outInfo.println( " " + pathElement.getAbsolutePath() ); } m_outInfo.println( " Please see the documentation for the wrapper.java.library.path" ); m_outInfo.println( " configuration property." ); } else { // The library file was found but could not be loaded for some reason. // This message is logged when localization is unavailable. m_outInfo.println( "ERROR - Unable to load the Wrapper's native library '" + libFile.getName() + "'." ); m_outInfo.println( " The file is located on the path at the following location but" ); m_outInfo.println( " could not be loaded:" ); m_outInfo.println( " " + libFile.getAbsolutePath() ); m_outInfo.println( " Please verify that the file is both readable and executable by the" ); m_outInfo.println( " current user and that the file has not been corrupted in any way." ); m_outInfo.println( " One common cause of this problem is running a 32-bit version" ); m_outInfo.println( " of the Wrapper with a 64-bit version of Java, or vice versa." ); if ( m_jvmBits > 0 ) { m_outInfo.println( " This is a " + m_jvmBits + "-bit JVM." ); } else { m_outInfo.println( " The bit depth of this JVM could not be determined." ); } m_outInfo.println( " Reported cause:" ); m_outInfo.println( " " + error ); } } m_outInfo.println(); } } /** * Compares the version of the wrapper which launched this JVM with that of * the jar. If they differ then a Warning message will be displayed. The * Wrapper application will still be allowed to start. */ private static void verifyWrapperVersion() { // If we are not being controlled by the wrapper then return. if ( !WrapperManager.isControlledByNativeWrapper() ) { return; } // Lookup the version from the wrapper. It should have been set as a property // when the JVM was launched. String wrapperVersion = System.getProperty( "wrapper.version" ); if ( wrapperVersion == null ) { wrapperVersion = getRes().getString( "unknown" ); } if ( wrapperVersion.endsWith( "-pro" ) ) { wrapperVersion = wrapperVersion.substring( 0, wrapperVersion.length() - 4 ); } else if ( wrapperVersion.endsWith( "-st" ) ) { wrapperVersion = wrapperVersion.substring( 0, wrapperVersion.length() - 3 ); } if ( !WrapperInfo.getVersion().equals( wrapperVersion ) ) { m_outInfo.println( getRes().getString( "ERROR - The version of the Wrapper which launched this JVM is \"{0}\"\n while the version of the Wrapper jar file currently in use\n is \"{1}\".", wrapperVersion, WrapperInfo.getVersion() ) ); m_outInfo.println(); m_wrapperVersionOk = false; } else { m_wrapperVersionOk = true; } } /** * Compares the version of the native library with that of this jar. If * they differ then a Warning message will be displayed. The Wrapper * application will still be allowed to start. */ private static void verifyNativeLibraryVersion() { // Request the version from the native library. Be careful as the method // will not exist if the library is old. String jniVersion; try { jniVersion = nativeGetLibraryVersion(); } catch ( Throwable e ) { if ( m_debug ) { m_outDebug.println( getRes().getString( "Call to nativeGetLibraryVersion() failed: {0}", e ) ); } jniVersion = getRes().getString( "unknown" ); } String wrapperVersion = System.getProperty( "wrapper.version" ); if ( wrapperVersion == null ) { wrapperVersion = getRes().getString( "unknown" ); } if ( !wrapperVersion.equals( jniVersion ) ) { m_outInfo.println( getRes().getString( "ERROR - The version of the Wrapper which launched this JVM is\n \"{0}\" while the version of the native library\n is \"{1}\".", wrapperVersion, jniVersion ) ); m_outInfo.println(); m_libraryVersionOk = false; } else { m_libraryVersionOk = true; } } /** * Checks to make sure that the configured temp directory is writable. Failures are only logged * to debug output. */ private static void checkTmpDir() { File tmpDir = new File( System.getProperty( "java.io.tmpdir" ) ); if ( m_debug ) { m_outDebug.println( getRes().getString( "Java temporary directory: {0}", tmpDir ) ); } boolean tmpDirCheck = getProperties().getProperty( "wrapper.java.tmpdir.check", "TRUE").equalsIgnoreCase( "TRUE" ); if ( !tmpDirCheck ) { if ( m_debug ) { m_outDebug.println( getRes().getString( "Validation of temporary directory disabled." ) ); } return; } boolean tmpDirRequired = getProperties().getProperty( "wrapper.java.tmpdir.required", "FALSE" ).equalsIgnoreCase( "TRUE" ); boolean tmpDirWarnSilently = getProperties().getProperty( "wrapper.java.tmpdir.warn_silently", "TRUE" ).equalsIgnoreCase( "TRUE" ); Exception ex = null; try { tmpDir = tmpDir.getCanonicalFile(); File tempFile = new File( tmpDir, "wrapper-" + System.currentTimeMillis() + "-" + getJavaPID() ); if ( tempFile.createNewFile() ) { if ( !tempFile.delete() ) { m_outError.println( "Unable to delete temporary file: " + tempFile ); } } else { if ( m_debug ) { m_outDebug.println( "Unable to create temporary file: " + tempFile ); } } } catch ( IOException e ) { ex = e; } catch ( SecurityException e ) { ex = e; } if ( ex != null ) { if ( tmpDirRequired ) { m_outError.println( getRes().getString( "Unable to write to the configured Java temporary directory: {0} : {1}", tmpDir, ex.toString() ) ); m_outError.println( getRes().getString( "Shutting down." ) ); System.exit( 1 ); } else { if ( tmpDirWarnSilently ) { if ( m_debug ) { m_outDebug.println( getRes().getString( "Unable to write to the configured Java temporary directory: {0} : {1}", tmpDir, ex.toString() ) ); } } else { m_outInfo.println( getRes().getString( "Unable to write to the configured Java temporary directory: {0} : {1}", tmpDir,ex.toString() ) ); } if ( m_debug ) { m_outDebug.println( getRes().getString( " The lack of a temp directory could lead to problems with features that store temporary data, including remote jar class loading." ) ); m_outDebug.println( getRes().getString( " The Java temporary directory can be redefined with the java.io.tmpdir system property." ) ); } } } } /** * Loads a WrapperResources based on the current locale of the JVM. * * @param domain Domain of the resource. * @param folder Location of the resource. * * @return The requested resource. */ private static WrapperResources loadWrapperResourcesInner( String domain, String folder, boolean makeActive ) { if ( WrapperManager.isStandardEdition() && isNativeLibraryOk() ) { if ( folder == null ) { folder = WrapperSystemPropertyUtil.getStringProperty( "wrapper.lang.folder", "../lang" ); } return nativeLoadWrapperResources( domain, folder, makeActive ); } else { return new WrapperResources(); } } /** * Loads a WrapperResources based on the current locale of the JVM. * * @param domain Domain of the resource. * @param folder Location of the resource. * * @return The requested resource. */ public static WrapperResources loadWrapperResources( String domain, String folder ) { return loadWrapperResourcesInner( domain, folder, false ); } /** * Obtain the current version of Wrapper. * * @return The version of the Wrapper. */ public static String getVersion() { return WrapperInfo.getVersion(); } /** * Obtain the build time of Wrapper. * * @return The time that the Wrapper was built. */ public static String getBuildTime() { return WrapperInfo.getBuildTime(); } /** * Returns the Id of the current JVM. JVM Ids increment from 1 each time * the wrapper restarts a new one. * * @return The Id of the current JVM. */ public static int getJVMId() { return m_jvmId; } private static String[] parseCommandLine( String cmdLine ) { ArrayList argList = new ArrayList(); StringBuffer arg = new StringBuffer(); boolean quoteMode = false; boolean escapeNextCharIfQuote = false; char c[] = cmdLine.toCharArray(); for ( int i = 0; i < cmdLine.length(); i++ ) { if ( ( c[i] == '\\' ) && !escapeNextCharIfQuote ) { escapeNextCharIfQuote = true; } else { if ( Character.isWhitespace( c[i] ) && ( quoteMode == false ) ) { if ( arg.length() > 0 ) { argList.add( arg.toString() ); arg.setLength( 0 ); } } else { if ( c[i] == '\"' ) { if ( escapeNextCharIfQuote == false ) { quoteMode = ( ( quoteMode == true ) ? false : true ); escapeNextCharIfQuote = false; continue; } else { // arg.append('\\'); escapeNextCharIfQuote = false; } arg.append( c[i] ); } else if ( c[i] == '\\' ) { if ( escapeNextCharIfQuote == true ) { escapeNextCharIfQuote = false; } arg.append( '\\' ); } else { if ( escapeNextCharIfQuote == true ) { arg.append( '\\' ); escapeNextCharIfQuote = false; } arg.append( c[i] ); } } // else } // else } // for if ( arg.length() > 0 ) { argList.add( arg.toString() ); } String[] args = new String[ argList.size() ]; argList.toArray( args ); return args; } /** * A more powerful replacement to the java.lang.Runtime.exec method. *

* When the JVM exits or is terminated for any reason, the Wrapper will * clean up any child processes launched with this method automatically * before shutting down or launching a new JVM. *

* This method is the same as calling

WrapperManager.exec(command, new WrapperProcessConfig());
*

* The returned WrapperProcess object can be used to control the child * process, supply input, or process output. *

* Professional Edition feature. * * @param command A specified system command in one String. * * @return A new WrapperProcess object for managing the subprocess. * * @throws IOException Will be thrown if an I/O error occurs * @throws NullPointerException If command is null. * @throws IllegalArgumentException If command is empty * @throws SecurityException If a SecurityManager is present and its * checkExec method doesn't allow creation of a * subprocess. * @throws WrapperJNIError If the native library has not been loaded or is in the * process of shutting down. * @throws WrapperLicenseError If the function is called other than in * the Professional Edition or if the native * library has not been loaded. * @throws UnsatisfiedLinkError If the posix_spawn function couldn't be found * * @see #isProfessionalEdition() * @see #exec(String command, WrapperProcessConfig config) * @see WrapperProcessConfig * @since Wrapper 3.4.0 */ public static WrapperProcess exec( String command ) throws SecurityException, IOException, NullPointerException, IllegalArgumentException, WrapperJNIError, WrapperLicenseError, UnsatisfiedLinkError { WrapperProcess proc = exec( command, new WrapperProcessConfig() ); return proc; } /** * A more powerful replacement to the java.lang.Runtime.exec method. *

* By configuring the WrapperProcessConfig object, it is possible to * control whether or not the child process will be automatically * cleaned up when the JVM exits or is terminated. It is also possible * to control how the child process is launched to work around memory * issues on some platforms. *

* For example, on Solaris when the JVM is very large, doing a fork will * duplicate the entire JVM's memory space and cause an out of memory * error or JVM crash, to avoid such memory problems the child process * can be launched using posix spawn as follows: *

WrapperManager.exec( command, new WrapperProcessConfig().setStartType( WrapperProcessConfig.POSIX_SPAWN ) );
*

* Please review the WrapperProcessConfig class for a full list of * options. *

* The returned WrapperProcess object can be used to control the child * process, supply input, or process output. *

* Professional Edition feature. * * @param command A specified system command in one String. * @param config A WrapperProcessConfig object representing the Start/Run * Configurations of the subprocess * * @return A new WrapperProcess object for managing the subprocess. * * @throws IOException Will be thrown if an I/O error occurs * @throws NullPointerException If command is null. * @throws IllegalArgumentException If command is empty or the configuration is invalid * @throws SecurityException If a SecurityManager is present and its * checkExec method doesn't allow creation of a * subprocess. * @throws WrapperJNIError If the native library has not been loaded or is in the * process of shutting down. * @throws WrapperLicenseError If the function is called other than in * the Professional Edition or if the native * library has not been loaded. * @throws UnsatisfiedLinkError If the posix_spawn function couldn't be found * * @see #isProfessionalEdition() * @see WrapperProcessConfig * @since Wrapper 3.4.0 */ public static WrapperProcess exec( String command, WrapperProcessConfig config ) throws SecurityException, IOException, NullPointerException, IllegalArgumentException, WrapperJNIError, WrapperLicenseError, UnsatisfiedLinkError { if ( ( command == null ) || ( command.length() == 0 ) ) { throw new IllegalArgumentException( getRes().getString( "No command specified" ) ); } return exec( null, command, config ); } /** * A more powerful replacement to the java.lang.Runtime.exec method. *

* When the JVM exits or is terminated for any reason, the Wrapper will * clean up any child processes launched with this method automatically * before shutting down or launching a new JVM. *

* This method is the same as calling

WrapperManager.exec(cmdArray, new WrapperProcessConfig());
*

* The returned WrapperProcess object can be used to control the child * process, supply input, or process output. *

* Professional Edition feature. * * @param cmdArray A specified system command in array format for each * parameter a single element. * * @return A new WrapperProcess object for managing the subprocess * * @throws IOException Will be thrown at any error realated with Memory * allocation, or if the command does not exist. * @throws NullPointerException If cmdarray is null, or one of the elements * of cmdarray is null. * @throws IndexOutOfBoundsException If cmdarray is an empty array (has * length 0) * @throws SecurityException If a SecurityManager is present and its * checkExec method doesn't allow creation of a * subprocess. * @throws IllegalArgumentException If there are any problems with the * WrapperProcessConfig object. * @throws WrapperJNIError If the native library has not been loaded or is in the * process of shutting down. * @throws WrapperLicenseError If the function is called other than in * the Professional Edition or if the native * library has not been loaded. * @throws UnsatisfiedLinkError If the posix_spawn function couldn't be found * * @see #isProfessionalEdition() * @since Wrapper 3.4.0 */ public static WrapperProcess exec( String[] cmdArray ) throws SecurityException, IOException, NullPointerException, IndexOutOfBoundsException, IllegalArgumentException, WrapperJNIError, UnsatisfiedLinkError, WrapperLicenseError { WrapperProcess proc = exec( cmdArray, new WrapperProcessConfig() ); return proc; } /** * A more powerful replacement to the java.lang.Runtime.exec method. *

* By configuring the WrapperProcessConfig object, it is possible to * control whether or not the child process will be automatically * cleaned up when the JVM exits or is terminated. It is also possible * to control how the child process is launched to work around memory * issues on some platforms. *

* For example, on Solaris when the JVM is very large, doing a fork will * duplicate the entire JVM's memory space and cause an out of memory * error or JVM crash, to avoid such memory problems the child process * can be launched using posix spawn as follows: *

WrapperManager.exec( cmdArray, new WrapperProcessConfig().setStartType( WrapperProcessConfig.POSIX_SPAWN ) );
*

* Please review the WrapperProcessConfig class for a full list of * options. *

* The returned WrapperProcess object can be used to control the child * process, supply input, or process output. *

* Professional Edition feature. * * @param cmdArray A specified system command in array format, for each * parameter a single element. * @param config A WrapperProcessConfig object representing the Start/Run * Configurations of the subprocess * * @return A new WrapperProcess object for managing the subprocess. * * @throws IOException Will be thrown if an I/O error occurs * @throws NullPointerException If cmdarray is null, or one of the elements * of cmdarray is null. * @throws IndexOutOfBoundsException If cmdarray is an empty array (has * length 0) * @throws SecurityException If a SecurityManager is present and its * checkExec method doesn't allow creation of a * subprocess. * @throws IllegalArgumentException If there are any problems with the * WrapperProcessConfig object. * @throws WrapperJNIError If the native library has not been loaded or is in the * process of shutting down. * @throws WrapperLicenseError If the function is called other than in * the Professional Edition or if the native * library has not been loaded. * @throws UnsatisfiedLinkError If the posix_spawn function couldn't be found * * @see #isProfessionalEdition() * @since Wrapper 3.4.0 */ public static WrapperProcess exec( String[] cmdArray, WrapperProcessConfig config ) throws SecurityException, IOException, NullPointerException, IndexOutOfBoundsException, IllegalArgumentException, WrapperJNIError, WrapperLicenseError, UnsatisfiedLinkError { return exec( cmdArray, null, config ); } /** * Executes an external command. */ private static WrapperProcess exec( String[] cmdArray, String cmdLine, WrapperProcessConfig config ) throws SecurityException, IOException, NullPointerException, IndexOutOfBoundsException, IllegalArgumentException, WrapperJNIError, WrapperLicenseError, UnsatisfiedLinkError { // If the cmdArray parameter is null then the cmdLine will be parsed into an a cmdArray. // Then both the cmdArray and cmdLine will be passed off to the native code. // The cmdLine may be null. assertProfessionalEdition(); if ( ( cmdArray == null ) && ( cmdLine == null ) ) { throw new NullPointerException( getRes().getString( "No command specified" ) ); } else if ( ( cmdArray != null) && ( cmdArray.length == 0 ) ) { throw new IndexOutOfBoundsException( getRes().getString( "cmdArray is empty" ) ); } if ( ( cmdArray == null ) && ( cmdLine != null ) ) { cmdArray = parseCommandLine( cmdLine ); } if ( config == null ) { throw new NullPointerException( getRes().getString( "config is null" ) ); } // Make sure the call stack has permission to execute this command. SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkExec( cmdArray[0] ); } // Keep track of how many threads are trying to execute child processes. // This is critical to avoid notification failures on shutdown. synchronized( WrapperManager.class ) { m_runningExecs++; } try { if ( isNativeLibraryOk() ) { for ( int i = 0; i < cmdArray.length; i++ ) { if ( cmdArray[i] == null ) { throw new NullPointerException( getRes().getString( "cmdarray[{0}]: Invalid element (isNull).", new Integer( i) ) ); } } // On UNIX platforms, we want to try and make sure the command is // valid before we run it to avoid problems later. Not necessary // on Windows. if ( !m_windows ) { if ( !new File( cmdArray[0] ).exists() ) { boolean found = false; String path = nativeWrapperGetEnv( "PATH" ); if ( path != null ) { String[] paths = path.split( File.pathSeparator ); for ( int i = 0; i < paths.length; i++ ) { File file = new File( paths[i] + File.separator + cmdArray[0] ); // m_outInfo.println( blu.getPath() ); if ( file.exists() ) { cmdArray[0] = file.getPath(); found = true; break; } } } if ( !found ) { throw new IOException(getRes().getString( "''{0}'' not found." , cmdArray[0] ) ); } } } if ( m_debug ) { for ( int j = 0; j < cmdArray.length; j++ ) { m_outDebug.println( "args[" + j + "] = " + cmdArray[j] ); } } return nativeExec( cmdArray, cmdLine, config.setEnvironment( config.getEnvironment() ), WrapperSystemPropertyUtil.getBooleanProperty( "wrapper.child.allowCWDOnSpawn", false ) ); } else { if ( m_stopped ) { // This message is logged when localization is no longer available. throw new WrapperJNIError( "Wrapper native library shutting down." ); } else { // This message is logged when localization is not available. throw new WrapperJNIError( "Wrapper native library not loaded." ); } } } finally { synchronized( WrapperManager.class ) { m_runningExecs--; if ( m_runningExecs <= 0 ) { WrapperManager.class.notifyAll(); } } } } /** * Returns true if the native library has been loaded successfully, false * otherwise. This value can switch to false on shutdown when the native * library can no longer be reliably referenced. * * @return True if the native library is loaded and available. */ public static boolean isNativeLibraryOk() { return m_libraryOK && ( !m_stopped ); } /** * Asserts that the Professional Edition of the Wrapper is being used and * that the native library is available. * * @throws WrapperLicenseError If the function is called other than in * the Professional Edition or if the native * library has not been loaded. */ static void assertProfessionalEdition() throws WrapperLicenseError { if ( !m_libraryOK ) { throw new WrapperLicenseError( getRes().getString( "Requires that the Professional Edition native library be loaded. Please check for errors earlier in the log." ) ); } else if ( m_stopped ) { throw new WrapperLicenseError( getRes().getString( "Requires that the Professional Edition native library be loaded, but it has already been unloaded as part of the shutdown process." ) ); } else if ( !isProfessionalEdition() ) { throw new WrapperLicenseError( getRes().getString( "Requires the Professional Edition." ) ); } } /** * Returns the OS that the Wrapper has resolved. * This will be the same as the WRAPPER_OS environment variable. * Used when resolving the platform specific native library name. * * @return The resolved Wrapper OS name. * * @since Wrapper 3.5.31 */ public static String getOS() { return m_os; } /** * Returns the Architecture that the Wrapper has resolved. * This will be the same as the WRAPPER_ARCH environment variable. * Used when resolving the platform specific native library name. * * @return The resolved Wrapper Architecture name. * * @since Wrapper 3.5.31 */ public static String getArch() { return m_arch; } /** * Returns true if the current JVM is Windows. * * @return True if this is Windows. * * @since Wrapper 3.5.1 */ public static boolean isWindows() { return m_windows; } /** * Returns true if the current JVM is Mac OSX. * * @return True if this is Mac OSX. * * @since Wrapper 3.5.1 */ public static boolean isMacOSX() { return m_macosx; } /** * Returns true if the current JVM is AIX. * * @return True if this is AIX. * * @since Wrapper 3.5.27 */ public static boolean isAIX() { return m_aix; } /** * Returns true if the current JVM is z/OS. * * @return True if this is z/OS. * * @since Wrapper 3.5.35 */ public static boolean isZOS() { return m_zos; } /** * Returns true if the current Wrapper edition has support for Professional * Edition features. * * False will also be returned if the native library has not been initialized correctly. * * @return True if professional features are supported. */ public static boolean isProfessionalEdition() { // Use a cached value rather than calling the JNI call here to avoid confusing errors on shutdown. return m_professionalEdition; } /** * Returns true if the current Wrapper edition has support for Standard * Edition features. * * False will also be returned if the native library has not been initialized correctly. * * @return True if standard features are supported. */ public static boolean isStandardEdition() { // Use a cached value rather than calling the JNI call here to avoid confusing errors on shutdown. return m_standardEdition; } /** * Fires a user event user_n specified in the conf file * * @param eventNr The user-event number to fire. Must be in the range of 1-32767. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("fireUserEvent") * permission. * @throws WrapperLicenseError If the function is called other than in * the Professional Edition or if the native * library has not been loaded. */ public static void fireUserEvent( int eventNr ) { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperUserEventPermission( "fireUserEvent", String.valueOf( eventNr ) ) ); } if ( eventNr <= 0 || eventNr > 32767 ) { throw new java.lang.IllegalArgumentException( getRes().getString( "The user-event number must be in the range of 1-32767." ) ); } assertProfessionalEdition(); sendCommand( WRAPPER_MSG_FIRE_USER_EVENT, String.valueOf( eventNr ) ); } /** * Sets the title of the console in which the Wrapper is running. This * is currently only supported on Windows platforms. *

* As an alternative, it is also possible to set the console title from * within the wrapper.conf file using the wrapper.console.title property. * * @param title The new title. The specified string will be encoded * to a byte array using the default encoding for the * current platform. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("setConsoleTitle") * permission. * * @see WrapperPermission */ public static void setConsoleTitle( String title ) { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "setConsoleTitle" ) ); } if ( isNativeLibraryOk() ) { nativeSetConsoleTitle( title ); } } /** * Returns a WrapperUser object which describes the user under which the * Wrapper is currently running. Additional platform specific information * can be obtained by casting the object to a platform specific subclass. * WrapperWin32User, for example. * * @param groups True if the user's groups should be returned as well. * Requesting the groups that a user belongs to increases * the CPU load required to complete the call. * * @return An object describing the current user. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("getUser") permission. * * @see WrapperPermission */ public static WrapperUser getUser( boolean groups ) { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "getUser" ) ); } WrapperUser user = null; if ( isNativeLibraryOk() ) { user = nativeGetUser( groups ); } return user; } /** * Returns a WrapperUser object which describes the interactive user whose * desktop is being interacted with. When a service running on a Windows * platform has its interactive flag set, this method will return the user * who is currently logged in. Additional platform specific information * can be obtained by casting the object to a platform specific subclass. * WrapperWin32User, for example. *

* If a user is not currently logged on then this method will return null. * User code can repeatedly call this method to detect when a user has * logged in. To detect when a user has logged out, there are two options. * 1) The user code can continue to call this method until it returns null. * 2) Or if the WrapperListener method is being implemented, the * WrapperListener.controlEvent method will receive a WRAPPER_CTRL_LOGOFF_EVENT * event when the user logs out. *

* On XP systems, it is possible to switch to another account rather than * actually logging out. In such a case, the interactive user will be * the first user that logged in. This will also be the only user with * which the service will interact. If other users are logged in when the * interactive user logs out, the service will not automatically switch to * another logged in user. Rather, the next user to log in will become * the new user which the service will interact with. *

* This method will always return NULL on versions of NT prior to Windows * 2000. This can not be helped as some required functions were not added * to the windows API until NT version 5.0, also known as Windows 2000. * * @param groups True if the user's groups should be returned as well. * Requesting the groups that a user belongs to increases * the CPU load required to complete the call. * * @return The current interactive user, or null. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("getInteractiveUser") * permission. * * @see WrapperPermission */ public static WrapperUser getInteractiveUser( boolean groups ) { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "getInteractiveUser" ) ); } WrapperUser user = null; if ( isNativeLibraryOk() ) { user = nativeGetInteractiveUser( groups ); } return user; } /** * Returns a Properties object containing expanded the contents of the * configuration file used to launch the Wrapper. * * All properties are included so it is possible to define properties * not used by the Wrapper in the configuration file and have then * be available in this Properties object. * * @return The contents of the Wrapper configuration file. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("getProperties") * permission. * * @see WrapperPermission */ public static Properties getProperties() { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "getProperties" ) ); } return m_properties; } /** * Returns the PID of the Wrapper process. * * A PID of 0 will be returned if the JVM was launched standalone. * * This value can also be obtained using the 'wrapper.pid' system property. * * @return The PID of the Wrpper process. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("getWrapperPID") permission. * * @see WrapperPermission */ public static int getWrapperPID() { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "getWrapperPID" ) ); } return WrapperSystemPropertyUtil.getIntProperty( "wrapper.pid", 0 ); } /** * Returns the PID of the Java process. * * A PID of 0 will be returned if the native library has not been initialized. * * This value can also be obtained using the 'wrapper.java.pid' system property. * * @return The PID of the Java process. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("getJavaPID") permission. * * @see WrapperPermission */ public static int getJavaPID() { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "getJavaPID" ) ); } return WrapperSystemPropertyUtil.getIntProperty( "wrapper.java.pid", 0 ); } /** * Requests that the current JVM process request a thread dump. This is * the same as pressing CTRL-BREAK (under Windows) or CTRL-\ (under Unix) * in the the console in which Java is running. This method does nothing * if the native library is not loaded. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("requestThreadDump") * permission. * * @see WrapperPermission */ public static void requestThreadDump() { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "requestThreadDump" ) ); } if ( isNativeLibraryOk() ) { nativeRequestThreadDump(); } else { m_outInfo.println( getRes().getString( " wrapper library not loaded." ) ); } } /** * (Testing Method) Causes the WrapperManager to go into a state which makes the JVM appear * to be hung when viewed from the native Wrapper code. Does not have any effect when the * JVM is not being controlled from the native Wrapper. Useful for testing the Wrapper * functions. * * @throws IllegalStateException If testing methods have been disabled by setting * the wrapper.disable_tests=true system property. * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("test.appearHung") permission. * * @see WrapperPermission */ public static void appearHung() { if ( m_disableTests ) { throw new IllegalStateException( getRes().getString( "Test methods have been disabled." ) ); } SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "test.appearHung" ) ); } m_outInfo.println( getRes().getString( "WARNING: Making JVM appear to be hung..." ) ); m_appearHung = true; } /** * (Testing Method) Causes the WrapperManager to go into a state which makes * the JVM appear to be sluggish when viewed from the native Wrapper code. * Each packet received from the Wrapper is delayed by the specified number * of seconds. * * If several packets are received in succession then this delay will be * cumulative. The delay reported by the Wrapper may appear to be quite a * bit different if multiple packets are being delayed in series. Some * packets, like pings, which are sent multiple times may also be reported * as being processed faster than this delay because the previous response * is what is actually being received. * * Does not have any effect when the JVM is not being controlled from the * native Wrapper. Useful for testing the Wrapper functions. * * @param slowSeconds The number of seconds to delay reponding to any incoming * commands from the wrapper. * * @throws IllegalStateException If testing methods have been disabled by setting * the wrapper.disable_tests=true system property. * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("test.appearSlow") permission. * * @see WrapperPermission */ public static void appearSlow( int slowSeconds ) { if ( m_disableTests ) { throw new IllegalStateException( getRes().getString( "Test methods have been disabled." ) ); } SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "test.appearSlow" ) ); } if ( slowSeconds > 0 ) { m_outInfo.println( getRes().getString( "WARNING: Making JVM appear to be slow using a delay of {0} seconds...", new Integer( slowSeconds ) ) ); m_slowSeconds = slowSeconds; } else if ( m_slowSeconds > 0 ) { m_outInfo.println( getRes().getString( "Resetting the JVM delayed response to normal..." ) ); m_slowSeconds = 0; } } /** * @deprecated Removed as of 3.5.8 */ public static void appearOrphan() { } /** * (Testing Method) Cause an access violation within the Java code. Useful * for testing the Wrapper functions. This currently only crashes Sun * JVMs and takes advantage of Bug #4369043 which does not exist in newer * JVMs. Use of the accessViolationNative() method is preferred. * * @throws IllegalStateException If testing methods have been disabled by setting * the wrapper.disable_tests=true system property. * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("test.accessViolation") * permission. * * @see WrapperPermission */ public static void accessViolation() { if ( m_disableTests ) { throw new IllegalStateException( getRes().getString( "Test methods have been disabled." ) ); } SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "test.accessViolation" ) ); } m_outInfo.println( getRes().getString( "WARNING: Attempting to cause an access violation..." ) ); try { Class c = Class.forName( "java.lang.String" ); java.lang.reflect.Method m = c.getDeclaredMethod( (String)null, (Class[])null ); } catch( NoSuchMethodException ex ) { // Correctly did not find method. access_violation attempt failed. Not Sun JVM? } catch( Exception ex ) { if ( ex instanceof NoSuchFieldException ) { // Can't catch this in a catch because the compiler doesn't think it is being // thrown. But it is thrown on IBM jvms at least // Correctly did not find method. access_violation attempt failed. Not Sun JVM? } else { // Shouldn't get here. ex.printStackTrace( m_outError ); } } m_outInfo.println( getRes().getString( " Attempt to cause access violation failed. JVM is still alive." ) ); } /** * (Testing Method) Cause an access violation within native JNI code. * Useful for testing the Wrapper functions. This currently causes the * access violation by attempting to write to a null pointer. * * @throws IllegalStateException If testing methods have been disabled by setting * the wrapper.disable_tests=true system property. * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("test.accessViolationNative") * permission. * * @see WrapperPermission */ public static void accessViolationNative() { if ( m_disableTests ) { throw new IllegalStateException( getRes().getString( "Test methods have been disabled." ) ); } SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "test.accessViolationNative" ) ); } m_outInfo.println( getRes().getString( "WARNING: Attempting to cause an access violation..." ) ); if ( isNativeLibraryOk() ) { accessViolationInner(); m_outInfo.println( getRes().getString( " Attempt to cause access violation failed. JVM is still alive." ) ); } else { m_outInfo.println( getRes().getString( " wrapper library not loaded." ) ); } } /** * (Testing Method) raise an exception in native code. * Useful for testing the Wrapper functions. * * Ignored when not running on Windows. * * @param code Exception code to Raise. * * @throws IllegalStateException If testing methods have been disabled by setting * the wrapper.disable_tests=true system property. * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("test.raiseExceptionNative") * permission. * * @see WrapperPermission */ public static void raiseExceptionNative( int code ) { if ( m_disableTests ) { throw new IllegalStateException( getRes().getString( "Test methods have been disabled." ) ); } SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "test.raiseExceptionNative" ) ); } m_outInfo.println( getRes().getString( "WARNING: Attempting to raise an exception in native code..." ) ); if ( isNativeLibraryOk() ) { nativeRaiseExceptionInner( code ); m_outInfo.println( getRes().getString( " Attempt to raise a native exception failed. JVM is still alive." ) ); } else { m_outInfo.println( getRes().getString( " wrapper library not loaded." ) ); } } /** * (Testing Method) raise a fail fast exception in native code. * Useful for testing the Wrapper functions. * * Ignored when not running on Windows. * * @throws IllegalStateException If testing methods have been disabled by setting * the wrapper.disable_tests=true system property. * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("test.raiseFailFastExceptionNative") * permission. * * @see WrapperPermission */ public static void raiseFailFastExceptionNative() { if ( m_disableTests ) { throw new IllegalStateException( getRes().getString( "Test methods have been disabled." ) ); } SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "test.raiseFailFastExceptionNative" ) ); } m_outInfo.println( getRes().getString( "WARNING: Attempting to raise a fail fast exception in native code..." ) ); if ( isNativeLibraryOk() ) { nativeRaiseFailFastExceptionInner(); m_outInfo.println( getRes().getString( " Attempt to raise a native fail fast exception failed. JVM is still alive." ) ); } else { m_outInfo.println( getRes().getString( " wrapper library not loaded." ) ); } } /** * Returns true if the JVM was launched by the Wrapper application. False * if the JVM was launched manually without the Wrapper controlling it. * * @return True if the current JVM was launched by the Wrapper. */ public static boolean isControlledByNativeWrapper() { return m_key != null; } /** * Returns true if the Wrapper was launched as an NT service on Windows or * as a daemon process on UNIX platforms. False if launched as a console. * This can be useful if you wish to display a user interface when in * Console mode. On UNIX platforms, this is not as useful because an * X display may not be visible even if launched in a console. * * @return True if the Wrapper is running as an NT service or daemon * process. */ public static boolean isLaunchedAsService() { return m_service; } /** * Returns true if the JVM should ignore user logoff events. Mainly used * within WrapperListener.controlEvent() method implemenations. * @return True if user logoff events should be ignroed. */ public static boolean isIgnoreUserLogoffs() { return m_ignoreUserLogoffs; } /** * Returns true if the wrapper.debug property, or any of the logging * channels are set to DEBUG in the wrapper configuration file. Useful * for deciding whether or not to output certain information to the * console. * * @return True if the Wrapper is logging any Debug level output. */ public static boolean isDebugEnabled() { return m_debug; } /** * Internal method to let Wrapper classes decide whether or not to write finalizer log output. */ static boolean isLoggingFinalizers() { return m_logFinalizer; } /** * Start the Java side of the Wrapper code running. This will make it * possible for the native side of the Wrapper to detect that the Java * Wrapper is up and running. *

* This method must be called on startup and then can only be called once * so there is no reason for any security permission checks on this call. * * @param listener The WrapperListener instance which represents the * application being started. * @param args The argument list passed to the JVM when it was launched. */ public static void start( final WrapperListener listener, final String[] args ) { // As was done in the static initializer, we need to execute the following // code in a privileged action so it is not necessary for the calling code // to have the same privileges as the wrapper jar. // This is safe because this method can only be called once and that one call // will presumably be made on JVM startup. AccessController.doPrivileged( new PrivilegedAction() { public Object run() { privilegedStart( listener, args ); return null; } } ); } /** * Called by the start method within a PrivilegedAction. * * @param WrapperListener The WrapperListener instance which represents * the application being started. * @param args The argument list passed to the JVM when it was launched. */ private static void privilegedStart( WrapperListener listener, String[] args ) { // Check the SecurityManager here as it is possible that it was set before this call. checkSecurityManager(); // Just in case the user failed to provide an argument list, recover by creating one // here. This will avoid possible problems down stream. if ( args == null ) { args = new String[0]; } if ( m_debug ) { StringBuffer sb = new StringBuffer(); sb.append( "args[" ); for ( int i = 0; i < args.length; i++ ) { if ( i > 0 ) { sb.append( ", " ); } sb.append( "\"" ); sb.append( args[i] ); sb.append( "\"" ); } sb.append( "]" ); m_outDebug.println( getRes().getString( "{0} called by thread: {1}", "WrapperManager.start(a " + listener.getClass().getName() + ", " + sb.toString() + ")", Thread.currentThread().getName() ) ); } synchronized( WrapperManager.class ) { // Make sure that the class has not already been disposed. if ( m_disposed) { throw new IllegalStateException( getRes().getString( "WrapperManager has already been disposed." ) ); } if ( m_listener != null ) { throw new IllegalStateException( getRes().getString( "WrapperManager has already been started with a WrapperListener." ) ); } if ( listener == null ) { throw new IllegalStateException( getRes().getString( "A WrapperListener must be specified." ) ); } m_listener = listener; m_args = args; if ( m_debug ) { Thread thisThread = Thread.currentThread(); m_outDebug.println( getRes().getString( "Initial thread: {0} Priority: {1}", thisThread.getName(), new Integer( thisThread.getPriority() ) ) ); } // Setup the thread to handle backend communications. startRunner(); // If this JVM is being controlled by a native wrapper, then we want to // wait for the command to start. However, if this is a standalone // JVM, then we want to start now. if ( !isControlledByNativeWrapper() ) { startInner( true ); } } } /** * Returns true if the JVM is in the process of shutting down. This can be * useful to avoid starting long running processes when it is known that the * JVM will be shutting down shortly. * * @return true if the JVM is shutting down. */ public static boolean isShuttingDown() { return m_stopping; } private static class ShutdownLock extends Object { private final Thread m_thread; private int m_count; private ShutdownLock( Thread thread ) { m_thread = thread; } } /** * Increase the number of locks which will prevent the Wrapper from letting * the JVM process exit on shutdown. This is primarily useful around * calls to native JNI functions in daemon threads where it has been shown * that premature JVM exits can cause the JVM process to crash on shutdown. *

* Normal non-daemon threads should not require these locks as the very * fact that the non-daemon thread is still running will prevent the JVM * from shutting down. *

* It is possible to make multiple calls within a single thread. Each call * should always be paired with a call to releaseShutdownLock(). * * @throws WrapperShuttingDownException If called after the Wrapper has * already begun the shutdown of the * JVM. */ public static void requestShutdownLock() throws WrapperShuttingDownException { synchronized( WrapperManager.class ) { if ( m_stopping ) { throw new WrapperShuttingDownException(); } Thread thisThread = Thread.currentThread(); ShutdownLock lock = (ShutdownLock)m_shutdownLockMap.get( thisThread ); if ( lock == null ) { lock = new ShutdownLock( thisThread ); m_shutdownLockMap.put( thisThread, lock ); } lock.m_count++; m_shutdownLocks++; if ( m_debug ) { m_outDebug.println( getRes().getString( "{0} called by thread: {1} (New thread lock count: {2}, total lock count: {3})", "WrapperManager.requestShutdownLock()", thisThread.getName(), new Integer( lock.m_count ), new Integer( m_shutdownLocks ) ) ); } } } /** * Called by a thread which has previously called requestShutdownLock(). * * @throws IllegalStateException If called without first calling requestShutdownLock() from * the same thread. */ public static void releaseShutdownLock() throws IllegalStateException { synchronized( WrapperManager.class ) { Thread thisThread = Thread.currentThread(); ShutdownLock lock = (ShutdownLock)m_shutdownLockMap.get( thisThread ); if ( lock == null ) { throw new IllegalStateException( getRes().getString( "requestShutdownLock was not called from this thread." ) ); } lock.m_count--; m_shutdownLocks--; if ( m_debug ) { m_outDebug.println( getRes().getString( "{0} called by thread: {1} (New thread lock count: {2}, total lock count: {3})", "WrapperManager.releaseShutdownLock()", thisThread.getName(), new Integer( lock.m_count ), new Integer( m_shutdownLocks ) ) ); } if ( lock.m_count <= 0 ) { m_shutdownLockMap.remove( thisThread ); } WrapperManager.class.notify(); } } /** * Waits for any outstanding locks to be released before shutting down. */ private static void waitForShutdownLocks() { synchronized( WrapperManager.class ) { if ( m_debug ) { m_outDebug.println( getRes().getString( "wait for {0} shutdown locks to be released.", new Integer(m_shutdownLocks ) ) ); } while ( m_shutdownLocks > 0 ) { try { WrapperManager.class.wait( 5000 ); } catch ( InterruptedException e ) { // Ignore and continue. } if ( m_shutdownLocks > 0 ) { m_outInfo.println( getRes().getString( "Waiting for {0} shutdown locks to be released..." , new Integer(m_shutdownLocks ) ) ); } } } } /** * Tells the native wrapper that the JVM wants to restart, then informs * all listeners that the JVM is about to shutdown before killing the JVM. *

* This method will not return. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("restart") permission. * * @see WrapperPermission */ public static void restart() throws SecurityException { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "restart" ) ); } m_stoppingInit = true; if ( m_debug ) { m_outDebug.println( getRes().getString( "{0} called by thread: {1}", "WrapperManager.restart()", Thread.currentThread().getName() ) ); } restartInner(); } /** * Tells the native wrapper that the JVM wants to restart, then informs * all listeners that the JVM is about to shutdown before killing the JVM. *

* This method requests that the JVM be restarted but then returns. This * allows components to initiate a JVM exit and then continue, allowing * a normal shutdown initiated by the JVM via shutdown hooks. In * applications which are designed to be shutdown when the user presses * CTRL-C, this may result in a cleaner shutdown. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("restart") permission. * * @see WrapperPermission */ public static void restartAndReturn() throws SecurityException { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "restart" ) ); } m_stoppingInit = true; synchronized( WrapperManager.class ) { if ( m_stopping ) { if ( m_debug ) { m_outDebug.println( getRes().getString( "{0} called by thread: {1} (already stopping)", "WrapperManager.restartAndReturn()", Thread.currentThread().getName() ) ); } return; } else { if ( m_debug ) { m_outDebug.println( getRes().getString( "{0} called by thread: {1}", "WrapperManager.restartAndReturn()", Thread.currentThread().getName() ) ); } } } // To make this possible, we have to create a new thread to actually do the shutdown. Thread restarter = new Thread( "Wrapper-Restarter" ) { public void run() { restartInner(); } }; restarter.setDaemon( false ); restarter.start(); } /** * Common code used to restart the JVM. It is assumed that the calling * thread has has passed security checks before this is called. */ private static void restartInner() { boolean stopping; synchronized( WrapperManager.class ) { stopping = m_stopping; if ( !stopping ) { m_stopping = true; } } if ( !stopping ) { // Always send the restart command sendCommand( WRAPPER_MSG_RESTART, "restart" ); } // Give the Wrapper a chance to register the stop command before stopping. // This avoids any errors thrown by the Wrapper because the JVM died before // it was expected to. try { Thread.sleep( 1000 ); } catch ( InterruptedException e ) { } // This is safe because we are already checking for the privilege to restart the JVM // above. If we get this far then we want the Wrapper to be able to do everything // necessary to stop the JVM. AccessController.doPrivileged( new PrivilegedAction() { public Object run() { privilegedStopInner( 0 ); return null; } } ); } /** * Tells the native wrapper that the JVM wants to shut down, then informs * all listeners that the JVM is about to shutdown before killing the JVM. *

* This method will not return. * * @param exitCode The exit code that the Wrapper will return when it exits. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("stop") permission. * * @see WrapperPermission */ public static void stop( final int exitCode ) { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "stop" ) ); } m_stoppingInit = true; if ( m_debug ) { m_outDebug.println( getRes().getString( "{0} called by thread: {1}", "WrapperManager.stop(" + exitCode + ")", Thread.currentThread().getName() ) ); } stopCommon( exitCode, 1000 ); // This is safe because we are already checking for the privilege to stop the JVM // above. If we get this far then we want the Wrapper to be able to do everything // necessary to stop the JVM. AccessController.doPrivileged( new PrivilegedAction() { public Object run() { privilegedStopInner( exitCode ); return null; } } ); } /** * Tells the native wrapper that the JVM wants to shut down, then informs * all listeners that the JVM is about to shutdown before killing the JVM. *

* This method requests that the JVM be shutdown but then returns. This * allows components to initiate a JVM exit and then continue, allowing * a normal shutdown initiated by the JVM via shutdown hooks. In * applications which are designed to be shutdown when the user presses * CTRL-C, this may result in a cleaner shutdown. * * @param exitCode The exit code that the Wrapper will return when it exits. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("stop" ) permission. * * @see WrapperPermission */ public static void stopAndReturn( final int exitCode ) { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "stop" ) ); } m_stoppingInit = true; synchronized( WrapperManager.class ) { if ( m_stopping ) { if ( m_debug ) { m_outDebug.println( getRes().getString( "{0} called by thread: {1} (already stopping)", "WrapperManager.stopAndReturn(" + exitCode + ")", Thread.currentThread().getName() ) ); } return; } else { if ( m_debug ) { m_outDebug.println( getRes().getString( "{0} called by thread: {1}", "WrapperManager.stopAndReturn(" + exitCode + ")", Thread.currentThread().getName() ) ); } } } // To make this possible, we have to create a new thread to actually do the shutdown. Thread stopper = new Thread( "Wrapper-Stopper" ) { public void run() { stopCommon( exitCode, 1000 ); // This is safe because we are already checking for the privilege to stop the JVM // above. If we get this far then we want the Wrapper to be able to do everything // necessary to stop the JVM. AccessController.doPrivileged( new PrivilegedAction() { public Object run() { privilegedStopInner( exitCode ); return null; } } ); } }; stopper.setDaemon( false ); stopper.start(); } /** * Tells the native wrapper that the JVM wants to shut down and then * promptly halts. Be careful when using this method as an application * will not be given a chance to shutdown cleanly. * * @param exitCode The exit code that the Wrapper will return when it exits. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("stopImmediate") permission. * * @see WrapperPermission */ public static void stopImmediate( final int exitCode ) { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "stopImmediate" ) ); } if ( m_debug ) { m_outDebug.println( getRes().getString( "{0} called by thread: {1}", "WrapperManager.stopImmediate(" + exitCode + ")", Thread.currentThread().getName() ) ); } stopCommon( exitCode, 250 ); signalStopped( exitCode ); // Really all done. if ( m_debug ) { m_outDebug.println( getRes().getString( "WrapperManager stopped due to {0}", getRes().getString( "Halt" ) ) ); } m_stopped = true; Runtime.getRuntime().halt( exitCode ); } /** * Signal the native wrapper that the startup is progressing but that more * time is needed. The current startup timeout will be extended if * necessary so it will be at least 'waitHint' milliseconds in the future. *

* This call will have no effect if the current startup timeout is already * more than 'waitHint' milliseconds in the future. * * @param waitHint Time in milliseconds to allow for the startup to * complete. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("signalStarting") permission. * * @see WrapperPermission */ public static void signalStarting( int waitHint ) { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "signalStarting" ) ); } sendCommand( WRAPPER_MSG_START_PENDING, Integer.toString( waitHint ) ); } /** * Signal the native wrapper that the shutdown is progressing but that more * time is needed. The current shutdown timeout will be extended if * necessary so it will be at least 'waitHint' milliseconds in the future. *

* This call will have no effect if the current shutdown timeout is already * more than 'waitHint' milliseconds in the future. * * @param waitHint Time in milliseconds to allow for the shutdown to * complete. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("signalStopping") permission. * * @see WrapperPermission */ public static void signalStopping( int waitHint ) { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "signalStopping" ) ); } m_stopping = true; sendCommand( WRAPPER_MSG_STOP_PENDING, Integer.toString( waitHint ) ); } /** * This method should not normally be called by user code as it is called * from within the stop and restart methods. However certain applications * which stop the JVM may need to call this method to let the wrapper code * know that the shutdown was intentional. * * @param exitCode The exit code. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("signalStopped") permission. * * @see WrapperPermission */ public static void signalStopped( int exitCode ) { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "signalStopped" ) ); } m_stopping = true; sendCommand( WRAPPER_MSG_STOPPED, Integer.toString( exitCode ) ); // Give the socket time to actuall send the packet to the Wrapper // as this call is often immediately followed by a halt command. try { Thread.sleep( 250 ); } catch ( InterruptedException e ) { // Ignore. } } /** * Returns true if the ShutdownHook for the JVM has already been triggered. * Some code needs to know whether or not the system is shutting down. * * @return True if the ShutdownHook for the JVM has already been triggered. */ public static boolean hasShutdownHookBeenTriggered() { return m_hookTriggered; } /** * Suspends the timeouts used by the Wrapper to monitor the JVM. * A call to this method can be made before performing a long blocking task * to avoid having the Wrapper consider the application as unresponsive. *

* Timeouts may also be suspended using actions specified in the configuration * file (filters, timers, etc.), or from the command file. *

* If several requests to suspend timeouts are made, the number of seconds * specified by each request will not be summed. Instead the newly specified * time will replace the remaining suspension time if it is longer, and will * be ignored otherwise. * * @param seconds Number of seconds to suspend timeouts. */ public static void suspendTimeouts( int seconds ) { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "suspendTimeouts" ) ); } if ( isStandardEdition() ) { if ( seconds < 1 ) { throw new IllegalArgumentException( getRes().getString( "The specified number of seconds is invalid." ) ); } else if ( seconds > 32767 ) { /* Clip to the maximum value of a 2-byte c integer (32767) to avoid wrap arounds. * The value will be clipped again in the c code. This is done to control the * range with the same conditions regardless of whether the timeouts were * suspended from this method, Wrapper actions or the command file. */ seconds = 32767; } sendCommand( WRAPPER_MSG_SUSPEND_TIMEOUTS, Integer.toString( seconds ) ); } } /** * Resumes Wrapper timeouts if they were previously suspended * (see suspendTimeouts()). * A call to this method can be made after completing a long blocking task * to inform the Wrapper that it can start monitoring again the JVM normally. *

* Timeouts may also be resumed using actions specified in the configuration * file (filters, timers, etc.), or from the command file. */ public static void resumeTimeouts() { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "resumeTimeouts" ) ); } if ( isStandardEdition() ) { sendCommand( WRAPPER_MSG_RESUME_TIMEOUTS, "" ); } } /** * Requests that the Wrapper log a message at the specified log level. * If the JVM is not being managed by the Wrapper then calls to this * method will be ignored. This method has been optimized to ignore * messages at a log level which will not be logged given the current * log levels of the Wrapper. *

* Log messages will currently by trimmed by the Wrapper at 4k (4096 bytes). *

* Because of differences in the way console output is collected and * messages logged via this method, it is expected that interspersed * console and log messages will not be in the correct order in the * resulting log file. *

* This method was added to allow simple logging to the wrapper.log * file. This is not meant to be a full featured log file and should * not be used as such. Please look into a logging package for most * application logging. * * @param logLevel The level to log the message at can be one of * WRAPPER_LOG_LEVEL_DEBUG, WRAPPER_LOG_LEVEL_INFO, * WRAPPER_LOG_LEVEL_STATUS, WRAPPER_LOG_LEVEL_WARN, * WRAPPER_LOG_LEVEL_ERROR, or WRAPPER_LOG_LEVEL_FATAL. * @param message The message to be logged. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("log") permission. * * @see WrapperPermission */ public static void log( int logLevel, String message ) { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "log" ) ); } // Make sure that the logLevel is valid to avoid problems with the // command sent to the server. if ( ( logLevel < WRAPPER_LOG_LEVEL_DEBUG ) || ( logLevel > WRAPPER_LOG_LEVEL_NOTICE ) ) { throw new IllegalArgumentException( getRes().getString( "The specified logLevel is not valid." ) ); } if ( message == null ) { throw new IllegalArgumentException( getRes().getString( "The message parameter can not be null." ) ); } if ( m_lowLogLevel <= logLevel ) { sendCommand( (byte)( WRAPPER_MSG_LOG + logLevel ), message ); } } /** * Returns an array of all registered services. This method is only * supported on Windows platforms which support services. Calling this * method on other platforms will result in null being returned. * * @return An array of services. * * @throws SecurityException If a SecurityManager has not been set in the * JVM or if the calling code has not been * granted the WrapperPermission "listServices" * permission. A SecurityManager is required * for this operation because this method makes * it possible to learn a great deal about the * state of the system. * * @see WrapperPermission */ public static WrapperWin32Service[] listServices() throws SecurityException { SecurityManager sm = System.getSecurityManager(); if ( sm == null ) { throw new SecurityException( getRes().getString( "A SecurityManager has not yet been set." ) ); } else { sm.checkPermission( new WrapperPermission( "listServices" ) ); } if ( isNativeLibraryOk() ) { return nativeListServices(); } else { return null; } } /** * Sends a service control code to the specified service. The state of the * service should be tested on return. If the service was not currently * running then the control code will not be sent. *

* The control code sent can be one of the system control codes: * WrapperManager.SERVICE_CONTROL_CODE_START, * WrapperManager.SERVICE_CONTROL_CODE_STOP, * WrapperManager.SERVICE_CONTROL_CODE_PAUSE, * WrapperManager.SERVICE_CONTROL_CODE_CONTINUE, or * WrapperManager.SERVICE_CONTROL_CODE_INTERROGATE. In addition, user * defined codes in the range 128-255 can also be sent. * * @param serviceName Name of the Windows service which will receive the * control code. * @param controlCode The actual control code to be sent. User defined * control codes should be in the range 128-255. * * @return A WrapperWin32Service containing the last known status of the * service after sending the control code. This will be null if * the currently platform is not a version of Windows which * supports services. * * @throws WrapperServiceException If there are any problems accessing the * specified service. * @throws SecurityException If a SecurityManager has not been set in the * JVM or if the calling code has not been * granted the WrapperServicePermission * permission for the specified service and * control code. A SecurityManager is required * for this operation because this method makes * it possible to control any service on the * system, which is of course rather dangerous. * * @see WrapperServicePermission */ public static WrapperWin32Service sendServiceControlCode( String serviceName, int controlCode ) throws WrapperServiceException, SecurityException { SecurityManager sm = System.getSecurityManager(); if ( sm == null ) { throw new SecurityException( getRes().getString( "A SecurityManager has not yet been set." ) ); } else { String action; switch( controlCode ) { case SERVICE_CONTROL_CODE_START: action = WrapperServicePermission.ACTION_START; break; case SERVICE_CONTROL_CODE_STOP: action = WrapperServicePermission.ACTION_STOP; break; case SERVICE_CONTROL_CODE_PAUSE: action = WrapperServicePermission.ACTION_PAUSE; break; case SERVICE_CONTROL_CODE_CONTINUE: action = WrapperServicePermission.ACTION_CONTINUE; break; case SERVICE_CONTROL_CODE_INTERROGATE: action = WrapperServicePermission.ACTION_INTERROGATE; break; default: if ( ( controlCode >= 128 ) && ( controlCode <= 255 ) ) { action = WrapperServicePermission.ACTION_USER_CODE; } else { throw new IllegalArgumentException( getRes().getString( "The specified controlCode is invalid." ) ); } break; } sm.checkPermission( new WrapperServicePermission( serviceName, action ) ); } WrapperWin32Service service = null; if ( isNativeLibraryOk() ) { service = nativeSendServiceControlCode( serviceName, controlCode ); } return service; } /** * Adds a WrapperEventListener which will receive WrapperEvents. The * specific events can be controlled using the mask parameter. This API * was chosen to allow for additional events in the future. * * To avoid future compatibility problems, WrapperEventListeners should * always test the class of an event before making use of it. This will * avoid problems caused by new event classes added in future versions * of the Wrapper. * * This method should only be called once for a given WrapperEventListener. * Build up a single mask to receive events of multiple types. * * @param listener WrapperEventListener to be start receiving events. * @param mask A mask specifying the event types that the listener is * interrested in receiving. See the WrapperEventListener * class for a full list of flags. A mask is created by * combining multiple flags using the binary '|' OR operator. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the appropriate * WrapperEventPermission(...) permission. * * @see WrapperEventPermission */ public static void addWrapperEventListener( WrapperEventListener listener, long mask ) { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { StringBuffer sb = new StringBuffer(); boolean first = true; if ( ( mask & WrapperEventListener.EVENT_FLAG_SERVICE ) != 0 ) { first = false; sb.append( WrapperEventPermission.EVENT_TYPE_SERVICE ); } if ( ( mask & WrapperEventListener.EVENT_FLAG_CONTROL ) != 0 ) { if ( first ) { first = false; } else { sb.append( "," ); } sb.append( WrapperEventPermission.EVENT_TYPE_CONTROL ); } if ( ( mask & WrapperEventListener.EVENT_FLAG_REMOTE_CONTROL ) != 0 ) { if ( first ) { first = false; } else { sb.append( "," ); } sb.append( WrapperEventPermission.EVENT_TYPE_REMOTE_CONTROL ); } if ( ( mask & WrapperEventListener.EVENT_FLAG_CORE ) != 0 ) { if ( first ) { first = false; } else { sb.append( "," ); } sb.append( WrapperEventPermission.EVENT_TYPE_CORE ); } sm.checkPermission( new WrapperEventPermission( sb.toString() ) ); } synchronized( WrapperManager.class ) { WrapperEventListenerMask listenerMask = new WrapperEventListenerMask(); listenerMask.m_listener = listener; listenerMask.m_mask = mask; m_wrapperEventListenerMaskList.add( listenerMask ); m_wrapperEventListenerMasks = null; } updateWrapperEventListenerFlags(); } /** * Removes a WrapperEventListener so it will not longer receive WrapperEvents. * * @param listener WrapperEventListener to be stop receiving events. * * @throws SecurityException If a SecurityManager is present and the * calling thread does not have the * WrapperPermission("removeWrapperEventListener") * permission. * * @see WrapperPermission */ public static void removeWrapperEventListener( WrapperEventListener listener ) { SecurityManager sm = System.getSecurityManager(); if ( sm != null ) { sm.checkPermission( new WrapperPermission( "removeWrapperEventListener" ) ); } synchronized( WrapperManager.class ) { // Look for the first instance of a given listener in the list. for ( Iterator iter = m_wrapperEventListenerMaskList.iterator(); iter.hasNext(); ) { WrapperEventListenerMask listenerMask = (WrapperEventListenerMask)iter.next(); if ( listenerMask.m_listener == listener ) { iter.remove(); m_wrapperEventListenerMasks = null; break; } } } updateWrapperEventListenerFlags(); } /** * Returns the Log file currently being used by the Wrapper. If log file * rolling is enabled in the Wrapper then this file may change over time. * * @throws IllegalStateException If this method is called before the Wrapper * instructs this class to start the user * application. * * @return The Wrapper log file. */ public static File getWrapperLogFile() { File logFile = m_logFile; if ( logFile == null ) { throw new IllegalStateException( getRes().getString( "Not yet initialized." ) ); } return logFile; } /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * This class can not be instantiated. */ private WrapperManager() { } /*--------------------------------------------------------------- * Private methods *-------------------------------------------------------------*/ /** * Checks for the existence of a SecurityManager and then makes sure that * the Wrapper jar has been granted AllPermissions. If not then a warning * will be displayed as this will most likely result in the Wrapper * failing to function correctly. * * This method is called at various points in the startup as it is possible * and in fact likely that any SecurityManager will be set by user code * during or shortly after initialization. Once a SecurityManager has * been located and tested then this method will become a noop. */ private static void checkSecurityManager() { if ( m_securityManagerChecked ) { return; } SecurityManager securityManager = System.getSecurityManager(); if ( securityManager != null ) { if ( m_debug ) { m_outDebug.println(getRes().getString( "Detected a SecurityManager: {0} " , securityManager.getClass().getName() ) ); } try { securityManager.checkPermission( new java.security.AllPermission() ); } catch ( SecurityException e ) { m_outDebug.println(); m_outDebug.println( getRes().getString( "WARNING - Detected that a SecurityManager has been installed but the " ) ); m_outDebug.println( getRes().getString( " wrapper.jar has not been granted the java.security.AllPermission" ) ); m_outDebug.println( getRes().getString( " permission. This will most likely result in SecurityExceptions" ) ); m_outDebug.println( getRes().getString( " being thrown by the Wrapper." ) ); m_outDebug.println(); } // Always set the flag. m_securityManagerChecked = true; } } /** * Returns an array of WrapperEventListenerMask instances which can * be safely used outside of synchronization. * * @return An array of WrapperEventListenerMask instances. */ private static WrapperEventListenerMask[] getWrapperEventListenerMasks() { WrapperEventListenerMask[] listenerMasks = m_wrapperEventListenerMasks; if ( listenerMasks == null ) { synchronized( WrapperManager.class ) { if ( listenerMasks == null ) { listenerMasks = new WrapperEventListenerMask[m_wrapperEventListenerMaskList.size()]; m_wrapperEventListenerMaskList.toArray( listenerMasks ); m_wrapperEventListenerMasks = listenerMasks; } } } return listenerMasks; } /** * Updates the internal flags based on the WrapperEventListeners currently * registered. */ private static void updateWrapperEventListenerFlags() { boolean core = false; WrapperEventListenerMask[] listenerMasks = getWrapperEventListenerMasks(); for ( int i = 0; i < listenerMasks.length; i++ ) { long mask = listenerMasks[i].m_mask; // See whether particular event types are required. core = core | ( ( mask & WrapperEventListener.EVENT_FLAG_CORE ) != 0 ); } m_produceCoreEvents = core; } /** * Notifies registered listeners that an event has been fired. * * @param event Event to notify the listeners of. */ private static void fireWrapperEvent( WrapperEvent event ) { long eventMask = event.getFlags(); WrapperEventListenerMask[] listenerMasks = getWrapperEventListenerMasks(); for ( int i = 0; i < listenerMasks.length; i++ ) { long listenerMask = listenerMasks[i].m_mask; // See if the event should be passed to this listner. if ( ( listenerMask & eventMask ) != 0 ) { // The listener wants the event. WrapperEventListener listener = listenerMasks[i].m_listener; try { listener.fired( event ); } catch ( Throwable t ) { m_outError.println( getRes().getString( "Encountered an uncaught exception while notifying WrapperEventListener of an event:" ) ); t.printStackTrace( m_outError ); } } } } /** * Executed code common to the stop and stopImmediate methods. */ private static void stopCommon( int exitCode, int delay ) { boolean stopping; synchronized( WrapperManager.class ) { stopping = m_stopping; if ( !stopping ) { m_stopping = true; } } if ( !stopping ) { // Always send the stop command sendCommand( WRAPPER_MSG_STOP, Integer.toString( exitCode ) ); if ( delay > 0 ) { // Give the Wrapper a chance to register the stop command before stopping. // This avoids any errors thrown by the Wrapper because the JVM died before // it was expected to. if ( m_debug ) { m_outDebug.println( getRes().getString( "Pausing for {0}ms to allow a clean shutdown...", new Integer( delay ) ) ); } try { Thread.sleep( delay ); } catch ( InterruptedException e ) { } } } } /** * Dispose of all resources used by the WrapperManager. Closes the server * socket which is used to listen for events from the Wrapper process. */ private static void dispose() { synchronized( WrapperManager.class ) { m_disposed = true; // Close the open backend if it exists. closeBackend(); // Give the Connection Thread a chance to stop itself. try { Thread.sleep( 500 ); } catch ( InterruptedException e ) { } // Dispose the Native resources if ( isNativeLibraryOk() ) { nativeDispose( m_debug ); } } } /** * Called by startInner when the WrapperListner.start method has completed. * * Only called when WrapperManager.class is synchronized. */ private static void startCompleted() { m_startedTicks = getTicks(); // Let the startup thread die since the application has been started. m_startupRunner = null; // Check the SecurityManager here as it is possible that it was set in the // listener's start method. checkSecurityManager(); // Signal that the application has started. signalStarted(); // Wake up any threads waiting for this. WrapperManager.class.notifyAll(); } /** * Informs the listener that it should start. * * WrapperManager.class will be synchronized when called. * * @param block True if this call should block for the WrapperListener.start * method to complete. This is true when java is being run in * standalone mode without the Wrapper. */ private static void startInner( boolean block ) { m_starting = true; // Do any setup which should happen just before we actually start the application. checkTmpDir(); // This method can be called from the connection thread which must be a // daemon thread by design. We need to call the WrapperListener.start method // from a non-daemon thread. This means that if the current thread is a // daemon we need to launch a new thread while we wait for the start method // to return. if ( m_listener == null ) { if ( m_debug ) { m_outDebug.println( getRes().getString( "No WrapperListener has been set. Nothing to start." ) ); } startCompleted(); } else { if ( m_debug ) { m_outDebug.println( getRes().getString( "calling WrapperListener.start()" ) ); } // These arrays aren't pretty, but we need final variables for the inline // class and this makes it possible to get the values back. final Integer[] resultF = new Integer[1]; final Throwable[] tF = new Throwable[1]; // Start in a dedicated thread. Thread startRunner = new Thread( "WrapperListener_start_runner" ) { public void run() { if ( m_debug ) { m_outDebug.println( getRes().getString( "WrapperListener.start runner thread started." ) ); } // If the calling thread was the Backend handler then it is running at a higher priority. // We need to restore the thread priority to the default before proceeding to make sure // that the rest of the application runs at the correct priority. Thread thisThread = Thread.currentThread(); thisThread.setPriority( Thread.NORM_PRIORITY ); if ( m_debug ) { m_outDebug.println( getRes().getString( "Application start main thread: {0} Priority: {1}", thisThread.getName(), new Integer( thisThread.getPriority() ) ) ); } try { // This is user code, so don't trust it. try { resultF[0] = m_listener.start( m_args ); } catch ( Throwable t ) { tF[0] = t; } } finally { // Make sure the rest of this thread does not fall behind the application. thisThread.setPriority( Thread.MAX_PRIORITY ); // Now that we are back, handle the results. if ( tF[0] != null ) { m_outError.println( getRes().getString( "Error in WrapperListener.start callback. {0}", tF[0] ) ); tF[0].printStackTrace( m_outError ); // Kill the JVM, but don't tell the wrapper that we want to stop. // This may be a problem with this instantiation only. privilegedStopInner( 1 ); // Won't make it here. return; } if ( m_debug ) { m_outDebug.println( getRes().getString( "returned from WrapperListener.start()" ) ); } if ( resultF[0] != null ) { int exitCode = resultF[0].intValue(); if ( m_debug ) { m_outDebug.println( getRes().getString( "WrapperListener.start() returned an exit code of {0}.", new Integer( exitCode ) ) ); } // Signal the native code. WrapperManager.stop( exitCode ); // Won't make it here. return; } synchronized( WrapperManager.class ) { startCompleted(); } if ( m_debug ) { m_outDebug.println( getRes().getString( "WrapperListener.start runner thread stopped." ) ); } } } }; startRunner.setDaemon( false ); startRunner.start(); if ( block ) { // Wait for the start runner to complete. if ( m_debug ) { m_outDebug.println( getRes().getString( "Waiting for WrapperListener.start runner thread to complete." ) ); } while ( ( startRunner != null ) && ( startRunner.isAlive() ) ) { try { WrapperManager.class.wait(); } catch ( InterruptedException e ) { // Ignore and keep waiting. } } } } } private static void shutdownJVM( int exitCode ) { if ( m_debug ) { m_outDebug.println(getRes().getString( "shutdownJVM({0}) Thread: {1}", new Integer( exitCode ), Thread.currentThread().getName() ) ); } // Make sure that any shutdown locks are released. waitForShutdownLocks(); // Signal that the application has stopped and the JVM is about to shutdown. signalStopped( exitCode ); // Dispose the wrapper. dispose(); m_shutdownJVMComplete = true; // Do not call System.exit if this is the ShutdownHook if ( Thread.currentThread() == m_hook ) { // This is the shutdown hook, so fall through because things are // already shutting down. } else { if ( m_debug ) { m_outDebug.println(getRes().getString( "calling System.exit({0})", new Integer( exitCode ) ) ); } safeSystemExit( exitCode ); } } /** * A user ran into a JVM bug where a call to System exit was causing an * IllegalThreadStateException to be thrown. Not sure how widespread * this problem is. But it is easy to avoid it causing serious problems * for the wrapper. */ private static void safeSystemExit( int exitCode ) { // Really all done. if ( m_debug ) { m_outDebug.println( getRes().getString( "WrapperManager stopped due to {0}", getRes().getString( "System Exit" ) ) ); } m_stopped = true; try { System.exit( exitCode ); } catch ( IllegalThreadStateException e ) { m_outError.println( getRes().getString( "Attempted System.exit({0}) call failed: {1}" , new Integer( exitCode ), e.toString() ) ); m_outError.println( getRes().getString( " Trying Runtime.halt({0})", new Integer( exitCode ) ) ); Runtime.getRuntime().halt( exitCode ); } } /** * Informs the listener that the JVM will be shut down. * * This should only be called from within a PrivilegedAction or in a * context that came from a PrivilegedAction. * * This method will only return if called from the Communications thread. */ private static void privilegedStopInner( final int exitCode ) { if ( Thread.currentThread() == m_commRunner ) { // The stop is being initiated from the Communications thread. // It needs to handle further communication, so stop in the background. Thread stopper = new Thread( "Wrapper-Stopper" ) { public void run() { if ( m_debug ) { m_outDebug.println( getRes().getString( "Stopper started" ) ); } privilegedStopInner( exitCode ); // Will never get back here. } }; stopper.setDaemon( true ); stopper.start(); // Wait until stopping starts. synchronized( WrapperManager.class ) { while ( !m_stopping ) { if ( m_debug ) { m_outDebug.println( getRes().getString( "Waiting for Stopper thread..." ) ); } try { WrapperManager.class.wait( 50 ); } catch ( InterruptedException e ) { } } return; } } boolean block; synchronized( WrapperManager.class ) { // Always set the stopping flag. (Depending on how the shutdown was initiated, it may already be set.) m_stopping = true; // Only one thread can be allowed to continue. if ( m_stoppingThread == null ) { m_stoppingThread = Thread.currentThread(); block = false; } else { if ( Thread.currentThread() == m_stoppingThread ) { throw new IllegalStateException( getRes().getString( "WrapperManager.stop() can not be called recursively." ) ); } block = true; } } if ( block ) { if ( m_debug ) { m_outDebug.println( getRes().getString( "Thread, {0}, waiting for the JVM to exit.", Thread.currentThread().getName() ) ); if ( Thread.currentThread() == m_hook ) { if ( !m_hookRemoveFailed ) { m_outDebug.println( getRes().getString( "System.exit appears to have been called from within the\n WrapperListener.stop() method. If possible the application\n should be modified to avoid this behavior.\n To avoid a deadlock, this thread will only wait 5 seconds\n for the application to shutdown. This may result in the\n application failing to shutdown completely before the JVM\n exits. Removing the offending System.exit call will\n resolve this." ) ); } } } // This thread needs to be put into an infinite loop until the JVM exits. // This thread can not be allowed to return to the caller, but another // thread is already responsible for shutting down the JVM, so this // one can do nothing but wait. int loops = 0; int wait = 50; while( true ) { try { Thread.sleep( wait ); } catch ( InterruptedException e ) { } // If this is the wrapper's shutdown hook then we only want to loop until // the shutdownJVM method has completed. We will only get into this state // if user code calls System.exit from within the WrapperListener.stop // method. Failing to return here will cause the shutdown process to hang. // If the user code calls System.exit directly in the stop method then the // m_shutdownJVMComplete flag will never be set. Always time out after // 5 seconds so the JVM will not hang in such cases. if ( Thread.currentThread() == m_hook ) { if ( m_shutdownJVMComplete || ( loops > 5000 / wait ) ) { if ( !m_shutdownJVMComplete ) { if ( m_debug ) { m_outDebug.println( getRes().getString( "Thread, {0}, continuing after 5 seconds.", Thread.currentThread().getName() ) ); } } // To keep the wrapper from showing a JVM exited unexpectedly message // on shutdown, tell the wrapper that we are ready to stop. // If the WrapperListener.stop method is taking a long time, we will // also get here. In that case, the Wrapper will still wait for // the configured exit timeout before killing the JVM process. // In theory, the shutdown process of an application will only call // System.exit after the shutdown is complete so this should be Ok. // Use the exit code from the thread which initiated the call rather // than this call as that one is the one we really want. signalStopped( m_exitCode ); return; } } loops++; } } if ( m_debug ) { m_outDebug.println( getRes().getString( "Thread, {0}, handling the shutdown process.", Thread.currentThread().getName() ) ); } m_exitCode = exitCode; // If appropriate, unregister the shutdown hook. This must be done before the // user stop code is called to avoid nested shutdowns. if ( Thread.currentThread() != m_hook ) { // We do not want the ShutdownHook to execute, so unregister it before calling exit. // It can't be unregistered if it has already fired however. The only way that this // could happen is if user code calls System.exit from within the listener stop // method. if ( ( !m_hookTriggered ) && ( m_hook != null ) ) { // Remove the shutdown hook. try { Runtime.getRuntime().removeShutdownHook( m_hook ); } catch ( AccessControlException e ) { // This can happen if the security policy is not setup correctly. m_outError.println( getRes().getString( "Unable to remove the Wrapper''s shutdownhook: {0}", e ) ); m_hookRemoveFailed = true; } } } // Only stop the listener if the app has been asked to start. Does not need to have actually started. int code = exitCode; if ( ( m_listenerForceStop && m_starting ) || m_started ) { // This method can be called from the connection thread which must be a // daemon thread by design. We need to call the WrapperListener.stop method // from a non-daemon thread. This means that if the current thread is a // daemon we need to launch a new thread while we wait for the stop method // to return. if ( m_listener == null ) { if ( m_debug ) { m_outDebug.println( getRes().getString( "No WrapperListener has been set. Nothing to stop." ) ); } } else { if ( m_debug ) { m_outDebug.println( getRes().getString( "calling listener.stop()" ) ); } if ( Thread.currentThread().isDaemon() ) { // This array isn't pretty, but we need final variables for the inline // class and this makes it possible to get the values back. final Integer[] codeF = new Integer[] {new Integer(code)}; // Start in a dedicated thread. Thread stopRunner = new Thread( "WrapperListener_stop_runner" ) { public void run() { if ( m_debug ) { m_outDebug.println( getRes().getString( "WrapperListener.stop runner thread started." ) ); } // If the calling thread was the Backend handler then it is running at a higher priority. // We need to restore the thread priority to the default before proceeding to make sure // that the rest of the application runs at the correct priority. Thread thisThread = Thread.currentThread(); thisThread.setPriority( Thread.NORM_PRIORITY ); if ( m_debug ) { m_outDebug.println( getRes().getString( "Application stop main thread: {0} Priority: {1}", thisThread.getName(), new Integer( thisThread.getPriority() ) ) ); } try { // This is user code, so don't trust it. try { codeF[0] = new Integer( m_listener.stop( codeF[0].intValue() ) ); } catch ( Throwable t ) { m_outError.println( getRes().getString( "Error in WrapperListener.stop callback." ) ); t.printStackTrace( m_outError ); } } finally { if ( m_debug ) { m_outDebug.println( getRes().getString( "WrapperListener.stop runner thread stopped." ) ); } } } }; stopRunner.setDaemon( false ); stopRunner.start(); // Wait for the stop runner to complete. if ( m_debug ) { m_outDebug.println( getRes().getString( "Waiting for WrapperListener.stop runner thread to complete." ) ); } while ( ( stopRunner != null ) && ( stopRunner.isAlive() ) ) { try { stopRunner.join(); stopRunner = null; } catch ( InterruptedException e ) { // Ignore and keep waiting. } } // Get the exit code back from the array. code = codeF[0].intValue(); } else { // This is user code, so don't trust it. try { code = m_listener.stop( code ); } catch ( Throwable t ) { m_outError.println( getRes().getString( "Error in WrapperListener.stop callback." ) ); t.printStackTrace( m_outError ); } } if ( m_debug ) { m_outDebug.println( getRes().getString( "returned from listener.stop() -> {0}", new Integer( code ) ) ); } } } shutdownJVM( code ); } private static void signalStarted() { sendCommand( WRAPPER_MSG_STARTED, "" ); m_started = true; } /** * Called by the native code when a control event is trapped by native code. * Can have the values: WRAPPER_CTRL_C_EVENT, WRAPPER_CTRL_CLOSE_EVENT, * WRAPPER_CTRL_LOGOFF_EVENT, WRAPPER_CTRL_SHUTDOWN_EVENT, * WRAPPER_CTRL_TERM_EVENT, or WRAPPER_CTRL_HUP_EVENT. */ private static void controlEvent( int event ) { String eventName; boolean ignore; switch( event ) { case WRAPPER_CTRL_C_EVENT: eventName = "WRAPPER_CTRL_C_EVENT"; ignore = m_ignoreSignals; break; case WRAPPER_CTRL_CLOSE_EVENT: eventName = "WRAPPER_CTRL_CLOSE_EVENT"; ignore = m_ignoreSignals; break; case WRAPPER_CTRL_LOGOFF_EVENT: eventName = "WRAPPER_CTRL_LOGOFF_EVENT"; ignore = false; break; case WRAPPER_CTRL_SHUTDOWN_EVENT: eventName = "WRAPPER_CTRL_SHUTDOWN_EVENT"; ignore = false; break; case WRAPPER_CTRL_TERM_EVENT: eventName = "WRAPPER_CTRL_TERM_EVENT"; ignore = m_ignoreSignals; break; case WRAPPER_CTRL_HUP_EVENT: eventName = "WRAPPER_CTRL_HUP_EVENT"; ignore = m_ignoreSignals; break; case WRAPPER_CTRL_USR1_EVENT: eventName = "WRAPPER_CTRL_USR1_EVENT"; ignore = m_ignoreSignals; break; case WRAPPER_CTRL_USR2_EVENT: eventName = "WRAPPER_CTRL_USR2_EVENT"; ignore = m_ignoreSignals; break; default: eventName = getRes().getString( "Unexpected event: {0}", new Integer( event ) ); ignore = false; break; } WrapperControlEvent controlEvent = new WrapperControlEvent( event, eventName ); if ( ignore ) { // Preconsume the event if it is set to be ignored, but go ahead and fire it so // user can can still have the oportunity to recognize it. controlEvent.consume(); } fireWrapperEvent( controlEvent ); if ( ignore ) { // Event will always be consumed. if ( m_debug ) { m_outDebug.println( getRes().getString( "Ignoring control event({0})", eventName ) ); } } else { if ( controlEvent.isConsumed() ) { if ( m_debug ) { m_outDebug.println(getRes().getString( "Control event({0}) was consumed by user listener.", eventName ) ); } } else { if ( m_debug ) { m_outDebug.println(getRes().getString( "Processing control event({0})", eventName ) ); } // This is user code, so don't trust it. if ( m_listener != null ) { try { m_listener.controlEvent( event ); } catch ( Throwable t ) { m_outError.println( getRes().getString( "Error in WrapperListener.controlEvent callback." ) ); t.printStackTrace( m_outError ); } } else { // A listener was never registered. Always respond by exiting. // This can happen if the user does not initialize things correctly. stop( 0 ); } } } } /** * Parses a long tab separated string of properties into an internal * properties object. Actual tabs are escaped by real tabs. */ private static char PROPERTY_SEPARATOR = '\t'; private static void readProperties( String rawProps ) { WrapperProperties properties = new WrapperProperties(); int len = rawProps.length(); int first = 0; while ( first < len ) { StringBuffer sb = new StringBuffer(); boolean foundEnd = false; do { int pos = rawProps.indexOf( PROPERTY_SEPARATOR, first ); if ( pos >= 0 ) { if ( pos > 0 ) { sb.append( rawProps.substring( first, pos ) ); } if ( pos < len - 1 ) { if ( rawProps.charAt( pos + 1 ) == PROPERTY_SEPARATOR ) { // Two separators in a row, it was escaped. sb.append( PROPERTY_SEPARATOR ); first = pos + 2; } else { foundEnd = true; first = pos + 1; } } else { foundEnd = true; first = pos + 1; } } else { // No more separators. The rest is the last property. sb.append( rawProps.substring( first ) ); foundEnd = true; first = len; } } while ( !foundEnd ); String property = sb.toString(); // Parse the property. int pos = property.indexOf( '=' ); if ( pos > 0 ) { String key = property.substring( 0, pos ); String value; if ( pos < property.length() - 1 ) { value = property.substring( pos + 1 ); } else { value = ""; } properties.setProperty( key, value ); // Process special properties if ( key.equals( "wrapper.ignore_user_logoffs" ) ) { m_ignoreUserLogoffs = value.equalsIgnoreCase( "true" ); } } } // Lock the properties object and store it. properties.lock(); m_properties = properties; } /** * Opens a socket to the Wrapper process. */ private static synchronized void openBackendSocket() { if ( m_debug ) { m_outDebug.println( getRes().getString( "Open socket to Wrapper...{0}", Thread.currentThread().getName() ) ); } InetAddress iNetAddress; try { iNetAddress = InetAddress.getByName( m_wrapperPortAddress ); } catch ( UnknownHostException e ) { // This is pretty fatal. m_outError.println( getRes().getString( "Unable to resolve localhost name: {0}", e ) ); m_outError.println( getRes().getString( "Exiting JVM..." ) ); stop( 1 ); return; // please the compiler } // If the user has specified a specific port to use then we want to try that first. boolean connected = false; int tryPort; boolean fixedPort; if ( m_jvmPort >= 0 ) { tryPort = m_jvmPort; fixedPort = true; } else { tryPort = m_jvmPortMin; fixedPort = false; } // Loop until we find a port we can connect using. SocketException causeException = null; do { // On Windows if we attempt to open a socket with a port that was recently used and still in a TIME_WAIT // state, then Windows will sometimes cause a 4227 Event entry to be logged in the System EventLog. // It is not possible in Java to check on the state of a port before using it so we do so in native // code. This check is not critical other than this warning, so fall through to the normal attempt if // we are unable to precheck the port status for any reason. int portStatus; if ( isNativeLibraryOk() ) { try { portStatus = nativeGetPortStatus( tryPort, m_wrapperPortAddress, ( m_backendType == BACKEND_TYPE_SOCKET_V6 ? 1 : 0 ) ); } catch ( UnsatisfiedLinkError e ) { // This can happen if an old native library is used. Fall through. m_outError.println( getRes().getString( "Unable to precheck status of port {0} due to: {1}", new Integer( tryPort ), e.toString() ) ); portStatus = -1; } } else { portStatus = -1; } // 0 means the port is available, <0 means there was an error checking its status. // The check is an optimization to avoid messages in the Event Log. // Go ahead and try using the port anyway. if ( portStatus <= 0 ) { try { m_backendSocket = new Socket( iNetAddress, m_port, iNetAddress, tryPort ); if ( m_debug ) { m_outDebug.println(getRes().getString( "Opened Socket from {0} to {1}", new Integer( tryPort ), new Integer( m_port ) ) ); } connected = true; break; } catch ( SocketException e ) { String eMessage = e.getMessage(); if ( e instanceof ConnectException ) { m_outError.println(getRes().getString( "Failed to connect to the Wrapper at port {0}. Cause: {1}", new Integer( m_port ), e ) ); // This is fatal because there is nobody listening. m_outError.println( "Exiting JVM..." ); stopImmediate( 1 ); // For compiler m_backendSocket = null; return; } else if ( ( e instanceof BindException ) || ( ( eMessage != null ) && ( ( eMessage.indexOf( "errno: 48" ) >= 0 ) || ( eMessage.indexOf( "Address already in use" ) >= 0 ) ) || ( eMessage.indexOf( "Unrecognized Windows Sockets error: 0: JVM_Bind" ) >= 0 ) ) ) /* This message is caused by a JVM Bug: http://bugs.sun.com/view_bug.do?bug_id=6965962 */ { // Most Java implementations throw a BindException when the port is in use, // but FreeBSD throws a SocketException with a specific message. // This happens if the local port is already in use. In this case, we want // to loop and try again. if ( m_debug ) { m_outDebug.println( getRes().getString( "Unable to open socket to Wrapper from port {0}, already in use.", new Integer( tryPort ) ) ); } // Keep this exception around in case we need to log it. if ( causeException == null ) { causeException = e; } } else { // Unexpected exception. m_outError.println( getRes().getString( "Unexpected exception opening backend socket: {0}", e ) ); m_backendSocket = null; return; } } catch ( IOException e ) { m_outError.println( getRes().getString( "Unable to open backend socket: {0}", e ) ); m_backendSocket = null; return; } } else { // The native port status returned was not 0. if ( m_debug ) { m_outDebug.println( getRes().getString( "Unable to open socket to Wrapper from port {0}, already in use. ({1})", new Integer( tryPort ), Integer.toString( portStatus ) ) ); } } if ( fixedPort ) { // The last port checked was the fixed port, switch to the dynamic range. tryPort = m_jvmPortMin; fixedPort = false; } else { tryPort++; } } while ( tryPort <= m_jvmPortMax ); if ( connected ) { if ( ( m_jvmPort >= 0 ) && ( m_jvmPort != tryPort ) ) { m_outInfo.println(getRes().getString( "Port {0} already in use, using port {1} instead.", new Integer( m_jvmPort ), new Integer( tryPort ) ) ); } } else { String causeMessage; if ( causeException == null ) { causeMessage = getRes().getString( "Port already in use." ); } else { causeMessage = causeException.toString(); } if ( m_jvmPortMax > m_jvmPortMin ) { m_outError.println( getRes().getString( "Failed to connect to the Wrapper at port {0} by binding to any ports in the range {1} to {2}. Cause: {3}", new Integer( m_port ), new Integer( m_jvmPortMin ), new Integer( m_jvmPortMax ), causeMessage ) ); } else { m_outError.println(getRes().getString( "Failed to connect to the Wrapper at port {0} by binding to port {1}. Cause: {2}", new Integer( m_port ), new Integer( m_jvmPortMin ), causeMessage ) ); } // This is fatal because there is nobody listening. m_outError.println( getRes().getString( "Exiting JVM..." ) ); stopImmediate( 1 ); } // Now that we have a connected socket, continue on to configure it. try { // Turn on the TCP_NODELAY flag. This is very important for speed!! m_backendSocket.setTcpNoDelay( true ); // If configured, set the SO_TIMEOUT for the socket (max block time) if ( m_soTimeout >= 0 ) { if ( m_debug ) { m_outDebug.println( getRes().getString( "Setting backend socket SO_TIMEOUT to {0}ms from {1}ms.", new Integer( m_soTimeout ), new Integer( m_backendSocket.getSoTimeout() ) ) ); } m_backendSocket.setSoTimeout( m_soTimeout ); } m_backendOS = m_backendSocket.getOutputStream(); m_backendIS = m_backendSocket.getInputStream(); } catch ( IOException e ) { m_outError.println( e ); closeBackend(); return; } m_backendConnected = true; } private static synchronized void openBackendPipe() { String s; if ( WrapperManager.isWindows() ) { s = "\\\\.\\pipe\\wrapper-" + WrapperManager.getWrapperPID() + "-" + WrapperManager.getJVMId(); } else { s = "/tmp/wrapper-" + WrapperManager.getWrapperPID() + "-" + WrapperManager.getJVMId(); } try { m_backendIS = new FileInputStream( new File( s + "-out") ); m_backendOS = new FileOutputStream( new File( s + "-in" ) ); } catch ( IOException e ) { m_outInfo.println( "write error " + e ); e.printStackTrace(); closeBackend(); return; } catch (Exception ex) { ex.printStackTrace(); closeBackend(); return; } m_backendConnected = true; } private static synchronized void openBackend() { m_backendConnected = false; if ( m_backendType == BACKEND_TYPE_PIPE ) { openBackendPipe(); } else { openBackendSocket(); } if ( !m_backendConnected ) { m_outError.println( getRes().getString( "The backend could not be initialized. Restart to resync with the Wrapper." ) ); restart(); // Will not get here. return; } // The backend is open. // Send the key back to the wrapper so that the wrapper can feel safe // that it is talking to the correct JVM sendCommand( WRAPPER_MSG_KEY, m_key ); // If there is a stop pending then send it immediately now. if ( m_pendingStopMessage != null ) { m_outDebug.println( getRes().getString( "Resend pending packet {0} : {1}", getPacketCodeName( WRAPPER_MSG_STOP ), m_pendingStopMessage ) ); sendCommand( WRAPPER_MSG_STOP, m_pendingStopMessage ); m_pendingStopMessage = null; } } private static synchronized void closeBackend() { if ( m_backendConnected ) { if ( m_debug ) { m_outDebug.println( getRes().getString( "Closing backend connection." ) ); } // To avoid the case where a child process is launched by we fail to notify the Wrapper, // we need to wait until there are no longer any child processes in the process of being launched. long start = System.currentTimeMillis(); while ( m_runningExecs > 0 ) { if ( m_debug ) { m_outDebug.println( getRes().getString( "Waiting for {0} threads to finish launching child processes...", new Integer( m_runningExecs ) ) ); } try { WrapperManager.class.wait( 1000 ); } catch ( InterruptedException e ) { } if ( System.currentTimeMillis() - start > 30000 ) { m_outError.println( getRes().getString( "Timed out waiting for {0} threads to finish launching child processes.", new Integer( m_runningExecs ) ) ); break; } } // Clear the connected flag first so other threads will recognize that we // are closing correctly. m_backendConnected = false; // On HP platforms, an attempt to close a socket while a read is blocking will // cause the close to block until the read completes. To avoid this, send an // interrupt to the communications runner to abort any existing reads. Thread commRunner = m_commRunner; if ( commRunner != null ) { try { commRunner.interrupt(); } catch ( SecurityException e ) { m_outError.println( getRes().getString( "Failed to interrupt communications thread: {0}", e.getMessage() ) ); } } } if ( m_backendOS != null ) { try { m_backendOS.close(); } catch ( IOException e ) { if ( m_debug ) { m_outDebug.println( getRes().getString( "Unable to close backend output stream: {0}", e.toString() ) ); } } m_backendOS = null; } if ( m_backendIS != null ) { // Closing m_backendIS here will block on some platforms. Let the Wrapper end it cleanup. m_backendIS = null; } if ( m_backendSocket != null ) { try { m_backendSocket.close(); } catch ( IOException e ) { if ( m_debug ) { m_outDebug.println( getRes().getString( "Unable to close backend socket: {0}", e.toString() ) ); } } m_backendSocket = null; } } private static String getPacketCodeName( byte code ) { String name; switch ( code ) { case WRAPPER_MSG_START: name ="START"; break; case WRAPPER_MSG_STOP: name ="STOP"; break; case WRAPPER_MSG_RESTART: name ="RESTART"; break; case WRAPPER_MSG_PING: name ="PING"; break; case WRAPPER_MSG_STOP_PENDING: name ="STOP_PENDING"; break; case WRAPPER_MSG_START_PENDING: name ="START_PENDING"; break; case WRAPPER_MSG_STARTED: name ="STARTED"; break; case WRAPPER_MSG_STOPPED: name ="STOPPED"; break; case WRAPPER_MSG_JAVA_PID: name ="JAVA_PID"; break; case WRAPPER_MSG_KEY: name ="KEY"; break; case WRAPPER_MSG_BADKEY: name ="BADKEY"; break; case WRAPPER_MSG_LOW_LOG_LEVEL: name ="LOW_LOG_LEVEL"; break; case WRAPPER_MSG_PING_TIMEOUT: /* No longer used. */ name ="PING_TIMEOUT"; break; case WRAPPER_MSG_SERVICE_CONTROL_CODE: name ="SERVICE_CONTROL_CODE"; break; case WRAPPER_MSG_PROPERTIES: name ="PROPERTIES"; break; case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_DEBUG: name ="LOG(DEBUG)"; break; case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_INFO: name ="LOG(INFO)"; break; case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_STATUS: name ="LOG(STATUS)"; break; case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_WARN: name ="LOG(WARN)"; break; case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_ERROR: name ="LOG(ERROR)"; break; case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_FATAL: name ="LOG(FATAL)"; break; case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_ADVICE: name ="LOG(ADVICE)"; break; case WRAPPER_MSG_LOG + WRAPPER_LOG_LEVEL_NOTICE: name ="LOG(NOTICE)"; break; case WRAPPER_MSG_CHILD_LAUNCH: name ="CHILD_LAUNCH"; break; case WRAPPER_MSG_CHILD_TERM: name ="CHILD_TERM"; break; case WRAPPER_MSG_LOGFILE: name ="LOGFILE"; break; case WRAPPER_MSG_CHECK_DEADLOCK: name ="CHECK_DEADLOCK"; break; case WRAPPER_MSG_DEADLOCK: name ="DEADLOCK"; break; case WRAPPER_MSG_APPEAR_ORPHAN: /* No longer used. */ name ="APPEAR_ORPHAN"; break; case WRAPPER_MSG_PAUSE: name ="PAUSE"; break; case WRAPPER_MSG_RESUME: name ="RESUME"; break; case WRAPPER_MSG_GC: name ="GC"; break; case WRAPPER_MSG_FIRE_USER_EVENT: name ="FIRE_USER_EVENT"; break; case WRAPPER_MSG_SECOND_INVOCATION_EVENT: name ="SECOND_INVOCATION_EVENT"; break; case WRAPPER_MSG_FIRE_CTRL_EVENT: name ="FIRE_CTRL_EVENT"; break; case WRAPPER_MSG_SUSPEND_TIMEOUTS: name ="SUSPEND_TIMEOUTS"; break; case WRAPPER_MSG_RESUME_TIMEOUTS: name ="RESUME_TIMEOUTS"; break; default: name = "UNKNOWN(" + code + ")"; break; } return name; } /** * Send a command to the Wrapper. * * @param code Command to send. * @param message Message to send with the command. */ private static synchronized void sendCommand( byte code, String message ) { if ( m_debug ) { // We want to show these messages even if we are pretending to be hung. if ( ( code == WRAPPER_MSG_PING ) && ( message.startsWith( "silent" ) ) ) { // m_outDebug.println( "Send silent ping packet." ); } else if ( !m_backendConnected ) { m_outDebug.println( getRes().getString( "Backend not connected, not sending packet {0} : {1}", getPacketCodeName( code ), message ) ); if ( code == WRAPPER_MSG_STOP ) { // Store this message so we can send it later if and when we connect. m_pendingStopMessage = message; } } else { m_outDebug.println( getRes().getString( "Send a packet {0} : {1}", getPacketCodeName( code ) , message ) ); } } boolean sentCommand = false; if ( m_appearHung ) { // The WrapperManager is attempting to make the JVM appear hung, so do nothing } else { if ( ( code == WRAPPER_MSG_START_PENDING ) || ( code == WRAPPER_MSG_STARTED ) ) { // Set the last ping time so that the startup process does not time out // thinking that the JVM has not received a Ping for too long. m_lastPingTicks = getTicks(); } // If the backend is open, then send the command, otherwise just throw it away. if ( m_backendConnected ) { try { // It is possible that a logged message is quite large. Expand the size // of the command buffer if necessary so that it can be included. This // means that the command buffer will be the size of the largest message. byte[] messageBytes = message.getBytes(); if ( m_commandBuffer.length < messageBytes.length + 2 ) { m_commandBuffer = new byte[messageBytes.length + 2]; } // Writing the bytes one by one was sometimes causing the first byte to be lost. // Try to work around this problem by creating a buffer and sending the whole lot // at once. m_commandBuffer[0] = code; System.arraycopy( messageBytes, 0, m_commandBuffer, 1, messageBytes.length ); int len = messageBytes.length + 2; m_commandBuffer[len - 1] = 0; m_backendOS.write( m_commandBuffer, 0, len ); m_backendOS.flush(); sentCommand = true; } catch ( IOException e ) { m_outError.println( e ); e.printStackTrace( m_outError ); closeBackend(); } } } if ( !sentCommand ) { // We failed to send a command. Some commands require that we log it as it could cause later problems. switch ( code ) { case WRAPPER_MSG_CHILD_LAUNCH: m_outError.println( getRes().getString( "Failed to notify the Wrapper process that child with PID={0} was launched. The Wrapper will not be able to make sure it is terminated when the Java process exits.", message ) ); break; case WRAPPER_MSG_CHILD_TERM: if ( m_debug ) { m_outDebug.println( getRes().getString( "Failed to notify the Wrapper process that child with PID={0} completed. The Wrapper will recover on its own.", message ) ); } break; default: break; } } } /** * Loop reading packets from the native side of the Wrapper until the * connection is closed or the WrapperManager class is disposed. * Each packet consists of a packet code followed by a null terminated * string up to 256 characters in length. If the entire packet has not * yet been received, then it must not be read until the complete packet * has arived. */ private static byte[] m_backendReadBuffer = new byte[256]; private static void handleBackend() { WrapperPingEvent pingEvent = new WrapperPingEvent(); PrintStream out_prev = m_out; PrintStream err_prev = m_err; try { if ( m_debug ) { m_outDebug.println( getRes().getString( "handleBackend()" ) ); } DataInputStream is = new DataInputStream( m_backendIS ); while ( !m_disposed ) { try { if ( m_debug ) { // Print debug messages whenever the standard output streams are reassigned. if ( out_prev != System.out ) { m_outDebug.println( getRes().getString( "WARNING - {0} has been reassigned by the Java Application to an instance of {1}\n Depending on the implementation of {1}, log output to {0} may no longer be redirected to the Wrapper log or console.", "STDOUT", System.out.getClass().getName() ) ); out_prev = System.out; } if ( err_prev != System.err ) { m_outDebug.println( getRes().getString( "WARNING - {0} has been reassigned by the Java Application to an instance of {1}\n Depending on the implementation of {1}, log output to {0} may no longer be redirected to the Wrapper log or console.", "STDERR", System.err.getClass().getName() ) ); err_prev = System.err; } } // A Packet code must exist. byte code = is.readByte(); // Always read from the buffer until a null '\0' is encountered. // A multi-byte string will never have a 0 as part of another character so this should be safe for all encodings. byte b; int i = 0; do { b = is.readByte(); if ( b != 0 ) { if ( i >= m_backendReadBuffer.length ) { byte[] tmp = m_backendReadBuffer; m_backendReadBuffer = new byte[tmp.length + 256]; System.arraycopy( tmp, 0, m_backendReadBuffer, 0, tmp.length ); } m_backendReadBuffer[i] = b; i++; } } while ( b != 0 ); // The message should be a multi-byte UTF-8 string (except on z/OS where the system encoding is used). String msg; if ( !isZOS() ) { msg = new String( m_backendReadBuffer, 0, i, "UTF-8" ); } else { msg = new String( m_backendReadBuffer, 0, i ); } if ( m_appearHung ) { // The WrapperManager is attempting to make the JVM appear hung, // so ignore all incoming requests } else { if ( m_debug ) { String logMsg; if ( code == WRAPPER_MSG_PROPERTIES ) { // The property values are very large and distracting in the log. // Plus if any triggers are defined, then logging them will fire // the trigger. logMsg = getRes().getString( "(Property Values, Size={0})", Integer.toString( i ) ); } else { logMsg = msg; } // Don't log silent pings. if ( ( code == WRAPPER_MSG_PING ) && ( msg.startsWith( "silent" ) ) ) { //m_outDebug.println( "Received silent ping packet." ); } else { m_outDebug.println( getRes().getString( "Received a packet {0} : {1}", getPacketCodeName( code ) , logMsg ) ); } } if ( m_slowSeconds > 0 ) { // We have been asked to be sluggish in the JVM's response. if ( m_debug ) { m_outDebug.println( getRes().getString( " Delay packet processing by {0} seconds.", new Integer( m_slowSeconds ) ) ); } try { Thread.sleep( m_slowSeconds * 1000 ); } catch ( InterruptedException e ) { } } // Ok, we got a packet. Do something with it. switch( code ) { case WRAPPER_MSG_START: // Don't start if we are already starting to stop. if ( m_stoppingInit) { if ( m_debug ) { m_outDebug.println( getRes().getString( "Java stop initiated. Skipping application startup." ) ); } } else { startInner( false ); } break; case WRAPPER_MSG_STOP: if ( m_stopping ) { // Don't do anything if we are already stopping if ( m_debug ) { m_outDebug.println( getRes().getString( "Already stopping" ) ); } } else { privilegedStopInner( 0 ); // Should never get back here. } break; case WRAPPER_MSG_PING: m_lastPingTicks = getTicks(); sendCommand( WRAPPER_MSG_PING, msg ); if ( m_produceCoreEvents ) { fireWrapperEvent( pingEvent ); } break; case WRAPPER_MSG_CHECK_DEADLOCK: boolean deadLocked = checkDeadlocks(); if ( deadLocked ) { sendCommand( WRAPPER_MSG_DEADLOCK, "deadLock" ); } break; case WRAPPER_MSG_BADKEY: // The key sent to the wrapper was incorrect. We need to shutdown. m_outError.println( getRes().getString("Authorization key rejected by Wrapper." ) ); m_outError.println( getRes().getString( "Exiting JVM..." ) ); closeBackend(); privilegedStopInner( 1 ); break; case WRAPPER_MSG_LOW_LOG_LEVEL: try { m_lowLogLevel = Integer.parseInt( msg ); m_debug = ( m_lowLogLevel <= WRAPPER_LOG_LEVEL_DEBUG ); if ( m_debug ) { m_outDebug.println( getRes().getString( "LowLogLevel from Wrapper is {0}", new Integer( m_lowLogLevel ) ) ); } } catch ( NumberFormatException e ) { m_outError.println( getRes().getString( "Encountered an Illegal LowLogLevel from the Wrapper: {0}", msg ) ); } break; case WRAPPER_MSG_PING_TIMEOUT: /* No longer used. This is still here in case a mix of versions are used. */ break; case WRAPPER_MSG_SERVICE_CONTROL_CODE: try { int serviceControlCode = Integer.parseInt( msg ); if ( m_debug ) { m_outDebug.println( getRes().getString( "ServiceControlCode from Wrapper with code {0}", new Integer( serviceControlCode ) ) ); } WrapperServiceControlEvent event = new WrapperServiceControlEvent( serviceControlCode ); fireWrapperEvent( event ); } catch ( NumberFormatException e ) { m_outError.println( getRes().getString( "Encountered an Illegal ServiceControlCode from the Wrapper: {0}", msg ) ); } break; case WRAPPER_MSG_PAUSE: try { int actionSourceCode = Integer.parseInt( msg ); if ( m_debug ) { m_outDebug.println( getRes().getString( "Pause from Wrapper with action source: {0}", WrapperServicePauseEvent.getSourceCodeName( actionSourceCode ) ) ); } WrapperServicePauseEvent event = new WrapperServicePauseEvent( actionSourceCode ); fireWrapperEvent( event ); } catch ( NumberFormatException e ) { m_outError.println( getRes().getString( "Encountered an Illegal action source code from the Wrapper: {0}", msg ) ); } break; case WRAPPER_MSG_RESUME: try { int actionSourceCode = Integer.parseInt( msg ); if ( m_debug ) { m_outDebug.println( getRes().getString("Resume from Wrapper with action source: {0}", WrapperServiceResumeEvent.getSourceCodeName( actionSourceCode ) ) ); } WrapperServiceResumeEvent event = new WrapperServiceResumeEvent( actionSourceCode ); fireWrapperEvent( event ); } catch ( NumberFormatException e ) { m_outError.println( getRes().getString( "Encountered an Illegal action source code from the Wrapper: {0}", msg ) ); } break; case WRAPPER_MSG_GC: try { int actionSourceCode = Integer.parseInt( msg ); if ( m_debug ) { m_outDebug.println( getRes().getString( "Garbage Collection request from Wrapper with action source: {0}", WrapperServiceActionEvent.getSourceCodeName( actionSourceCode ) ) ); } System.gc(); } catch ( NumberFormatException e ) { m_outError.println( getRes().getString( "Encountered an Illegal action source code from the Wrapper: {0}", msg ) ); } break; case WRAPPER_MSG_PROPERTIES: readProperties( msg ); nativeLoadWrapperProperties(); break; case WRAPPER_MSG_LOGFILE: m_logFile = new File( msg ); WrapperLogFileChangedEvent event = new WrapperLogFileChangedEvent( m_logFile ); fireWrapperEvent( event ); break; case WRAPPER_MSG_SECOND_INVOCATION_EVENT: WrapperSecondInvocationEvent secondInvocationEvent = new WrapperSecondInvocationEvent( ); fireWrapperEvent( secondInvocationEvent ); sendCommand( WRAPPER_MSG_SECOND_INVOCATION_EVENT, "" ); break; case WRAPPER_MSG_FIRE_CTRL_EVENT: if ( m_listener != null ) { if ( msg.equals( "WRAPPER_CTRL_LOGOFF_EVENT" ) ) { controlEvent( WRAPPER_CTRL_LOGOFF_EVENT ); } else if ( msg.equals( "WRAPPER_CTRL_SHUTDOWN_EVENT" ) ) { controlEvent( WRAPPER_CTRL_SHUTDOWN_EVENT ); } } break; default: // Ignore unknown messages m_outInfo.println( getRes().getString( "Wrapper code received an unknown packet type: {0}", new Integer( code ) ) ); break; } } } catch ( SocketTimeoutException e ) { if ( m_debug ) { m_outDebug.println( getRes().getString( "Backend socket timed out. Attempting to continue. (SO_TIMEOUT={0}ms.)", new Integer( m_backendSocket.getSoTimeout() ) ) ); } } } if ( m_debug ) { m_outDebug.println( getRes().getString( "Backend handler loop completed. Disposed: {0}", ( m_disposed ? "True" : "False" ) ) ); } return; } catch ( SocketException e ) { if ( m_debug ) { if ( m_backendSocket == null ) { // This error happens if the socket is closed while reading: // java.net.SocketException: Descriptor not a socket: JVM_recv in socket // input stream read m_outDebug.println( getRes().getString( "Closed backend socket (Normal): {0}", e ) ); } else { m_outDebug.println( getRes().getString( "Closed backend socket: {0}", e ) ); } } return; } catch ( IOException e ) { // This means that the connection was closed. Allow this to return. if ( m_debug ) { m_outDebug.println( getRes().getString( "Closed backend: {0}", e ) ); } return; } } /** * Starts up the thread to handle backend communications. * * If the Wrapper did not launch the JVM then the internal state * will be set to started to allow the application startup to continue. */ private static void startRunner() { if ( isControlledByNativeWrapper() ) { if ( m_commRunner == null ) { // Create and launch a new thread to manage this connection m_commRunner = new Thread( m_instance, WRAPPER_CONNECTION_THREAD_NAME ); m_commRunner.setDaemon( true ); m_commRunner.start(); } // Wait to give the runner a chance to connect. synchronized( WrapperManager.class ) { while ( !m_commRunnerStarted ) { try { WrapperManager.class.wait( 100 ); } catch ( InterruptedException e ) { } } } } else { // Immediately mark the runner as started as it will never be used. synchronized( WrapperManager.class ) { m_commRunnerStarted = true; WrapperManager.class.notifyAll(); } } } /*--------------------------------------------------------------- * Runnable Methods *-------------------------------------------------------------*/ /** * The main comm runner thread. * This will only be called when the Wrapper is controlling the JVM. */ public void run() { // Make sure that no other threads call this method. if ( Thread.currentThread() != m_commRunner ) { throw new IllegalStateException( getRes().getString( "Only the communications runner thread is allowed to call this method." ) ); } if ( m_debug ) { m_outDebug.println( getRes().getString( "Communications runner thread started." ) ); } // This thread needs to have a very high priority so that it never // gets put behind other threads. Thread.currentThread().setPriority( Thread.MAX_PRIORITY ); // Initialize the last ping tick count. m_lastPingTicks = getTicks(); boolean forceShutdown = false; try { openBackend(); // After the socket has been opened the first time, mark the thread as // started. This must be done here to make sure that exits work correctly // when called on startup. if ( !m_commRunnerStarted ) { synchronized( WrapperManager.class ) { m_commRunnerStarted = true; WrapperManager.class.notifyAll(); } } // Make sure that the version of the Wrapper is correct. verifyWrapperVersion(); // Make sure that the native library's version is correct. if ( m_libraryLoaded ) { verifyNativeLibraryVersion(); } if ( !m_wrapperVersionOk || !m_libraryVersionOk || !m_libraryLoaded) { // Just in case we fail in the stop() call below, set this flag to indicate that we still want to exit. forceShutdown = true; // We need to wait until the backend is opened before stopping the JVM, // else the Wrapper can't be informed that the JVM requested to stop. stop( 1 ); } else { // Send the Java PID back to the wrapper so it can confirm it has not // changed since the fork (may happen when the JVM was launched through // a script). This needs to be done after we know the library is ok // because we get the PID from a native function. sendCommand( WRAPPER_MSG_JAVA_PID, Integer.toString( getJavaPID() ) ); } if ( m_backendSocket != null || m_backendConnected == true) { handleBackend(); } else { // Failed, so wait for just a moment try { Thread.sleep( 100 ); } catch ( InterruptedException e ) { } } } catch ( ThreadDeath td ) { m_outError.println( getRes().getString( "Server daemon killed" ) ); } catch ( Throwable t ) { if ( !isShuttingDown() ) { // Show a stack trace here because this is fairly critical m_outError.println( getRes().getString( "Server daemon died!" ) ); t.printStackTrace( m_outError ); } else { if ( m_debug ) { m_outDebug.println( getRes().getString( "Server daemon died!" ) ); t.printStackTrace( m_outDebug ); } } } finally { if ( m_debug ) { m_outDebug.println( getRes().getString( "Returned from backend handler." ) ); } if ( forceShutdown ) { safeSystemExit( 1 ); } else { // Always close the backend here. closeBackend(); if ( !isShuttingDown() ) { if ( m_detachStarted && m_started ) { // This and all further output will not be visible anywhere as the Wrapper is now gone. m_outInfo.println( getRes().getString( "The backend was closed as expected." ) ); if ( isNativeLibraryOk() ) { nativeRedirectPipes(); } else { m_outError.println( getRes().getString( "Failed to redirect stdout and stderr before the Wrapper exits.\nOutput from the JVM may block.\nPlease make sure the native library has been properly initialized.")); } } else { m_outError.println( getRes().getString( "The backend was closed unexpectedly. Restart to resync with the Wrapper." ) ); restart(); // Will not get here. } } } } // Make sure that noone is ever left waiting for this thread to start. synchronized( WrapperManager.class ) { if ( !m_commRunnerStarted ) { m_commRunnerStarted = true; WrapperManager.class.notifyAll(); } } if ( m_debug ) { m_outDebug.println( getRes().getString( "Server daemon shut down" ) ); } } /*--------------------------------------------------------------- * Inner Classes *-------------------------------------------------------------*/ /** * Mapping between WrapperEventListeners and their registered masks. * This is necessary to support the case where the same listener is * registered more than once. It also makes it possible to reference * an array of these mappings without synchronization. */ private static class WrapperEventListenerMask { private WrapperEventListener m_listener; private long m_mask; } private static class WrapperTickEventImpl extends WrapperTickEvent { private int m_ticks; private int m_tickOffset; /** * Returns the tick count at the point the event is fired. * * @return The tick count at the point the event is fired. */ public int getTicks() { return m_ticks; } /** * Returns the offset between the tick count used by the Wrapper for time * keeping and the tick count generated directly from the system time. * * This will be 0 in most cases. But will be a positive value if the * system time is ever set back for any reason. It will be a negative * value if the system time is set forward or if the system is under * heavy load. If the wrapper.use_system_time property is set to TRUE * then the Wrapper will be using the system tick count for internal * timing and this value will always be 0. * * @return The tick count offset. */ public int getTickOffset() { return m_tickOffset; } } /** * When the JVM is being controlled by the Wrapper, stdin can not be used * as it is undefined. This class makes it possible to provide the user * application with a descriptive error message if System.in is accessed. */ private static class WrapperInputStream extends InputStream { /** * This method will always throw an IOException as the read method is * not valid. */ public int read() throws IOException { m_outInfo.println( getRes().getString( "WARNING - System.in has been disabled by the wrapper.disable_console_input property. Calls will block indefinitely." ) ); // Go into a loop that will never return. while ( true ) { synchronized( this ) { try { this.wait(); } catch ( InterruptedException e ) { // Ignore. } } } } } /** * Scans the JVM for deadlocked threads. *

* Standard Edition feature. * * @see #isStandardEdition() * @since Wrapper 3.5.0 */ private static boolean checkDeadlocks() throws WrapperLicenseError { if ( isStandardEdition() ) { boolean result = false; if ( isNativeLibraryOk() ) { result = nativeCheckDeadLocks(); } else { if ( m_debug ) { m_outDebug.println( getRes().getString( "Deadlock check skipped. Native call unavailable." ) ); } result = false; } return result; } else { return false; } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperPrintStream.java100644 0 0 6747 14333053652 25070 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.io.PrintStream; import java.io.UnsupportedEncodingException; /** * Not all methods are currently overridden as this is an internal class. */ final class WrapperPrintStream extends PrintStream { private String m_header; /** * Creates a new WrapperPrintStream wrapped around another PrintStream. * * @param out The PrintStream which will be wrapped by this new stream. * @param header The header to append at the beginning of any printed messages. */ WrapperPrintStream( PrintStream parent, String header ) { super( parent ); m_header = header; } /** * Creates a new WrapperPrintStream wrapped around another PrintStream. * * @param out The PrintStream which will be wrapped by this new stream. * @param autoFlush Whether the output buffer should be automatically flushed or not. The value will be passed to the parent stream. * @param encoding The name of a supported character encoding. The value will be passed to the parent stream. * @param header The header to append at the beginning of any printed messages. * * @throws UnsupportedEncodingException If the named encoding is not supported. */ WrapperPrintStream( PrintStream parent, boolean autoFlush, String encoding, String header ) throws UnsupportedEncodingException { super( parent, autoFlush, encoding ); m_header = header; } /** * Terminate the current line by writing the line separator string. The * line separator string is defined by the system property * line.separator, and is not necessarily a single newline * character ('\n'). */ public void println() { super.println( m_header ); } /** * Print a String and then terminate the line. This method behaves as * though it invokes {@link #print(String)} and then * {@link #println()}. * * @param x The String to be printed. */ public void println( String x ) { if ( x.indexOf( "\n" ) >= 0 ) { String[] lines = x.split( "[\n]", -1 ); StringBuffer sb = new StringBuffer(); for ( int i = 0; i < lines.length; i++ ) { if ( i > 0 ) { sb.append( "\n" ); } sb.append( m_header ); sb.append( lines[i] ); } super.println( sb.toString() ); } else { super.println( m_header + x ); } } /** * Print an Object and then terminate the line. This method behaves as * though it invokes {@link #print(Object)} and then * {@link #println()}. * * @param x The Object to be printed. */ public void println( Object x ) { if ( x == null ) { println( "null" ); } else { println( x.toString() ); } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperProcess.java100644 0 0 21065 14333053652 24244 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.io.InputStream; import java.io.IOException; import java.io.OutputStream; /** * A WrapperProcess is returned by a call to WrapperManager.exec and can * be used to reference the new child process after it was launched. * * @author Tanuki Software Development Team <support@tanukisoftware.com> * @since Wrapper 3.4.0 */ public class WrapperProcess { private WrapperProcessOutputStream m_wpis; private WrapperProcessInputStream m_wpos; private WrapperProcessInputStream m_wpes; /* The PID of the process. */ private int m_pid; private int m_pgid; private int m_exitcode; private boolean m_isDetached; private int m_softShutdownTimeout; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * The default constructor */ private WrapperProcess() { m_exitcode = Integer.MIN_VALUE; } /*--------------------------------------------------------------- * Native Methods *-------------------------------------------------------------*/ private native boolean nativeIsAlive(); private native void nativeDestroy(); private native void nativeExitValue(); private native void nativeWaitFor(); /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Finalize method. */ protected void finalize() throws Throwable { try { //System.out.println( m_pid + " finalized"); m_wpes.close(); m_wpis.close(); m_wpos.close(); } finally { super.finalize(); } } /** * Returns the PID of the process. * * @return The PID of the process. */ public int getPID() { return m_pid; } /** * Returns the PGID of the process. * * @return The PGID of the process. */ public int getPGID() { return m_pgid; } /** * Gets the input stream of the subprocess. The stream obtains data piped * from the standard output stream of the process represented by this * WrapperProcess object. *

* This is an alias of the getStdOut() method. *

* Implementation note: It is a good idea for the input stream to be * buffered. * * @return The input stream connected to the normal output of the * subprocess. * * @throws IOException If we are unable to access the stream. */ public InputStream getInputStream() throws IOException { return m_wpos; } /** * Gets the input stream of the subprocess. The stream obtains data piped * from the standard output stream of the process represented by this * WrapperProcess object. *

* This is an alias of the getInputStream() method. *

* Implementation note: It is a good idea for the input stream to be * buffered. * * @return The input stream connected to the normal output of the * subprocess. * * @throws IOException If we are unable to access the stream. */ public InputStream getStdOut() throws IOException { return m_wpos; } /** * Gets the error stream of the subprocess. The stream obtains data piped * from the error output stream of the process represented by this * WrapperProcess object. *

* This is an alias of the getStdErr() method. *

* Implementation note: It is a good idea for the input stream to be * buffered. * * @return The input stream connected to the error stream of the * subprocess. * * @throws IOException If we are unable to access the stream. */ public InputStream getErrorStream() throws IOException { return m_wpes; } /** * Gets the error stream of the subprocess. The stream obtains data piped * from the error output stream of the process represented by this * WrapperProcess object. *

* This is an alias of the getErrorStream() method. *

* Implementation note: It is a good idea for the input stream to be * buffered. * * @return The input stream connected to the error stream of the * subprocess. * * @throws IOException If we are unable to access the stream. */ public InputStream getStdErr() throws IOException { return m_wpes; } /** * Gets the output stream of the subprocess. Output to the stream is piped * into the standard input stream of the process represented by this * WrapperProcess object. *

* This is an alias of the getStdIn() method. *

* Implementation note: It is a good idea for the output stream to be * buffered. * * @return The output stream connected to the normal input of the * subprocess. * * @throws IOException If we are unable to access the stream. */ public OutputStream getOutputStream() throws IOException { return m_wpis; } /** * Gets the output stream of the subprocess. Output to the stream is piped * into the standard input stream of the process represented by this * WrapperProcess object. *

* This is an alias of the getOutputStream() method. *

* Implementation note: It is a good idea for the output stream to be * buffered. * * @return The output stream connected to the normal input of the * subprocess. * * @throws IOException If we are unable to access the stream. */ public OutputStream getStdIn() throws IOException { return m_wpis; } /** * Causes the current thread to wait, if necessary, until the process * represented by this Process object has terminated. This method returns * immediately if the subprocess has already terminated. If the subprocess * has not yet terminated, the calling thread will be blocked until the * subprocess exits. * * @return The exit value of the process. By convention, 0 indicates normal * termination. * * @throws InterruptedException If the current thread is interrupted by * another thread while it is waiting, then * the wait is ended and an * InterruptedException is thrown. */ public int waitFor() throws InterruptedException { if ( m_exitcode == Integer.MIN_VALUE ) { nativeWaitFor(); } return m_exitcode; } /** * Returns the exit value for the subprocess. * * @return The exit value of the subprocess represented by this * WrapperProcess object. By convention, the value 0 * indicates normal termination. * * @throws IllegalThreadStateException if the process is still alive * and has not yet teminated. */ public int exitValue() throws IllegalThreadStateException { if ( m_exitcode == Integer.MIN_VALUE ) { nativeExitValue(); } return m_exitcode; } /** * Returns true if the process is still alive. * @throws WrapperLicenseError If the function is called other than in * the Professional Edition or from a Standalone JVM. * @return True if the process is alive, false if it has terminated. */ public boolean isAlive() { return nativeIsAlive(); } /** * Kills the subprocess. The subprocess represented by this Process object * is forcibly terminated if it is still running. * * @throws WrapperLicenseError If the function is called other than in * the Professional Edition or from a Standalone JVM. */ public void destroy() { nativeDestroy(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperProcessConfig.java100644 0 0 50210 14333053652 25364 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.io.File; import java.io.IOException; import java.util.Iterator; import java.util.Map; import java.util.HashMap; import org.tanukisoftware.wrapper.WrapperLicenseError; /** * With WrapperProcessConfig Class the startup configuration for the Process * can be passed to the WrapperManager.exec methods. The configuration makes * it possible to control the way the OS spawns the child process, specify * environment variables, working directory, and how the Wrapper should handle * process when the JVM exits. Please review each of the methods a more * detailed explanation of how they work. *

* The setter methods are designed to be optionally be chained as follows: *

 * WrapperProcess proc = WrapperManager.exec( "command", new WrapperProcessConfig().setDetached( true ).setStartType( WrapperProcessConfig.POSIX_SPAWN ) );
 * 
* * @author Tanuki Software Development Team <support@tanukisoftware.com> * @since Wrapper 3.4.0 */ public final class WrapperProcessConfig { public static final int POSIX_SPAWN = 1; public static final int FORK_EXEC = 2; public static final int VFORK_EXEC = 3; public static final int DYNAMIC = 4; private boolean m_isDetached; private boolean m_isInteractive; private File m_defdir; private int m_startType; private boolean m_isNewProcessGroup; private boolean m_isAutoCloseInputStreams; private Map m_environment; private int m_softShutdownTimeout; private native String[] nativeGetEnv(); private static native boolean isSupportedNative( int startType ); /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a default configuration. * * @throws WrapperLicenseError If the function is called other than in * the Professional Edition or if the native * library has not been loaded. */ public WrapperProcessConfig() { WrapperManager.assertProfessionalEdition(); m_isDetached = false; m_defdir = null; m_startType = DYNAMIC; m_isNewProcessGroup = true; if ( WrapperManager.isWindows() ) { m_isAutoCloseInputStreams = false; } else { m_isAutoCloseInputStreams = true; } m_environment = null; setSoftShutdownTimeout( 5 ); m_isInteractive = false; } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Indicates whether the specified start type is supported on the current * plattform. * * @param startType The start type to test. * * @return true if supported, false otherwise. On Windows, this method always returns * true. * * @throws WrapperLicenseError If the function is called other than in * the Professional Edition or if the native * library has not been loaded. * @throws IllegalArgumentException If the startType is invalid. */ public static boolean isSupported( int startType ) throws WrapperLicenseError, IllegalArgumentException { WrapperManager.assertProfessionalEdition(); verifyStartType( startType ); if ( WrapperManager.isNativeLibraryOk() ) { return isSupportedNative( startType ); } else { return false; } } /** * Returns the detached flag. * * @return The detached flag. */ public boolean isDetached() { return m_isDetached; } /** * Sets the detached flag. This makes it possible to control whether or * not the Wrapper will terminate any child processes launched by a JVM * when that JVM exits or crashes. * Note that when running the Wrapper as daemon with systemd, systemd * may kill all child processes (including detached ones) on shutdown. * To prevent this, please adjust the value of the SYSTEMD_KILLMODE * property in the Wrapper Shell script. * * @param detached If false the Wrapper will remember that the process was * launched and then make sure that it is terminated when * the JVM exits. * * @return This configration to allow chaining. */ public WrapperProcessConfig setDetached( boolean detached ) { m_isDetached = detached; return this; } /** * Returns the start type. * * @return The start type. */ public int getStartType() { return m_startType; } /** * Sets the start type. *

* The start type is used to control how the subprocess will be started by * the OS. This property has no effect on Windows. *

    *
  • FORK_EXEC - The most common UNIX/LINUX way to create a child * process. On some operating systems (esp. Solaris) this call causes * results in the operating system momentarily duplicating the JVM's * memory before launching the child process. If the JVM is large then * this can result in system level memory errors that can cause the * child process to fail or even the JVM to crash.
  • *
  • VFORK_EXEC - The vfork function differs from fork only in that the * child process can share code and data with the parent process. This * speeds cloning activity significantly. Care is taken in this * implementation to avoid the kind of integrety problems that are * possible with this method. On some systems, vfork is the same as * fork.
  • *
  • POSIX_SPAWN - The process will be spawned in such a way that no * memory duplication takes place. This makes it possible to spawn * child processes when the JVM is very large on Solaris systems. * (See http://www.opengroup.org/onlinepubs/009695399/functions/posix_spawn.html) * This is available on LINUX, SOLARIS (10+), AIX, z/OS and MACOS. * It will not be possible to set the working directory when using * this start type.
  • *
  • DYNAMIC - The ideal forking method will be used for the current * platform. * It will not be possible to set the working directory when using * this start type as the start type used on some platforms does not * support setting a working directory.
  • *
* * @param startType The start type to use when launching the child process. * * @return This configration to allow chaining. * * @throws IllegalArgumentException If the startType is invalid. */ public WrapperProcessConfig setStartType( int startType ) throws IllegalArgumentException { verifyStartType( startType ); if ( !WrapperManager.isWindows() ) { m_startType = startType; } return this; } /** * Returns the new process group flag. * * @return The new process group flag. */ public boolean isNewProcessGroup() { return m_isNewProcessGroup; } /** * Sets the new process group flag. *

* When a process is created as a new process group, the Wrapper will monitor and terminate * all processes in the new process group. *

* When false, the process will be a member of the same process group as Java. The Wrapper * will only monitor the process which was directly launched. *

* Defaults to true. *

* On Windows, this value is ignored. A new process group is required to make sure console * events are handled correctly. * * @param newProcessGroup True to create a new process group, false to inherit the process group. * * @return This configration to allow chaining. */ public WrapperProcessConfig setNewProcessGroup( boolean newProcessGroup ) { if ( !WrapperManager.isWindows() ) { m_isNewProcessGroup = newProcessGroup; } return this; } /** * Returns whether the input streams of the process should be read in 'auto-close' mode. * * @return true if non-blocking mode is enabled, FALSE otherwise. */ public boolean isAutoCloseInputStreams() { return m_isAutoCloseInputStreams; } /** * Specifies how the pipes of the process should be read. *

* Two modes are possible: *

* With the 'auto-close' mode, which is the default, the input stream will be closed when the process * ends and there is nothing else to read from the std[out/err] to which it is connected. *

* In the other mode, the input stream will wait for data to be read even if the process is gone * as long as the other end of the stream is open. *

* While the second mode might be slightly more efficient during the process of reading data, it can * cause to block longer after the process termination in case other sub-processes using the same * standard streams were initiated. These sub-processes may indeed force the standard streams to * stay open. *

* The 'auto-close' mode is also more reliable in the event of the process crashing. *

* Defaults to true. *

* On z/OS, this value is ignored. The 'auto-close' mode is required to make sure the pipes are * read correctly. * On Windows, this value is ignored. The 'auto-close' mode is not needed. * * @param isAutoCloseInputStreams true to enable 'auto-close' mode, false to disable it. * * @return This configration to allow chaining. */ public WrapperProcessConfig setAutoCloseInputStreams( boolean isAutoCloseInputStreams ) { if ( !WrapperManager.isWindows() && !WrapperManager.isZOS() ) { m_isAutoCloseInputStreams = isAutoCloseInputStreams; } return this; } /** * Returns the working directory. * * @return The working directory. */ public File getWorkingDirectory() { return m_defdir; } /** * Sets the working directory. * * @param workingDirectory The working directory of the subprocess, or null * if the subprocess should inherit the working * directory of the JVM. * On Unix, when using the POSIX_SPAWN or DYNAMIC start * type, it is not possible to set the working * directory. Doing so will result in an error when running exec. * On Windows, it is always possible to set the working directly. * * @return This configration to allow chaining. * * @throws IOException If the specified working directory can not be resolved. */ public WrapperProcessConfig setWorkingDirectory( File workingDirectory ) throws IOException { if ( workingDirectory != null ) { if ( !workingDirectory.exists() ) { throw new IllegalArgumentException( WrapperManager.getRes().getString( "Working directory does not exist." ) ); } else if ( !workingDirectory.isDirectory() ) { throw new IllegalArgumentException( WrapperManager.getRes().getString( "Must be a directory." ) ); } } m_defdir = workingDirectory.getCanonicalFile(); return this; } /** * Returns a Map containing the environment which will be used to launch * the child process. *

* If this Map is modified those changes will be reflected when the process * is launched. Alternately, the environment can be set with the * setEnvironment method. Clearing the Map will result in an empty * environment being used. * @throws WrapperLicenseError If the function is called other than in * the Professional Edition or from a Standalone JVM. * * @return A Map containing the environment which will be used to launch * the child process. */ public Map getEnvironment() throws WrapperLicenseError { if ( m_environment == null ) { m_environment = getDefaultEnvironment(); } return m_environment; } /** * Sets the environment for the child process. * * @param environment A Map containing the environment to use when launching * the process. Passing in an empty Map will result in * an empty Environment being used. A null native will * cause the process to be launched using the same * environment as the JVM. * * @return This configration to allow chaining. * * @throws IllegalArgumentException If any of the names or values are not * Strings or if a name is empty. */ public WrapperProcessConfig setEnvironment( Map environment ) { if ( environment != null ) { for ( Iterator iter = environment.entrySet().iterator(); iter.hasNext(); ) { Map.Entry entry = (Map.Entry)iter.next(); Object key = entry.getKey(); if ( !( key instanceof String ) ) { throw new IllegalArgumentException( WrapperManager.getRes().getString( "Map entry names must be Strings." ) ); } else if ( ( (String)key ).length() <= 0 ) { throw new IllegalArgumentException( WrapperManager.getRes().getString( "Map entry names must not be empty Strings." ) ); } else if ( ( (String)key ).indexOf( '=' ) != -1 ) { throw new IllegalArgumentException( WrapperManager.getRes().getString( "Map entry names must not contain an equal sign (''='')." ) ); } Object value = entry.getKey(); if ( !( value instanceof String ) ) { throw new IllegalArgumentException( WrapperManager.getRes().getString( "Map entry values must be Strings." ) ); } } } m_environment = environment; return this; } /** * Returns the soft shutdown timeout value. * * @return The soft shutdown timeout value. */ public int getSoftShutdownTimeout() { return m_softShutdownTimeout; } /** * Sets the timeout for the soft shtudown in seconds. * When WrapperProcess.destroy() is called the wrapper will first try to * stop the application softly giving it time to stop itself properly. * If the specified timeout however ellapsed, the Child Process will be * terminated by hard. * If 0 was specified, the wrapper will instantly force the termination. * If -1 was specified, the wrapper will wait indefinitely for the child * to perform the stop. * The default value of this property is 5 - giving a process 5 sec to * react on the shutdown request. * * On Windows, Window based applications will be asked to shutdown by sending * WM_CLOSE messages to each of their Window event queues. * The soft shutdown will be ignored for console processes as it is not * currently possible to ask them to shutdown cleanly with a CTRL-C. * * @param softShutdownTimeout The max timeout for an application to stop, before * killing forcibly * * @return This configration to allow chaining. * * @throws IllegalArgumentException If the value of the specified timeout is invalid. */ public WrapperProcessConfig setSoftShutdownTimeout( int softShutdownTimeout ) { if ( softShutdownTimeout < -1 ) { throw new IllegalArgumentException( WrapperManager.getRes().getString( "{0} is not a valid value for a timeout.", new Integer ( softShutdownTimeout ) ) ); } m_softShutdownTimeout = softShutdownTimeout; return this; } /*--------------------------------------------------------------- * Private Methods *-------------------------------------------------------------*/ /** * Makes sure that the specified startType is valid. * * @param startType Start type to test. * * @throws IllegalArgumentException If the startType is invalid. */ private static void verifyStartType( int startType ) throws IllegalArgumentException { switch( startType ) { case POSIX_SPAWN: case VFORK_EXEC: case FORK_EXEC: case DYNAMIC: break; default: throw new IllegalArgumentException( WrapperManager.getRes().getString( "Unknown start type: {0}", new Integer( startType ) ) ); } } /** * Returns a Map containing the environment of the current Java process. */ private Map getDefaultEnvironment() { Map environment = new HashMap(); if ( WrapperManager.isNativeLibraryOk() ) { String[] nativeEnv = nativeGetEnv(); for ( int i = 0; i < nativeEnv.length; i++ ) { int pos = nativeEnv[i].indexOf( '=' ); String name = nativeEnv[i].substring( 0, pos ); String value = nativeEnv[i].substring( pos + 1 ); environment.put( name, value ); } } return environment; } /** * Called by the native code to get the environment. */ private String[] getNativeEnv() { if ( m_environment == null ) { if ( WrapperManager.isNativeLibraryOk() ) { return nativeGetEnv(); } else { return new String[0]; } } else { String[] nativeEnv = new String[ m_environment.size() ]; Iterator iter = m_environment.entrySet().iterator(); int i = 0; while ( iter.hasNext() ) { Map.Entry pairs = (Map.Entry)iter.next(); nativeEnv[ i++ ] = pairs.getKey() + "=" + pairs.getValue(); } return nativeEnv; } } /** * Specifies if the ChildProcesses should be launched * in the current session. * This property only makes sense if the application was * launched as Windows Service under the System User (or any * other user, having SE_TCB_NAME previledge with the OS) * On non-Windows platforms or when launched in Console mode, * the setting will be ignored silently. * * @param isInteractive true to enable the feature. * * @return An instance of WrapperProcessConfig. */ public WrapperProcessConfig setCreateForActiveUser( boolean isInteractive ) { if ( isInteractive && WrapperManager.isWindows() && WrapperManager.isLaunchedAsService()) { m_isInteractive = true; } else { m_isInteractive = false; } return this; } /** * Tells if the CreateForActiveUser feature was enabled. * @return CreateForActiveUser. */ public boolean isCreateForActiveUser() { return m_isInteractive; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperProcessInputStream.java100644 0 0 30172 14333053652 26437 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.IOException; /** * The InputStream Class of a WrapperProcess, representing all the data the * ChildProcess writes to the Wrapper. * * @author Tanuki Software Development Team <support@tanukisoftware.com> * @since Wrapper 3.4.0 */ public class WrapperProcessInputStream extends InputStream { /** Platform specific pointer to the underlying native pipe being read from. */ private long m_ptr; /** Flag which keeps track of whether the close method has been called. */ private boolean m_closed; /** Buffer containing all unread data from the pipe. This will only exist if the process terminates before a read method has been called. */ private ByteArrayInputStream m_bais; /** Read flag is set the first time a thread calls any of the read methods. * This is used to decide whther or not pending data in the buffer needs to be stored in m_bais once the process is terminated. * Storing the unread data is necessary if the child process terminates before the use code has a chance to access the InputStreams. */ private boolean m_read; /** Flag which is set when the child process is terminated. Used within native code. */ private volatile boolean m_terminated; /** Indicates if the pipes should be read in a non-blocking mode with check of the m_terminated flag between retries. Used within native code. */ private volatile boolean m_checkTerminated; /** Semaphore to synchronize access to the m_read flag. * It is important that the synchronization is made on a distinct object so that m_read can be accessed while other threads hold a lock on 'this'. */ private Object m_readSm = new Object(); /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * This class can only be instantiated by native code. */ private WrapperProcessInputStream() { } /*--------------------------------------------------------------- * Native Methods *-------------------------------------------------------------*/ private native int nativeRead( boolean blocking ); private native void nativeClose(); private native int nativeRead2( byte[] b, int off, int len ); /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Closes the InputStream * @throws IOException in case of any file errors * @throws WrapperLicenseError If the function is called other than in * the Professional Edition or from a Standalone JVM. */ public void close() throws IOException { synchronized( this ) { if ( !m_closed ) { if ( WrapperManager.isNativeLibraryOk() ) { nativeClose(); } m_closed = true; } } } /** * Tests if this input stream supports the mark and reset methods. * * The markSupported method of InputStream returns false. * * @return false. */ public boolean markSupported() { return false; } /** * @return TRUE if ready, otherwise FALSE. */ public boolean ready() { synchronized( this ) { if ( !m_closed || ( ( m_bais != null ) && ( m_bais.available() > 0 ) ) ) { return true; } else { return false; } } } /** * Read a character from the Stream and moves the position in the stream * * @return A single character from the stream, or -1 when the end of stream is reached. * * @throws IOException in case the stream has been already closed or any other file error * @throws WrapperLicenseError If the function is called other than in * the Professional Edition or from a Standalone JVM. */ public int read() throws IOException { if ( !m_read ) { synchronized ( m_readSm ) { m_read = true; } } synchronized( this ) { if ( ( !m_closed ) && WrapperManager.isNativeLibraryOk() ) { return nativeRead( true ); } else { if ( m_bais != null ) { return m_bais.read(); } else { throw new IOException( WrapperManager.getRes().getString( "Stream is closed." ) ); } } } } /** * Reads some number of bytes from the input stream and stores them into the buffer array b. * The number of bytes actually read is returned as an integer. This method blocks until input * data is available, end of file is detected, or an exception is thrown. * * If the length of b is zero, then no bytes are read and 0 is returned; otherwise, there is an * attempt to read at least one byte. If no byte is available because the stream is at the end * of the file, the value -1 is returned; otherwise, at least one byte is read and stored into b. * * The first byte read is stored into element b[0], the next one into b[1], and so on. The number * of bytes read is, at most, equal to the length of b. Let k be the number of bytes actually * read; these bytes will be stored in elements b[0] through b[k-1], leaving elements b[k] * through b[b.length-1] unaffected. * * The read(b) method for class InputStream has the same effect as: * * read(b, 0, b.length) * * @param b The buffer into which the data is read. * * @return The total number of bytes read into the buffer, or -1 is there is no more data because * the end of the stream has been reached. * * @throws IOException If the first byte cannot be read for any reason other than the end of the * file, if the input stream has been closed, or if some other I/O error * occurs. * @throws WrapperLicenseError If the function is called other than in * the Professional Edition or from a Standalone JVM. */ public int read( byte b[ ] ) throws IOException { return read( b, 0, b.length ); } /** * Reads up to len bytes of data from the input stream into an array of bytes. An attempt is made * to read as many as len bytes, but a smaller number may be read. The number of bytes actually * read is returned as an integer. * * This method blocks until input data is available, end of file is detected, or an exception is * thrown. * * If len is zero, then no bytes are read and 0 is returned; otherwise, there is an attempt to * read at least one byte. If no byte is available because the stream is at end of file, the * value -1 is returned; otherwise, at least one byte is read and stored into b. * * The first byte read is stored into element b[off], the next one into b[off+1], and so on. The * number of bytes read is, at most, equal to len. Let k be the number of bytes actually read; * these bytes will be stored in elements b[off] through b[off+k-1], leaving elements b[off+k] * through b[off+len-1] unaffected. * * In every case, elements b[0] through b[off] and elements b[off+len] through b[b.length-1] are * unaffected. * * @param b The buffer into which the data is read. * @param off The start offset in array b from which the data is read. * @param len The maximum number of bytes to read. * * @return The total number of bytes read into the buffer, or -1 is there is no more data because * the end of the stream has been reached. * * @throws IOException If the first byte cannot be read for any reason other than the end of the * file, if the input stream has been closed, or if some other I/O error * occurs. * @throws WrapperLicenseError If the function is called other than in * the Professional Edition or from a Standalone JVM. */ public int read( byte b[], int off, int len ) throws IOException { if ( !m_read ) { synchronized ( m_readSm ) { m_read = true; } } synchronized ( this ) { if ( b == null ) { throw new NullPointerException(); } else if ( ( off < 0 ) || ( len < 0 ) || ( len > b.length - off ) ) { throw new IndexOutOfBoundsException(); } else if ( len == 0 ) { return 0; } if ( !ready() ) { return -1; } int result; if ( ( !m_closed ) && WrapperManager.isNativeLibraryOk() ) { result = nativeRead2( b, off, len ); } else { if ( m_bais != null ) { result = m_bais.read( b, off, len ); } else { throw new IOException(WrapperManager.getRes().getString( "Stream is closed." ) ); } } return result; } } /*--------------------------------------------------------------- * Private Methods *-------------------------------------------------------------*/ /** * This method gets called when a spawned Process has terminated * and the pipe buffer gets read and stored in an byte array. * This way we can close the Filedescriptor and keep the number * of open FDs as small as possible. */ private void readAndCloseOpenFDs() { // Set the terminated flag. This must be done even when read has already been called. // It is also important that this be set even if another thread is currently reading. m_terminated = true; synchronized ( m_readSm ) { if ( m_read ) { // Another thread is reading from the stream so we can trust that thread to complete the reads and close on its own. return; } } synchronized( this ) { int count; int msg; if ( m_closed || ( !WrapperManager.isNativeLibraryOk() ) ) { return; } try { byte[] buffer = new byte[1024]; count = 0; while ( ( msg = nativeRead( false ) ) != -1 ) { if ( count >= buffer.length ) { byte[] temp = new byte[buffer.length + 1024]; System.arraycopy( buffer, 0, temp, 0, buffer.length ); buffer = temp; } buffer[count++] = (byte)msg; } m_bais = new ByteArrayInputStream( buffer, 0, count ); close(); } catch( IOException ioe ) { System.out.println( WrapperManager.getRes().getString( "WrapperProcessStream encountered a ReadError: " ) ); ioe.printStackTrace(); } } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperProcessOutputStream.java100644 0 0 5601 14333053652 26617 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.io.IOException; import java.io.OutputStream; /** * The OutputStream Class of a WrapperProcess, representing all the data the * ChildProcess read from the Wrapper. * * @author Tanuki Software Development Team <support@tanukisoftware.com> * @since Wrapper 3.4.0 */ public class WrapperProcessOutputStream extends OutputStream { private long m_ptr; private boolean m_closed; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * This class can only be instantiated by native code. */ private WrapperProcessOutputStream() { } /*--------------------------------------------------------------- * Native Methods *-------------------------------------------------------------*/ private native void nativeWrite( int b ); private native void nativeClose(); /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Writes a byte to the Stream. * * @param b byte to write. * * @throws IOException in case the stream has been already closed or any * other IO error. * @throws WrapperLicenseError If the function is called other than in * the Professional Edition or from a Standalone JVM. * */ public void write( int b ) throws IOException { synchronized( this ) { if ( ( !m_closed ) && WrapperManager.isNativeLibraryOk() ) { nativeWrite( b ); } else { throw new IOException( WrapperManager.getRes().getString( "Stream is closed." ) ); } } } /** * Closes the OutputStream. * * @throws IOException If there were any problems closing the stream. * @throws WrapperLicenseError If the function is called other than in * the Professional Edition or from a Standalone JVM. */ public void close() throws IOException { synchronized( this ) { if ( !m_closed ) { if ( WrapperManager.isNativeLibraryOk() ) { nativeClose(); } m_closed = true; } } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperProperties.java100644 0 0 6251 14333053652 24742 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.io.InputStream; import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Properties; import java.util.Set; /** * Provides a Properties object which can be locked to prevent modification * by the user. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ class WrapperProperties extends Properties { /** * Serial Version UID. */ private static final long serialVersionUID = 1991422118345246456L; boolean m_locked = false; /** * Locks the Properties object against future modification. */ public void lock() { m_locked = true; } public void load( InputStream inStream ) throws IOException { if ( m_locked ) { throw new IllegalStateException( WrapperManager.getRes().getString( "Read Only" ) ); } super.load( inStream ); } public Object setProperty( String key, String value ) { if ( m_locked ) { throw new IllegalStateException( WrapperManager.getRes().getString( "Read Only" ) ); } return super.setProperty( key, value ); } public void clear() { if ( m_locked ) { throw new IllegalStateException( WrapperManager.getRes().getString( "Read Only" ) ); } super.clear(); } public Set entrySet() { if ( m_locked ) { return Collections.unmodifiableSet( super.entrySet() ); } else { return super.entrySet(); } } public Set keySet() { if ( m_locked ) { return Collections.unmodifiableSet( super.keySet() ); } else { return super.keySet(); } } public Object put( Object key, Object value ) { if ( m_locked ) { throw new IllegalStateException( WrapperManager.getRes().getString( "Read Only" ) ); } return super.put( key, value ); } public void putAll( Map map ) { if ( m_locked ) { throw new IllegalStateException( WrapperManager.getRes().getString( "Read Only" ) ); } super.putAll( map ); } public Object remove( Object key ) { if ( m_locked ) { throw new IllegalStateException( WrapperManager.getRes().getString( "Read Only" ) ); } return super.remove( key ); } public Collection values() { if ( m_locked ) { return Collections.unmodifiableCollection( super.values() ); } else { return super.values(); } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperPropertyUtil.java100644 0 0 7174 14333053652 25275 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * A collection of utility methods which make it easy to work with Wrapper * configuration Properties without littering code with error handling. * * System Properties can be accessed with the WrapperSystemPropertyUtil * class. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public final class WrapperPropertyUtil { /*--------------------------------------------------------------- * Static Methods *-------------------------------------------------------------*/ /** * Resolves a string property. * * @param name The name of the property to lookup. * @param defaultValue The value to return if it is not set or is invalid. * * @return The requested property value. */ public static String getStringProperty( String name, String defaultValue ) { String val = WrapperManager.getProperties().getProperty( name ); if ( val == null ) { return defaultValue; } return val; } /** * Resolves a boolean property. * * @param name The name of the property to lookup. * @param defaultValue The value to return if it is not set or is invalid. * * @return The requested property value. */ public static boolean getBooleanProperty( String name, boolean defaultValue ) { String val = getStringProperty( name, null ); if ( val != null ) { if ( val.equalsIgnoreCase( "TRUE" ) ) { return true; } else if ( val.equalsIgnoreCase( "FALSE" ) ) { return false; } } return defaultValue; } /** * Resolves an integer property. * * @param name The name of the property to lookup. * @param defaultValue The value to return if it is not set or is invalid. * * @return The requested property value. */ public static int getIntProperty( String name, int defaultValue ) { String val = getStringProperty( name, null ); if ( val == null ) { return defaultValue; } try { return Integer.parseInt( val ); } catch ( NumberFormatException e ) { return defaultValue; } } /** * Resolves a long property. * * @param name The name of the property to lookup. * @param defaultValue The value to return if it is not set or is invalid. * * @return The requested property value. */ public static long getLongProperty( String name, long defaultValue ) { String val = getStringProperty( name, null ); if ( val == null ) { return defaultValue; } try { return Long.parseLong( val ); } catch ( NumberFormatException e ) { return defaultValue; } } /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Not instantiable. */ private WrapperPropertyUtil() { } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperResources.java100644 0 0 27726 14333053652 24612 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.io.UnsupportedEncodingException; import java.text.MessageFormat; /** * A resource bundle which is used to help localize applications to the default * locale of the JVM. Resources are stored in MO files using the standard UNIX * gettext resources. * * For example,

* * WrapperResources res = WrapperManager.loadWrapperResources( "myapp", "../lang/" ); * * * To use the WrapperResources, make a call to any of the getString() * methods. If the resource files are not found, or the specific key is not found * then the key is returned unmodified. * * All resource keys passed to getString() will be processed using the * java.util.MessageFormat class. As such, single quotes must be escaped. * This class can optionally validate all such keys and logs warnings about * keys which fail these checks. It is possible to enable this validation with * the following property. (Defaults to FALSE) * -Dorg.tanukisoftware.wrapper.WrapperResources.validateResourceKeys=TRUE * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public final class WrapperResources { /** Error level log channel */ private static WrapperPrintStream m_outError; /** True if resource keys should be validated. */ private static boolean m_validateResourceKeys; /** Helper object to reduce number of new objects. */ private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; /** Id of the resource. Assigned within native code. */ private long m_Id; /*--------------------------------------------------------------- * Static Methods *-------------------------------------------------------------*/ static { boolean streamSet = false; if ( "true".equals( System.getProperty( "wrapper.use_sun_encoding" ) ) ) { String sunStdoutEncoding = System.getProperty( "sun.stdout.encoding" ); if ( ( sunStdoutEncoding != null ) && !sunStdoutEncoding.equals( System.getProperty( "file.encoding" ) ) ) { /* We need to create the stream using the same encoding as the one used for stdout, else this will lead to encoding issues. */ try { m_outError = new WrapperPrintStream( System.out, false, sunStdoutEncoding, "WrapperResources Error: " ); streamSet = true; } catch ( UnsupportedEncodingException e ) { /* This should not happen because we always make sure the encoding exists before launching a JVM. */ System.out.println( WrapperManager.getRes().getString( "Failed to set the encoding ''{0}'' when creating a WrapperPrintStream.\n Make sure the value of sun.stdout.encoding is correct.", sunStdoutEncoding ) ); } } } if ( !streamSet ) { m_outError = new WrapperPrintStream( System.out, "WrapperResources Error: " ); } m_validateResourceKeys = WrapperSystemPropertyUtil.getBooleanProperty( WrapperResources.class.getName() + ".validateResourceKeys", false ); } /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * WrapperResources instances are created using the WrapperManager.loadWrapperResources method. */ protected WrapperResources() { } /*--------------------------------------------------------------- * Finalizers *-------------------------------------------------------------*/ protected void finalize() throws Throwable { try { if ( WrapperManager.isLoggingFinalizers() ) { // This can't be localized because of when it happens. System.out.println( "WrapperResources.finalize Id=" + m_Id ); } if ( m_Id != 0 ) { if ( WrapperManager.isNativeLibraryOk() ) { // clean up after the resource. nativeDestroyResource(); } } } finally { super.finalize(); } } /*--------------------------------------------------------------- * Native Methods *-------------------------------------------------------------*/ private native String nativeGetLocalizedString(String key); private native void nativeDestroyResource(); /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Checks the resource key to make sure that all of its single quotes are excaped correctly. * * @param str String which was or will be localized. * @param localized True if str is a localized string, false if it is the key. */ private void validateResourceKey( String str, boolean localized ) { int pos = 0; int len = str.length(); do { pos = str.indexOf( '\'', pos ); if ( pos < 0 ) { break; } pos++; if ( ( pos >= len ) || ( str.charAt( pos ) != '\'' ) ) { // Unescaped quote at end of string. // If one of the following keys or their localized strings have such a problem this will recurse. if ( localized ) { m_outError.println( WrapperManager.getRes().getString( "Localized resource string''s single quotes not escaped correctly: {0}", str ) ); } else { m_outError.println( WrapperManager.getRes().getString( "Resource key''s single quotes not escaped correctly: {0}", str ) ); } break; } pos++; } while ( pos < len ); } /** * Request a localized version of the specified key. * * The returned string will be the raw string which has not yet * been processed by MessageFormat. * * @param key Resource to be localized. * * @return The localized version of the key. */ private String getStringInner( String key ) { if ( m_validateResourceKeys ) { validateResourceKey( key, false ); } if ( ( m_Id != 0 ) && WrapperManager.isNativeLibraryOk() ) { String str = nativeGetLocalizedString( key ); if ( !str.equals( key ) ) { if ( m_validateResourceKeys ) { validateResourceKey( str, true ); } } return str; } else { return key; } } /** * Request a localized version of the specified key. * * @param key Resource to be localized. * * @return The localized version of the key. */ public String getString( String key ) { // Even through we are not replacing any tokens, always call the format // method so things like escaped quotes will be handled in a consistent way. return MessageFormat.format( getStringInner( key ), EMPTY_OBJECT_ARRAY ); } /** * Request a localized version of the specified key. *

* Individual tokens will be replaced with the specified parameters using the * Java MessageFormat format method. * * @param key Resource to be localized. * @param arguments An array of argumens to be replaced in the resource. * * @return The localized version of the key. * * @see java.text.MessageFormat */ public String getString( String key, Object[] arguments ) { return MessageFormat.format( getStringInner( key ), arguments ); } /** * Request a localized version of the specified key. *

* Individual tokens will be replaced with the specified parameters using the * Java MessageFormat format method. * * @param key Resource to be localized. * @param arg0 An argument to be replaced in the resource. * * @return The localized version of the key. * * @see java.text.MessageFormat */ public String getString( String key, Object arg0 ) { return MessageFormat.format( getStringInner( key ), new Object[] { arg0 } ); } /** * Request a localized version of the specified key. *

* Individual tokens will be replaced with the specified parameters using the * Java MessageFormat format method. * * @param key Resource to be localized. * @param arg0 An argument to be replaced in the resource. * @param arg1 An argument to be replaced in the resource. * * @return The localized version of the key. * * @see java.text.MessageFormat */ public String getString( String key, Object arg0, Object arg1 ) { return MessageFormat.format( getStringInner( key ), new Object[] { arg0, arg1 } ); } /** * Request a localized version of the specified key. *

* Individual tokens will be replaced with the specified parameters using the * Java MessageFormat format method. * * @param key Resource to be localized. * @param arg0 An argument to be replaced in the resource. * @param arg1 An argument to be replaced in the resource. * @param arg2 An argument to be replaced in the resource. * * @return The localized version of the key. * * @see java.text.MessageFormat */ public String getString( String key, Object arg0, Object arg1, Object arg2 ) { return MessageFormat.format( getStringInner( key ), new Object[] { arg0, arg1, arg2 } ); } /** * Request a localized version of the specified key. *

* Individual tokens will be replaced with the specified parameters using the * Java MessageFormat format method. * * @param key Resource to be localized. * @param arg0 An argument to be replaced in the resource. * @param arg1 An argument to be replaced in the resource. * @param arg2 An argument to be replaced in the resource. * @param arg3 An argument to be replaced in the resource. * * @return The localized version of the key. * * @see java.text.MessageFormat */ public String getString( String key, Object arg0, Object arg1, Object arg2, Object arg3 ) { return MessageFormat.format( getStringInner( key ), new Object[] { arg0, arg1, arg2, arg3 } ); } /** * Request a localized version of the specified key. *

* Individual tokens will be replaced with the specified parameters using the * Java MessageFormat format method. * * @param key Resource to be localized. * @param arg0 An argument to be replaced in the resource. * @param arg1 An argument to be replaced in the resource. * @param arg2 An argument to be replaced in the resource. * @param arg3 An argument to be replaced in the resource. * @param arg4 An argument to be replaced in the resource. * * @return The localized version of the key. * * @see java.text.MessageFormat */ public String getString( String key, Object arg0, Object arg1, Object arg2, Object arg3, Object arg4 ) { return MessageFormat.format( getStringInner( key ), new Object[] { arg0, arg1, arg2, arg3, arg4 } ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperServiceException.java100644 0 0 3707 14333053652 26070 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * WrapperServiceExceptions are thrown when the Wrapper is unable to obtain * information on a requested service. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperServiceException extends Exception { /** * Serial Version UID. */ private static final long serialVersionUID = 5163822791166376887L; /** * The error code. */ private final int m_errorCode; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperServiceException. * * @param errorCode ErrorCode which was encountered. * @param message Message describing the exception. */ WrapperServiceException( int errorCode, byte[] message ) { super( new String( message ) ); m_errorCode = errorCode; } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns the error code. * * @return The error code. */ public int getErrorCode() { return m_errorCode; } /** * Return string representation of the Exception. * * @return String representation of the Exception. */ public String toString() { return this.getClass().getName() + " " + getMessage() + WrapperManager.getRes().getString( " Error Code: " ) + getErrorCode(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperShuttingDownException.java100644 0 0 1751 14333053652 27122 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * WrapperShuttingDownExceptions are thrown when certain Wrapper functions * are accessed after the Wrapper has started shutting down. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperShuttingDownException extends Exception { /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperShuttingDownException. */ WrapperShuttingDownException() { super(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperSimpleApp.java100644 0 0 62656 14333053652 24533 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; /** * By default the WrapperSimpleApp will only wait for 2 seconds for the main * method of the start class to complete. This was done because the main * methods of many applications never return. It is possible to force the * class to wait for the startup main method to complete by defining the * following system property when launching the JVM (defaults to FALSE): * -Dorg.tanukisoftware.wrapper.WrapperSimpleApp.waitForStartMain=TRUE *

* Using the waitForStartMain property will cause the startup to wait * indefinitely. This is fine if the main method will always return * within a predefined period of time. But if there is any chance that * it could hang, then the maxStartMainWait property may be a better * option. It allows the 2 second wait time to be overridden. To wait * for up to 5 minutes for the startup main method to complete, set * the property to 300 as follows (defaults to 2 seconds): * -Dorg.tanukisoftware.wrapper.WrapperSimpleApp.maxStartMainWait=300 *

* By default, the WrapperSimpleApp will tell the Wrapper to exit with an * exit code of 1 if any uncaught exceptions are thrown in the configured * main method. This is good in most cases, but is a little different than * the way Java works on its own. Java will stay up and running if it has * launched any other non-daemon threads even if the main method ends because * of an uncaught exception. To get this same behavior, it is possible to * specify the following system property when launching the JVM (defaults to * FALSE): * -Dorg.tanukisoftware.wrapper.WrapperSimpleApp.ignoreMainExceptions=TRUE *

* It is possible to extend this class but make absolutely sure that any * overridden methods call their super method or the class will fail to * function correctly. Most users will have no need to override this * class. Remember that if overridden, the main method will also need to * be recreated in the child class to make sure that the correct instance * is created. *

* NOTE - The main methods of many applications are designed not to * return. In these cases, you must either stick with the default 2 second * startup timeout or specify a slightly longer timeout, using the * maxStartMainWait property, to simulate the amount of time your application * takes to start up. *

* WARNING - If the waitForStartMain is specified for an application * whose start method never returns, the Wrapper will appear at first to be * functioning correctly. However the Wrapper will never enter a running * state, this means that the Windows Service Manager and several of the * Wrapper's error recovery mechanisms will not function correctly. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperSimpleApp implements WrapperListener, Runnable { /** Info level log channel */ private static WrapperPrintStream m_outInfo; /** Error level log channel */ private static WrapperPrintStream m_outError; /** Debug level log channel */ private static WrapperPrintStream m_outDebug; /** * Application's main method */ private Method m_mainMethod; /** * Command line arguments to be passed on to the application */ private String[] m_appArgs; /** * Gets set to true when the thread used to launch the application * actuially starts. */ private boolean m_mainStarted; /** * Gets set to true when the thread used to launch the application * completes. */ private boolean m_mainComplete; /** * Exit code to be returned if the application fails to start. */ private Integer m_mainExitCode; /** * True if uncaught exceptions in the user app's main method should be ignored. */ private boolean m_ignoreMainExceptions; /** * Flag used to signify that the start method has completed. */ private boolean m_startComplete; /** * Flag that is set if there were any initialization problems. */ private boolean m_initFailed; /** * Error message which should be shown if initialization Failed. */ private String m_initError; /** * True if usage should be shown as part of an initialization error. */ private boolean m_initShowUsage; /** * The exception which caused the error. Only needs to be set if the stacktrace is required. */ private Throwable m_initException; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates an instance of a WrapperSimpleApp. * * @param args The full list of arguments passed to the JVM. */ protected WrapperSimpleApp( String args[] ) { // Initialize the WrapperManager class on startup by referencing it. Class wmClass = WrapperManager.class; m_mainMethod = null; // Set up some log channels boolean streamsSet = false; if ( "true".equals( System.getProperty( "wrapper.use_sun_encoding" ) ) ) { String sunStdoutEncoding = System.getProperty( "sun.stdout.encoding" ); if ( ( sunStdoutEncoding != null ) && !sunStdoutEncoding.equals( System.getProperty( "file.encoding" ) ) ) { /* We need to create the stream using the same encoding as the one used for stdout, else this will lead to encoding issues. */ try { m_outInfo = new WrapperPrintStream( System.out, false, sunStdoutEncoding, "WrapperSimpleApp: " ); m_outError = new WrapperPrintStream( System.out, false, sunStdoutEncoding, "WrapperSimpleApp Error: " ); m_outDebug = new WrapperPrintStream( System.out, false, sunStdoutEncoding, "WrapperSimpleApp Debug: " ); streamsSet = true; } catch ( UnsupportedEncodingException e ) { /* This should not happen because we always make sure the encoding exists before launching a JVM. * If any of the above streams failed, we want to fall back to streams that use the same encoding. */ System.out.println( WrapperManager.getRes().getString( "Failed to set the encoding ''{0}'' when creating a WrapperPrintStream.\n Make sure the value of sun.stdout.encoding is correct.", sunStdoutEncoding ) ); } } } if ( !streamsSet ) { m_outInfo = new WrapperPrintStream( System.out, "WrapperSimpleApp: " ); m_outError = new WrapperPrintStream( System.out, "WrapperSimpleApp Error: " ); m_outDebug = new WrapperPrintStream( System.out, "WrapperSimpleApp Debug: " ); } // Do all of our initialization here so the modified array list which is passed // to the WrapperListener.start method can remain unchanged. Ideally we would // want to handle this within the start method, but that would be an API change // that could effect users. // appArgs will be an args array with the main class name stripped off. String[] appArgs; // Get the class name of the application if ( args.length < 1 ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Not enough arguments. Minimum {0} required.", "1" ); m_initShowUsage = true; // No main class, do the best we can for now. appArgs = new String[0]; } else { // Look for the specified class by name String ar[] = args[0].split( "/" ); String mainClassString; String mainMethodString; if ( ar.length > 1 ) { mainClassString = ar[0]; mainMethodString = ar[1]; } else { mainClassString = args[0]; mainMethodString = "main"; } Class mainClass; try { mainClass = Class.forName( mainClassString ); } catch ( ClassNotFoundException e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Unable to locate the class {0} : {1}", mainClassString, e ); m_initShowUsage = true; mainClass = null; } catch ( ExceptionInInitializerError e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Class {0} found but could not be initialized due to:", mainClassString ); m_initException = e; mainClass = null; } catch ( LinkageError e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Class {0} found but could not be initialized: {1}", mainClassString, e ); mainClass = null; } if ( !m_initFailed ) { // Look for the main method try { // getDeclaredMethod will return any method named main in the specified class, // while getMethod will only return public methods, but it will search up the // inheritance path. m_mainMethod = mainClass.getMethod( mainMethodString, new Class[] { String[].class } ); } catch ( NoSuchMethodException e ) { try { // getDeclaredMethod will return any method named in the specified class, // while getMethod will only return public methods, but it will search up the // inheritance path. // try without parameters m_mainMethod = mainClass.getMethod( mainMethodString, new Class[] { } ); } catch ( NoSuchMethodException e2 ) { // Handle with first exception. } if ( m_mainMethod == null ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Unable to locate a public static {2} method in class {0} : {1}", mainClassString, e, mainMethodString ); } } catch ( SecurityException e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Unable to locate a public static {2} method in class {0} : {1}", mainClassString, e, mainMethodString ); } if ( !m_initFailed ) { // Make sure that the method is public and static int modifiers = m_mainMethod.getModifiers(); if ( !( Modifier.isPublic( modifiers ) && Modifier.isStatic( modifiers ) ) ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "The {1} method in class {0} must be declared public and static.", mainClassString, mainMethodString ); } } } // Strip the main class off of the args list. // This is assuming the main class was valid for now. appArgs = new String[args.length - 1]; System.arraycopy( args, 1, appArgs, 0, appArgs.length ); } // Start the application. If the JVM was launched from the native // Wrapper then the application will wait for the native Wrapper to // call the application's start method. Otherwise the start method // will be called immediately. WrapperManager.start( this, appArgs ); // This thread ends, the WrapperManager will start the application after the Wrapper has // been properly initialized by calling the start method above. } /*--------------------------------------------------------------- * Runnable Methods *-------------------------------------------------------------*/ /** * Used to launch the application in a separate thread. */ public void run() { // Notify the start method that the thread has been started by the JVM. synchronized( this ) { m_mainStarted = true; notifyAll(); } Throwable t = null; try { if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "invoking main method" ) ); } try { try { m_mainMethod.invoke( null, new Object[] { m_appArgs } ); } catch ( IllegalArgumentException iae ) { m_mainMethod.invoke( null, new Object[] { } ); } } finally { // Make sure the rest of this thread does not fall behind the application. Thread.currentThread().setPriority( Thread.MAX_PRIORITY ); } if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "main method completed" ) ); } synchronized(this) { // Let the start() method know that the main method returned, in case it is // still waiting. m_mainComplete = true; this.notifyAll(); } return; } catch ( IllegalAccessException e ) { t = e; } catch ( IllegalArgumentException e ) { t = e; } catch ( InvocationTargetException e ) { t = e.getTargetException(); if ( t == null ) { t = e; } } // If we get here, then an error was thrown. If this happened quickly // enough, the start method should be allowed to shut things down. m_outInfo.println(); m_outError.println( WrapperManager.getRes().getString( "Encountered an error running main:" ) ); // We should print a stack trace here, because in the case of an // InvocationTargetException, the user needs to know what exception // their app threw. t.printStackTrace( m_outError ); synchronized( this ) { if ( m_ignoreMainExceptions ) { if ( !m_startComplete ) { // An exception was thrown, but we want to let the application continue. m_mainComplete = true; this.notifyAll(); } return; } else { if ( m_startComplete ) { // Shut down here. WrapperManager.stop( 1 ); return; // Will not get here. } else { // Let start method handle shutdown. m_mainComplete = true; m_mainExitCode = new Integer( 1 ); this.notifyAll(); return; } } } } /*--------------------------------------------------------------- * WrapperListener Methods *-------------------------------------------------------------*/ /** * The start method is called when the WrapperManager is signaled by the * native wrapper code that it can start its application. This * method call is expected to return, so a new thread should be launched * if necessary. * If there are any problems, then an Integer should be returned, set to * the desired exit code. If the application should continue, * return null. * * @param args Arguments passed to the application. */ public Integer start( String[] args ) { // See if there were any startup problems. if ( m_initFailed ) { if ( m_initError != null ) { m_outError.println( m_initError ); } if ( m_initException != null ) { m_initException.printStackTrace( m_outError ); } if ( m_initShowUsage ) { showUsage(); } return new Integer( 1 ); } // Decide whether or not to wait for the start main method to complete before returning. boolean waitForStartMain = WrapperSystemPropertyUtil.getBooleanProperty( WrapperSimpleApp.class.getName() + ".waitForStartMain", false ); m_ignoreMainExceptions = WrapperSystemPropertyUtil.getBooleanProperty( WrapperSimpleApp.class.getName() + ".ignoreMainExceptions", false ); int maxStartMainWait = WrapperSystemPropertyUtil.getIntProperty( WrapperSimpleApp.class.getName() + ".maxStartMainWait", 2 ); maxStartMainWait = Math.max( 1, maxStartMainWait ); // Decide the maximum number of times to loop waiting for the main start method. int maxLoops; if ( waitForStartMain ) { maxLoops = Integer.MAX_VALUE; if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "start(args) Will wait indefinitely for the main method to complete." ) ); } } else { maxLoops = maxStartMainWait; // 1s loops. if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "start(args) Will wait up to {0} seconds for the main method to complete.", new Integer(maxLoops) ) ); } } Thread mainThread = new Thread( this, "WrapperSimpleAppMain" ); synchronized(this) { m_appArgs = args; mainThread.start(); // Make sure the rest of this thread does not fall behind the application. Thread.currentThread().setPriority( Thread.MAX_PRIORITY ); // To avoid problems with the main thread starting slowly on heavily loaded systems, // do not continue until the thread has actually started. while ( !m_mainStarted ) { try { this.wait( 1000 ); } catch ( InterruptedException e ) { // Continue. } } // Wait for startup main method to complete. int loops = 0; while ( ( loops < maxLoops ) && ( !m_mainComplete ) ) { try { this.wait( 1000 ); } catch ( InterruptedException e ) { // Continue. } if ( !m_mainComplete ) { // If maxLoops is large then this could take a while. Notify the // WrapperManager that we are still starting so it doesn't give up. WrapperManager.signalStarting( 5000 ); } loops++; } // Always set the flag stating that the start method completed. This is needed // so the run method can decide whether or not it needs to be responsible for // shutting down the JVM in the event of an exception thrown by the start main // method. m_startComplete = true; // The main exit code will be null unless an error was thrown by the start // main method. if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "start(args) end. Main Completed={0}, exitCode={1}", new Boolean( m_mainComplete ), m_mainExitCode ) ); } return m_mainExitCode; } } /** * Called when the application is shutting down. */ public int stop( int exitCode ) { if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "stop({0})", new Integer ( exitCode ) ) ); } // Normally an application will be asked to shutdown here. Standard Java applications do // not have shutdown hooks, so do nothing here. It will be as if the user hit CTRL-C to // kill the application. return exitCode; } /** * Called whenever the native wrapper code traps a system control signal * against the Java process. It is up to the callback to take any actions * necessary. Possible values are: WrapperManager.WRAPPER_CTRL_C_EVENT, * WRAPPER_CTRL_CLOSE_EVENT, WRAPPER_CTRL_LOGOFF_EVENT, or * WRAPPER_CTRL_SHUTDOWN_EVENT */ public void controlEvent( int event ) { if ( ( event == WrapperManager.WRAPPER_CTRL_LOGOFF_EVENT ) && ( WrapperManager.isLaunchedAsService() || WrapperManager.isIgnoreUserLogoffs() ) ) { // Ignore m_outInfo.println( WrapperManager.getRes().getString( "User logged out. Ignored." ) ); } else { if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "controlEvent({0}) Stopping", new Integer( event ) ) ); } WrapperManager.stop( 0 ); // Will not get here. } } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Displays application usage */ protected void showUsage() { // Show this output without headers. System.out.println(); System.out.println( WrapperManager.getRes().getString( "WrapperSimpleApp Usage:" ) ); System.out.println( WrapperManager.getRes().getString( " java org.tanukisoftware.wrapper.WrapperSimpleApp '{'app_class'{'/app_method'}}' [app_arguments]" ) ); System.out.println(); System.out.println( WrapperManager.getRes().getString( "Where:" ) ); System.out.println( WrapperManager.getRes().getString( " app_class: The fully qualified class name of the application to run." ) ); System.out.println( WrapperManager.getRes().getString( " app_arguments: The arguments that would normally be passed to the" ) ); System.out.println( WrapperManager.getRes().getString( " application." ) ); } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ /** * Used to Wrapper enable a standard Java application. This main * expects the first argument to be the class name of the application * to launch. All remaining arguments will be wrapped into a new * argument list and passed to the main method of the specified * application. * * @param args Arguments passed to the application. */ public static void main( String args[] ) { new WrapperSimpleApp( args ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperStartStopApp.java100644 0 0 125001 14333053652 25245 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ import java.io.UnsupportedEncodingException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; /** * By default the WrapperStartStopApp will only wait for 2 seconds for the main * method of the start class to complete. This was done because the main * methods of many applications never return. It is possible to force the * class to wait for the startup main method to complete by defining the * following system property when launching the JVM (defaults to FALSE): * -Dorg.tanukisoftware.wrapper.WrapperStartStopApp.waitForStartMain=TRUE *

* Using the waitForStartMain property will cause the startup to wait * indefinitely. This is fine if the main method will always return * within a predefined period of time. But if there is any chance that * it could hang, then the maxStartMainWait property may be a better * option. It allows the 2 second wait time to be overridden. To wait * for up to 5 minutes for the startup main method to complete, set * the property to 300 as follows (defaults to 2 seconds): * -Dorg.tanukisoftware.wrapper.WrapperStartStopApp.maxStartMainWait=300 *

* By default, the WrapperStartStopApp will tell the Wrapper to exit with an * exit code of 1 if any uncaught exceptions are thrown in the configured * main method. This is good in most cases, but is a little different than * the way Java works on its own. Java will stay up and running if it has * launched any other non-daemon threads even if the main method ends because * of an uncaught exception. To get this same behavior, it is possible to * specify the following system property when launching the JVM (defaults to * FALSE): * -Dorg.tanukisoftware.wrapper.WrapperStartStopApp.ignoreMainExceptions=TRUE *

* By default, passthrough parameters are ignored, however it is possible to * specify a different behaviour using the following system property: * -Dorg.tanukisoftware.wrapper.WrapperStartStopApp.passthroughMode=START
* Possible values are: *

    *
  • START : add passthrough parameters to the parameters list of the start method
  • *
  • STOP : add passthrough parameters to the parameters list of the stop method
  • *
  • BOTH : add passthrough parameters to both parameters lists of the start * and stop methods
  • *
  • IGNORE : (default) ignore the passthrough parameters
  • *
*

* It is possible to extend this class but make absolutely sure that any * overridden methods call their super method or the class will fail to * function correctly. Most users will have no need to override this * class. Remember that if overridden, the main method will also need to * be recreated in the child class to make sure that the correct instance * is created. *

* NOTE - The main methods of many applications are designed not to * return. In these cases, you must either stick with the default 2 second * startup timeout or specify a slightly longer timeout, using the * maxStartMainWait property, to simulate the amount of time your application * takes to start up. *

* WARNING - If the waitForStartMain is specified for an application * whose start method never returns, the Wrapper will appear at first to be * functioning correctly. However the Wrapper will never enter a running * state, this means that the Windows Service Manager and several of the * Wrapper's error recovery mechanisms will not function correctly. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperStartStopApp implements WrapperListener, Runnable { /** Info level log channel */ private static WrapperPrintStream m_outInfo; /** Error level log channel */ private static WrapperPrintStream m_outError; /** Debug level log channel */ private static WrapperPrintStream m_outDebug; /** * Application's start main method */ private Method m_startMainMethod; /** * Command line arguments to be passed on to the start main method */ private String[] m_startMainArgs; /** * Application's stop main method */ private Method m_stopMainMethod; /** * Should the stop process force the JVM to exit, or wait for all threads * to die on their own. */ private boolean m_stopWait; /** * Command line arguments to be passed on to the stop main method */ private String[] m_stopMainArgs; /** * Gets set to true when the thread used to launch the application * actuially starts. */ private boolean m_mainStarted; /** * Gets set to true when the thread used to launch the application * completes. */ private boolean m_mainComplete; /** * Exit code to be returned if the application fails to start. */ private Integer m_mainExitCode; /** * True if uncaught exceptions in the user app's main method should be ignored. */ private boolean m_ignoreMainExceptions; /** * Flag used to signify that the start method has completed. */ private boolean m_startComplete; /** * Flag that is set if there were any initialization problems. */ private boolean m_initFailed; /** * Error message which should be shown if initialization Failed. */ private String m_initError; /** * True if usage should be shown as part of an initialization error. */ private boolean m_initShowUsage; /** * The exception which caused the error. Only needs to be set if the stacktrace is required. */ private Throwable m_initException; /** * Params passed by passthrough are ignored. */ private final String PASSTHROUGH_MODE_IGNORE = "ignore"; /** * Params passed by passthrough are added in the start arguments list and stop arguments list. */ private final String PASSTHROUGH_MODE_BOTH = "both"; /** * Params passed by passthrough are added in the start arguments list. */ private final String PASSTHROUGH_MODE_START = "start"; /** * Params passed by passthrough are added in the stop arguments list. */ private final String PASSTHROUGH_MODE_STOP = "stop"; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates an instance of a WrapperStartStopApp. * * @param args The full list of arguments passed to the JVM. */ protected WrapperStartStopApp( String args[] ) { // Initialize the WrapperManager class on startup by referencing it. Class wmClass = WrapperManager.class; // Set up some log channels boolean streamsSet = false; if ( "true".equals( System.getProperty( "wrapper.use_sun_encoding" ) ) ) { String sunStdoutEncoding = System.getProperty( "sun.stdout.encoding" ); if ( ( sunStdoutEncoding != null ) && !sunStdoutEncoding.equals( System.getProperty( "file.encoding" ) ) ) { /* We need to create the stream using the same encoding as the one used for stdout, else this will lead to encoding issues. */ try { m_outInfo = new WrapperPrintStream( System.out, false, sunStdoutEncoding, "WrapperStartStopApp: " ); m_outError = new WrapperPrintStream( System.out, false, sunStdoutEncoding, "WrapperStartStopApp Error: " ); m_outDebug = new WrapperPrintStream( System.out, false, sunStdoutEncoding, "WrapperStartStopApp Debug: " ); streamsSet = true; } catch ( UnsupportedEncodingException e ) { /* This should not happen because we always make sure the encoding exists before launching a JVM. * If any of the above streams failed, we want to fall back to streams that use the same encoding. */ System.out.println( WrapperManager.getRes().getString( "Failed to set the encoding ''{0}'' when creating a WrapperPrintStream.\n Make sure the value of sun.stdout.encoding is correct.", sunStdoutEncoding ) ); } } } if ( !streamsSet ) { m_outInfo = new WrapperPrintStream( System.out, "WrapperStartStopApp: " ); m_outError = new WrapperPrintStream( System.out, "WrapperStartStopApp Error: " ); m_outDebug = new WrapperPrintStream( System.out, "WrapperStartStopApp Debug: " ); } // Do all of our initialization here so the modified array lists which are passed // to the WrapperListener.start method can remain unchanged. Ideally we would // want to handle this within the start method, but that would be an API change // that could effect users. // startArgs will be an args array with the main class name and stop args stripped off. String[] startArgs; // Get the class name of the application if ( args.length < 5 ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Not enough arguments. Minimum {0} required.", "5" ); m_initShowUsage = true; // No main class, do the best we can for now. startArgs = new String[0]; } else { // Look for the start main method. m_startMainMethod = getMainMethod( args[0] ); int argCount = getArgCount( args, 1 ); if ( argCount < 0 ) { // Failed, but we need an empty array for the start method below. startArgs = new String[0]; // m_initFailed and m_initError already set. } else { // Get the start arguments startArgs = getArgs( args, 1, argCount ); if ( startArgs == null ) { // Failed, but we need an empty array for the start method below. startArgs = new String[0]; // m_initFailed and m_initError already set. } else { // Where do the stop arguments start int stopArgBase = 2 + startArgs.length; if ( args.length < stopArgBase + 3 ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Not enough arguments. Minimum 3 after start arguments." ); m_initShowUsage = true; } else { // Look for the stop main method. m_stopMainMethod = getMainMethod( args[stopArgBase] ); // Get the stopWait flag if ( args[stopArgBase + 1].equalsIgnoreCase( "true" ) ) { m_stopWait = true; } else if ( args[stopArgBase + 1].equalsIgnoreCase( "false" ) ) { m_stopWait = false; } else { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "The stop_wait argument must be either true or false." ); m_initShowUsage = true; } if ( !m_initFailed ) { argCount = getArgCount( args, stopArgBase + 2 ); if ( argCount < 0 ) { // m_initFailed and m_initError already set. } else { // Get the stop arguments m_stopMainArgs = getArgs( args, stopArgBase + 2, argCount ); if ( m_stopMainArgs == null ) { // m_initFailed and m_initError already set. } else { // Let's see if there is any passthrough params. // Calculate the expected size of args if there is no passthrough params int expectedSize = stopArgBase + 2 + argCount + 1; if ( expectedSize < args.length ) { // Passthrough parameters are present. Read passthrough mode, this will tell us // to which array we should add these params. String passthroughMode = WrapperSystemPropertyUtil.getStringProperty( WrapperStartStopApp.class.getName() + ".passthroughMode", PASSTHROUGH_MODE_IGNORE ); if ( passthroughMode.equalsIgnoreCase( PASSTHROUGH_MODE_BOTH ) ) { startArgs = addPassthroughParams( startArgs, args, expectedSize ); m_stopMainArgs = addPassthroughParams( m_stopMainArgs, args, expectedSize ); } else if ( passthroughMode.equalsIgnoreCase( PASSTHROUGH_MODE_START ) ) { startArgs = addPassthroughParams( startArgs, args, expectedSize ); } else if ( passthroughMode.equalsIgnoreCase( PASSTHROUGH_MODE_STOP ) ) { m_stopMainArgs = addPassthroughParams( m_stopMainArgs, args, expectedSize ); } else { // By default, ignore the extra parameters. Nothing to do. } } } } } } } } } // Start the application. If the JVM was launched from the native // Wrapper then the application will wait for the native Wrapper to // call the application's start method. Otherwise the start method // will be called immediately. WrapperManager.start( this, startArgs ); // This thread ends, the WrapperManager will start the application after the Wrapper has // been propperly initialized by calling the start method above. } /** * Helper method to make it easier for user classes extending this class to have their * own methods of parsing the command line. * * @param startMainMethod Name of the start method. * @param stopMainMethod Name of the stop method. * @param stopWait Should the stop process force the JVM to exit, or wait for all threads to die on their own. * @param stopMainArgs Arguments for the stop method. */ protected WrapperStartStopApp( Method startMainMethod, Method stopMainMethod, boolean stopWait, String[] stopMainArgs ) { m_startMainMethod = startMainMethod; m_stopMainMethod = stopMainMethod; m_stopWait = stopWait; m_stopMainArgs = stopMainArgs; // NOTE - The call to WrapperManager.start() appears to be missing here, but it can't be added // as doing so would break how existing users are making use of this constructor. } /*--------------------------------------------------------------- * Runnable Methods *-------------------------------------------------------------*/ /** * Used to launch the application in a separate thread. */ public void run() { // Notify the start method that the thread has been started by the JVM. synchronized( this ) { m_mainStarted = true; notifyAll(); } Throwable t = null; try { if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "invoking start main method" ) ); } try { try { m_startMainMethod.invoke( null, new Object[] { m_startMainArgs } ); } catch ( IllegalArgumentException iae ) { m_startMainMethod.invoke( null, new Object[] { } ); } } finally { // Make sure the rest of this thread does not fall behind the application. Thread.currentThread().setPriority( Thread.MAX_PRIORITY ); } if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "start main method completed" ) ); } synchronized(this) { // Let the start() method know that the main method returned, in case it is // still waiting. m_mainComplete = true; this.notifyAll(); } return; } catch ( IllegalAccessException e ) { t = e; } catch ( IllegalArgumentException e ) { t = e; } catch ( InvocationTargetException e ) { t = e.getTargetException(); if ( t == null ) { t = e; } } // If we get here, then an error was thrown. If this happened quickly // enough, the start method should be allowed to shut things down. m_outInfo.println(); m_outError.println( WrapperManager.getRes().getString( "Encountered an error running start main: {0}", t ) ); // We should print a stack trace here, because in the case of an // InvocationTargetException, the user needs to know what exception // their app threw. t.printStackTrace( m_outError ); synchronized( this ) { if ( m_ignoreMainExceptions ) { if ( !m_startComplete ) { // An exception was thrown, but we want to let the application continue. m_mainComplete = true; this.notifyAll(); } return; } else { if ( m_startComplete ) { // Shut down here. WrapperManager.stop( 1 ); return; // Will not get here. } else { // Let start method handle shutdown. m_mainComplete = true; m_mainExitCode = new Integer( 1 ); this.notifyAll(); return; } } } } /*--------------------------------------------------------------- * WrapperListener Methods *-------------------------------------------------------------*/ /** * The start method is called when the WrapperManager is signaled by the * native wrapper code that it can start its application. This * method call is expected to return, so a new thread should be launched * if necessary. * If there are any problems, then an Integer should be returned, set to * the desired exit code. If the application should continue, * return null. */ public Integer start( String[] args ) { // See if there were any startup problems. if ( m_initFailed ) { if ( m_initError != null ) { m_outError.println( m_initError ); } if ( m_initException != null ) { m_initException.printStackTrace( m_outError ); } if ( m_initShowUsage ) { showUsage(); } return new Integer( 1 ); } // Decide whether or not to wait for the start main method to complete before returning. boolean waitForStartMain = WrapperSystemPropertyUtil.getBooleanProperty( WrapperStartStopApp.class.getName() + ".waitForStartMain", false ); m_ignoreMainExceptions = WrapperSystemPropertyUtil.getBooleanProperty( WrapperStartStopApp.class.getName() + ".ignoreMainExceptions", false ); int maxStartMainWait = WrapperSystemPropertyUtil.getIntProperty( WrapperStartStopApp.class.getName() + ".maxStartMainWait", 2 ); maxStartMainWait = Math.max( 1, maxStartMainWait ); // Decide the maximum number of times to loop waiting for the main start method. int maxLoops; if ( waitForStartMain ) { maxLoops = Integer.MAX_VALUE; if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "start(args) Will wait indefinitely for the main method to complete." ) ); } } else { maxLoops = maxStartMainWait; // 1s loops. if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "start(args) Will wait up to {0} seconds for the main method to complete.", new Integer(maxLoops) ) ); } } Thread mainThread = new Thread( this, "WrapperStartStopAppMain" ); synchronized(this) { m_startMainArgs = args; mainThread.start(); // Make sure the rest of this thread does not fall behind the application. Thread.currentThread().setPriority( Thread.MAX_PRIORITY ); // To avoid problems with the main thread starting slowly on heavily loaded systems, // do not continue until the thread has actually started. while ( !m_mainStarted ) { try { this.wait( 1000 ); } catch ( InterruptedException e ) { // Continue. } } // Wait for startup main method to complete. int loops = 0; while ( ( loops < maxLoops ) && ( !m_mainComplete ) ) { try { this.wait( 1000 ); } catch ( InterruptedException e ) { // Continue. } if ( !m_mainComplete ) { // If maxLoops is large then this could take a while. Notify the // WrapperManager that we are still starting so it doesn't give up. WrapperManager.signalStarting( 5000 ); } loops++; } // Always set the flag stating that the start method completed. This is needed // so the run method can decide whether or not it needs to be responsible for // shutting down the JVM in the event of an exception thrown by the start main // method. m_startComplete = true; // The main exit code will be null unless an error was thrown by the start // main method. if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "start(args) end. Main Completed={0}, exitCode={1}", new Boolean( m_mainComplete ), m_mainExitCode ) ); } return m_mainExitCode; } } /** * Called when the application is shutting down. */ public int stop( int exitCode ) { if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println(WrapperManager.getRes().getString( "stop({0})", new Integer( exitCode ) ) ); } // Execute the main method in the stop class Throwable t = null; try { if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "invoking stop main method" ) ); } try { m_stopMainMethod.invoke( null, new Object[] { m_stopMainArgs } ); } catch ( IllegalArgumentException iae ) { m_stopMainMethod.invoke( null, new Object[] { } ); } if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "stop main method completed" ) ); } if ( m_stopWait ) { // This feature exists to make sure the stop process waits for the main // application to fully shutdown. This can only be done by looking for // and counting the number of non-daemon threads still running in the // system. int systemThreadCount = WrapperSystemPropertyUtil.getIntProperty( WrapperStartStopApp.class.getName() + ".systemThreadCount", 1 ); systemThreadCount = Math.max( 0, systemThreadCount ); int threadCnt; while( ( threadCnt = getNonDaemonThreadCount() ) > systemThreadCount ) { if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "stopping. Waiting for {0} threads to complete.", new Integer( threadCnt - systemThreadCount ) ) ); } try { Thread.sleep( 1000 ); } catch ( InterruptedException e ) { } } } // Success return exitCode; } catch ( IllegalAccessException e ) { t = e; } catch ( IllegalArgumentException e ) { t = e; } catch ( InvocationTargetException e ) { t = e; } // If we get here, then an error was thrown. m_outError.println( WrapperManager.getRes().getString( "Encountered an error running stop main: {0}", t ) ); // We should print a stack trace here, because in the case of an // InvocationTargetException, the user needs to know what exception // their app threw. t.printStackTrace( m_outError ); // Return a failure exit code return 1; } /** * Called whenever the native wrapper code traps a system control signal * against the Java process. It is up to the callback to take any actions * necessary. Possible values are: WrapperManager.WRAPPER_CTRL_C_EVENT, * WRAPPER_CTRL_CLOSE_EVENT, WRAPPER_CTRL_LOGOFF_EVENT, or * WRAPPER_CTRL_SHUTDOWN_EVENT */ public void controlEvent( int event ) { if ( ( event == WrapperManager.WRAPPER_CTRL_LOGOFF_EVENT ) && ( WrapperManager.isLaunchedAsService() || WrapperManager.isIgnoreUserLogoffs() ) ) { // Ignore m_outInfo.println( WrapperManager.getRes().getString( "User logged out. Ignored." ) ); } else { if ( WrapperManager.isDebugEnabled() ) { m_outDebug.println( WrapperManager.getRes().getString( "controlEvent({0}) Stopping", new Integer( event ) ) ); } WrapperManager.stop( 0 ); // Will not get here. } } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns a count of all non-daemon threads in the JVM, starting with the top * thread group. * * @return Number of non-daemon threads. */ private int getNonDaemonThreadCount() { // Locate the top thread group. ThreadGroup topGroup = Thread.currentThread().getThreadGroup(); while ( topGroup.getParent() != null ) { topGroup = topGroup.getParent(); } // Get a list of all threads. Use an array that is twice the total number of // threads as the number of running threads may be increasing as this runs. Thread[] threads = new Thread[topGroup.activeCount() * 2]; topGroup.enumerate( threads, true ); // Only count any non daemon threads which are // still alive other than this thread. int liveCount = 0; for ( int i = 0; i < threads.length; i++ ) { /* if ( threads[i] != null ) { m_outDebug.println( "Check " + threads[i].getName() + " daemon=" + threads[i].isDaemon() + " alive=" + threads[i].isAlive() ); } */ if ( ( threads[i] != null ) && threads[i].isAlive() ) { // Do not count this thread. if ( ( Thread.currentThread() != threads[i] ) && ( !threads[i].isDaemon() ) ) { // Non-Daemon living thread liveCount++; //m_outDebug.println( " -> Non-Daemon" ); } } } //m_outDebug.println( " => liveCount = " + liveCount ); return liveCount; } /** * Returns the main method of the specified class. If there are any problems, * an error message will be displayed and the Wrapper will be stopped. This * method will only return if it has a valid method. */ private Method getMainMethod( String className ) { // Look for the start class by name Class mainClass; String methodName = "main"; String [] arr = className.split("/"); if ( arr.length > 1 ) { className = arr[0]; methodName = arr[1]; } try { mainClass = Class.forName( className ); } catch ( ClassNotFoundException e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Unable to locate the class {0}: {1}", className, e ); m_initShowUsage = true; return null; // Will not get here } catch ( ExceptionInInitializerError e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Class {0} found but could not be initialized due to:", className ); m_initException = e; return null; // Will not get here } catch ( LinkageError e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Class {0} found but could not be initialized: {1}", className , e ); return null; // Will not get here } // Look for the start method Method mainMethod = null; try { // getDeclaredMethod will return any method named main in the specified class, // while getMethod will only return public methods, but it will search up the // inheritance path. mainMethod = mainClass.getMethod( methodName, new Class[] { String[].class } ); } catch ( NoSuchMethodException e ) { try { // getDeclaredMethod will return any method named in the specified class, // while getMethod will only return public methods, but it will search up the // inheritance path. // try without parameters mainMethod = mainClass.getMethod( methodName, new Class[] { } ); } catch ( NoSuchMethodException e2 ) { } if ( mainMethod == null ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Unable to locate a public static {2} method in class {0}: {1}", className, e, methodName ); return null; // Will not get here } } catch ( SecurityException e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Unable to locate a public static {2} method in class {0}: {1}", className, e, methodName ); return null; // Will not get here } // Make sure that the method is public and static int modifiers = mainMethod.getModifiers(); if ( !( Modifier.isPublic( modifiers ) && Modifier.isStatic( modifiers ) ) ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "The {1} method in class {0} must be declared public and static.", className, methodName ); return null; // Will not get here } return mainMethod; } /** * Parses a set of arguments starting with a count. * * @param args List of arguments. * @param argBase From which index we want to copy the args. * @param argCount Count of the number of available arguments. * @return the Argument list, or null if there was a problem. */ private String[] getArgs( String[] args, int argBase, int argCount ) { // Make sure argCount is a valid value. if ( argCount < 0 ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Illegal argument count: {0}", args[argBase] ); m_initShowUsage = true; return null; } // Make sure that there are enough arguments in the array. if ( args.length < argBase + 1 + argCount ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Not enough arguments. Argument count of {0} was specified.", new Integer( argCount) ); m_initShowUsage = true; return null; } // Create the argument array String[] mainArgs = new String[argCount]; System.arraycopy( args, argBase + 1, mainArgs, 0, argCount ); return mainArgs; } /** * Convert a String to an int. * @param args List of Strings. * @param argBase Index at which we expect to find an int. * @return Arguments count or -1 if error. */ private int getArgCount ( String[] args, int argBase ) { int argCount; try { argCount = Integer.parseInt( args[argBase] ); } catch ( NumberFormatException e ) { m_initFailed = true; m_initError = WrapperManager.getRes().getString( "Illegal argument count: {0}", args[argBase] ); m_initShowUsage = true; return -1; } return argCount; } /** * Add remaining elements from array 'extra' starting at position 'position' * to array 'source'. * Concat both arrays, but only use elements of second array from index position. * source = { "a", "b", "c" }; * extra = { "d", "e", "f", "g" }; * position = 2; * result = { "a", "b", "c", "f", "g" }; * * @param source The source array. All its elements are added to the * return value. * @param extra Array of extra values to concact. * @param position Indicate from which index to concat the values of 'extra'. * @return Array containing values of 'source' and 'extra' from index 'position'. * Return an empty array if error. */ private String[] addPassthroughParams( String[] source, String[] extra, int position ) { if ( (extra == null ) || ( extra.length == 0 ) || ( position >= extra.length ) ) { if (source == null) { return new String[0]; } else { return source; } } // Count how many extra elements we need to add int numberExtraElements = extra.length - position; int sizeSource = 0; if ( source != null ) { sizeSource = source.length; } String[] result = new String[sizeSource + numberExtraElements]; // add all elements from 'source' for ( int i = 0; i < sizeSource; i++) { result[i] = source[i]; } // add elements from 'extra' starting from index 'position' for ( int i = 0; i < numberExtraElements; i++ ) { result[sizeSource + i] = extra[position + i]; } return result; } /** * Displays application usage */ protected void showUsage() { // Show this output without headers. System.out.println(); System.out.println( WrapperManager.getRes().getString( "WrapperStartStopApp Usage:" ) ); System.out.println( WrapperManager.getRes().getString( " java org.tanukisoftware.wrapper.WrapperStartStopApp '{'start_class'{'/start_method'}}' '{'start_arg_count'}' [start_arguments] '{'stop_class'{'/stop_method'}}' '{'stop_wait'}' '{'stop_arg_count'}' [stop_arguments]" ) ); System.out.println(); System.out.println( WrapperManager.getRes().getString( "Where:" ) ); System.out.println( WrapperManager.getRes().getString( " start_class: The fully qualified class name to run to start the " ) ); System.out.println( WrapperManager.getRes().getString( " application." ) ); System.out.println( WrapperManager.getRes().getString( " start_arg_count: The number of arguments to be passed to the start class''s " ) ); System.out.println( WrapperManager.getRes().getString( " main method." ) ); System.out.println( WrapperManager.getRes().getString( " start_arguments: The arguments that would normally be passed to the start " ) ); System.out.println( WrapperManager.getRes().getString( " class application." ) ); System.out.println( WrapperManager.getRes().getString( " stop_class: The fully qualified class name to run to stop the " ) ); System.out.println( WrapperManager.getRes().getString( " application." ) ); System.out.println( WrapperManager.getRes().getString( " stop_wait: When stopping, should the Wrapper wait for all threads to " ) ); System.out.println( WrapperManager.getRes().getString( " complete before exiting (true/false)." ) ); System.out.println( WrapperManager.getRes().getString( " stop_arg_count: The number of arguments to be passed to the stop class''s " ) ); System.out.println( WrapperManager.getRes().getString( " main method." ) ); System.out.println( WrapperManager.getRes().getString( " stop_arguments: The arguments that would normally be passed to the stop " ) ); System.out.println( WrapperManager.getRes().getString( " class application." ) ); } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ /** * Used to Wrapper enable a standard Java application. This main * expects the first argument to be the class name of the application * to launch. All remaining arguments will be wrapped into a new * argument list and passed to the main method of the specified * application. * * @param args Arguments passed to the application. */ public static void main( String args[] ) { new WrapperStartStopApp( args ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperSystemPropertyUtil.java100644 0 0 6541 14333053652 26477 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * A collection of utility methods which make it easy to work with System * Properties without littering code with error handling. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public final class WrapperSystemPropertyUtil { /*--------------------------------------------------------------- * Static Methods *-------------------------------------------------------------*/ /** * Resolves a string property. * * @param name The name of the property to lookup. * @param defaultValue The value to return if it is not set or is invalid. * * @return The requested property value. */ public static String getStringProperty( String name, String defaultValue ) { String val = System.getProperty( name ); if ( val == null ) { return defaultValue; } return val; } /** * Resolves a boolean property. * * @param name The name of the property to lookup. * @param defaultValue The value to return if it is not set or is invalid. * * @return The requested property value. */ public static boolean getBooleanProperty( String name, boolean defaultValue ) { String val = getStringProperty( name, null ); if ( val == null ) { return defaultValue; } return val.equalsIgnoreCase( "TRUE" ); } /** * Resolves an integer property. * * @param name The name of the property to lookup. * @param defaultValue The value to return if it is not set or is invalid. * * @return The requested property value. */ public static int getIntProperty( String name, int defaultValue ) { String val = getStringProperty( name, null ); if ( val == null ) { return defaultValue; } try { return Integer.parseInt( val ); } catch ( NumberFormatException e ) { return defaultValue; } } /** * Resolves a long property. * * @param name The name of the property to lookup. * @param defaultValue The value to return if it is not set or is invalid. * * @return The requested property value. */ public static long getLongProperty( String name, long defaultValue ) { String val = getStringProperty( name, null ); if ( val == null ) { return defaultValue; } try { return Long.parseLong( val ); } catch ( NumberFormatException e ) { return defaultValue; } } /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Not instantiable. */ private WrapperSystemPropertyUtil() { } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperUNIXGroup.java100644 0 0 3061 14333053652 24402 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * A WrapperGroup contains information about a group which a user * belongs to. A WrapperGroup is obtained via a WrapperUser. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperUNIXGroup extends WrapperGroup { /** The GID of the Group. */ private int m_gid; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ WrapperUNIXGroup( int gid, String name ) { super( name ); m_gid = gid; } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns the GID of the group. * * @return The GID of the group. */ public int getGID() { return m_gid; } public String toString() { StringBuffer sb = new StringBuffer(); sb.append( "WrapperUNIXGroup[" ); sb.append( getGID() ); sb.append( getGroup() ); sb.append( "]" ); return sb.toString(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperUNIXUser.java100644 0 0 11035 14333053652 24244 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * A WrapperUser contains information about a user account on the platform * running the Wrapper. A WrapperUser is obtained by calling * WrapperManager.getUser() or WrapperManager.getInteractiveUser(). * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperUNIXUser extends WrapperUser { /** The UID of the user. */ private int m_uid; /** The GID of the user. */ private int m_gid; /** The Group of the user. */ private WrapperUNIXGroup m_group; /** The real name of the user. */ private String m_realName; /** The home directory of the user. */ private String m_home; /** The shell of the user. */ private String m_shell; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ WrapperUNIXUser( int uid, int gid, String user, String realName, String home, String shell ) { super( user ); m_uid = uid; m_gid = gid; m_realName = realName; m_home = home; m_shell = shell; // The real name field appears to contain several fields, we only want the first. int pos = m_realName.indexOf( ',' ); if ( pos == 1000 ) { m_realName = ""; } else if ( pos >= 0 ) { m_realName = m_realName.substring( 0, pos ); } } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns the UID of the user account. * * @return The UID of the user account. */ public int getUID() { return m_uid; } /** * Returns the GID of the user account. * * @return The GID of the user account. */ public int getGID() { return m_gid; } /** * Returns the WrapperUNIXGroup which corresponds to the GID. * Null will be returned if groups were not requested with the * user. * * @return The WrapperUNIXGroup which corresponds to the GID. */ public WrapperUNIXGroup getGroup() { return m_group; } /** * Returns the real name of the user. * * @return The real name of the user. */ public String getRealName() { return m_realName; } /** * Returns the home of the user. * * @return The home of the user. */ public String getHome() { return m_home; } /** * Returns the shell of the user. * * @return The shell of the user. */ public String getShell() { return m_shell; } /** * Called by native code to set the primary Group of the User. */ private void setGroup( int gid, String name ) { m_group = new WrapperUNIXGroup( gid, name ); addGroup( m_group ); } /** * Called by native code to add a Group to the User. */ private void addGroup( int gid, String name ) { addGroup( new WrapperUNIXGroup( gid, name ) ); } /** * Returns a string representation of the user. * * @return A string representation of the user. */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append( "WrapperUNIXUser[" ); sb.append( getUID() ); sb.append( ", " ); sb.append( getGID() ); sb.append( ", " ); sb.append( getUser() ); sb.append( ", " ); sb.append( getRealName() ); sb.append( ", " ); sb.append( getHome() ); sb.append( ", " ); sb.append( getShell() ); sb.append( ", groups {" ); WrapperGroup[] groups = getGroups(); for ( int i = 0; i < groups.length; i++ ) { if ( i > 0 ) { sb.append( ", " ); } sb.append( groups[i].toString() ); } sb.append( "}" ); sb.append( "]" ); return sb.toString(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperUser.java100644 0 0 4146 14333053652 23525 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.util.ArrayList; import java.util.List; /** * A WrapperUser contains information about a user account on the platform * running the Wrapper. A WrapperUser is obtained by calling * WrapperManager.getUser() or WrapperManager.getInteractiveUser(). * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public abstract class WrapperUser { /* The name of the user. */ private String m_user; /** A list of the groups that this user is registered with. */ private List m_groups = new ArrayList(); /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ WrapperUser( String user ) { // Decode the parameters using the default system encoding. m_user = user; } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns the name of the user. * * @return The name of the user. */ public String getUser() { return m_user; } /** * Adds a group to the user. * * @param group WrapperGroup to be added. */ void addGroup( WrapperGroup group ) { m_groups.add( group ); } /** * Returns an array of WrapperGroup instances which define the groups that * the user belongs to. * * @return An array of WrapperGroups. */ public WrapperGroup[] getGroups() { WrapperGroup[] groups = new WrapperGroup[m_groups.size()]; m_groups.toArray( groups ); return groups; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperWin32Group.java100644 0 0 4055 14333053652 24525 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * A WrapperGroup contains information about a group which a user * belongs to. A WrapperGroup is obtained via a WrapperUser. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperWin32Group extends WrapperGroup { /** The current SID of the Group. */ private String m_sid; /** The domain of the User Account. */ private String m_domain; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ WrapperWin32Group( String sid, String user, String domain ) { super( user ); // Decode the parameters using the default system encoding. m_sid = sid; m_domain = domain; } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns the current Security Identifier (SID) of the user account. * * @return The SID of the user account. */ public String getSID() { return m_sid; } /** * Returns the domain name of the user account. * * @return The domain name of the user account. */ public String getDomain() { return m_domain; } /** * Returns the full name of the group. * * @return The full name of the group. */ public String getAccount() { return m_domain + "/" + getGroup(); } public String toString() { return "WrapperWin32Group[" + getAccount() + "]"; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperWin32Service.java100644 0 0 10543 14333053652 25050 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * A WrapperWin32Service contains information about an individual service * registered with the current system. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperWin32Service { public static final int SERVICE_STATE_STOPPED = 0x00000001; public static final int SERVICE_STATE_START_PENDING = 0x00000002; public static final int SERVICE_STATE_STOP_PENDING = 0x00000003; public static final int SERVICE_STATE_RUNNING = 0x00000004; public static final int SERVICE_STATE_CONTINUE_PENDING = 0x00000005; public static final int SERVICE_STATE_PAUSE_PENDING = 0x00000006; public static final int SERVICE_STATE_PAUSED = 0x00000007; /** The name of the service. */ private String m_name; /** The display name of the service. */ private String m_displayName; /** The last known state of the service. */ private int m_serviceState; /** The exit of the service. */ private int m_exitCode; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ WrapperWin32Service( String name, String displayName, int serviceState, int exitCode ) { // Decode the parameters using the default system encoding. m_name = name; m_displayName = displayName; m_serviceState = serviceState; m_exitCode = exitCode; } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns the name of the service. * * @return The name of the service. */ public String getName() { return m_name; } /** * Returns the display name of the service. * * @return The display name of the service. */ public String getDisplayName() { return m_displayName; } /** * Returns the last known state name of the service. * * @return The last known state name of the service. */ public String getServiceStateName() { int serviceState = getServiceState(); switch( serviceState ) { case SERVICE_STATE_STOPPED: return "STOPPED"; case SERVICE_STATE_START_PENDING: return "START_PENDING"; case SERVICE_STATE_STOP_PENDING: return "STOP_PENDING"; case SERVICE_STATE_RUNNING: return "RUNNING"; case SERVICE_STATE_CONTINUE_PENDING: return "CONTINUE_PENDING"; case SERVICE_STATE_PAUSE_PENDING: return "PAUSE_PENDING"; case SERVICE_STATE_PAUSED: return "PAUSED"; default: return "UNKNOWN(" + serviceState + ")"; } } /** * Returns the last known state of the service. * * @return The last known state of the service. */ public int getServiceState() { return m_serviceState; } /** * Returns the exit of the service, or 0 if it is still running. * * @return The exit of the service. */ public int getExitCode() { return m_exitCode; } /** * Returns a string representation of the user. * * @return A string representation of the user. */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append( "WrapperWin32Service[name=\"" ); sb.append( getName() ); sb.append( "\", displayName=\"" ); sb.append( getDisplayName() ); sb.append( "\", state=" ); sb.append( getServiceStateName() ); sb.append( ", exitCode=" ); sb.append( getExitCode() ); sb.append( "]" ); return sb.toString(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/WrapperWin32User.java100644 0 0 6637 14333053652 24357 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.util.Date; /** * A WrapperUser contains information about a user account on the platform * running the Wrapper. A WrapperUser is obtained by calling * WrapperManager.getUser() or WrapperManager.getInteractiveUser(). * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperWin32User extends WrapperUser { /** The current SID of the User Account. */ private String m_sid; /** The domain of the User Account. */ private String m_domain; /** Time that the user logged in. */ private long m_loginTime; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ WrapperWin32User( String sid, String user, String domain, int loginTime) { super( user ); // Decode the parameters using the default system encoding. m_sid = sid; m_domain = domain; // Convert the login time to milliseconds. m_loginTime = loginTime * 1000L; } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns the current Security Identifier (SID) of the user account. * * @return The SID of the user account. */ public String getSID() { return m_sid; } /** * Returns the domain name of the user account. * * @return The domain name of the user account. */ public String getDomain() { return m_domain; } /** * Returns the full name of the user account. * * @return The full name of the user account. */ public String getAccount() { return m_domain + "/" + getUser(); } /** * Returns the login time of the user account. * * @return The login time of the user account. */ public long getLoginTime() { return m_loginTime; } /** * Called by native code to add a Group to the User. */ private void addGroup( String sid, String user, String domain ) { addGroup( new WrapperWin32Group( sid, user, domain ) ); } /** * Returns a string representation of the user. * * @return A string representation of the user. */ public String toString() { StringBuffer sb = new StringBuffer(); sb.append( "WrapperWin32User[" ); sb.append( getAccount() ); sb.append( ", " ); sb.append( new Date( m_loginTime ).toString() ); sb.append( ", groups {" ); WrapperGroup[] groups = getGroups(); for ( int i = 0; i < groups.length; i++ ) { if ( i > 0 ) { sb.append( ", " ); } sb.append( groups[i].toString() ); } sb.append( "}" ); sb.append( "]" ); return sb.toString(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/AboutDialog.java100644 0 0 10433 14333053652 24400 0ustar 0 0 package org.tanukisoftware.wrapper.demo; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.awt.Color; import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.IOException; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.border.EmptyBorder; import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.test.Main; public class AboutDialog extends JDialog { private static final long serialVersionUID = 1L; public AboutDialog( JFrame parent ) { super( parent, DemoApp.getRes().getString("About Dialog"), true ); JPanel main = new JPanel(); main.setBorder( new EmptyBorder( 10, 20, 10, 20 ) ); this.setResizable( false ); Box b = Box.createVerticalBox(); b.add( Box.createGlue() ); b.add( new JLabel( DemoApp.getRes().getString("Demo Application for the Java Service Wrapper" ) ) ); b.add( new JLabel( "By Tanuki Software Ltd." ) ); final JLabel url = new JLabel(); url.setText( "http://wrapper.tanukisoftware.com" ); url.setForeground( Color.BLUE ); url.addMouseListener( new MouseListener() { public void mouseReleased( MouseEvent e ) { // TODO Auto-generated method stub } public void mousePressed( MouseEvent e ) { // TODO Auto-generated method stub } public void mouseExited( MouseEvent e ) { url.setCursor( new Cursor( Cursor.DEFAULT_CURSOR ) ); } public void mouseEntered( MouseEvent e ) { url.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); } public void mouseClicked( MouseEvent e ) { if ( e.getClickCount() > 0 ) { String url = "http://wrapper.tanukisoftware.com"; String cmd; if ( WrapperManager.isWindows() ) { cmd = "cmd.exe /c start " + url; } else if ( WrapperManager.isMacOSX() ) { cmd = "open " + url; } else { cmd = "firefox " + url; } try { Runtime.getRuntime().exec( cmd ); } catch ( IOException ex ) { System.out.println( DemoApp.getRes().getString( "Failed to launch external browser to view web page using command:" ) ); System.out.println( " " + cmd ); System.out.println( DemoApp.getRes().getString(" Error: ") + ex.getMessage() ); System.out.println(); System.out.println( DemoApp.getRes().getString( "Please enter URL into your browser: " ) + url ); System.out.println(); } } } } ); b.add( url ); b.add( Box.createGlue() ); main.add( b, "Center" ); JPanel p2 = new JPanel(); JButton ok = new JButton( "Ok " ); p2.add( ok ); main.add( p2, "South" ); ok.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent evt ) { setVisible( false ); } } ); getContentPane().add( main, "Center" ); this.setLocation( this.getParent().getLocation() ); this.pack(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/CustomizeDialog.java100644 0 0 34422 14333053652 25314 0ustar 0 0 package org.tanukisoftware.wrapper.demo; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JSeparator; import javax.swing.JTextField; import javax.swing.filechooser.FileFilter; import org.tanukisoftware.wrapper.demo.ExtensionFilter; ; class CustomizeDialog extends JDialog { /** * */ private static final long serialVersionUID = -5258541969990785046L; private JPanel jPanel1; private JLabel jLabel1, jLabel2, jLabel3; private JButton jButton1, jButton2, jButton3, jButton4, jButton5; private JTextField jTextField1, jTextField2, jTextField3; private JSeparator jSeparator1; private String selectedSource, selectedIcon, selectedSplashScreen, selectedDestination; private int result; public int getResult() { return result; } public String getSelectedSource() { return selectedSource; } public String getSelectedIcon() { return selectedIcon; } public String getSelectedSplashScreen() { return selectedSplashScreen; } public String getSelectedDestination() { return selectedDestination; } protected CustomizeDialog() { super(); java.awt.GridBagConstraints gridBagConstraints; jPanel1 = new javax.swing.JPanel(); jLabel1 = new javax.swing.JLabel(); jLabel2 = new javax.swing.JLabel(); jLabel3 = new javax.swing.JLabel(); jButton1 = new javax.swing.JButton(); jButton2 = new javax.swing.JButton(); jButton3 = new javax.swing.JButton(); jButton4 = new javax.swing.JButton(); jButton5 = new javax.swing.JButton(); jTextField1 = new javax.swing.JTextField(); jTextField2 = new javax.swing.JTextField(); jTextField3 = new javax.swing.JTextField(); jSeparator1 = new javax.swing.JSeparator(); setDefaultCloseOperation( javax.swing.WindowConstants.HIDE_ON_CLOSE ); this.getContentPane().setLayout( new java.awt.GridBagLayout() ); this.setTitle( DemoApp.getRes().getString( "Wrapper DemoApp: Customize" ) ); jPanel1.setLayout( new java.awt.GridBagLayout() ); jLabel1.setHorizontalAlignment( javax.swing.SwingConstants.RIGHT ); jLabel1.setText( DemoApp.getRes().getString( "Source Binary" ) ); jPanel1.add( jLabel1, new java.awt.GridBagConstraints() ); jLabel2.setHorizontalAlignment( javax.swing.SwingConstants.RIGHT ); jLabel2.setText( DemoApp.getRes().getString( "Icon" ) ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 1; jPanel1.add( jLabel2, gridBagConstraints ); jLabel3.setText( DemoApp.getRes().getString( "Splash screen" ) ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 2; jPanel1.add( jLabel3, gridBagConstraints ); jButton1.setText( "..." ); jButton1.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { JFileChooser fd = new JFileChooser(); fd.setMultiSelectionEnabled( false ); fd.setCurrentDirectory( new File( "." ) ); fd.setDialogTitle( DemoApp.getRes().getString( "Select Executable for customization" ) ); fd.setFileHidingEnabled( true ); fd.setApproveButtonText( DemoApp.getRes().getString( "Load Executable" ) ); FileFilter filter = new ExtensionFilter( DemoApp.getRes().getString( "Wrapper Executable (*.exe)" ), new String[] { "exe" } ); fd.setFileFilter( filter ); int returnVal = fd.showOpenDialog( CustomizeDialog.this ); if ( returnVal == JFileChooser.APPROVE_OPTION ) { try { selectedSource = fd.getSelectedFile().getCanonicalPath(); jTextField1.setText( selectedSource ); } catch ( IOException e1 ) { // TODO Auto-generated catch block e1.printStackTrace(); } } } } ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 0; gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; jPanel1.add( jButton1, gridBagConstraints ); jButton2.setText( "..." ); jButton2.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { JFileChooser fd = new JFileChooser(); fd.setMultiSelectionEnabled( false ); fd.setCurrentDirectory( new File( "." ) ); fd.setDialogTitle( DemoApp.getRes().getString( "Select Icon for customization" ) ); fd.setFileHidingEnabled( true ); fd.setApproveButtonText( DemoApp.getRes().getString( "Load Icon" ) ); FileFilter filter = new ExtensionFilter( DemoApp.getRes().getString( "Icon File (*.ico)" ), new String[] { "ico" } ); fd.setFileFilter( filter ); int returnVal = fd.showOpenDialog( CustomizeDialog.this ); if ( returnVal == JFileChooser.APPROVE_OPTION ) { try { selectedIcon = fd.getSelectedFile().getCanonicalPath(); jTextField2.setText( selectedIcon ); } catch ( IOException e1 ) { // TODO Auto-generated catch block e1.printStackTrace(); } } } } ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 1; gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; jPanel1.add( jButton2, gridBagConstraints ); jButton3.setText( "..." ); jButton3.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { JFileChooser fd = new JFileChooser(); fd.setMultiSelectionEnabled( false ); fd.setCurrentDirectory( new File( "." ) ); fd.setDialogTitle( DemoApp.getRes().getString( "Select splash screen for customization" ) ); fd.setFileHidingEnabled( true ); fd.setApproveButtonText( DemoApp.getRes().getString( "Load splash screen" ) ); FileFilter filter = new ExtensionFilter( DemoApp.getRes().getString( "Splash screen File (*.bmp)" ), new String[] { "bmp" } ); fd.setFileFilter( filter ); int returnVal = fd.showOpenDialog( CustomizeDialog.this ); if ( returnVal == JFileChooser.APPROVE_OPTION ) { try { selectedSplashScreen = fd.getSelectedFile().getCanonicalPath(); jTextField3.setText( selectedSplashScreen ); } catch ( IOException e1 ) { // TODO Auto-generated catch block e1.printStackTrace(); } } } } ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 2; gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; jPanel1.add( jButton3, gridBagConstraints ); jButton4.setText( DemoApp.getRes().getString( "Customize" ) ); jButton4.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { String errorMsg = ""; if ( jTextField1.getText().length() == 0 || !new File( jTextField1.getText() ).exists() ) { jTextField1.setBackground( Color.red ); errorMsg = errorMsg.concat( DemoApp.getRes().getString( "No valid Source Binary Specified\n" ) ); } if ( jTextField2.getText().length() != 0 && !new File( jTextField2.getText() ).exists() ) { jTextField2.setBackground( Color.red ); errorMsg = errorMsg.concat( DemoApp.getRes().getString( "No valid Icon File Specified\n" ) ); } if ( jTextField3.getText().length() != 0 && !new File( jTextField3.getText() ).exists() ) { jTextField3.setBackground( Color.red ); errorMsg = errorMsg.concat( DemoApp.getRes().getString( "No valid Splash Screen File Specified\n" ) ); } if ( jTextField2.getText().length() == 0 && jTextField3.getText().length() == 0 ) { errorMsg = errorMsg.concat( DemoApp.getRes().getString( "Please select at least one Icon or Splash Screen File.\n" ) ); jTextField2.setBackground( Color.yellow ); jTextField3.setBackground( Color.yellow ); } if ( errorMsg.length() > 0 ) { JOptionPane.showMessageDialog( CustomizeDialog.this, DemoApp.getRes().getString( "See the following list of errors:\n" ) + errorMsg, DemoApp.getRes().getString( "Error" ), JOptionPane.ERROR_MESSAGE ); return; } JFileChooser fd = new JFileChooser(); fd.setMultiSelectionEnabled( false ); fd.setCurrentDirectory( new File( "." ) ); fd.setDialogTitle( DemoApp.getRes().getString( "Select destination file of the customization" ) ); fd.setFileHidingEnabled( true ); fd.setApproveButtonText( DemoApp.getRes().getString( "Customize Binary" ) ); FileFilter filter = new ExtensionFilter( DemoApp.getRes().getString( "Executable File (*.exe)" ), new String[] { "exe" } ); fd.setFileFilter( filter ); int returnVal = fd.showSaveDialog( CustomizeDialog.this ); if ( returnVal == JFileChooser.APPROVE_OPTION ) { try { selectedDestination = fd.getSelectedFile().getCanonicalPath(); if ( !selectedDestination.toLowerCase().endsWith( ".exe" ) ) { selectedDestination = selectedDestination.concat( ".exe" ); } if ( selectedDestination.equals( jTextField1.getText() ) ) { JOptionPane.showMessageDialog( CustomizeDialog.this, DemoApp.getRes().getString( "You cannot set the Source and Destination to the same File" ), DemoApp.getRes().getString( "Error" ), JOptionPane.ERROR_MESSAGE ); selectedDestination = ""; } else { result = 1; CustomizeDialog.this.setVisible( false ); } } catch ( IOException ex ) { ex.printStackTrace(); } } } } ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 4; jPanel1.add( jButton4, gridBagConstraints ); jButton5.setText( "Cancel" ); jButton5.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { result = 0; CustomizeDialog.this.setVisible( false ); } } ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 4; gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; jPanel1.add( jButton5, gridBagConstraints ); jTextField1.setColumns( 20 ); jTextField1.setToolTipText( "" ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 0; jPanel1.add( jTextField1, gridBagConstraints ); jTextField2.setColumns( 20 ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 1; jPanel1.add( jTextField2, gridBagConstraints ); jTextField3.setColumns( 20 ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 2; jPanel1.add( jTextField3, gridBagConstraints ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 3; gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; jPanel1.add( jSeparator1, gridBagConstraints ); this.getContentPane().add( jPanel1, new java.awt.GridBagConstraints() ); this.setLocation( this.getParent().getLocation() ); this.setResizable( false ); this.setModal( true ); this.pack(); pack(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/DeadLock.java100644 0 0 5345 14333053652 23642 0ustar 0 0 package org.tanukisoftware.wrapper.demo; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ public class DeadLock { private int m_id; private Object m_obj1; private Object m_obj2; protected DeadLock( int id, Object obj1, Object obj2 ) { m_id = id; m_obj1 = obj1; m_obj2 = obj2; Thread runner = new Thread( "Locker-" + m_id ) { public void run() { System.out.println( DemoApp.getRes().getString( "Locker-{0}: Started", new Integer( m_id ) ) ); try { lockFirst(); } catch ( Throwable t ) { t.printStackTrace(); } System.out.println( DemoApp.getRes().getString( "Locker-{0}: Complete", new Integer( m_id ) ) ); } }; runner.start(); } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ private void lockSecond() { System.out.println( DemoApp.getRes().getString( "Locker-{0}: Try locking {1}...", new Integer( m_id ), m_obj2.toString() ) ); synchronized ( m_obj2 ) { System.out.println( DemoApp.getRes().getString( "Locker-{0}: Oops! Locked {1}", new Integer( m_id ), m_obj2.toString() ) ); } } public void create3ObjectDeadlock() { Object obj1 = new Object(); Object obj2 = new Object(); Object obj3 = new Object(); new DeadLock( 1, obj1, obj2 ); new DeadLock( 2, obj2, obj3 ); new DeadLock( 3, obj3, obj1 ); } public void create2ObjectDeadlock() { Object obj1 = new Object(); Object obj2 = new Object(); new DeadLock( 1, obj1, obj2 ); new DeadLock( 2, obj2, obj1 ); } private void lockFirst() { System.out.println( DemoApp.getRes().getString( "Locker-{0}: Locking {1}...", new Integer( m_id ), m_obj1.toString() ) ); synchronized ( m_obj1 ) { System.out.println( DemoApp.getRes().getString( "Locker-{0}: Locked {1}", new Integer( m_id ), m_obj1.toString() ) ); try { Thread.sleep( 2000 ); } catch ( InterruptedException e ) { } lockSecond(); } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/DemoApp.java100644 0 0 101651 14333053652 23556 0ustar 0 0 package org.tanukisoftware.wrapper.demo; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.awt.Color; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import javax.swing.JOptionPane; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import org.tanukisoftware.wrapper.WrapperActionServer; import org.tanukisoftware.wrapper.WrapperJNIError; import org.tanukisoftware.wrapper.WrapperLicenseError; import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.WrapperListener; import org.tanukisoftware.wrapper.WrapperProcessConfig; import org.tanukisoftware.wrapper.WrapperResources; import org.tanukisoftware.wrapper.WrapperSystemPropertyUtil; /** * This is a Test / Example program which can be used to test the main features * of the Wrapper. *

* It is also an example of Integration Method #3, where you implement the * WrapperListener interface manually. *

* NOTE that in most cases you will want to use Method #1, using the * WrapperSimpleApp helper class to integrate your application. Please see the * * integration section of the documentation for more details. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class DemoApp implements WrapperListener { private WrapperActionServer m_actionServer; private static DemoAppMainFrame m_frame; private static boolean m_isTestCaseRunning; private static Process m_testCase; private static PrintStream m_childPrintStream; private static WrapperResources m_res; private File m_wrapperBin; private String m_wrapperBinS; private String m_confFile; private String m_loggingProps; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ private DemoApp() { m_isTestCaseRunning = false; File wrapperBinDeltaPack; File wrapperBinStd; // See if the binary exists with delta pack naming. if ( WrapperManager.isWindows() ) { wrapperBinDeltaPack = new File( "../bin/" + WrapperManager.generateDetailedNativeBaseName( "wrapper" ) + ".exe" ); wrapperBinStd = new File( "../bin/wrapper.exe" ); } else { wrapperBinDeltaPack = new File( "../bin/" + WrapperManager.generateDetailedNativeBaseName( "wrapper" ) ); wrapperBinStd = new File( "../bin/wrapper" ); } if ( wrapperBinDeltaPack.exists() ) { m_wrapperBin = wrapperBinDeltaPack; } else if ( wrapperBinStd.exists() ) { m_wrapperBin = wrapperBinStd; } else { throw new IllegalStateException( getRes().getString( "Unable to locate Wrapper binary at {0} or {1}", wrapperBinDeltaPack.toString(), wrapperBinStd.toString() ) ); } m_wrapperBinS = m_wrapperBin.toString(); if ( WrapperManager.isWindows() ) { m_wrapperBinS = m_wrapperBinS.replace( '/', '\\' ); } System.out.println( "wrapperBinS=" + m_wrapperBinS ); m_confFile = "../conf/demoapp.conf"; m_loggingProps = "wrapper.console.flush=TRUE wrapper.console.format=LPM wrapper.logfile="; } protected DemoAppMainFrame getFrame() { return m_frame; } protected void setTestCaseRunning( boolean val ) { m_isTestCaseRunning = val; } protected boolean isTestCaseRunning() { return m_isTestCaseRunning; } public static WrapperResources getRes() { if ( m_res == null ) { // Synchronize and then recheck to avoid this method always synchronizing. synchronized( DemoApp.class ) { if ( m_res == null ) { m_res = WrapperManager.loadWrapperResources( "wrapperTestApp", WrapperSystemPropertyUtil.getStringProperty( "wrapper.lang.folder", "../lang" ) ); } } } return m_res; } /*--------------------------------------------------------------- * Inner Classes *-------------------------------------------------------------*/ /*--------------------------------------------------------------- * WrapperListener Methods *-------------------------------------------------------------*/ public void controlEvent( int event ) { System.out.println( getRes().getString( "TestWrapper: controlEvent({0})", new Integer( event ) ) ); if ( event == WrapperManager.WRAPPER_CTRL_LOGOFF_EVENT ) { if ( WrapperManager.isLaunchedAsService() || WrapperManager.isIgnoreUserLogoffs() ) { System.out.println( getRes().getString( "TestWrapper: Ignoring logoff event" ) ); // Ignore } else { WrapperManager.stop( 0 ); } } else if ( event == WrapperManager.WRAPPER_CTRL_C_EVENT ) { // WrapperManager.stop(0); // May be called before the runner is started. } else { WrapperManager.stop( 0 ); } } public Integer start( String[] args ) { String command; if ( args.length <= 0 ) { command = "dialog"; } else { command = args[0]; } if ( command.equals( "dialog" ) ) { System.out.println( "Demo: start()" ); System.out.println( getRes().getString( "Demo: Showing dialog..." ) ); try { m_frame = new DemoAppMainFrame( this ); m_frame.setVisible( true ); try { m_testCase = this.callAction( "start" ); } catch ( IOException e ) { // TODO Auto-generated catch block e.printStackTrace(); } } catch ( java.lang.InternalError e ) { System.out.println( getRes().getString( "Demo: " ) ); System.out.println( getRes().getString( "Demo: ERROR - Unable to display the GUI:" ) ); System.out.println( "Demo: " + e.toString() ); System.out.println( "Demo: " ); System.out.println( getRes().getString( "Demo: This demo requires a display to show its GUI. Exiting..." ) ); command = "console"; } catch ( java.awt.AWTError e ) { System.out.println( "Demo: " ); System.out.println( getRes().getString( "Demo: ERROR - Unable to display the GUI:" ) ); System.out.println( "Demo: " + e.toString() ); System.out.println( "Demo: " ); System.out.println( getRes().getString( "Demo: This demo requires a display to show its GUI. Exiting..." ) ); command = "console"; } catch ( java.lang.UnsupportedOperationException e ) { // java.awt.HeadlessException does not exist in Java versions // prior to 1.4 if ( e.getClass().getName().equals( "java.awt.HeadlessException" ) ) { System.out.println( "Demo: " ); System.out.println( getRes().getString( "Demo: ERROR - Unable to display the GUI:" ) ); System.out.println( "Demo: " + e.toString() ); System.out.println( "Demo: " ); System.out.println( getRes().getString( "Demo: This demo requires a display to show its GUI. Exiting..." ) ); command = "console"; } else { throw e; } } } else if ( command.equals( "start" ) ) { Thread commandRunner = new Thread( "DemoApp-Command-Runner" ) { public void run() { BufferedReader br = new BufferedReader( new InputStreamReader( System.in ) ); String command = ""; System.out.println( getRes().getString( "Started and waiting for a command from the Demo Application" ) ); while ( command.compareToIgnoreCase( "finish" ) != 0 ) { try { command = br.readLine(); if ( command == null ) { command = "finish"; } System.out.println( getRes().getString( "Read action: {0}", command ) ); if ( command.equals( "crash" ) ) { Thread.sleep( 5000 ); System.out.println( getRes().getString( "Going to cause the JVM to crash." ) ); WrapperManager.accessViolationNative(); } else if ( command.equals( "frozen" ) ) { WrapperManager.appearHung(); System.out.println( getRes().getString( "Waiting until wrapper stops this JVM." ) ); while ( true ) { } } else if ( command.equals( "out_of_mem" ) ) { int ii = 5 - 3; System.out.println( getRes().getString( "Going to cause a Out of Memory Error" ) ); Thread.sleep( 5000 ); // System.out.println("got java.lang.OutOfMemoryError"); if ( 5 > ii ) { getOutOfMemError(); } System.out.println( getRes().getString( "Application should get restarted now..." ) ); try { Thread.sleep( 5000 ); } catch ( InterruptedException e ) { // TODO Auto-generated catch block e.printStackTrace(); } } else if ( command.equals( "deadlock" ) ) { if ( WrapperManager.isStandardEdition() ) { System.out.println( getRes().getString( "Deadlock Tester Running..." ) ); Object obj1 = new Object(); Object obj2 = new Object(); int exitCode = 1; DeadLock dl = new DeadLock( 1, obj1, obj2 ); switch ( exitCode ) { case 1: System.out.println( getRes().getString( "2-object deadlock." ) ); dl.create2ObjectDeadlock(); break; case 2: System.out.println( getRes().getString( "Wait then 2-object deadlock." ) ); try { Thread.sleep( 10000 ); } catch ( InterruptedException e ) { } dl.create2ObjectDeadlock(); break; case 3: System.out.println( getRes().getString( "3-object deadlock." ) ); dl.create3ObjectDeadlock(); break; default: System.out.println( getRes().getString( "Done." ) ); } // Always wait a couple seconds to make sure the above // threads have time to start. try { System.out.println( getRes().getString( "Sleeping for 5 sec..." ) ); Thread.sleep( 5000 ); } catch ( InterruptedException e ) { } System.out.println( getRes().getString( "Main Complete." ) ); } else { System.out.println( getRes().getString( "Deadlock checks require at least the Standard Edition." ) ); } } else if ( command.indexOf( "exec" ) == 0 ) { try { String input = command.substring(5); System.out.println( getRes().getString( "Starting a simple application: " ) + input ); if ( input != null && input.length() > 0 ) { WrapperManager.exec( input, new WrapperProcessConfig().setDetached( false ) ); System.out.println( getRes().getString( "Successfully executed!") ); } } catch ( SecurityException e ) { e.printStackTrace(); } catch ( NullPointerException e ) { e.printStackTrace(); } catch ( IllegalArgumentException e ) { e.printStackTrace(); } catch ( UnsatisfiedLinkError e ) { e.printStackTrace(); } catch ( IOException e ) { e.printStackTrace(); } catch ( WrapperJNIError e ) { e.printStackTrace(); } catch ( WrapperLicenseError e ) { e.printStackTrace(); } } } catch ( IOException e ) { // TODO Auto-generated catch block e.printStackTrace(); } catch ( InterruptedException e ) { // TODO Auto-generated catch block e.printStackTrace(); } } } }; commandRunner.start(); } return null; } public int stop( int exitCode ) { System.out.println( "Demo: stop(" + exitCode + ")" ); if ( m_actionServer != null ) { try { m_actionServer.stop(); } catch ( Exception e ) { System.out.println( getRes().getString( "Demo: Unable to stop the action server: {0}", e.getMessage() ) ); } } if ( m_frame != null ) { if ( !WrapperManager.hasShutdownHookBeenTriggered() ) { m_frame.setVisible( false ); m_frame.dispose(); } m_frame = null; } return exitCode; } private Process callAction( String action ) throws IOException { Process p = null; if ( action.equals( "mail" ) ) { MailDialog md = new MailDialog(); md.setVisible( true ); if ( md.getResult() != 0 ) { callAction( "finish" ); try { Thread.sleep( 3000 ); if ( m_isTestCaseRunning ) { System.out.println( getRes().getString( "destroy!" ) ); m_testCase.destroy(); } } catch ( InterruptedException e ) { // TODO Auto-generated catch block e.printStackTrace(); } String arg = m_wrapperBinS + " -c " + m_confFile + " " + m_loggingProps + " wrapper.app.parameter.1=start " + md.getEvents() + " wrapper.event.default.email.debug=TRUE wrapper.event.default.email.smtp.host=" + md.getServer() + " wrapper.event.default.email.smtp.port=" + md.getPort() + " wrapper.event.default.email.sender=" + md.getSender() + " wrapper.event.default.email.recipient=" + md.getRecipients(); // System.out.println( "execing: " + arg ); p = Runtime.getRuntime().exec( arg ); m_childPrintStream = new PrintStream( p.getOutputStream() ); if ( p != null ) { m_frame.jTabbedPane2.setSelectedIndex( 1 ); m_isTestCaseRunning = true; BufferedReader br = new BufferedReader( new InputStreamReader( p.getInputStream() ) ); Runnable setTextRun = new LoggerThread( br, this ); Thread t = new Thread( setTextRun ); t.start(); // return true; } } } else if ( action.equals( "daemon" ) ) { System.out.println( getRes().getString( "Going to install an application as daemon - this requires root privileges" ) ); p = Runtime.getRuntime().exec( " ../bin/testwrapper install" ); if ( p != null ) { m_frame.jTabbedPane2.setSelectedIndex( 1 ); BufferedReader br = new BufferedReader( new InputStreamReader( p.getInputStream() ) ); Runnable setTextRun = new LoggerThread( br, this, true ); Thread t = new Thread( setTextRun ); t.start(); try { p.waitFor(); } catch ( InterruptedException e ) { // TODO Auto-generated catch block e.printStackTrace(); } p = null; p = Runtime.getRuntime().exec( "../bin/testwrapper remove" ); if ( p != null ) { m_frame.jTabbedPane2.setSelectedIndex( 1 ); BufferedReader br2 = new BufferedReader( new InputStreamReader( p.getInputStream() ) ); Runnable setTextRun2 = new LoggerThread( br2, this, true ); Thread t2 = new Thread( setTextRun2 ); t2.start(); try { p.waitFor(); } catch ( InterruptedException e ) { // TODO Auto-generated catch block e.printStackTrace(); } // return true; } } p = null; // WrapperManager.exec( "sudo ../test/demoapp remove" ); } else if ( action.equals( "service" ) ) { p = Runtime.getRuntime().exec( m_wrapperBinS + " -it " + m_confFile + " " + m_loggingProps ); // WrapperManager.exec( "sudo ../test/demoapp remove" ); if ( p != null ) { m_frame.jTabbedPane2.setSelectedIndex( 1 ); BufferedReader br = new BufferedReader( new InputStreamReader( p.getInputStream() ) ); Runnable setTextRun = new LoggerThread( br, this, true ); Thread t = new Thread( setTextRun ); t.start(); // return true; } p = null; } else if ( action.equals( "customize" ) ) { CustomizeDialog cd = new CustomizeDialog(); cd.setVisible( true ); if ( cd.getResult() == 1 ) { String arg = "\"" + cd.getSelectedSource() + "\"" + " --customize --target \"" + cd.getSelectedDestination() + "\""; if ( cd.getSelectedIcon() != null && cd.getSelectedIcon().length() > 0 ) { arg = arg.concat( " --icon \"" + cd.getSelectedIcon() + "\"" ); } if ( cd.getSelectedSplashScreen() != null && cd.getSelectedSplashScreen().length() > 0 ) { arg = arg.concat( " --splash \"" + cd.getSelectedSplashScreen() + "\"" ); } p = Runtime.getRuntime().exec( arg ); if ( p != null ) { m_frame.jTabbedPane2.setSelectedIndex( 1 ); BufferedReader br = new BufferedReader( new InputStreamReader( p.getInputStream() ) ); Runnable setTextRun = new LoggerThread( br, this, true); Thread t = new Thread( setTextRun ); t.start(); // return true; } p = null; } } else if ( action.equals( "start" ) ) { String arg = m_wrapperBinS + " -c " + m_confFile + " " + m_loggingProps + " wrapper.app.parameter.1=" + action; // System.out.println( "calling: " + arg ); if ( !m_isTestCaseRunning ) { // System.out.println( "execing: " + arg ); p = Runtime.getRuntime().exec( arg ); m_childPrintStream = new PrintStream( p.getOutputStream() ); } if ( p != null ) { m_frame.jTabbedPane2.setSelectedIndex( 1 ); m_isTestCaseRunning = true; BufferedReader br = new BufferedReader( new InputStreamReader( p.getInputStream() ) ); Runnable setTextRun = new LoggerThread( br, this ); Thread t = new Thread( setTextRun ); t.start(); } // ps.close(); } else if ( action.equals( "finish" ) ) { if ( m_isTestCaseRunning ) { m_childPrintStream.println( action ); m_childPrintStream.flush(); m_childPrintStream.close(); // ps.close(); } } else if ( action.equals( "exec" ) ) { String input = ""; if ( System.getProperty( "os.name" ).indexOf( "Windows" ) >= 0 ) { input = ( String )JOptionPane.showInputDialog( m_frame, getRes().getString( "Please enter the command you wish to execute:" ), getRes().getString( "Child Process Execution" ), JOptionPane.QUESTION_MESSAGE, null, null, ( Object )"notepad" ); } else { input = ( String )JOptionPane.showInputDialog( m_frame, getRes().getString( "Please enter the command you wish to execute:" ), getRes().getString( "Child Process Execution" ), JOptionPane.QUESTION_MESSAGE, null, null, ( Object )"xclock" ); } if ( input != null && input.length() > 0 ) { String arg = m_wrapperBinS + " -c " + m_confFile + " " + m_loggingProps + " wrapper.app.parameter.1=start"; //System.out.println( "calling: " + arg ); if ( !m_isTestCaseRunning ) { p = Runtime.getRuntime().exec( arg ); m_childPrintStream = new PrintStream( p.getOutputStream() ); DemoApp.m_frame.getJMenuBar().getMenu( 0 ).getItem( 0 ).setEnabled( false ); DemoApp.m_frame.getJMenuBar().getMenu( 0 ).getItem( 1 ).setEnabled( true ); } if ( p != null ) { m_frame.jTabbedPane2.setSelectedIndex( 1 ); m_isTestCaseRunning = true; BufferedReader br = new BufferedReader( new InputStreamReader( p.getInputStream() ) ); Runnable setTextRun = new LoggerThread( br, this ); Thread t = new Thread( setTextRun ); t.start(); // return true; } m_childPrintStream.println( action + " " + input); m_childPrintStream.flush(); } } else { String arg = m_wrapperBinS + " -c " + m_confFile + " " + m_loggingProps + " wrapper.app.parameter.1=start"; //System.out.println( "calling: " + arg ); if ( !m_isTestCaseRunning ) { p = Runtime.getRuntime().exec( arg ); m_childPrintStream = new PrintStream( p.getOutputStream() ); DemoApp.m_frame.getJMenuBar().getMenu( 0 ).getItem( 0 ).setEnabled( false ); DemoApp.m_frame.getJMenuBar().getMenu( 0 ).getItem( 1 ).setEnabled( true ); } if ( p != null ) { m_frame.jTabbedPane2.setSelectedIndex( 1 ); m_isTestCaseRunning = true; BufferedReader br = new BufferedReader( new InputStreamReader( p.getInputStream() ) ); Runnable setTextRun = new LoggerThread( br, this ); Thread t = new Thread( setTextRun ); t.start(); // return true; } m_childPrintStream.println( action ); m_childPrintStream.flush(); // ps.close(); } return p == null ? m_testCase : p; } /*--------------------------------------------------------------- * Static Methods *-------------------------------------------------------------*/ protected boolean doAction( String action ) { //System.out.println( "doAction " + action ); try { m_testCase = this.callAction( action ); } catch ( IOException e ) { // TODO Auto-generated catch block e.printStackTrace(); } return false; } static void getOutOfMemError() { try { throw new java.lang.OutOfMemoryError( getRes().getString( "BANG" ) ); } catch ( OutOfMemoryError e ) { e.printStackTrace(); } } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ /** * IMPORTANT: Please read the Javadocs for this class at the top of the page * before you start to use this class as a template for integrating your own * application. This will save you a lot of time. * @param args Main arguments. */ public static void main( String[] args ) { System.out.println( getRes().getString( "DemoApp: Initializing..." ) ); WrapperManager.start( ( new DemoApp() ), args ); } } class LoggerThread implements Runnable { BufferedReader br; DemoApp m_this; boolean optional; public LoggerThread( BufferedReader br, DemoApp m_this ) { this.br = br; this.m_this = m_this; this.optional = false; } public LoggerThread( BufferedReader br, DemoApp m_this, boolean optional ) { this.br = br; this.m_this = m_this; this.optional = optional; } public void run() { try { String str; int j = 0; if ( !optional ) { m_this.getFrame().getlogTextArea().getDocument().remove( 0, m_this.getFrame().getlogTextArea().getDocument().getLength() ); } while ( ( str = br.readLine() ) != null ) { if ( j++ > 0 && !str.equals( "" ) ) { if ( m_this != null && m_this.getFrame() != null ) { SimpleAttributeSet sas = new SimpleAttributeSet(); String insString = getStyle( str, sas ); m_this.getFrame().getlogTextArea().getDocument().insertString( m_this.getFrame().getlogTextArea().getDocument().getEndPosition().getOffset() - 1, insString + "\n", sas ); int p1 = m_this.getFrame().getlogTextArea().getDocument().getLength(); m_this.getFrame().getlogTextArea().setCaretPosition( p1 ); } } } if (!optional) { m_this.setTestCaseRunning( false ); } } catch ( Exception x ) { x.printStackTrace(); } } private String getStyle( String insString, SimpleAttributeSet sas ) { String returnVal = insString; StyleConstants.setFontFamily( sas, "monospaced" ); StyleConstants.setFontSize( sas, 12 ); StyleConstants.setBold( sas, true ); if ( insString.indexOf( "STATUS |" ) >= 0 || insString.indexOf( "NOTICE |" ) >= 0) { StyleConstants.setForeground( sas, Color.black ); returnVal = insString.substring( 9 ); } else if ( insString.indexOf( "DEBUG |" ) >= 0 ) { StyleConstants.setForeground( sas, Color.blue ); returnVal = insString.substring( 9 ); } else if ( insString.indexOf( "INFO |" ) >= 0 ) { StyleConstants.setForeground( sas, new Color( 52, 169, 88 ) ); returnVal = insString.substring( 9 ); } else if ( insString.indexOf( "WARN |" ) >= 0 ) { StyleConstants.setForeground( sas, new Color( 230, 140, 20 ) ); returnVal = insString.substring( 9 ); } else if ( insString.indexOf( "FATAL |" ) >= 0 ) { StyleConstants.setForeground( sas, Color.red ); returnVal = insString.substring( 9 ); } else if ( insString.indexOf( "ERROR |" ) >= 0 ) { StyleConstants.setForeground( sas, Color.red ); returnVal = insString.substring( 9 ); } if ( insString.indexOf( "WARNING" ) >= 0 ) { StyleConstants.setForeground( sas, new Color( 230, 140, 20 ) ); } if ( insString.indexOf( "WrapperManager Error:" ) >= 0 || insString.indexOf( "java.lang.OutOfMemoryError:" ) >= 0 ) { StyleConstants.setForeground( sas, Color.red ); } return returnVal; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/DemoAppMainFrame.java100644 0 0 35433 14333053652 25322 0ustar 0 0 package org.tanukisoftware.wrapper.demo; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.net.URL; import java.util.Locale; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JEditorPane; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.UIManager; import javax.swing.WindowConstants; import javax.swing.text.html.HTMLDocument; import org.tanukisoftware.wrapper.WrapperManager; public class DemoAppMainFrame extends JFrame implements ActionListener, WindowListener { /** * Serial Version UID. */ private DemoApp m_this; private static final long serialVersionUID = -3847376282833547574L; protected JEditorPane getlogTextArea() { return m_logTextArea; } protected JEditorPane getDescTextArea() { return jEditorPane2; } JScrollPane m_logPane; protected JEditorPane m_logTextArea; JScrollPane jScrollPane2; JEditorPane jEditorPane2; JTabbedPane jTabbedPane2; DemoAppMainFrame( DemoApp m_this ) { super( DemoApp.getRes().getString( "Wrapper Demo Application" ) ); this.m_this = m_this; init(); setLocationRelativeTo( null ); // setSize( 450, 500 ); setResizable( true ); } private void init() { JMenuBar menuBar = new JMenuBar(); JMenu menu = new JMenu( "?" ); JMenuItem about = new JMenuItem( DemoApp.getRes().getString( "About.." ) ); // this.setLayout(new BorderLayout()); about.setActionCommand( "about" ); about.addActionListener( this ); addWindowListener( this ); JMenu jMenu1 = new JMenu(); jMenu1.setText( DemoApp.getRes().getString( "Test" ) ); JMenuItem jMenuItem1 = new JMenuItem(); jMenuItem1.setText( DemoApp.getRes().getString( "Start Test" ) ); jMenuItem1.setEnabled( false ); jMenuItem1.setActionCommand( "start" ); jMenuItem1.addActionListener( this ); jMenu1.add( jMenuItem1 ); JMenuItem jMenuItem2 = new JMenuItem(); jMenuItem2.setText( DemoApp.getRes().getString( "Stop Test" ) ); jMenuItem2.setActionCommand( "finish" ); jMenuItem2.addActionListener( this ); jMenu1.add( jMenuItem2 ); JMenuItem jMenuItem3 = new JMenuItem(); jMenuItem3.setAccelerator( javax.swing.KeyStroke.getKeyStroke( java.awt.event.KeyEvent.VK_F4, java.awt.event.InputEvent.ALT_DOWN_MASK ) ); jMenuItem3.setText( DemoApp.getRes().getString( "Close" ) ); jMenuItem3.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { m_this.doAction( "finish" ); WrapperManager.stopAndReturn( 0 ); } } ); jMenu1.add( jMenuItem3 ); this.setJMenuBar( menuBar ); menu.add( about ); menuBar.add( jMenu1 ); menuBar.add( menu ); this.setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE ); GridBagLayout gridBag1 = new GridBagLayout(); GridBagLayout gridBag2 = new GridBagLayout(); GridBagConstraints c1 = new GridBagConstraints(); GridBagConstraints c2 = new GridBagConstraints(); // this.setLayout(new FlowLayout()); // this.setLayout(layout); JPanel panel1 = new JPanel(); // panel1.setBackground( new java.awt.Color( 235, 124, 25 ) ); panel1.setLayout( gridBag1 ); JTabbedPane tabbedPane = new JTabbedPane(); // tabbedPane.setLayout(new BorderLayout()); tabbedPane.addTab( DemoApp.getRes().getString( "Failure Detections" ), panel1 ); tabbedPane.setMnemonicAt( 0, KeyEvent.VK_1 ); buildCommand( panel1, gridBag1, c1, 1, DemoApp.getRes().getString( "Crash" ), "crash", DemoApp.getRes().getString( "Simulate an Application Crash" ) ); buildCommand( panel1, gridBag1, c1, 1, DemoApp.getRes().getString( "Out of Memory" ), "out_of_mem", DemoApp.getRes().getString( "Simulate a Out Of Memory Error" ) ); buildCommand( panel1, gridBag1, c1, 1, DemoApp.getRes().getString( "Frozen" ), "frozen", DemoApp.getRes().getString( "Simulate a Frozen JVM" ) ); buildCommand( panel1, gridBag1, c1, 2, DemoApp.getRes().getString( "Deadlock" ), "deadlock", DemoApp.getRes().getString( "Simulate a Thread Deadlock" ) ); JPanel panel2 = new JPanel(); panel2.setLayout( gridBag2 ); // panel2.setBackground( Color.yellow ); tabbedPane.addTab( DemoApp.getRes().getString( "Feature Demo" ), panel2 ); tabbedPane.setMnemonicAt( 1, KeyEvent.VK_2 ); buildCommand( panel2, gridBag2, c2, 3, DemoApp.getRes().getString( "Email" ), "mail", DemoApp.getRes().getString( "Activates the email functionality" ) ); buildCommand( panel2, gridBag2, c2, 3, DemoApp.getRes().getString( "WrapperExec" ), "exec", DemoApp.getRes().getString( "Creates a managed Child Process" ) ); String os = System.getProperty( "os.name" ); if ( os.indexOf( "Windows" ) >= 0 ) { buildCommand( panel2, gridBag2, c2, 2, DemoApp.getRes().getString( "Customization" ), "customize", DemoApp.getRes().getString( "Creates a customized Binary of the wrapper" ) ); buildCommand( panel2, gridBag2, c2, 1, DemoApp.getRes().getString( "Service" ), "service", DemoApp.getRes().getString( "Installs and starts this app as Windows Service" ) ); } else { buildCommand( panel2, gridBag2, c2, 1, DemoApp.getRes().getString( "Daemon" ), "daemon", DemoApp.getRes().getString( "Installs and starts this app as Daemon" ) ); } m_logTextArea = new JEditorPane(); jEditorPane2 = new JEditorPane(); m_logTextArea.setContentType( "text/html;" ); jEditorPane2.setContentType( "text/html; charset=UTF-8" ); jEditorPane2.setEditable( false ); // Set CSS format rule Font font = UIManager.getFont( "Label.font" ); String bodyRule = "body { font-family: " + font.getFamily() + "; " + "font-size: 14pt; }"; ( ( HTMLDocument )jEditorPane2.getDocument() ).getStyleSheet().addRule( bodyRule ); m_logTextArea.setEditable( false ); //setMinimumSize( new java.awt.Dimension( 699, 300 ) ); jTabbedPane2 = new javax.swing.JTabbedPane(); m_logPane = new JScrollPane( m_logTextArea ); jScrollPane2 = new JScrollPane( jEditorPane2 ); //jTabbedPane2.setPreferredSize( new Dimension( this.getMinimumSize().width, 400 ) ); jTabbedPane2.setPreferredSize(new java.awt.Dimension(800, 400)); jTabbedPane2.addTab( DemoApp.getRes().getString( "Description" ), jScrollPane2 ); jTabbedPane2.addTab( DemoApp.getRes().getString( "Wrapper Output" ), m_logPane ); getContentPane().setLayout( new BorderLayout() ); getContentPane().add( tabbedPane, java.awt.BorderLayout.PAGE_START ); getContentPane().add( jTabbedPane2, java.awt.BorderLayout.CENTER ); this.setVisible( true ); this.pack(); tabbedPane.setMaximumSize( new Dimension( tabbedPane.getMaximumSize().width, tabbedPane.getSize().height ) ); } private void buildCommand( JComponent container, GridBagLayout gridBag, GridBagConstraints c, int level, String label, String command, Object description ) { JButton button = new JButton( label ); button.setActionCommand( command ); c.fill = GridBagConstraints.BOTH; c.gridwidth = 1; c.gridx = 0; c.insets = new Insets( 10, 10, 10, 10 ); gridBag.setConstraints( button, c ); container.add( button ); button.addActionListener( this ); // button.addMouseListener( this ); // Timer t = new Timer(10, this); JButton buttonhelp = new JButton( "?" ); buttonhelp.setActionCommand( "help" + command ); c.fill = GridBagConstraints.NONE; c.gridwidth = 1; c.gridx = 1; c.insets = new Insets( 10, 10, 10, 10 ); gridBag.setConstraints( buttonhelp, c ); container.add( buttonhelp ); buttonhelp.addActionListener( this ); c.fill = GridBagConstraints.NONE; c.gridwidth = GridBagConstraints.REMAINDER; JComponent desc; if ( description instanceof String ) { desc = new JLabel( ( String )description ); } else if ( description instanceof JComponent ) { desc = ( JComponent )description; } else { desc = new JLabel( description.toString() ); } c.gridx = 2; c.insets = new Insets( 10, 10, 10, 10 ); gridBag.setConstraints( desc, c ); container.add( desc ); if ( level == 2 ) { if ( !WrapperManager.isStandardEdition() ) { button.setEnabled( false ); button.setToolTipText( DemoApp.getRes().getString( "Requires the Standard Edition." ) ); } } else if ( level == 3 ) { if ( !WrapperManager.isProfessionalEdition() ) { button.setEnabled( false ); button.setToolTipText( DemoApp.getRes().getString( "Requires the Professional Edition." ) ); } } } String getHTMLDescription( String action ) { try { String rsname = "html/" + action + "_" + Locale.getDefault().getLanguage() + ".html"; // System.out.println( Locale.getDefault().getLanguage() + // " trying ot open " +rsname); URL url = this.getClass().getResource( rsname ); Reader br; try { br = new BufferedReader( new InputStreamReader( url.openStream(), "utf8" ) ); } catch ( NullPointerException npe1 ) { try { // String test = this.getClass().getResource( "html/" + // action + "_en.html" ).getFile(); br = new BufferedReader( new InputStreamReader( this.getClass().getResource( "html/" + action + "_en.html" ).openStream(), "utf8" ) ); } catch ( NullPointerException npe2 ) { return "" + DemoApp.getRes().getString( "No description for {0} found...", action ) + ""; } } char[] buffer = new char[ 4096 ]; String t = ""; while ( br.read( buffer ) != -1 ) { t = t.concat( new String( buffer ) ); } br.close(); return t; } catch ( IOException e ) { e.printStackTrace(); } return "" + DemoApp.getRes().getString( "No description for {0} found...", action ) + ""; } /************************************************************************** * ActionListener Methods *************************************************************************/ public void actionPerformed( ActionEvent event ) { String action = event.getActionCommand(); if ( !action.startsWith( "help" ) ) { if ( action.equals( "start" ) ) { this.getJMenuBar().getMenu( 0 ).getItem( 0 ).setEnabled( false ); this.getJMenuBar().getMenu( 0 ).getItem( 1 ).setEnabled( true ); m_this.doAction( action ); } else if ( action.equals( "finish" ) ) { this.getJMenuBar().getMenu( 0 ).getItem( 0 ).setEnabled( true ); this.getJMenuBar().getMenu( 0 ).getItem( 1 ).setEnabled( false ); m_this.doAction( action ); } else if ( action.equals( "about" ) ) { // Create the mask. createAboutScreen(); } else if ( action.equals( "daemon" ) ) { jEditorPane2.setText( getHTMLDescription( action ) ); jEditorPane2.setCaretPosition( 0 ); // Create the mask. this.jTabbedPane2.setSelectedIndex( 1 ); m_this.doAction( action ); } else { jEditorPane2.setText( getHTMLDescription( action ) ); jEditorPane2.setCaretPosition( 0 ); this.jTabbedPane2.setSelectedIndex( 1 ); m_this.doAction( action ); } } else { jEditorPane2.setText( getHTMLDescription( action.substring( 4 ) ) ); // System.out.println(getHTMLDescription( action.substring( 4 ) )); jEditorPane2.setCaretPosition( 0 ); this.jTabbedPane2.setSelectedIndex( 0 ); } } private void createAboutScreen() { new AboutDialog( this ).setVisible( true ); } /************************************************************************** * WindowListener Methods *************************************************************************/ public void windowOpened( WindowEvent e ) { } public void windowClosing( WindowEvent e ) { if ( !m_this.isTestCaseRunning() || JOptionPane.showConfirmDialog( this, DemoApp.getRes().getString( "A test case is still running.\nDo you really want to exit and stop this one??" ) ) == JOptionPane.YES_OPTION ) { System.out.println( DemoApp.getRes().getString( "Stopping..." ) ); m_this.doAction( "finish" ); WrapperManager.stopAndReturn( 0 ); } } public void windowClosed( WindowEvent e ) { } public void windowIconified( WindowEvent e ) { } public void windowDeiconified( WindowEvent e ) { } public void windowActivated( WindowEvent e ) { } public void windowDeactivated( WindowEvent e ) { } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/ExtensionFilter.java100644 0 0 3605 14333053652 25313 0ustar 0 0 package org.tanukisoftware.wrapper.demo; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.io.File; import javax.swing.filechooser.FileFilter; class ExtensionFilter extends FileFilter { String description; String extensions[]; public ExtensionFilter( String description, String extension ) { this( description, new String[] { extension } ); } public ExtensionFilter( String description, String extensions[] ) { if ( description == null ) { this.description = extensions[0]; } else { this.description = description; } this.extensions = ( String[] )extensions.clone(); toLower( this.extensions ); } private void toLower( String array[] ) { for ( int i = 0, n = array.length; i < n; i++ ) { array[i] = array[i].toLowerCase(); } } public String getDescription() { return description; } public boolean accept( File file ) { if ( file.isDirectory() ) { return true; } else { String path = file.getAbsolutePath().toLowerCase(); for ( int i = 0, n = extensions.length; i < n; i++ ) { String extension = extensions[i]; if ( ( path.endsWith( extension ) && ( path.charAt( path.length() - extension.toLowerCase().length() - 1 ) ) == '.' ) ) { return true; } } } return false; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/MailDialog.java100644 0 0 41577 14333053652 24225 0ustar 0 0 package org.tanukisoftware.wrapper.demo; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.awt.GridBagConstraints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import org.tanukisoftware.wrapper.WrapperManager; public class MailDialog extends JDialog { /** * */ private static final long serialVersionUID = -5989594238193947229L; private JPanel jPanel1, jPanel2, jPanel3, jPanel4; private JCheckBox jCheckBox1, jCheckBox2, jCheckBox3, jCheckBox4, jCheckBox5, jCheckBox6, jCheckBox7, jCheckBox8, jCheckBox9, jCheckBox10, jCheckBox11, jCheckBox12, jCheckBox13, jCheckBox14; private JTextField jTextField1, jTextField2, jTextField3, jTextField4; private JLabel jLabel1, jLabel2, jLabel3, jLabel4; private JButton jButton1, jButton2; private GridBagConstraints gridBagConstraints; private int result; private int m_port; private String m_recipient, m_sender, m_server, m_events; public String getEvents() { return this.m_events; } public String getRecipients() { return this.m_recipient; } public String getSender() { return this.m_sender; } public String getServer() { return this.m_server; } public int getPort() { return this.m_port; } public int getResult() { return this.result; } public MailDialog() { jPanel1 = new javax.swing.JPanel(); jPanel2 = new javax.swing.JPanel(); jCheckBox1 = new javax.swing.JCheckBox(); jCheckBox2 = new javax.swing.JCheckBox(); jCheckBox3 = new javax.swing.JCheckBox(); jCheckBox4 = new javax.swing.JCheckBox(); jCheckBox5 = new javax.swing.JCheckBox(); jCheckBox6 = new javax.swing.JCheckBox(); jCheckBox7 = new javax.swing.JCheckBox(); jCheckBox8 = new javax.swing.JCheckBox(); jPanel3 = new javax.swing.JPanel(); jTextField1 = new javax.swing.JTextField(); jLabel1 = new javax.swing.JLabel(); jLabel2 = new javax.swing.JLabel(); jTextField2 = new javax.swing.JTextField(); jLabel3 = new javax.swing.JLabel(); jTextField3 = new javax.swing.JTextField(); jLabel4 = new javax.swing.JLabel(); jTextField4 = new javax.swing.JTextField(); jPanel4 = new javax.swing.JPanel(); jButton1 = new javax.swing.JButton(); jButton2 = new javax.swing.JButton(); this.m_events = ""; this.getContentPane().setLayout( new java.awt.GridBagLayout() ); this.setTitle( DemoApp.getRes().getString( "Wrapper DemoApp: Event Mails" ) ); jPanel1.setLayout( new java.awt.GridBagLayout() ); jPanel2.setBorder( javax.swing.BorderFactory.createTitledBorder( DemoApp.getRes().getString( "EventTypes" ) ) ); jPanel2.setAlignmentX( 0.0F ); jPanel2.setAlignmentY( 0.0F ); jPanel2.setMinimumSize( new java.awt.Dimension( 350, 61 ) ); jPanel2.setLayout( new java.awt.GridBagLayout() ); jCheckBox1.setText( "wrapper_start" ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; jPanel2.add( jCheckBox1, gridBagConstraints ); jCheckBox2.setText( "jvm_prelaunch" ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; jPanel2.add( jCheckBox2, gridBagConstraints ); jCheckBox3.setText( "jvm_start" ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; jPanel2.add( jCheckBox3, gridBagConstraints ); jCheckBox4.setText( "jvm_started" ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 1; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; jPanel2.add( jCheckBox4, gridBagConstraints ); jCheckBox5.setText( "jvm_stop" ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 1; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; jPanel2.add( jCheckBox5, gridBagConstraints ); jCheckBox6.setText( "jvm_stopped" ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 1; gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; jPanel2.add( jCheckBox6, gridBagConstraints ); jCheckBox7.setText( "wrapper_stop" ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 2; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; jPanel2.add( jCheckBox7, gridBagConstraints ); jCheckBox14 = new JCheckBox(); jCheckBox14.setText( "jvm_restart" ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 2; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; jPanel2.add( jCheckBox14, gridBagConstraints ); jCheckBox9 = new JCheckBox(); jCheckBox9.setText( "jvm_failed_invocation" ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 2; gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; jPanel2.add( jCheckBox9, gridBagConstraints ); jCheckBox10 = new JCheckBox(); jCheckBox10.setText( "jvm_max_failed_invocations" ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 3; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; jPanel2.add( jCheckBox10, gridBagConstraints ); jCheckBox11 = new JCheckBox(); jCheckBox11.setText( "jvm_kill" ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 3; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; jPanel2.add( jCheckBox11, gridBagConstraints ); jCheckBox12 = new JCheckBox(); jCheckBox12.setText( "jvm_killed" ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 3; gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; jPanel2.add( jCheckBox12, gridBagConstraints ); jCheckBox13 = new JCheckBox(); jCheckBox13.setText( "jvm_unexpected_exit" ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 4; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; jPanel2.add( jCheckBox13, gridBagConstraints ); jPanel1.add( jPanel2, new java.awt.GridBagConstraints() ); jPanel3.setBorder( javax.swing.BorderFactory.createTitledBorder( DemoApp.getRes().getString( "Mail Setup" ) ) ); jPanel3.setLayout( new java.awt.GridBagLayout() ); jTextField1.setColumns( 20 ); jTextField1.setText( WrapperManager.getProperties().getProperty( "wrapper.event.default.email.sender" ) ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 0; jPanel3.add( jTextField1, gridBagConstraints ); jLabel1.setText( DemoApp.getRes().getString( "Sender:" ) ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; jPanel3.add( jLabel1, gridBagConstraints ); jCheckBox8.setText( DemoApp.getRes().getString( "Attach Logfile" ) ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 0; gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; jPanel3.add( jCheckBox8, gridBagConstraints ); jLabel2.setText( DemoApp.getRes().getString( "Recipient:" ) ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 1; jPanel3.add( jLabel2, gridBagConstraints ); jTextField2.setColumns( 30 ); jTextField2.setText( WrapperManager.getProperties().getProperty( "wrapper.event.default.email.recipient" ) ); jTextField2.setToolTipText( DemoApp.getRes().getString( "Please separate multiple recipients with a semicolon '';''" ) ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 1; jPanel3.add( jTextField2, gridBagConstraints ); jLabel3.setText( "Mail Server:" ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 2; jPanel3.add( jLabel3, gridBagConstraints ); jTextField3.setColumns( 15 ); jTextField3.setText( WrapperManager.getProperties().getProperty( "wrapper.event.default.email.smtp.host" ) ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 1; gridBagConstraints.gridy = 2; jPanel3.add( jTextField3, gridBagConstraints ); jLabel4.setText( "Port:" ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 2; gridBagConstraints.gridy = 2; jPanel3.add( jLabel4, gridBagConstraints ); jTextField4.setColumns( 3 ); String port = WrapperManager.getProperties().getProperty( "wrapper.event.default.email.smtp.port" ); jTextField4.setText( port == null ? "25" : port ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 3; gridBagConstraints.gridy = 2; jPanel3.add( jTextField4, gridBagConstraints ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 1; jPanel1.add( jPanel3, gridBagConstraints ); jButton1.setText( DemoApp.getRes().getString( "OK" ) ); jButton1.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { if ( jCheckBox1.isSelected() || jCheckBox2.isSelected() || jCheckBox3.isSelected() || jCheckBox4.isSelected() || jCheckBox5.isSelected() || jCheckBox6.isSelected() || jCheckBox7.isSelected() || jCheckBox9.isSelected() || jCheckBox10.isSelected() || jCheckBox11.isSelected() || jCheckBox12.isSelected() || jCheckBox13.isSelected() || jCheckBox14.isSelected() ) { if ( jTextField1.getText().length() == 0 || jTextField2.getText().length() == 0 || jTextField3.getText().length() == 0 || jTextField4.getText().length() == 0 ) { JOptionPane.showMessageDialog( MailDialog.this, DemoApp.getRes().getString( "All text fields need to be filled out!" ), DemoApp.getRes().getString( "Input missing!" ), JOptionPane.OK_OPTION ); } else { if ( jCheckBox1.isSelected() ) { m_events = m_events.concat( "wrapper.event." + jCheckBox1.getText() + ".email=TRUE " ); } if ( jCheckBox2.isSelected() ) { m_events = m_events.concat( "wrapper.event." + jCheckBox2.getText() + ".email=TRUE " ); } if ( jCheckBox3.isSelected() ) { m_events = m_events.concat( "wrapper.event." + jCheckBox3.getText() + ".email=TRUE " ); } if ( jCheckBox4.isSelected() ) { m_events = m_events.concat( "wrapper.event." + jCheckBox4.getText() + ".email=TRUE " ); } if ( jCheckBox5.isSelected() ) { m_events = m_events.concat( "wrapper.event." + jCheckBox5.getText() + ".email=TRUE " ); } if ( jCheckBox6.isSelected() ) { m_events = m_events.concat( "wrapper.event." + jCheckBox6.getText() + ".email=TRUE " ); } if ( jCheckBox7.isSelected() ) { m_events = m_events.concat( "wrapper.event." + jCheckBox7.getText() + ".email=TRUE " ); } if ( jCheckBox9.isSelected() ) { m_events = m_events.concat( "wrapper.event." + jCheckBox9.getText() + ".email=TRUE " ); } if ( jCheckBox10.isSelected() ) { m_events = m_events.concat( "wrapper.event." + jCheckBox10.getText() + ".email=TRUE " ); } if ( jCheckBox11.isSelected() ) { m_events = m_events.concat( "wrapper.event." + jCheckBox11.getText() + ".email=TRUE " ); } if ( jCheckBox12.isSelected() ) { m_events = m_events.concat( "wrapper.event." + jCheckBox12.getText() + ".email=TRUE " ); } if ( jCheckBox13.isSelected() ) { m_events = m_events.concat( "wrapper.event." + jCheckBox13.getText() + ".email=TRUE " ); } if ( jCheckBox14.isSelected() ) { m_events = m_events.concat( "wrapper.event." + jCheckBox14.getText() + ".email=TRUE " ); } if ( jCheckBox8.isSelected() ) { m_events = m_events.concat( "wrapper.event.default.email.attach_log=TRUE" ); } m_recipient = jTextField2.getText(); m_sender = jTextField1.getText(); m_server = jTextField3.getText(); m_port = Integer.parseInt( jTextField4.getText() ); result = 1; MailDialog.this.setVisible( false ); } } else { JOptionPane.showMessageDialog( MailDialog.this, DemoApp.getRes().getString( "Please select at least one event!" ), DemoApp.getRes().getString( "Input missing!" ), JOptionPane.OK_OPTION ); } } } ); jPanel4.add( jButton1 ); jButton2.setText( DemoApp.getRes().getString( "Cancel" ) ); jButton2.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { result = 0; MailDialog.this.dispose(); } } ); jPanel4.add( jButton2 ); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 2; jPanel1.add( jPanel4, gridBagConstraints ); this.getContentPane().add( jPanel1, new java.awt.GridBagConstraints() ); this.setLocation( this.getParent().getLocation() ); this.setResizable( false ); this.setModal( true ); this.pack(); // this.setVisible(true); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/crash_en.html100644 0 0 3535 14333053652 24744 0ustar 0 0

Crash

The Wrapper includes a lightweight native library which is used to handle system events. The library also contains a native method, which is used for testing, to cause an access violation in native code due to a NULL reference.

Depending on the JVM, this will give different output. In the example below, the JVM created a core dump which takes about a minute to save. During this time, the JVM appears to be hung so the Wrapper kills and restarts it.

Console Output:
wrapper  | --> Wrapper Started as Console
wrapper  | Launching a JVM...
jvm 1    | Initializing...
jvm 1    | Wrapper (Version 3.x.x)
jvm 1    |
jvm 1    | start()
jvm 1    | WARNING: Attempting to cause an access violation...
jvm 1    | #
jvm 1    | # An EXCEPTION_ACCESS_VIOLATION exception has been detected in native code outside the VM.
jvm 1    | # Program counter=0x8f7105f
jvm 1    | #
wrapper  | JVM is hung: Timed out waiting for signal from JVM.
wrapper  | Java Virtual Machine did not exit on request, terminated
wrapper  | Launching a JVM...
jvm 2    | Initializing...
jvm 2    | Wrapper (Version 3.x.x)
jvm 2    |
jvm 2    | start()
wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/crash_ja.html100644 0 0 4256 14333053652 24735 0ustar 0 0

Crash

Wrapper??軽???イティブ・ライブラリーを?ん??り? システム・イベントを?り扱?????利用?れ??。 ライブラリー????イティブ・メソッドも?ん??り? テスト用?利用?れ? NULL ?照?よる?イティブ・コード?アクセス???を引??起????。

JVM次第?????れ?異?る出力?果??り??。 ??下記?例????存?1分??り????コア・ダンプを作????。 ???所?時間?もより???? JVM??ングアップ???るよ??見?る?能性も?り? ???場??Wrapper??ングアップ?検知??強制終了???起動??る?能性も?り??。

コンソール出力:
wrapper  | --> Wrapper Started as Console
wrapper  | Launching a JVM...
jvm 1    | Initializing...
jvm 1    | Wrapper (Version 3.x.x)
jvm 1    |
jvm 1    | start()
jvm 1    | WARNING: Attempting to cause an access violation...
jvm 1    | #
jvm 1    | # An EXCEPTION_ACCESS_VIOLATION exception has been detected in native code outside the VM.
jvm 1    | # Program counter=0x8f7105f
jvm 1    | #
wrapper  | JVM is hung: Timed out waiting for signal from JVM.
wrapper  | Java Virtual Machine did not exit on request, terminated
wrapper  | Launching a JVM...
jvm 2    | Initializing...
jvm 2    | Wrapper (Version 3.x.x)
jvm 2    |
jvm 2    | start()
wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/customize_en.html100644 0 0 24015 14333053652 25702 0ustar 0 0

Customize your Wrapper

With the release of Wrapper version 3.3.7, the Wrapper provides the ability to change some of its resources and to customize its appearance on Windows.

When customizing the Wrapper binary "wrapperw.exe", a new version of the binary will be created with the new resources specified with the "--customize" command. The resulting binary will be locked, meaning that it is no longer possible to customize it.

When creating a new customer binary, any of the following arguments can be combined. The "--target {target.exe}" argument is always required.

Please note that the paths of all file names are relative to the location of the Wrapper binary, not necessarily relative to the location of the caller. For example, the following command will create the target file in the same directory as the wrapperw.exe binary and expect to find the new icon and splashscreen in that directory as well:

bin\wrapperw.exe --customize --target myapp.exe --icon myicon.ico --splash mysplash.bmp

The Target Executable

When the Wrapper binary is customized, it is necessary to always specify a new file name for the customized version. The Wrapper will always overwrite the file if it already exists.

wrapperw.exe --target myapp.exe

Putting the parameter all together would result to a command line like this:

bin\wrapperw.exe --customize --icon MyIcon.ico --splash MySplash.bmp --target MyApp.exe

This command will create a new executable MyApp.exe in the same directory the wrapperw.exe is located, with a customized Icon and Splash Screen.

Customize The Icon

NOTE

This function requires either Standard or Professional Edition of the Java Service Wrapper.

The Wrapper is shipped with a default icon, however the Wrapper has proven to be also an excellent Application launcher, for this purpose a software developer may wish to replace the Icon with that of the product the Wrapper is being used with.

The icon file is a standard multi layer icon file that contains all of the resolution and icon sizes that need to be supported.

Example:
wrapperw.exe --target myapp.exe --icon myicon.ico

Customize The Splash Screen

NOTE

This function requires either Standard or Professional Edition of the Java Service Wrapper.

The consoleless version of the Wrapper, wrapperw.exe, will by default show a Splash Screen on startup. This splash screen can be customized with an image representing the product that the Wrapper is being used with.

Having the Wrapper display a splash screen rather than doing so from within Java has the benefit of making the applications startup seem very responsive because the splash screen will be displayed before the JVM is launched and can remain visible as the Java application's classes are loaded and initialized.

The way the splash screen behaves can be controled with the wrapper.splashscreen.mode property.

This argument is illegal for the wrapper.exe binary because it does not display a splash screen.

Example:
wrapperw.exe --target myapp.exe --splash mysplash.bmp

Currently only bitmap (*.bmp) files are supported. There are no size or bit-depth restrictions set.

wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/customize_ja.html100644 0 0 26047 14333053652 25701 0ustar 0 0

自分?Wrapperをカスタマイズ?る

Wrapper?ージョン3.3.7?リリース?? Wrapper??リソース??????を変更??り? Windows上?外観をカスタマイズ??り????能力?備??機能???供を開始????。

Wrapper?イナリ版「wrapperw.exe?をカスタマイズ?る???? "--customize"コマンド?指定?れ?新??リソース?新???ージョン??イナリ?作??れ??。 ????果??る?イナリ?ロック?れ?も?やカスタマイズ????????り??。

新??カスタマー・?イナリを作??る????次?引数を???????。 引数 "--target {target.exe}" ??常?必???。

全??ファイル???パス??呼?出?元??相対的??置場所??る必?????? Wrapper?イナリ??置場所??相対??る???注????????。 例???次?コマンド???「wrapperw.exe??イナリ? ??ディレクトリー?ターゲット・ファイルを作????様?????ディレクトリー?新??アイコンや スプラッシュ・スクリーンを探?????り??:

bin\wrapperw.exe --customize --target myapp.exe --icon myicon.ico --splash mysplash.bmp

実行ターゲット:

Wrapper?イナリ?カスタマイズ?れる???? 常??カスタマイズ・?ージョン用??新?? ファイル??を指定?る必???り??。 既???ファイル???存在?る場????Wrapper?常????ファイルを上書?????。

wrapperw.exe --target myapp.exe

全?????パラメーターを置??????よ?? コマンドライン??り??:

bin\wrapperw.exe --customize --icon MyIcon.ico --splash MySplash.bmp --target MyApp.exe

??コマンド?? 「wrapperw.exe???置????る??ディレクトリー?? カスタマイズ?れ?アイコンやスプラッシュ・スクリーン?共?? 新??実行ファイル「MyApp.exe?を作????。

アイコンをカスタマイズ?る

NOTE

??機能を利用?る????? Java Service Wrapper?スタンダード版??る???プロフェッショナル版を?利用?????。

Wrapper??デフォルト・アイコンを?梱???布?れ?????? ?????ら?Wrapperを?梱???る商??アイコン?置?????????開発者?希望????? Wrapper??素晴ら??アプリケーション・ラン?ャー?も?る???役目も??供?????。

アイコン・ファイル??標準?マル?・レイヤー・アイコン・ファイル??り? サ?ート?れる必???る全??解?度やアイコン・サイズを?ん????。

設定例:
wrapperw.exe --target myapp.exe --icon myicon.ico

スプラッシュ・スクリーンをカスタマイズ?る

NOTE

??機能を利用?る????? Java Service Wrapper?スタンダード版??る???プロフェッショナル版を?利用?????。

Wrapper?コンソールレス版「wrapperw.exe??? デフォルト??スタート時?スプラッシュ・スクリーンを表示???。 ??スプラッシュ・スクリーン??Wrapperを?梱???る商??????イメージ画??置????る???カスタマイズ?る???????

Java内部?ら??????Wrapper?スプラッシュ・スクリーンを表示??る???? アプリケーション・スタートアップを??もresponsiveら?????る利点??り??。 ????由??スプラッシュ・スクリーン?JVM起動??表示?れ? Javaアプリケーション?クラス?ロード(読?込?)?れ??期化?れ?見?る状態を維????る????。

wrapper.splashscreen.mode] プロパティを使???スプラッシュ・スクリーン?動作????コントロール?る????????。

??引数???スプラッシュ・スクリーンを表示?????? 「wrapper.exe??イナリ????法??。

Example:
wrapperw.exe --target myapp.exe --splash mysplash.bmp

?在?????唯一?ビットマップ (*.bmp) ファイルをサ?ート?????。 画?サイズやビット深??制???り??ん。

wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/daemon_en.html100644 0 0 5115 14333053652 25103 0ustar 0 0

Installing The Application To Start on Reboot

Introduced in version 3.4.0, the script provides a way to embed the wrapper (and finally your application).
The script will automatically detect your OS - on Linux plattforms also some distributions - and perform the steps necessary to install the wrapper script into the initialization process of your OS.

$ sudo bin/testwrapper install
[sudo] password for tanuki:
Detected Ubuntu:
Installing service testwrapper from current directory /home/tanuki/wrapper/bin
Adding system startup for /etc/init.d/testwrapper ...
...

As the script installs itself into your OS, it is necessary to run the install (and uninstall) option as user root!

For freebsd and MacOSX users the script provides a default configuration file how launchd or initd will handle the script during the boot process. However it is possible to customize this step.

If you wish to uninstall/remove the wrapper from the system initialization run the script (as root!) passing the action parameter "remove". This will first stop the running wrapper application and finally clean itself from initialization.

$ sudo bin/testwrapper remove
[sudo] password for tanuki:
Stopping Test Wrapper Sample Application...
Stopped Test Wrapper Sample Application.
Detected Ubuntu:
Removing service testwrapper from current directory /home/tanuki/wrapper/bin
...
wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/daemon_ja.html100644 0 0 5214 14333053652 25073 0ustar 0 0

リブート(?起動)?スタート?るよ??アプリケーションをインストール?る

Introduced in version 3.4.0, the script provides a way to embed the wrapper (and finally your application).
The script will automatically detect your OS - on Linux plattforms also some distributions - and perform the steps necessary to install the wrapper script into the initialization process of your OS.

$ sudo bin/testwrapper install
[sudo] password for tanuki:
Detected Ubuntu:
Installing service testwrapper from current directory /home/tanuki/wrapper/bin
Adding system startup for /etc/init.d/testwrapper ...
...

As the script installs itself into your OS, it is necessary to run the install (and uninstall) option as user root!

For freebsd and MacOSX users the script provides a default configuration file how launchd or initd will handle the script during the boot process. However it is possible to customize this step.

If you wish to uninstall/remove the wrapper from the system initialization run the script (as root!) passing the action parameter "remove". This will first stop the running wrapper application and finally clean itself from initialization.

$ sudo bin/testwrapper remove
[sudo] password for tanuki:
Stopping Test Wrapper Sample Application...
Stopped Test Wrapper Sample Application.
Detected Ubuntu:
Removing service testwrapper from current directory /home/tanuki/wrapper/bin
...
wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/deadlock_en.html100644 0 0 33552 14333053652 25434 0ustar 0 0

wrapper.check.deadlock

This property, in combination with the other properties,

are used to configure how the Wrapper monitors a JVM for deadlocked threads. This can be very useful to detect and then work around potentially fatal problems which would otherwise be difficult if not impossible to work around.

Thread deadlock checks require that at least Java version 1.5 is used. Only JVMs will ignore the checks.

Checking for Deadlocks is fairly quick, but it does involve briefly locking and taking a snapshot of all threads so this property is FALSE by default.

Example: (Deadlock Check: Off)
wrapper.check.deadlock=FALSE

Simple Example:

Please read over the property details below, but the following simple example will configure the Wrapper to log the location of a deadlock and then immediately restart the JVM.

Example:
wrapper.check.deadlock=TRUE
wrapper.check.deadlock.interval=60
wrapper.check.deadlock.action=RESTART
wrapper.check.deadlock.output=FULL

Deadlock?:

Deadlocks can occur when two or more threads are locking resources in an order which results in all threads waiting indefinitely.

The simplest example is where Thread A locks Object A and then attempts to lock Object B, while another Thread B has Object B locked and is waiting to lock Object A. In this case, Thread A will never release Object A because it is waiting for Object B. This will never happen because Thread B will keep Object B locked indefinitely as it waits for Object A to become available.

wrapper.check.deadlock.interval

The wrapper.check.deadlock.interval property makes it possible to control the interval at which the Wrapper looks for deadlocks in an application. It can be set to any interval starting with once per second, but it defaults to "60" (once per minute). In general, for applications which are known to be stable, it may be fine to greatly decrease the frequency of these deadlock checks.

Example: (every 60 seconds)
wrapper.check.deadlock.interval=60

wrapper.check.deadlock.action

The wrapper.check.deadlock.action property makes it possible to control what the Wrapper does when a deadlock is detected. The default action is to RESTART.

Example:
wrapper.check.deadlock.action=RESTART

Possible actions are:

  • RESTART -

    will stop the current JVM and then restart a new invocation.

  • SHUTDOWN -

    will stop the JVM as well as the Wrapper.

  • DUMP -

    will invoke a thread dump. Note that because deadlocks are by definition permanent, the deadlocked state will persist until the JVM has been restarted, meaning that the thread dump will also be invoked at each interval.

  • USER_<n> -

    will cause a user defined event to be fired in the Professional Edition.

  • NONE -

    is useful because it will prevent any triggers with a higher number from being triggered.

It is possible to specify more than one actions by separating then with a space or comma. When more than one action is specified, they will be executed in rapid succession in the order specified.

The following example will perform a thread dump and then restart the JVM.

Example:
wrapper.check.deadlock.action=DUMP,RESTART

wrapper.check.deadlock.output

The wrapper.check.deadlock.output property makes it possible to control what information the Wrapper causes to be logged when a deadlock is detected. The default output level is FULL.

Example:
wrapper.check.deadlock.output=FULL

Possible output levels are:

  • FULL -

    will cause the WrapperManager class within the JVM to output a report which includes full stack traces for the threads involved in the deadlock.

    Output Example of "FULL":
    INFO   | jvm 1    | WrapperManager Error: Found 2 deadlocked threads!
    INFO   | jvm 1    | WrapperManager Error: =============================
    INFO   | jvm 1    | WrapperManager Error: "Locker-2" tid=18
    INFO   | jvm 1    | WrapperManager Error:   java.lang.Thread.State: BLOCKED
    INFO   | jvm 1    | WrapperManager Error:     at org.tanukisoftware.wrapper.test.DeadLock.lockSecond(DeadLock.java:64)
    INFO   | jvm 1    | WrapperManager Error:       - waiting on <0x000000002fcac6db> (a java.lang.Object) owned by "Locker-1" tid=17
    INFO   | jvm 1    | WrapperManager Error:     at org.tanukisoftware.wrapper.test.DeadLock.lockFirst(DeadLock.java:83)
    INFO   | jvm 1    | WrapperManager Error:       - locked <0x0000000029c56c60> (a java.lang.Object)
    INFO   | jvm 1    | WrapperManager Error:     at org.tanukisoftware.wrapper.test.DeadLock.access$100(DeadLock.java:22)
    INFO   | jvm 1    | WrapperManager Error:     at org.tanukisoftware.wrapper.test.DeadLock$1.run(DeadLock.java:42)
    INFO   | jvm 1    | WrapperManager Error:
    INFO   | jvm 1    | WrapperManager Error: "Locker-1" tid=17
    INFO   | jvm 1    | WrapperManager Error:   java.lang.Thread.State: BLOCKED
    INFO   | jvm 1    | WrapperManager Error:     at org.tanukisoftware.wrapper.test.DeadLock.lockSecond(DeadLock.java:64)
    INFO   | jvm 1    | WrapperManager Error:       - waiting on <0x0000000029c56c60> (a java.lang.Object) owned by "Locker-2" tid=18
    INFO   | jvm 1    | WrapperManager Error:     at org.tanukisoftware.wrapper.test.DeadLock.lockFirst(DeadLock.java:83)
    INFO   | jvm 1    | WrapperManager Error:       - locked <0x000000002fcac6db> (a java.lang.Object)
    INFO   | jvm 1    | WrapperManager Error:     at org.tanukisoftware.wrapper.test.DeadLock.access$100(DeadLock.java:22)
    INFO   | jvm 1    | WrapperManager Error:     at org.tanukisoftware.wrapper.test.DeadLock$1.run(DeadLock.java:42)
    INFO   | jvm 1    | WrapperManager Error:
    INFO   | jvm 1    | WrapperManager Error: =============================
    STATUS | wrapper  | A Thread Deadlock was detected in the JVM.  Restarting JVM.
  • SIMPLE -

    will cause the WrapperManager class within the JVM to output a report which includes only a brief summary naming the threads and objects involved in the deadlock. In many cases, this is enough especially for known problems.

    Output Example of "SIMPLE":
    INFO   | jvm 1    | WrapperManager Error: Found 2 deadlocked threads!
    INFO   | jvm 1    | WrapperManager Error: =============================
    INFO   | jvm 1    | WrapperManager Error: "Locker-2" BLOCKED waiting on a java.lang.Object owned by "Locker-1"
    INFO   | jvm 1    | WrapperManager Error: "Locker-1" BLOCKED waiting on a java.lang.Object owned by "Locker-2"
    INFO   | jvm 1    | WrapperManager Error: =============================
    STATUS | wrapper  | A Thread Deadlock was detected in the JVM.  Restarting JVM.
  • NONE -

    will cause the WrapperManager class within the JVM to suppress all output. This may be desired for production systems when a problem is known but you don't want or need to maintain too much information in the logs. The Wrapper process, as in the other options, will always log a single entry to provide a reason why the triggered action takes place.

    Output Example of "NONE":
    STATUS | wrapper  | A Thread Deadlock was detected in the JVM.  Restarting JVM.

wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/deadlock_ja.html100644 0 0 36542 14333053652 25426 0ustar 0 0

コンフィギュレーション・プロパティ?概?

wrapper.check.deadlock プロパティ

??プロパティ??他?プロパティ??組??????

デッドロック??スレッド???? JVMを??よ??モニター(監視)?る?を設定?る???使?れ??。 ?れ??何??回?策??る場???? 難??よ??潜在的?致命的??題を検知??回??る?????も役?立???。

スレッド?デッドロック・?ェック??少????もJava?ージョン1.5?利用???る???必???。 JVM????ェックを無視???。

デッドロック??ェック?処????り?高速???? 全??スレッドを一時的?ロック??スナップショットを?り????? ??プロパティ?デフォルト?「FALSE????????。

設定例:(デッドロック・?ェック:OFF)
wrapper.check.deadlock=FALSE

設定例:

プロパティ?詳細????下記を?覧????????? 次?シンプル?例???デッドロック?場所をログ化?るよ??Wrapperを設定???り? ?座?JVMを?起動???。

設定例:
wrapper.check.deadlock=TRUE
wrapper.check.deadlock.interval=60
wrapper.check.deadlock.action=RESTART
wrapper.check.deadlock.output=FULL

デッドロック???

複数?スレッド?リソースをロック???る????? 全??スレッド?無制??待?状態??る状態??り? デッドロック?起??る????り??。

一番シンプル?例???スレッドA?オブジェクトAをロック???る状態?? オブジェクトB?ロックを試?るケース?????? ???間?他?スレッドB?オブジェクトBをロック??状態? オブジェクトAをロック?よ??待機???るケース??。 ??ケース??? スレッドA??オブジェクトB待????? オブジェクトAを決??開放?れる????り??ん。 ?時??スレッドB??オブジェクトA?有効??る?を待???る??? オブジェクトBを永久?ロック??????る??? 両者?も決????進?る????り??ん。

wrapper.check.deadlock.interval

wrapper.check.deadlock.interval] プロパティ??? Wrapper?アプリケーション?デッドロックを探? インター?ル(一定間隔?周期)?コントロールを?能????。 最短?1秒毎?1回?インター?ルを設定??????? デフォルト??「60?(1分??り1回)??。 一般的??状態?安定???る?分????るアプリケーション?場???? ??デッドロック・?ェック?頻度を大幅?下?る??も良???ょ?。

設定例:(6?秒毎)
wrapper.check.deadlock.interval=60

wrapper.check.deadlock.action

wrapper.check.deadlock.action] プロパティ??? デッドロックを検知??????Wrapper????る?? Wrapper?動作?????コントロールを?能????。 デフォルト?動作???「RESTART???。

設定例:(?起動?る)
wrapper.check.deadlock.action=RESTART

?能?動作?次???り:

  • RESTART -

    カレントJVMを?止???新??「起動?試?(invocation)???起動???。

  • SHUTDOWN -

    JVMを?止????様?Wrapperも?止???。

  • DUMP -

    スレッド・ダンプを呼?出???。 当然?????ら?デッドロック?永続???る??? JVM??起動?る??????デッドロック??ステート(状態)??続???? ??り?スレッド・ダンプも???インター?ル(一定間隔?周期)???呼?出?れ?? ???注??????。

  • DUMP_RESTART -

    スレッド・ダンプを呼?出?? カレントJVMを?止???新??「起動?試?(invocation)???起動???。

  • DUMP_SHUTDOWN -

    スレッド・ダンプを呼?出?? JVMを?止????様?Wrapperも?止???。

  • NONE -

    ?れ?便利??? ???ら?高?数値を??トリガー?ら? ???るトリガーもトリガー?れる??を??る??????る??。 because it will prevent any triggers with a higher number from being triggered.

wrapper.check.deadlock.output

wrapper.check.deadlock.output] プロパティ??? デッドロックを検知??????Wrapper?ログ化?る情報をコントロールを?能????。 デフォルト出力???「FULL???。

設定例:(完全?フルログ)
wrapper.check.deadlock.output=FULL

?能?出力レベル?次???り:

  • FULL -

    JVM内部?WrapperManagerクラス?? デッドロック?呼?出?れ?スレッド?完全?スタック・トレース(一時記憶?軌跡)を?む レ?ートを出力???。

    「FULL(フル)??出力例:
    INFO   | jvm 1    | WrapperManager Error: Found 2 deadlocked threads!
    INFO   | jvm 1    | WrapperManager Error: =============================
    INFO   | jvm 1    | WrapperManager Error: "Locker-2" tid=18
    INFO   | jvm 1    | WrapperManager Error:   java.lang.Thread.State: BLOCKED
    INFO   | jvm 1    | WrapperManager Error:     at org.tanukisoftware.wrapper.test.DeadLock.lockSecond(DeadLock.java:64)
    INFO   | jvm 1    | WrapperManager Error:       - waiting on <0x000000002fcac6db> (a java.lang.Object) owned by "Locker-1" tid=17
    INFO   | jvm 1    | WrapperManager Error:     at org.tanukisoftware.wrapper.test.DeadLock.lockFirst(DeadLock.java:83)
    INFO   | jvm 1    | WrapperManager Error:       - locked <0x0000000029c56c60> (a java.lang.Object)
    INFO   | jvm 1    | WrapperManager Error:     at org.tanukisoftware.wrapper.test.DeadLock.access$100(DeadLock.java:22)
    INFO   | jvm 1    | WrapperManager Error:     at org.tanukisoftware.wrapper.test.DeadLock$1.run(DeadLock.java:42)
    INFO   | jvm 1    | WrapperManager Error:
    INFO   | jvm 1    | WrapperManager Error: "Locker-1" tid=17
    INFO   | jvm 1    | WrapperManager Error:   java.lang.Thread.State: BLOCKED
    INFO   | jvm 1    | WrapperManager Error:     at org.tanukisoftware.wrapper.test.DeadLock.lockSecond(DeadLock.java:64)
    INFO   | jvm 1    | WrapperManager Error:       - waiting on <0x0000000029c56c60> (a java.lang.Object) owned by "Locker-2" tid=18
    INFO   | jvm 1    | WrapperManager Error:     at org.tanukisoftware.wrapper.test.DeadLock.lockFirst(DeadLock.java:83)
    INFO   | jvm 1    | WrapperManager Error:       - locked <0x000000002fcac6db> (a java.lang.Object)
    INFO   | jvm 1    | WrapperManager Error:     at org.tanukisoftware.wrapper.test.DeadLock.access$100(DeadLock.java:22)
    INFO   | jvm 1    | WrapperManager Error:     at org.tanukisoftware.wrapper.test.DeadLock$1.run(DeadLock.java:42)
    INFO   | jvm 1    | WrapperManager Error:
    INFO   | jvm 1    | WrapperManager Error: =============================
    STATUS | wrapper  | A Thread Deadlock was detected in the JVM.  Restarting JVM.
  • SIMPLE -

    JVM内部?WrapperManagerクラス?? デッドロック?呼?出?れ?スレッドやオブジェクト?簡??概???を?む レ?ートを出力???。 多???場??特?よ??知られ??題?関?????分??。

    「SIMPLE(シンプル)??出力例
    INFO   | jvm 1    | WrapperManager Error: Found 2 deadlocked threads!
    INFO   | jvm 1    | WrapperManager Error: =============================
    INFO   | jvm 1    | WrapperManager Error: "Locker-2" BLOCKED waiting on a java.lang.Object owned by "Locker-1"
    INFO   | jvm 1    | WrapperManager Error: "Locker-1" BLOCKED waiting on a java.lang.Object owned by "Locker-2"
    INFO   | jvm 1    | WrapperManager Error: =============================
    STATUS | wrapper  | A Thread Deadlock was detected in the JVM.  Restarting JVM.
  • NONE -

    JVM内部?WrapperManagerクラス?? 全??出力を抑???。 ?れ?システム製??利用???????。 ?題?把????り? ????ん?ログ情報を望?????る??????る必?????ケース???????。 ???他??択????Wrapperプロセス?? トリガー?れ?アクション? 発生???由を??供?る???? 常?シングル・エントリをログ化???????。

    「NONE(??)??出力例
    STATUS | wrapper  | A Thread Deadlock was detected in the JVM.  Restarting JVM.

wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/exec_en.html100644 0 0 2035 14333053652 24562 0ustar 0 0 The WrapperManager.exec() function is a alternative to the Java-Runtime.exec() which has the disadvantage to use the on some platforms memory expensive fork() method to create a new process.

The problem on those platforms is, that if the Parent Process from which one wants to start a side process, fork() causes to clone the memory of the parent for the child.
This will double the used memory for a short time.
Given a fairly large application which is close to the systems memory quota, it might fail to just create a small application like ls or the memory is going to get swapped on the HD, which decreases the performance drastically.

Another issue is to ease bind or detach the child while starting from the parent.
If the Java Process terminates expected or unexpected the wrapper will clean all bind processes which haven't finished yet.



For better consistency the exec() function was implemented as close to the Runtime.exec() function in Java as possible. wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/exec_ja.html100644 0 0 3755 14333053652 24564 0ustar 0 0

WrapperManager.exec()] 関数?? [Java-Runtime.exec()]?代替?案??。 [Java-Runtime.exec()]???????プラットフォーム? 新??プロセスを生??る??[fork()]メソッドを 利用??メモリを無駄?消費?る?利?点??り??。 ??れら?プラットフォーム上???題?? も??サイド・プロセスを開始???ペアレント(親)プロセス?場?? [fork()]??ャイルド(?)用?ペアレント(親)?メモリ?クローンを作????。 ?れ??短?時間?消費メモリ????り??。 システム・メモリ・割り当???近???り?大???アプリケーション?与?られる?? ls?よ??? ???アプリケーション?作??も失敗?る?能性??り??。 ?る????ードディスク????メモリ?スワッピング?発生?る?能性も?り?パフォーマンス?劇的?低下?る????り??。

も?一???題?????ペアレント(親)?ら開始???る間? ?ャイルド(?)??インドやデタッ?を緩和?る????。 も?Javaプロセス??期待??り????予想外????終了?る場?? Wrapper????終了?????全???インド・プロセスをクリーン???。

より良?一貫性??????能??り?Java??[Runtime.exec()]関数?近?? [WrapperManager.exec()]関数?実装?れ???。

wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/frozen_en.html100644 0 0 2501 14333053652 25137 0ustar 0 0

Simulate JVM Hang

This test causes the Wrapper to think that the JVM has become hung. After 30 seconds, the Wrapper times out and decides that the JVM will not come back and restarts it.

Console Output:
wrapper  | --> Wrapper Started as Console
wrapper  | Launching a JVM...
jvm 1    | Initializing...
jvm 1    | Wrapper (Version 3.x.x)
jvm 1    |
jvm 1    | start()
jvm 1    | WARNING: Making JVM appear to be hung...
wrapper  | JVM appears hung: Timed out waiting for signal from JVM.
wrapper  | Java Virtual Machine did not exit on request, terminated
wrapper  | Launching a JVM...
jvm 2    | Initializing...
jvm 2    | Wrapper (Version 3.x.x)
jvm 2    |
jvm 2    | start()
wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/frozen_ja.html100644 0 0 2651 14333053652 25135 0ustar 0 0

JVM?ングアップ?シミュレーション

JVM?ングアップ?シミュレーション ??テスト???Wrapper?「JVM??ングアップ???????るテスト??。 30秒後?タイムアウト??Wrapper?JVM?復旧????判断???JVMを?起動????。

コンソール出力:
wrapper  | --> Wrapper Started as Console
wrapper  | Launching a JVM...
jvm 1    | Initializing...
jvm 1    | Wrapper (Version 3.x.x)
jvm 1    |
jvm 1    | start()
jvm 1    | WARNING: Making JVM appear to be hung...
wrapper  | JVM appears hung: Timed out waiting for signal from JVM.
wrapper  | Java Virtual Machine did not exit on request, terminated
wrapper  | Launching a JVM...
jvm 2    | Initializing...
jvm 2    | Wrapper (Version 3.x.x)
jvm 2    |
jvm 2    | start()
wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/mail_en.html100644 0 0 3336 14333053652 24565 0ustar 0 0 The Wrapper is able to send out alert emails to in response to certain events.
See the documentation of individual properties for details on their use.
Example (send email in response to JVM restarts):
# Common Event Email settings.
#wrapper.event.default.email.debug=TRUE
wrapper.event.default.email.smtp.host=smtp.mycompany.com
wrapper.event.default.email.smtp.port=25
wrapper.event.default.email.subject=[%WRAPPER_HOSTNAME%:%WRAPPER_NAME%:%WRAPPER_EVENT_NAME%] Event Notification
wrapper.event.default.email.sender=myapp-noreply@mycompany.com
wrapper.event.default.email.recipient=myapp-admin@mycompany.com
# Event specific settings.
wrapper.event.jvm_restart.email=TRUE
wrapper.event.jvm_restart.email.body=The JVM was restarted.\n\nPlease check on its status.\n

The properties are all of the form wrapper.event.<x>.email*
where x is one of the "Event Types" defined above or the keyword default.

Whenever a property is not specifically defined for any of the event names,
the default value will be used. This is useful for setting values which will be common to all event emails.

wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/mail_ja.html100644 0 0 3706 14333053652 24556 0ustar 0 0 Wrapper????るイベント???応???アラート通知をメール??る????????。 使?方????ら?詳?????個別?プロパティ?説明を?覧?????。
例 (JVM?起動?応??メール通知を?信?る例):
# Common Event Email settings.
#wrapper.event.default.email.debug=TRUE
wrapper.event.default.email.smtp.host=smtp.mycompany.com
wrapper.event.default.email.smtp.port=25
wrapper.event.default.email.subject=[%WRAPPER_HOSTNAME%:%WRAPPER_NAME%:%WRAPPER_EVENT_NAME%] Event Notification
wrapper.event.default.email.sender=myapp-noreply@mycompany.com
wrapper.event.default.email.recipient=myapp-admin@mycompany.com
# Event specific settings.
wrapper.event.jvm_restart.email=TRUE
wrapper.event.jvm_restart.email.body=The JVM was restarted.\n\nPlease check on its status.\n

??プロパティ??[wrapper.event.<x>.email*]形??? 「x??部??上記?定義?れ?イベント?種類?一?? ?る???「default(デフォルト)??キーワード?入り??。

プロパティ?何もイベント???特別??定義?れ????時????も?デフォルト値??用?れ??。 全??イベント・メール?対??一般的?値を設定?る??便利??。

wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/out_of_mem_en.html100644 0 0 2327 14333053652 25773 0ustar 0 0

Out of Memory

The following example will monitor the JVM output and then restart the JVM automatically whenever a java.lang.OutOfMemoryError is thrown to the console. Depending on where in an application the error is thrown, it is not always possible to trap and handle the error in a useful way from within the JVM.

Filters work by monitoring the console output of the JVM. In order for a trigger to be fired by an exception, the Java application must print the message being filtered to the console.

Example:
wrapper.filter.trigger.1=java.lang.OutOfMemoryError
wrapper.filter.action.1=RESTART
wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/out_of_mem_ja.html100644 0 0 2764 14333053652 25770 0ustar 0 0

Out of Memory

下記?例???JVM出力をモニタリング(監視)??? [java.lang.OutOfMemoryError]?コンソール?投?られ?時????も? JVMを自動的??起動???。 エラー?アプリケーション????投?られる?次第?変?り????? 必??も「常?JVM内部?ら?エラーを検知???り扱???????る?????????り??ん。

フィルター??JVM?コンソール出力をモニタリング(監視)?る???動作???。 トリガー?例外?発動?れるよ???る???? Javaアプリケーション??コンソール?フィルタリング?れ?メッセージを表示????れ??り??ん。

Example:
wrapper.filter.trigger.1=java.lang.OutOfMemoryError
wrapper.filter.action.1=RESTART
wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/service_en.html100644 0 0 15350 14333053652 25322 0ustar 0 0

Running as a Windows Service

When running as a Windows Service, the Application will be unable to show its Dialog. So this sample application is not very interesting. We can however do a few tests to verify that the Wrapper acts as a Windows Service correctly.

Install-<Your App>.bat
You will see the output:
wrapper  | <Your App> installed.

Once your application is installed as a service, we will want to start it.

net start <Your App>
You will see the output:
<Your App> service is starting.
<Your App> service was started successfully.

The service can be uninstalled by executing the following command:

Uninstall<Your App>-NT.bat
If the service is running, you will see the output:
wrapper  | Service is running.  Stopping it...
wrapper  | Waiting to stop...
wrapper  | <Your App> stopped.
wrapper  | <Your App> removed.

If you look at the contents of logs/wrapper.log, you will see output very similar to that of the console. Except this time, the start message informs you that the application is being started as a Service.

wrapper.log
wrapper  | 2001/12/06 17:34:21 | --> Wrapper Started as Service
wrapper  | 2001/12/06 17:34:21 | Launching a JVM...
jvm 1    | 2001/12/06 17:34:22 | Initializing...
jvm 1    | 2001/12/06 17:34:22 | Wrapper (Version 3.x.x)
jvm 1    | 2001/12/06 17:34:22 |
jvm 1    | 2001/12/06 17:34:22 | start()

Here are the results of several more actions that can take place on Windows Service:

User Logging out and then back in

A log entry is made when the user logs out, but the service is unaffected.

wrapper.log
wrapper  | 2001/12/06 17:39:39 | --> Wrapper Started as Service
wrapper  | 2001/12/06 17:39:40 | Launching a JVM...
jvm 1    | 2001/12/06 17:39:40 | Initializing...
jvm 1    | 2001/12/06 17:39:40 | Wrapper (Version 3.x.x)
jvm 1    | 2001/12/06 17:39:40 |
jvm 1    | 2001/12/06 17:39:41 | start()
wrapper  | 2001/12/06 17:40:07 | User logged out.  Ignored.
jvm 1    | 2001/12/06 17:40:07 | controlEvent(202)

Restarting the machine

This will result in a logout signal followed by a shutdown signal. The service will be shutdown gracefully and then come back after the machine restarts.

A log entry is made when the user logs out, but the service is unaffected.

wrapper.log
wrapper  | 2001/12/06 17:41:04 | --> Wrapper Started as Service
wrapper  | 2001/12/06 17:41:05 | Launching a JVM...
jvm 1    | 2001/12/06 17:41:05 | Initializing...
jvm 1    | 2001/12/06 17:41:05 | Wrapper (Version 3.x.x)
jvm 1    | 2001/12/06 17:41:05 |
jvm 1    | 2001/12/06 17:41:05 | start()
wrapper  | 2001/12/06 17:41:25 | User logged out.  Ignored.
jvm 1    | 2001/12/06 17:41:26 | controlEvent(202)
wrapper  | 2001/12/06 17:41:27 | Machine is shutting down.
jvm 1    | 2001/12/06 17:41:27 | controlEvent(203)
jvm 1    | 2001/12/06 17:41:28 | stop(0)
wrapper  | 2001/12/06 17:41:29 | <-- Wrapper Stopped
wrapper  | 2001/12/06 17:44:12 | --> Wrapper Started as Service
wrapper  | 2001/12/06 17:44:12 | Launching a JVM...
jvm 1    | 2001/12/06 17:44:17 | Initializing...
jvm 1    | 2001/12/06 17:44:21 | Wrapper (Version 3.x.x)
jvm 1    | 2001/12/06 17:44:21 |
jvm 1    | 2001/12/06 17:44:23 | start()
wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/demo/html/service_ja.html100644 0 0 16716 14333053652 25321 0ustar 0 0

Windowsサービス?1????動??

Windowsサービス?1????動????????プリケーション?ダイアログを表示?る????????ん。 ????????サンプル・アプリケーション????り?白???り??ん?? ?????ら?????2?3?テストを??? Windowsサービス?1????正???Wrapper?動作?る??を実証????。

最???る??????アプリケーション?インストール??。 次?コマンドを実行???Windowsサービス?1????動????????:

Install-<Your App>.bat
??出力?見???:
wrapper  | <Your App> installed.

一旦?アプリケーション?サービス?1????インストール??ら? 早速???れをスタート?????ょ?。

net start <Your App>
??出力?見???
<Your App> service is starting.
<Your App> service was started successfully.

???サービス??次?コマンドを実行???アンインストール?る????????::

Uninstall<Your App>-NT.bat
も?サービス?実行中?ら????出力?見???:
wrapper  | Service is running.  Stopping it...
wrapper  | Waiting to stop...
wrapper  | <Your App> stopped.
wrapper  | <Your App> removed.

logs/wrapper.log? ?中身を見る????コンソール???も類似??出力?見られる?????。 今回を除??????スタート・メッセージ??アプリケーション?サービス???開始?れ???を知ら?????。

wrapper.log
wrapper  | 2001/12/06 17:34:21 | --> Wrapper Started as Service
wrapper  | 2001/12/06 17:34:21 | Launching a JVM...
jvm 1    | 2001/12/06 17:34:22 | Initializing...
jvm 1    | 2001/12/06 17:34:22 | Wrapper (Version 3.x.x)
jvm 1    | 2001/12/06 17:34:22 |
jvm 1    | 2001/12/06 17:34:22 | start()

Windowsサービス上?起??る?????????他?アクション??果??::

ユーザー・ログアウト ? ???復帰

ユーザー?ログアウト?ログ・エントリ?作??れ????サービス??影響?り??ん。

wrapper.log
wrapper  | 2001/12/06 17:39:39 | --> Wrapper Started as Service
wrapper  | 2001/12/06 17:39:40 | Launching a JVM...
jvm 1    | 2001/12/06 17:39:40 | Initializing...
jvm 1    | 2001/12/06 17:39:40 | Wrapper (Version 3.x.x)
jvm 1    | 2001/12/06 17:39:40 |
jvm 1    | 2001/12/06 17:39:41 | start()
wrapper  | 2001/12/06 17:40:07 | User logged out.  Ignored.
jvm 1    | 2001/12/06 17:40:07 | controlEvent(202)

マシン?起動

?れ??シャットダウン・シグナル?続???ログアウト・シグナル??果??り??。 サービス?キレイ?シャットダウン?れる??マシン?起動?後?戻??????。

ユーザー?ログアウト?ログ・エントリ?作??れ????サービス??影響?り??ん。

wrapper.log
wrapper  | 2001/12/06 17:41:04 | --> Wrapper Started as Service
wrapper  | 2001/12/06 17:41:05 | Launching a JVM...
jvm 1    | 2001/12/06 17:41:05 | Initializing...
jvm 1    | 2001/12/06 17:41:05 | Wrapper (Version 3.x.x)
jvm 1    | 2001/12/06 17:41:05 |
jvm 1    | 2001/12/06 17:41:05 | start()
wrapper  | 2001/12/06 17:41:25 | User logged out.  Ignored.
jvm 1    | 2001/12/06 17:41:26 | controlEvent(202)
wrapper  | 2001/12/06 17:41:27 | Machine is shutting down.
jvm 1    | 2001/12/06 17:41:27 | controlEvent(203)
jvm 1    | 2001/12/06 17:41:28 | stop(0)
wrapper  | 2001/12/06 17:41:29 | <-- Wrapper Stopped
wrapper  | 2001/12/06 17:44:12 | --> Wrapper Started as Service
wrapper  | 2001/12/06 17:44:12 | Launching a JVM...
jvm 1    | 2001/12/06 17:44:17 | Initializing...
jvm 1    | 2001/12/06 17:44:21 | Wrapper (Version 3.x.x)
jvm 1    | 2001/12/06 17:44:21 |
jvm 1    | 2001/12/06 17:44:23 | start()
wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/event/WrapperConsumableEvent.java100644 0 0 3415 14333053652 27020 0ustar 0 0 package org.tanukisoftware.wrapper.event; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * WrapperConsumableEvent is used to keep trace whether * the event has been handled or not. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public abstract class WrapperConsumableEvent extends WrapperEvent { /** True if the event has been consumed. */ private boolean m_consumed; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperConsumableEvent. */ public WrapperConsumableEvent() { } /*--------------------------------------------------------------- * WrapperConsumableEvent Methods *-------------------------------------------------------------*/ /** * Mark the event as consumed. This should be done if the event * has been handled. *

* On Windows, some events are sent both to the JVM and Wrapper processes. * Even if the CTRL-C event is ignored within the JVM, the Wrapper * process may still initiate a shutdown. */ public void consume() { m_consumed = true; } /** * Returns true if the event has been consumed. * * @return True if the event has been consumed. */ public boolean isConsumed() { return m_consumed; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/event/WrapperControlEvent.java100644 0 0 11245 14333053652 26370 0ustar 0 0 package org.tanukisoftware.wrapper.event; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * WrapperControlEvent are used to notify the listener whenever the native * wrapper code traps a system control signal against the Java process. * It is up to the listener to take any actions necessary. *

* The Wrapper will send this event to any registered listeners first, * then it will pass the control code to the WrapperListener.controlEvent * method. If the consume method is called, it will still be passed to * other WrapperEventListeners, but will not be passed to the * WrapperListener.controlEvent method. Other WrapperEventListeners should * check the isConsumed method to decide whether or not the even has already * been handled. *

* If the wrapper.ignore_signals property is set to true then the event will * still be fired, but its isConsumed() method will return true initially. *

* Possible values are: *

*
WrapperManager.WRAPPER_CTRL_C_EVENT
*
The user pressed CTRL-C in a command windown (Windows or UNIX). * Or the kill INT signal was received (UNIX).
*
WRAPPER_CTRL_CLOSE_EVENT
*
The user is trying to close the console in which the Wrapper is * running (Windows).
*
WRAPPER_CTRL_LOGOFF_EVENT
*
The user logged off (Windows).
*
WRAPPER_CTRL_SHUTDOWN_EVENT
*
The system is being shutdown (Windows).
*
WRAPPER_CTRL_TERM_EVENT
*
The kill TERM signal was received (UNIX).
*
WRAPPER_CTRL_HUP_EVENT
*
The kill HUP signal was received (UNIX).
*
* * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperControlEvent extends WrapperConsumableEvent { /** * Serial Version UID. */ private static final long serialVersionUID = -7033261694452001713L; /** The system control event. */ private int m_controlEvent; /** The name of the event. */ private String m_controlEventName; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperControlEvent. * * @param controlEvent Service control event. * @param controlEventName The name of the event. */ public WrapperControlEvent( int controlEvent, String controlEventName ) { m_controlEvent = controlEvent; m_controlEventName = controlEventName; } /*--------------------------------------------------------------- * WrapperEvent Methods *-------------------------------------------------------------*/ /** * Returns a set of event flags for which the event should be fired. * This value is compared with the mask supplied when when a * WrapperEventListener is registered to decide which listeners should * receive the event. *

* If subclassed, the return value of the super class should usually * be ORed with any additional flags. * * @return a set of event flags. */ public long getFlags() { return super.getFlags() | WrapperEventListener.EVENT_FLAG_CONTROL; } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns the system control event. *

* Possible values are: WrapperManager.WRAPPER_CTRL_C_EVENT, * WRAPPER_CTRL_CLOSE_EVENT, WRAPPER_CTRL_LOGOFF_EVENT, * WRAPPER_CTRL_SHUTDOWN_EVENT, WRAPPER_CTRL_TERM_EVENT, or * WRAPPER_CTRL_HUP_EVENT. * * @return The system control event. */ public int getControlEvent() { return m_controlEvent; } /** * Returns the name of the control event. * * @return The name of the control event. */ public String getControlEventName() { return m_controlEventName; } /** * Returns a string representation of the event. * * @return A string representation of the event. */ public String toString() { return "WrapperControlEvent[controlEvent=" + getControlEvent() + ", controlEventName=" + getControlEventName() + ", consumed=" + isConsumed() + "]"; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/event/WrapperCoreEvent.java100644 0 0 3711 14333053652 25617 0ustar 0 0 package org.tanukisoftware.wrapper.event; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * WrapperCoreEvents are used to notify the listener of the internal * workings of the Wrapper. * * WARNING - Great care should be taken when receiving events of this type. * They are sent from within the Wrapper's internal timing thread. If the * listner takes too much time working with the event, Wrapper performance * could be adversely affected. If unsure, it is recommended that events * of this type not be included. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public abstract class WrapperCoreEvent extends WrapperEvent { /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperCoreEvent. */ protected WrapperCoreEvent() { } /*--------------------------------------------------------------- * WrapperCoreEvent Methods *-------------------------------------------------------------*/ /** * Returns a set of event flags for which the event should be fired. * This value is compared with the mask supplied when when a * WrapperEventListener is registered to decide which listeners should * receive the event. *

* If subclassed, the return value of the super class should usually * be ORed with any additional flags. * * @return a set of event flags. */ public long getFlags() { return super.getFlags() | WrapperEventListener.EVENT_FLAG_CORE; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/event/WrapperEvent.java100644 0 0 3620 14333053652 25005 0ustar 0 0 package org.tanukisoftware.wrapper.event; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.util.EventObject; import org.tanukisoftware.wrapper.WrapperManager; /** * WrapperEvents are used to notify WrapperEventListeners of various wrapper * related events. *

* For performance reasons, some event instances may be reused by the code * which fires them off. For this reason, references to the event should * never be referenced outside the scope of the WrapperListener.processEvent * method. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public abstract class WrapperEvent extends EventObject { /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperEvent. */ protected WrapperEvent() { super( WrapperManager.class ); } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns a set of event flags for which the event should be fired. * This value is compared with the mask supplied when when a * WrapperEventListener is registered to decide which listeners should * receive the event. *

* If subclassed, the return value of the super class should usually * be ORed with any additional flags. * * @return a set of event flags. */ public long getFlags() { return 0; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/event/WrapperEventListener.java100644 0 0 6256 14333053652 26523 0ustar 0 0 package org.tanukisoftware.wrapper.event; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * WrapperEventListeners can be registered with the WrapperManager class * to receive WrapperEvents. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public interface WrapperEventListener { /** * Enabling service events will cause the listener to receive * WrapperServiceEvents. These events pertain to the Wrapper as a * service. */ static long EVENT_FLAG_SERVICE = 0x1; /** * Enabling control events will cause the listener to receive * WrapperControlEvents. These events are thrown whenever the * Java process receives control events from the system. These * include CTRL-C, HALT, TERM signals etc. */ static long EVENT_FLAG_CONTROL = 0x2; /** * Enabling logging events will cause the listener to receive * WrapperLoggingEvents. These events are thrown in response * to certain logging events such as updates to the log file * name. */ static long EVENT_FLAG_LOGGING = 0x4; /** * Enabling remoteControl events will cause the listener to receive * WrapperRemoteControlEvents. These events are fired when a signal is caught * from outside the Wrapper (for example a signal coming from a pipe). *

* WARNING - Those events should be handled carefully as they may be originally * triggered by unauthenticated sources. */ static long EVENT_FLAG_REMOTE_CONTROL = 0x8; /** * Enabling core events will cause the listener to receive * WrapperCoreEvents. These events provide information on the internal * timing of the Wrapper. *

* WARNING - Great care should be taken when receiving events of this type. * They are sent from within the Wrapper's internal timing thread. If the * listner takes too much time working with the event, Wrapper performance * could be adversely affected. If unsure, it is recommended that events * of this type not be included. */ static long EVENT_FLAG_CORE = 0xf000000000000000L; /** * Called whenever a WrapperEvent is fired. The exact set of events that a * listener will receive will depend on the mask supplied when * WrapperManager.addWrapperEventListener was called to register the * listener. *

* Listener implementations should never assume that they will only receive * events of a particular type. To assure that events added to future * versions of the Wrapper do not cause problems with user code, events * should always be tested with "if ( event instanceof {EventClass} )" * before casting it to a specific event type. * * @param event WrapperEvent which was fired. */ void fired( WrapperEvent event ); } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/event/WrapperLogFileChangedEvent.java100644 0 0 3555 14333053652 27530 0ustar 0 0 package org.tanukisoftware.wrapper.event; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.io.File; /** * WrapperLogFileChangedEvent are fired whenever the log file used by the * Wrapper is changed. This can happen due to nightly log rotation for * example. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperLogFileChangedEvent extends WrapperLoggingEvent { private final File m_logFile; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperLogFileChangedEvent. * @param logFile The log file. */ public WrapperLogFileChangedEvent( File logFile ) { m_logFile = logFile; } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns the updated log file name. * * @return The updated log file name. */ public File getLogFile() { return m_logFile; } /*--------------------------------------------------------------- * Method *-------------------------------------------------------------*/ /** * Returns a string representation of the event. * * @return A string representation of the event. */ public String toString() { return "WrapperLogFileChangedEvent[logFile=" + getLogFile() + "]"; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/event/WrapperLoggingEvent.java100644 0 0 3241 14333053652 26313 0ustar 0 0 package org.tanukisoftware.wrapper.event; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * WrapperLoggingEvents are used to notify the listener of events related * to logging such as updates to the log file name. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public abstract class WrapperLoggingEvent extends WrapperEvent { /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperLoggingEvent. */ protected WrapperLoggingEvent() { } /*--------------------------------------------------------------- * WrapperLoggingEvent Methods *-------------------------------------------------------------*/ /** * Returns a set of event flags for which the event should be fired. * This value is compared with the mask supplied when when a * WrapperEventListener is registered to decide which listeners should * receive the event. *

* If subclassed, the return value of the super class should usually * be ORed with any additional flags. * * @return a set of event flags. */ public long getFlags() { return super.getFlags() | WrapperEventListener.EVENT_FLAG_LOGGING; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/event/WrapperPingEvent.java100644 0 0 3440 14333053652 25623 0ustar 0 0 package org.tanukisoftware.wrapper.event; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * WrapperPingEvent are fired each time a ping is received from the Wrapper * process. This event is mainly useful for debugging and statistic * collection purposes. *

* WARNING - Great care should be taken when receiving events of this type. * They are sent from within the Wrapper's internal timing thread. If the * listner takes too much time working with the event, Wrapper performance * could be adversely affected. If unsure, it is recommended that events * of this type not be included. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperPingEvent extends WrapperCoreEvent { /** * Serial Version UID. */ private static final long serialVersionUID = 284255850873300689L; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperPingEvent. */ public WrapperPingEvent() { } /*--------------------------------------------------------------- * Method *-------------------------------------------------------------*/ /** * Returns a string representation of the event. * * @return A string representation of the event. */ public String toString() { return "WrapperPingEvent"; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/event/WrapperRemoteControlEvent.java100644 0 0 3511 14333053652 27521 0ustar 0 0 package org.tanukisoftware.wrapper.event; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * WrapperRemoteControlEvent are fired when a signal is caught from * outside the Wrapper (for example a signal coming from a pipe). *

* WARNING - Those events should be handled carefully as they may be originally * triggered by unauthenticated sources. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public abstract class WrapperRemoteControlEvent extends WrapperEvent { /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperRemoteControlEvent. */ public WrapperRemoteControlEvent() { } /*--------------------------------------------------------------- * WrapperRemoteControlEvent Methods *-------------------------------------------------------------*/ /** * Returns a set of event flags for which the event should be fired. * This value is compared with the mask supplied when when a * WrapperEventListener is registered to decide which listeners should * receive the event. *

* If subclassed, the return value of the super class should usually * be ORed with any additional flags. * * @return a set of event flags. */ public long getFlags() { return super.getFlags() | WrapperEventListener.EVENT_FLAG_REMOTE_CONTROL; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/event/WrapperSecondInvocationEvent.java100644 0 0 3253 14333053652 30175 0ustar 0 0 package org.tanukisoftware.wrapper.event; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * WrapperSecondInvocationEvent is fired whenever a second * instance of the Wrapper starts in single invocation mode. *

* This event is fired only when the properties 'wrapper.single_invocation' * and 'wrapper.single_invocation.notify' are both set to TRUE. *

* WARNING - This event should be handled carefully as it may be originally * triggered by unauthenticated sources. * * @author Tanuki Software Development Team <support@tanukisoftware.com> * * @since Wrapper 3.5.28 */ public class WrapperSecondInvocationEvent extends WrapperRemoteControlEvent { /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperSecondInvocationEvent. */ public WrapperSecondInvocationEvent() { } /*--------------------------------------------------------------- * WrapperSecondInvocationEvent Methods *-------------------------------------------------------------*/ /** * Returns a string representation of the event. * * @return A string representation of the event. */ public String toString() { return "WrapperSecondInvocationEvent"; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/event/WrapperServiceActionEvent.java100644 0 0 13505 14333053652 27507 0ustar 0 0 package org.tanukisoftware.wrapper.event; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.WrapperManager; /** * WrapperServicePauseResumeEvents are used to notify the listener that the Wrapper * is requesting that the Java application be paused or resumed. This does not * mean that it should exit, only that it should internally go into an idle state. * * See the wrapper.pausable and wrapper.pausable.stop_jvm properties for more * information. * * @author Tanuki Software Development Team <support@tanukisoftware.com> * * @since Wrapper 3.5.0 */ public abstract class WrapperServiceActionEvent extends WrapperServiceEvent { /** * Serial Version UID. */ private static final long serialVersionUID = 7901768955067874864L; /* * * Note - The following SOURCE_CODE_* values must match those in the wrapper.h file. * */ /** * Action result of a configured filter being fired. * See the wrapper.filter.action.<n> property. */ public static final int SOURCE_CODE_FILTER = 1; /** * Action result of a command from a command file. * See the wrapper.commandfile property. */ public static final int SOURCE_CODE_COMMANDFILE = 2; /** * Action resulted from the Windows Service Manager. This can happen * from a number of sources including the command line, Service Control * Panel, etc. */ public static final int SOURCE_CODE_WINDOWS_SERVICE_MANAGER = 3; /** * Action result of a matched exit code. * See the wrapper.on_exit.<n> property. */ public static final int SOURCE_CODE_ON_EXIT = 4; /** * Action result of a signal. * See the wrapper.on_exit.<n> property. */ public static final int SOURCE_CODE_SIGNAL = 5; /** * Action result of a deadlock being detected. * See the wrapper.check.deadlock.action property. */ public static final int SOURCE_CODE_DEADLOCK = 10; /** * Action result of a configured timer being fired. * See the wrapper.timer.<n>.action property. */ public static final int SOURCE_CODE_TIMER = 21; /** * Action result of an event command's block timeout expired. * See the wrapper.event.<event_name>.command.block.action property. */ public static final int SOURCE_CODE_COMMAND_BLOCK_TIMEOUT = 22; /** * Code which keeps track of how the service was paused. */ private int m_actionSourceCode; /*--------------------------------------------------------------- * Static Methods *-------------------------------------------------------------*/ /** * Returns the name of the specified Source Code. * * @param actionSourceCode The Source Code whose name is being requested. * * @return The name of the Source Code. */ public static String getSourceCodeName( int actionSourceCode ) { switch( actionSourceCode ) { case SOURCE_CODE_FILTER: return WrapperManager.getRes().getString( "Filter Action" ); case SOURCE_CODE_COMMANDFILE: return WrapperManager.getRes().getString( "Command File Action" ); case SOURCE_CODE_WINDOWS_SERVICE_MANAGER: return WrapperManager.getRes().getString( "Windows Service Manager" ); case SOURCE_CODE_ON_EXIT: return WrapperManager.getRes().getString( "On Exit Action" ); case SOURCE_CODE_SIGNAL: return WrapperManager.getRes().getString( "Signal Action" ); case SOURCE_CODE_DEADLOCK: return WrapperManager.getRes().getString( "Deadlock Action" ); case SOURCE_CODE_TIMER: return WrapperManager.getRes().getString( "Timer Action" ); case SOURCE_CODE_COMMAND_BLOCK_TIMEOUT: return WrapperManager.getRes().getString( "Block Timeout Action" ); default: return WrapperManager.getRes().getString( "Unknown Code {0}", new Integer( actionSourceCode ) ); } } /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperServiceActionEvent. * * @param actionSourceCode Source Code specifying where the action originated. */ public WrapperServiceActionEvent( int actionSourceCode ) { m_actionSourceCode = actionSourceCode; } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns the Source Code describing where the event originated. * * @return The Source Code. */ public int getSourceCode() { return m_actionSourceCode; } /** * Returns the Source Code name. * * @return The Source Code name. */ public String getSourceCodeName() { return getSourceCodeName( m_actionSourceCode ); } /** * Returns a string representation of the event. * * @return A string representation of the event. */ public String toString() { return "WrapperServiceActionEvent[actionSourceCode=" + getSourceCodeName() + "]"; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/event/WrapperServiceControlEvent.java100644 0 0 10277 14333053652 27715 0ustar 0 0 package org.tanukisoftware.wrapper.event; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * WrapperServiceControlEvents are used to notify the listener whenever a * Service Control Event is received by the service. These events will * only be fired on Windows platforms when the Wrapper is running as a * service. * *

*
WrapperManager.SERVICE_CONTROL_CODE_STOP (1)
*
The service was requested to stop.
*
WrapperManager.SERVICE_CONTROL_CODE_PAUSE (2)
*
The system requested that the service be paused.
*
WrapperManager.SERVICE_CONTROL_CODE_CONTINUE (3)
*
The system requested that the paused service be resumed.
*
WrapperManager.SERVICE_CONTROL_CODE_INTERROGATE (4)
*
The service manager queried the service to make sure it is still alive.
*
WrapperManager.SERVICE_CONTROL_CODE_SHUTDOWN (5)
*
The system is shutting down.
*
User code (128-255)
*
User defined code.
*
WrapperManager.SERVICE_CONTROL_CODE_POWEREVENT_QUERYSUSPEND (3328)
*
The system being suspended.
*
WrapperManager.SERVICE_CONTROL_CODE_POWEREVENT_QUERYSUSPENDFAILED (3330)
*
Permission to suspend the computer was denied by a process.
*
WrapperManager.SERVICE_CONTROL_CODE_POWEREVENT_SUSPEND (3332)
*
The computer is about to enter a suspended state.
*
WrapperManager.SERVICE_CONTROL_CODE_POWEREVENT_RESUMECRITICAL (3334)
*
The system has resumed operation. This event can indicate that some or * all applications did not receive a SERVICE_CONTROL_CODE_POWEREVENT_SUSPEND * event.
*
WrapperManager.SERVICE_CONTROL_CODE_POWEREVENT_RESUMESUSPEND (3335)
*
The system has resumed operation after being suspended.
*
WrapperManager.SERVICE_CONTROL_CODE_POWEREVENT_BATTERYLOW (3337)
*
The battery power is low.
*
WrapperManager.SERVICE_CONTROL_CODE_POWEREVENT_POWERSTATUSCHANGE (3338)
*
There is a change in the power status of the computer, such as a * switch from battery power to A/C.
*
WrapperManager.SERVICE_CONTROL_CODE_POWEREVENT_OEMEVENT (3339)
*
The APM BIOS has signaled an APM OEM event.
*
WrapperManager.SERVICE_CONTROL_CODE_POWEREVENT_RESUMEAUTOMATIC (3346)
*
The computer has woken up automatically to handle an event.
*
* * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperServiceControlEvent extends WrapperServiceEvent { /** * Serial Version UID. */ private static final long serialVersionUID = -8642470717850552167L; /** The event code of the Service Control Code. */ private int m_serviceControlCode; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperServiceControlEvent. * * @param serviceControlCode Service Control Code. */ public WrapperServiceControlEvent( int serviceControlCode ) { m_serviceControlCode = serviceControlCode; } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns the event code of the Service Control Code. * * @return The event code of the Service Control Code. */ public int getServiceControlCode() { return m_serviceControlCode; } /** * Returns a string representation of the event. * * @return A string representation of the event. */ public String toString() { return "WrapperServiceControlEvent[serviceControlCode=" + getServiceControlCode() + "]"; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/event/WrapperServiceEvent.java100644 0 0 3166 14333053652 26333 0ustar 0 0 package org.tanukisoftware.wrapper.event; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * WrapperServiceEvents are used to notify the listener of events related * the service. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public abstract class WrapperServiceEvent extends WrapperEvent { /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperServiceEvent. */ protected WrapperServiceEvent() { } /*--------------------------------------------------------------- * WrapperEvent Methods *-------------------------------------------------------------*/ /** * Returns a set of event flags for which the event should be fired. * This value is compared with the mask supplied when when a * WrapperEventListener is registered to decide which listeners should * receive the event. *

* If subclassed, the return value of the super class should usually * be ORed with any additional flags. * * @return a set of event flags. */ public long getFlags() { return super.getFlags() | WrapperEventListener.EVENT_FLAG_SERVICE; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/event/WrapperServicePauseEvent.java100644 0 0 3567 14333053652 27336 0ustar 0 0 package org.tanukisoftware.wrapper.event; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * WrapperServicePauseEvents are used to notify the listener that the Wrapper * is requesting that the Java application be paused. This does not mean that * it should exit, only that it should internally go into an idle state. * * See the wrapper.pausable and wrapper.pausable.stop_jvm properties for more * information. * * @author Tanuki Software Development Team <support@tanukisoftware.com> * * @since Wrapper 3.5.0 */ public class WrapperServicePauseEvent extends WrapperServiceActionEvent { /** * Serial Version UID. */ private static final long serialVersionUID = 1308747091110200773L; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperServicePauseEvent. * * @param actionSourceCode Source Code specifying where the pause action originated. */ public WrapperServicePauseEvent( int actionSourceCode ) { super( actionSourceCode ); } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns a string representation of the event. * * @return A string representation of the event. */ public String toString() { return "WrapperServicePauseEvent[actionSourceCode=" + getSourceCodeName() + "]"; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/event/WrapperServiceResumeEvent.java100644 0 0 3432 14333053652 27510 0ustar 0 0 package org.tanukisoftware.wrapper.event; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * WrapperServiceResumeEvents are used to notify the listener that the Wrapper * is requesting that the Java application be resumed. * * See the wrapper.pausable and wrapper.pausable.stop_jvm properties for more * information. * * @author Tanuki Software Development Team <support@tanukisoftware.com> * * @since Wrapper 3.5.0 */ public class WrapperServiceResumeEvent extends WrapperServiceActionEvent { /** * Serial Version UID. */ private static final long serialVersionUID = 338313484021328312L; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperServiceResumeEvent. * * @param actionSourceCode Source Code specifying where the resume action originated. */ public WrapperServiceResumeEvent( int actionSourceCode ) { super( actionSourceCode ); } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns a string representation of the event. * * @return A string representation of the event. */ public String toString() { return "WrapperServiceResumeEvent[actionSourceCode=" + getSourceCodeName() + "]"; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/event/WrapperTickEvent.java100644 0 0 5407 14333053652 25625 0ustar 0 0 package org.tanukisoftware.wrapper.event; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * WrapperPingEvent are fired each time a ping is received from the Wrapper * process. This event is mainly useful for debugging and statistic * collection purposes. *

* WARNING - Great care should be taken when receiving events of this type. * They are sent from within the Wrapper's internal timing thread. If the * listner takes too much time working with the event, Wrapper performance * could be adversely affected. If unsure, it is recommended that events * of this type not be included. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public abstract class WrapperTickEvent extends WrapperCoreEvent { /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperTickEvent. */ protected WrapperTickEvent() { } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns the tick count at the point the event is fired. * * @return The tick count at the point the event is fired. */ public abstract int getTicks(); /** * Returns the offset between the tick count used by the Wrapper for time * keeping and the tick count generated directly from the system time. *

* This will be 0 in most cases. But will be a positive value if the * system time is ever set back for any reason. It will be a negative * value if the system time is set forward or if the system is under * heavy load. If the wrapper.use_system_time property is set to TRUE * then the Wrapper will be using the system tick count for internal * timing and this value will always be 0. * * @return The tick count offset. */ public abstract int getTickOffset(); /*--------------------------------------------------------------- * Method *-------------------------------------------------------------*/ /** * Returns a string representation of the event. * * @return A string representation of the event. */ public String toString() { return "WrapperTickEvent[ticks=" + getTicks() + ", tickOffset=" + getTickOffset() + "]"; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/jmx/WrapperManager.java100644 0 0 17172 14333053652 25002 0ustar 0 0 package org.tanukisoftware.wrapper.jmx; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperManager implements WrapperManagerMBean { /*--------------------------------------------------------------- * WrapperManagerMBean Methods *-------------------------------------------------------------*/ /** * Obtain the current version of Wrapper. * * @return The version of the Wrapper. */ public String getVersion() { return org.tanukisoftware.wrapper.WrapperManager.getVersion(); } /** * Obtain the build time of Wrapper. * * @return The time that the Wrapper was built. */ public String getBuildTime() { return org.tanukisoftware.wrapper.WrapperManager.getBuildTime(); } /** * Returns the Id of the current JVM. JVM Ids increment from 1 each time * the wrapper restarts a new one. * * @return The Id of the current JVM. */ public int getJVMId() { return org.tanukisoftware.wrapper.WrapperManager.getJVMId(); } /** * Returns true if the current Wrapper edition has support for Professional * Edition features. * * @return True if professional features are supported. */ public boolean isProfessionalEdition() { return org.tanukisoftware.wrapper.WrapperManager.isProfessionalEdition(); } /** * Returns true if the current Wrapper edition has support for Standard * Edition features. * * @return True if standard features are supported. */ public boolean isStandardEdition() { return org.tanukisoftware.wrapper.WrapperManager.isStandardEdition(); } /** * Sets the title of the console in which the Wrapper is running. This * is currently only supported on Windows platforms. *

* As an alternative, it is also possible to set the console title from * within the wrapper.conf file using the wrapper.console.title property. * * @param title The new title. The specified string will be encoded * to a byte array using the default encoding for the * current platform. */ public void setConsoleTitle( String title ) { org.tanukisoftware.wrapper.WrapperManager.setConsoleTitle( title ); } /** * Returns the PID of the Wrapper process. * * A PID of 0 will be returned if the JVM was launched standalone. * * This value can also be obtained using the 'wrapper.pid' system property. * * @return The PID of the Wrpper process. */ public int getWrapperPID() { return org.tanukisoftware.wrapper.WrapperManager.getWrapperPID(); } /** * Returns the PID of the Java process. * * A PID of 0 will be returned if the native library has not been initialized. * * This value can also be obtained using the 'wrapper.java.pid' system property. * * @return The PID of the Java process. */ public int getJavaPID() { return org.tanukisoftware.wrapper.WrapperManager.getJavaPID(); } /** * Requests that the current JVM process request a thread dump. This is * the same as pressing CTRL-BREAK (under Windows) or CTRL-\ (under Unix) * in the the console in which Java is running. This method does nothing * if the native library is not loaded. */ public void requestThreadDump() { org.tanukisoftware.wrapper.WrapperManager.requestThreadDump(); } /** * Returns true if the JVM was launched by the Wrapper application. False * if the JVM was launched manually without the Wrapper controlling it. * * @return True if the current JVM was launched by the Wrapper. */ public boolean isControlledByNativeWrapper() { return org.tanukisoftware.wrapper.WrapperManager.isControlledByNativeWrapper(); } /** * Returns true if the Wrapper was launched as an NT service on Windows or * as a daemon process on UNIX platforms. False if launched as a console. * This can be useful if you wish to display a user interface when in * Console mode. On UNIX platforms, this is not as useful because an * X display may not be visible even if launched in a console. * * @return True if the Wrapper is running as an NT service or daemon * process. */ public boolean isLaunchedAsService() { return org.tanukisoftware.wrapper.WrapperManager.isLaunchedAsService(); } /** * Returns true if the wrapper.debug property, or any of the logging * channels are set to DEBUG in the wrapper configuration file. Useful * for deciding whether or not to output certain information to the * console. * * @return True if the Wrapper is logging any Debug level output. */ public boolean isDebugEnabled() { return org.tanukisoftware.wrapper.WrapperManager.isDebugEnabled(); } /** * Tells the native wrapper that the JVM wants to restart, then informs * all listeners that the JVM is about to shutdown before killing the JVM. *

* The restart is actually performed in a background thread allowing JMX * a chance to respond to the client. */ public void restart() { // This action normally will not return, so launch it in a background // thread giving JMX a chance to return a response to its client. new Thread() { public void run() { try { Thread.sleep( 1000 ); } catch ( InterruptedException e ) { } org.tanukisoftware.wrapper.WrapperManager.restart(); } }.start(); } /** * Tells the native wrapper that the JVM wants to shut down, then informs * all listeners that the JVM is about to shutdown before killing the JVM. *

* The stop is actually performed in a background thread allowing JMX * a chance to respond to the client. * * @param exitCode The exit code that the Wrapper will return when it exits. */ public void stop( final int exitCode ) { // This action normally will not return, so launch it in a background // thread giving JMX a chance to return a response to its client. new Thread() { public void run() { try { Thread.sleep( 1000 ); } catch ( InterruptedException e ) { } org.tanukisoftware.wrapper.WrapperManager.stop( exitCode ); } }.start(); } /** * Returns true if the ShutdownHook for the JVM has already been triggered. * Some code needs to know whether or not the system is shutting down. * * @return True if the ShutdownHook for the JVM has already been triggered. */ public boolean getHasShutdownHookBeenTriggered() { return org.tanukisoftware.wrapper.WrapperManager.hasShutdownHookBeenTriggered(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/jmx/WrapperManagerMBean.java100644 0 0 12216 14333053652 25677 0ustar 0 0 package org.tanukisoftware.wrapper.jmx; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public interface WrapperManagerMBean { /** * Obtain the current version of Wrapper. * * @return The version of the Wrapper. */ String getVersion(); /** * Obtain the build time of Wrapper. * * @return The time that the Wrapper was built. */ String getBuildTime(); /** * Returns the Id of the current JVM. JVM Ids increment from 1 each time * the wrapper restarts a new one. * * @return The Id of the current JVM. */ int getJVMId(); /** * Returns true if the current Wrapper edition has support for Professional * Edition features. * * @return True if professional features are supported. */ boolean isProfessionalEdition(); /** * Returns true if the current Wrapper edition has support for Standard * Edition features. * * @return True if standard features are supported. */ boolean isStandardEdition(); /** * Sets the title of the console in which the Wrapper is running. This * is currently only supported on Windows platforms. *

* As an alternative, it is also possible to set the console title from * within the wrapper.conf file using the wrapper.console.title property. * * @param title The new title. The specified string will be encoded * to a byte array using the default encoding for the * current platform. */ void setConsoleTitle( String title ); /** * Returns the PID of the Wrapper process. * * A PID of 0 will be returned if the JVM was launched standalone. * * This value can also be obtained using the 'wrapper.pid' system property. * * @return The PID of the Wrpper process. */ int getWrapperPID(); /** * Returns the PID of the Java process. * * A PID of 0 will be returned if the native library has not been initialized. * * This value can also be obtained using the 'wrapper.java.pid' system property. * * @return The PID of the Java process. */ int getJavaPID(); /** * Requests that the current JVM process request a thread dump. This is * the same as pressing CTRL-BREAK (under Windows) or CTRL-\ (under Unix) * in the the console in which Java is running. This method does nothing * if the native library is not loaded. */ void requestThreadDump(); /** * Returns true if the JVM was launched by the Wrapper application. False * if the JVM was launched manually without the Wrapper controlling it. * * @return True if the current JVM was launched by the Wrapper. */ boolean isControlledByNativeWrapper(); /** * Returns true if the Wrapper was launched as an NT service on Windows or * as a daemon process on UNIX platforms. False if launched as a console. * This can be useful if you wish to display a user interface when in * Console mode. On UNIX platforms, this is not as useful because an * X display may not be visible even if launched in a console. * * @return True if the Wrapper is running as an NT service or daemon * process. */ boolean isLaunchedAsService(); /** * Returns true if the wrapper.debug property, or any of the logging * channels are set to DEBUG in the wrapper configuration file. Useful * for deciding whether or not to output certain information to the * console. * * @return True if the Wrapper is logging any Debug level output. */ boolean isDebugEnabled(); /** * Tells the native wrapper that the JVM wants to restart, then informs * all listeners that the JVM is about to shutdown before killing the JVM. *

* The restart is actually performed in a background thread allowing JMX * a chance to respond to the client. */ void restart(); /** * Tells the native wrapper that the JVM wants to shut down, then informs * all listeners that the JVM is about to shutdown before killing the JVM. *

* The stop is actually performed in a background thread allowing JMX * a chance to respond to the client. * * @param exitCode The exit code that the Wrapper will return when it exits. */ void stop( int exitCode ); /** * Returns true if the ShutdownHook for the JVM has already been triggered. * Some code needs to know whether or not the system is shutting down. * * @return True if the ShutdownHook for the JVM has already been triggered. */ boolean getHasShutdownHookBeenTriggered(); } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/jmx/WrapperManagerTesting.java100644 0 0 5412 14333053652 26312 0ustar 0 0 package org.tanukisoftware.wrapper.jmx; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperManagerTesting implements WrapperManagerTestingMBean { /*--------------------------------------------------------------- * WrapperManagerTestingMBean Methods *-------------------------------------------------------------*/ /** * Causes the WrapperManager to go into a state which makes the JVM appear * to be hung when viewed from the native Wrapper code. Does not have * any effect when the JVM is not being controlled from the native * Wrapper. */ public void appearHung() { org.tanukisoftware.wrapper.WrapperManager.appearHung(); } /** * Cause an access violation within native JNI code. This currently causes * the access violation by attempting to write to a null pointer. */ public void accessViolationNative() { // This action normally will not return, so launch it in a background // thread giving JMX a chance to return a response to its client. new Thread() { public void run() { try { Thread.sleep( 1000 ); } catch ( InterruptedException e ) { } org.tanukisoftware.wrapper.WrapperManager.accessViolationNative(); } }.start(); } /** * Tells the native wrapper that the JVM wants to shut down and then * promptly halts. Be careful when using this method as an application * will not be given a chance to shutdown cleanly. * * @param exitCode The exit code that the Wrapper will return when it exits. */ public void stopImmediate( final int exitCode ) { // This action normally will not return, so launch it in a background // thread giving JMX a chance to return a response to its client. new Thread() { public void run() { try { Thread.sleep( 1000 ); } catch ( InterruptedException e ) { } org.tanukisoftware.wrapper.WrapperManager.stopImmediate( exitCode ); } }.start(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/jmx/WrapperManagerTestingMBean.java100644 0 0 3227 14333053652 27217 0ustar 0 0 package org.tanukisoftware.wrapper.jmx; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * This MBean interface provides access to a number of actions which can be * useful for testing how well an application responds to JVM crashes. It * has been broken out frtom the WrapperManagerMBean interface so system * administrators can easily disable the testing functions. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public interface WrapperManagerTestingMBean { /** * Causes the WrapperManager to go into a state which makes the JVM appear * to be hung when viewed from the native Wrapper code. Does not have * any effect when the JVM is not being controlled from the native * Wrapper. */ void appearHung(); /** * Cause an access violation within native JNI code. This currently causes * the access violation by attempting to write to a null pointer. */ void accessViolationNative(); /** * Tells the native wrapper that the JVM wants to shut down and then * promptly halts. Be careful when using this method as an application * will not be given a chance to shutdown cleanly. * * @param exitCode The exit code that the Wrapper will return when it exits. */ void stopImmediate( int exitCode ); } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/security/WrapperEventPermission.java100644 0 0 34131 14333053652 27625 0ustar 0 0 package org.tanukisoftware.wrapper.security; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.security.Permission; import java.security.PermissionCollection; import java.util.Enumeration; import java.util.Vector; import java.util.StringTokenizer; import org.tanukisoftware.wrapper.WrapperManager; /** * WrapperEventPermissions are used to grant the right to register to start * receiving events from the Wrapper. *

* Some of these permissions can result in performance degredations if used * impropperly. *

* The following are examples of how to specify the permission within a policy * file. *

 *   grant codeBase "file:../lib/-" {
 *     // Grant various permissions to a specific service.
 *     permission org.tanukisoftware.wrapper.security.WrapperEventPermission "service";
 *     permission org.tanukisoftware.wrapper.security.WrapperEventPermission "service, core";
 *     permission org.tanukisoftware.wrapper.security.WrapperEventPermission "*";
 *   };
 * 
*

* Possible eventTypes include the following: * * * * * * * * * * * * * * * * * * * * * * * * *
Permission Event Type NameWhat the Permission AllowsRisks of Allowing this Permission
serviceRegister to obtain events whenever the Wrapper service receives any service events.Malicious code could receive this event and never return and thus cause performance * and timeout problems with the Wrapper. Normal use of these events are quite safe * however.
controlRegister to obtain events whenever the Wrapper receives any system control signals.Malicious code could trap and consome control events, thus preventing an application * from being shut down cleanly.
coreRegister to obtain events on the core workings of the Wrapper.Malicious code or even well meaning code can greatly affect the performance of * the Wrapper simply by handling these methods slowly. Some of these events are * fired from within the core timing code of the Wrapper. They are useful for * testing and performance checks, but in general they should not be used by * most applications. *
* * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperEventPermission extends Permission { /** * Serial Version UID. */ private static final long serialVersionUID = 8916489326587298168L; public static String EVENT_TYPE_SERVICE = "service"; public static String EVENT_TYPE_CONTROL = "control"; public static String EVENT_TYPE_REMOTE_CONTROL = "remoteControl"; public static String EVENT_TYPE_CORE = "core"; private static int MASK_SERVICE = 1; private static int MASK_CONTROL = 2; private static int MASK_REMOTE_CONTROL = 8; private static int MASK_CORE = 65536; private static int MASK_ALL = MASK_SERVICE | MASK_CONTROL | MASK_REMOTE_CONTROL | MASK_CORE; private int m_eventTypeMask; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperEventPermission for the specified service. * * @param eventTypes The event type or event types to be registered. */ public WrapperEventPermission( String eventTypes ) { super( "*" ); m_eventTypeMask = buildEventTypeMask( eventTypes ); } /*--------------------------------------------------------------- * Permission Methods *-------------------------------------------------------------*/ /** * Checks two Permission objects for equality. *

* Do not use the equals method for making access control decisions; use * the implies method. * * @param obj The object we are testing for equality with this object. * * @return True if both Permission objects are equivalent. */ public boolean equals( Object obj ) { if ( obj == this ) { return true; } if ( !( obj instanceof WrapperEventPermission ) ) { return false; } WrapperEventPermission wsp = (WrapperEventPermission)obj; return ( m_eventTypeMask == wsp.m_eventTypeMask ) && getName().equals( wsp.getName() ); } /** * Return the canonical string representation of the eventTypes. * Always returns present eventTypes in the following order: * start, stop, pause, continue, interrogate. userCode. * * @return The canonical string representation of the eventTypes. */ public String getActions() { StringBuffer sb = new StringBuffer(); boolean first = true; if ( ( m_eventTypeMask & MASK_SERVICE ) != 0 ) { if ( first ) { sb.append( ',' ); } else { first = false; } sb.append( EVENT_TYPE_SERVICE ); } if ( ( m_eventTypeMask & MASK_CONTROL ) != 0 ) { if ( first ) { sb.append( ',' ); } else { first = false; } sb.append( EVENT_TYPE_CONTROL ); } if ( ( m_eventTypeMask & MASK_REMOTE_CONTROL ) != 0 ) { if ( first ) { sb.append( ',' ); } else { first = false; } sb.append( EVENT_TYPE_REMOTE_CONTROL ); } if ( ( m_eventTypeMask & MASK_CORE ) != 0 ) { if ( first ) { sb.append( ',' ); } else { first = false; } sb.append( EVENT_TYPE_CORE ); } return sb.toString(); } /** * Checks if this WrapperEventPermission object "implies" the * specified permission. *

* More specifically, this method returns true if: *

    *
  • p2 is an instanceof FilePermission, *
  • p2's eventTypes are a proper subset of this object's eventTypes, * and *
  • p2's service name is implied by this object's service name. * For example, "MyApp*" implies "MyApp". *
* * @param p2 the permission to check against. * * @return true if the specified permission is implied by this object, */ public boolean implies( Permission p2 ) { if ( !( p2 instanceof WrapperEventPermission ) ) { return false; } WrapperEventPermission wsp = (WrapperEventPermission)p2; // we get the effective mask. i.e., the "and" of this and that. // They must be equal to that.mask for implies to return true. return ( ( m_eventTypeMask & wsp.m_eventTypeMask ) == wsp.m_eventTypeMask ) && impliesIgnoreEventTypeMask( wsp ); } /** * Returns an custom WECollection implementation of a PermissionCollection. */ public PermissionCollection newPermissionCollection() { return new WECollection(); } /** * Returns the hash code value for this object. * * @return A hash code value for this object. */ public int hashCode() { return getName().hashCode(); } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns the eventType mask of the Permission. * * @return The eventType mask of the Permission. */ int getActionMask() { return m_eventTypeMask; } /** * Tests whether this permissions implies another without taking the * eventType mask into account. * * @param p2 The permission to check against. * * @return True if the specified permission is implied by this object. */ boolean impliesIgnoreEventTypeMask( WrapperEventPermission p2 ) { if ( getName().equals( p2.getName() ) ) { return true; } if ( p2.getName().endsWith( "*" ) ) { if ( getName().startsWith( p2.getName().substring( 0, p2.getName().length() - 1 ) ) ) { return true; } } return false; } /** * Builds an event type mask given a comma separated list of eventTypes. * * @param eventTypes The list of event types. * * @return The event type mask. */ private int buildEventTypeMask( String eventTypes ) { // Check for the constants first as they are used internally. if ( eventTypes == EVENT_TYPE_SERVICE ) { return MASK_SERVICE; } else if ( eventTypes == EVENT_TYPE_CONTROL ) { return MASK_CONTROL; } else if ( eventTypes == EVENT_TYPE_REMOTE_CONTROL ) { return MASK_REMOTE_CONTROL; } else if ( eventTypes == EVENT_TYPE_CORE ) { return MASK_CORE; } else if ( eventTypes.equals( "*" ) ) { return MASK_ALL; } int mask = 0; StringTokenizer st = new StringTokenizer( eventTypes, "," ); while ( st.hasMoreTokens() ) { String eventType = st.nextToken(); if ( eventType.equals( EVENT_TYPE_SERVICE ) ) { mask |= MASK_SERVICE; } else if ( eventType.equals( EVENT_TYPE_CONTROL ) ) { mask |= MASK_CONTROL; } else if ( eventType.equals( EVENT_TYPE_REMOTE_CONTROL ) ) { mask |= MASK_REMOTE_CONTROL; } else if ( eventType.equals( EVENT_TYPE_CORE ) ) { mask |= MASK_CORE; } else { throw new IllegalArgumentException( WrapperManager.getRes().getString("Invalid permission eventType: \"{0}\"", eventType ) ); } } return mask; } } final class WECollection extends PermissionCollection { /** * Serial Version UID. */ private static final long serialVersionUID = -5183704982261198435L; private Vector m_permissions = new Vector(); /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates an empty WECollection. */ public WECollection() { } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Adds a permission to the FilePermissions. The key for the hash is * permission.path. * * @param permission The Permission object to add. * * @exception IllegalArgumentException If the permission is not a * FilePermission * * @exception SecurityException If this FilePermissionCollection object * has been marked readonly */ public void add( Permission permission ) { if ( !( permission instanceof WrapperEventPermission ) ) { throw new IllegalArgumentException( WrapperManager.getRes().getString( "invalid permission: {0}", permission ) ); } if ( isReadOnly() ) { throw new SecurityException( WrapperManager.getRes().getString("Collection is read-only.") ); } m_permissions.add( permission ); } /** * Check and see if this set of permissions implies the permissions * expressed in "permission". * * @param permission The Permission object to compare * * @return True if "permission" is a proper subset of a permission in * the set, false if not. */ public boolean implies( Permission permission ) { if ( !( permission instanceof WrapperEventPermission ) ) { return false; } WrapperEventPermission wsp = (WrapperEventPermission)permission; int desiredMask = wsp.getActionMask(); int pendingMask = desiredMask; int foundMask = 0; for ( Enumeration en = m_permissions.elements(); en.hasMoreElements(); ) { WrapperEventPermission p2 = (WrapperEventPermission)en.nextElement(); if ( ( pendingMask & p2.getActionMask() ) != 0 ) { // This permission has one or more eventTypes that we need. if ( wsp.impliesIgnoreEventTypeMask( p2 ) ) { foundMask |= desiredMask & p2.getActionMask(); if ( foundMask == desiredMask ) { return true; } pendingMask = desiredMask ^ foundMask; } } } return false; } /** * Returns an enumeration of all the WrapperEventPermission * objects in the container. * * @return An enumeration of all the WrapperEventPermission * objects. */ public Enumeration elements() { return m_permissions.elements(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/security/WrapperPermission.java100644 0 0 10775 14333053652 26633 0ustar 0 0 package org.tanukisoftware.wrapper.security; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.security.BasicPermission; /** * WrapperPermissions are used to control access to the various methods of the * WrapperManager class. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Permission Target NameWhat the Permission AllowsRisks of Allowing this Permission
restartRestart the JVM * This is an extremely dangerous permission to grant. * Malicious applications can restart the JVM as a denial of service * attack. *
stopStop the JVM * This is an extremely dangerous permission to grant. * Malicious applications can stop the JVM as a denial of service * attack. *
stopImmediateStop the JVM immediately without running the shutdown hooks * This is an extremely dangerous permission to grant. * Malicious applications can stop the JVM as a denial of service * attack. *
signalStartingControl the starting timeouts. * Malicious code could set this to unrealistically small values as the application * is starting, thus causing startup failures. *
signalStoppingControl the stopping timeouts. * Malicious code could set this to unrealistically small values as the application * is stopping, thus causing the application to fail to shutdown cleanly. *
signalStoppedControl when the Wrapper is told that the Application has stopped. * Malicious code could call this before the application is actually stopped, * thus causing the application to fail to shutdown cleanly. *
logSends log output to the Wrapper over the back end socket at a specific log level. * Malicious code could send very large quanities of log output which could affect * the performance of the Wrapper. *
listServicesRequests the status of all services currently installed on the system. * Malicious code could use this information to find other weaknesses in the system. *
TODO: Complete javadocs. addWrapperEventListener service,core removeWrapperEventListener setConsoleTitle getUser getInteractiveUser getProperties getWrapperPID getJavaPID requestThreadDump test.appearHung test.accessViolation test.accessViolationNative * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperPermission extends BasicPermission { /** * Serial Version UID. */ private static final long serialVersionUID = -4947853086614625658L; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperPermission with the specified name. * The name is the symbolic name of the WrapperPermission, such as "stop", * "restart", etc. An asterisk may appear at the end of the name, following * a ".", or by itself, to signify a wildcard match. * * @param name the name of the WrapperPermission. */ public WrapperPermission( String name ) { super( name ); } /** * Creates a new WrapperPermission with the specified name. * The name is the symbolic name of the WrapperPermission, such as "stop", * "restart", etc. An asterisk may appear at the end of the name, following * a ".", or by itself, to signify a wildcard match. * * @param name the name of the WrapperPermission. * @param actions The event type or event types to be registered. */ public WrapperPermission( String name, String actions ) { super( name, actions ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/security/WrapperServicePermission.java100644 0 0 42511 14333053652 30145 0ustar 0 0 package org.tanukisoftware.wrapper.security; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.security.Permission; import java.security.PermissionCollection; import java.util.Enumeration; import java.util.Vector; import java.util.StringTokenizer; import org.tanukisoftware.wrapper.WrapperManager; /** * WrapperServicePermissions are used to grant the right to start, stop, * pause, continue, interrogate, or send custom codes to other services * running on a Windows system. *

* These permissions are inherently quite dangerous so great care should be * taken when granting them. When doing so, try to only grant permission to * those services which really need to be controlled. *

* The following are examples of how to specify the permission within a policy * file. *

 *   grant codeBase "file:../lib/-" {
 *     // Grant various permissions to a specific service.
 *     permission org.tanukisoftware.wrapper.security.WrapperServicePermission "myservice", "interrogate";
 *     permission org.tanukisoftware.wrapper.security.WrapperServicePermission "myservice", "interrogate,start,stop";
 *     permission org.tanukisoftware.wrapper.security.WrapperServicePermission "myservice", "userCode";
 *     permission org.tanukisoftware.wrapper.security.WrapperServicePermission "myservice", "*";
 *
 *     // Grant various permissions to any service starting with "my".
 *     permission org.tanukisoftware.wrapper.security.WrapperServicePermission "my*", "*";
 *
 *     // Let the calling code do anything to any service on the system
 *     permission org.tanukisoftware.wrapper.security.WrapperServicePermission "*", "*";
 *     permission org.tanukisoftware.wrapper.security.WrapperServicePermission "*";
 *   };
 * 
*

* Possible actions include the following: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
Permission Action NameWhat the Permission AllowsRisks of Allowing this Permission
startStart a service which is installed but has not been started.Malicious code could potentially start any service that is not currently running. * This includes services which were previously stopped or that are configured to be * started manually. Many Windows systems have several services stopped by default * because of the security hazards that they pose. Starting such services could open * the system up to attacks related to that service.
stopStop a service which is currently running.Malicious code could potentially stop running service. This could result in a * denial of service attack if the service is a web or database server. Or it * result in more dangerous attacks if the service is a firewall or virus scanner. *
pausePause a service which is currently running.Malicious code could potentially pause running service. This could result in a * denial of service attack if the service is a web or database server. Or it * result in more dangerous attacks if the service is a firewall or virus scanner. *
continueContinue a service which was previously paused.Malicious code could resume services which had been paused for a good reason.
interrogateInterrogate a service as to its current state.Malicious code learn a lot about a system and its weakness by probing which * services are currently running.
userCodeSend any custom user code to a service.The danger of this action depends on whether or not the service understands * custom user codes, and what it does with them. This could potentially be a * very dangerous permission to grant.
* * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class WrapperServicePermission extends Permission { /** * Serial Version UID. */ private static final long serialVersionUID = -6520453688353960444L; public static String ACTION_START = "start"; public static String ACTION_STOP = "stop"; public static String ACTION_PAUSE = "pause"; public static String ACTION_CONTINUE = "continue"; public static String ACTION_INTERROGATE = "interrogate"; public static String ACTION_USER_CODE = "userCode"; private static int MASK_START = 1; private static int MASK_STOP = 2; private static int MASK_PAUSE = 4; private static int MASK_CONTINUE = 8; private static int MASK_INTERROGATE = 16; private static int MASK_USER_CODE = 32; private static int MASK_ALL = MASK_START | MASK_STOP | MASK_PAUSE | MASK_CONTINUE | MASK_INTERROGATE | MASK_USER_CODE; private int m_actionMask; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperServicePermission for the specified service. * * @param serviceName The name of the service whose access is being * controlled. * @param actions The action or actions to be performed. */ public WrapperServicePermission( String serviceName, String actions ) { super( serviceName ); m_actionMask = buildActionMask( actions ); } /** * Creates a new WrapperServicePermission for the specified service. * This version of the constructor grants all actions. * * @param serviceName The name of the service whose access is being * controlled. */ public WrapperServicePermission( String serviceName ) { this( serviceName, "*" ); } /*--------------------------------------------------------------- * Permission Methods *-------------------------------------------------------------*/ /** * Checks two Permission objects for equality. *

* Do not use the equals method for making access control decisions; use * the implies method. * * @param obj The object we are testing for equality with this object. * * @return True if both Permission objects are equivalent. */ public boolean equals( Object obj ) { if ( obj == this ) { return true; } if ( !( obj instanceof WrapperServicePermission ) ) { return false; } WrapperServicePermission wsp = (WrapperServicePermission)obj; return ( m_actionMask == wsp.m_actionMask ) && getName().equals( wsp.getName() ); } /** * Return the canonical string representation of the actions. * Always returns present actions in the following order: * start, stop, pause, continue, interrogate. userCode. * * @return the canonical string representation of the actions. */ public String getActions() { StringBuffer sb = new StringBuffer(); boolean first = true; if ( ( m_actionMask & MASK_START ) != 0 ) { if ( first ) { sb.append( ',' ); } else { first = false; } sb.append( ACTION_START ); } if ( ( m_actionMask & MASK_STOP ) != 0 ) { if ( first ) { sb.append( ',' ); } else { first = false; } sb.append( ACTION_STOP ); } if ( ( m_actionMask & MASK_PAUSE ) != 0 ) { if ( first ) { sb.append( ',' ); } else { first = false; } sb.append( ACTION_CONTINUE ); } if ( ( m_actionMask & MASK_CONTINUE ) != 0 ) { if ( first ) { sb.append( ',' ); } else { first = false; } sb.append( ACTION_CONTINUE ); } if ( ( m_actionMask & MASK_INTERROGATE ) != 0 ) { if ( first ) { sb.append( ',' ); } else { first = false; } sb.append( ACTION_INTERROGATE ); } if ( ( m_actionMask & MASK_USER_CODE ) != 0 ) { if ( first ) { sb.append( ',' ); } else { first = false; } sb.append( ACTION_USER_CODE ); } return sb.toString(); } /** * Checks if this WrapperServicePermission object "implies" the * specified permission. *

* More specifically, this method returns true if: *

    *
  • p2 is an instanceof FilePermission, *
  • p2's actions are a proper subset of this object's actions, * and *
  • p2's service name is implied by this object's service name. * For example, "MyApp*" implies "MyApp". *
* * @param p2 The permission to check against. * * @return True if the specified permission is implied by this object. */ public boolean implies( Permission p2 ) { if ( !( p2 instanceof WrapperServicePermission ) ) { return false; } WrapperServicePermission wsp = (WrapperServicePermission)p2; // we get the effective mask. i.e., the "and" of this and that. // They must be equal to that.mask for implies to return true. return ( ( m_actionMask & wsp.m_actionMask ) == wsp.m_actionMask ) && impliesIgnoreActionMask( wsp ); } /** * Returns a custom WSCollection implementation of a PermissionCollection. * * @return A custom WSCollection implementation of a PermissionCollection. */ public PermissionCollection newPermissionCollection() { return new WSCollection(); } /** * Returns the hash code value for this object. * * @return A hash code value for this object. */ public int hashCode() { return getName().hashCode(); } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Returns the action mask of the Permission. * * @return The action mask of the Permission. */ int getActionMask() { return m_actionMask; } /** * Tests whether this permissions implies another without taking the * action mask into account. * * @param p2 The permission to check against. * * @return True if the specified permission is implied by this object. */ boolean impliesIgnoreActionMask( WrapperServicePermission p2 ) { if ( getName().equals( p2.getName() ) ) { return true; } if ( p2.getName().endsWith( "*" ) ) { if ( getName().startsWith( p2.getName().substring( 0, p2.getName().length() - 1 ) ) ) { return true; } } return false; } /** * Builds an action mask given a comma separated list of actions. * * @param actions The list of actions. * * @return The action mask. */ private int buildActionMask( String actions ) { // Check for the constants first as they are used internally. if ( actions == ACTION_START ) { return MASK_START; } else if ( actions == ACTION_STOP ) { return MASK_STOP; } else if ( actions == ACTION_PAUSE ) { return MASK_PAUSE; } else if ( actions == ACTION_CONTINUE ) { return MASK_CONTINUE; } else if ( actions == ACTION_INTERROGATE ) { return MASK_INTERROGATE; } else if ( actions == ACTION_USER_CODE ) { return MASK_USER_CODE; } else if ( actions.equals( "*" ) ) { return MASK_ALL; } int mask = 0; StringTokenizer st = new StringTokenizer( actions, "," ); while ( st.hasMoreTokens() ) { String action = st.nextToken(); if ( action.equals( ACTION_START ) ) { mask |= MASK_START; } else if ( action.equals( ACTION_STOP ) ) { mask |= MASK_STOP; } else if ( action.equals( ACTION_PAUSE ) ) { mask |= MASK_PAUSE; } else if ( action.equals( ACTION_CONTINUE ) ) { mask |= MASK_CONTINUE; } else if ( action.equals( ACTION_INTERROGATE ) ) { mask |= MASK_INTERROGATE; } else if ( action.equals( ACTION_USER_CODE ) ) { mask |= MASK_USER_CODE; } else { throw new IllegalArgumentException( WrapperManager.getRes().getString( "Invalid permission action: \"{0}\"" , action ) ); } } return mask; } } final class WSCollection extends PermissionCollection { /** * Serial Version UID. */ private static final long serialVersionUID = 7056999828486119722L; private Vector m_permissions = new Vector(); /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates an empty WSCollection. */ public WSCollection() { } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Adds a permission to the FilePermissions. The key for the hash is * permission.path. * * @param permission the Permission object to add. * * @exception IllegalArgumentException If the permission is not a * FilePermission * * @exception SecurityException If this FilePermissionCollection object * has been marked readonly */ public void add( Permission permission ) { if ( !( permission instanceof WrapperServicePermission ) ) { throw new IllegalArgumentException(WrapperManager.getRes().getString( "invalid permission: {0}", permission ) ); } if ( isReadOnly() ) { throw new SecurityException( WrapperManager.getRes().getString("Collection is read-only." ) ); } m_permissions.add( permission ); } /** * Check and see if this set of permissions implies the permissions * expressed in "permission". * * @param permission The Permission object to compare * * @return True if "permission" is a proper subset of a permission in * the set, false if not. */ public boolean implies( Permission permission ) { if ( !( permission instanceof WrapperServicePermission ) ) { return false; } WrapperServicePermission wsp = (WrapperServicePermission)permission; int desiredMask = wsp.getActionMask(); int pendingMask = desiredMask; int foundMask = 0; for ( Enumeration en = m_permissions.elements(); en.hasMoreElements(); ) { WrapperServicePermission p2 = (WrapperServicePermission)en.nextElement(); if ( ( pendingMask & p2.getActionMask() ) != 0 ) { // This permission has one or more actions that we need. if ( wsp.impliesIgnoreActionMask( p2 ) ) { foundMask |= desiredMask & p2.getActionMask(); if ( foundMask == desiredMask ) { return true; } pendingMask = desiredMask ^ foundMask; } } } return false; } /** * Returns an enumeration of all the WrapperServicePermission * objects in the container. * * @return An enumeration of all the WrapperServicePermission * objects. */ public Enumeration elements() { return m_permissions.elements(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/security/WrapperUserEventPermission.java100644 0 0 25163 14333053652 30471 0ustar 0 0 package org.tanukisoftware.wrapper.security; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.security.AccessControlException; import java.security.BasicPermission; import java.security.Permission; import java.util.ArrayList; import java.util.StringTokenizer; import org.tanukisoftware.wrapper.WrapperManager; /** * WrapperEventPermissions are used to grant the right to register to start * receiving events from the Wrapper. *

* Some of these permissions can result in performance degredations if used * impropperly. *

* The following are examples of how to specify the permission within a policy * file. * *

 *   grant codeBase "file:../lib/-" {
 *     // Grant various permissions to a specific service.
 *     permission org.tanukisoftware.wrapper.security.WrapperEventPermission "service";
 *     permission org.tanukisoftware.wrapper.security.WrapperEventPermission "service, core";
 *     permission org.tanukisoftware.wrapper.security.WrapperEventPermission "*";
 *   };
 * 
*

* Possible eventTypes include the following: * * * * * * * * * * * * * * * * * * * * * * * * *
Permission Event Type NameWhat the Permission AllowsRisks of Allowing this Permission
serviceRegister to obtain events whenever the Wrapper service receives any * service events.Malicious code could receive this event and never return and thus cause * performance and timeout problems with the Wrapper. Normal use of these events * are quite safe however.
controlRegister to obtain events whenever the Wrapper receives any system * control signals.Malicious code could trap and consome control events, thus preventing an * application from being shut down cleanly.
coreRegister to obtain events on the core workings of the Wrapper.Malicious code or even well meaning code can greatly affect the * performance of the Wrapper simply by handling these methods slowly. Some of * these events are fired from within the core timing code of the Wrapper. They * are useful for testing and performance checks, but in general they should not * be used by most applications.
* * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public final class WrapperUserEventPermission extends BasicPermission { /** * Serial Version UID. */ private static final long serialVersionUID = 8916489326587298168L; private final int EVENT_MIN = 1; private final int EVENT_MAX = 32767; private ArrayList m_eventArr; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new WrapperEventPermission for the specified service. * * @param action The event type or event types to be registered. */ public WrapperUserEventPermission( String action ) { super( "fireUserEvent", String.valueOf( action ) ); parseValids( action ); } /** * Creates a new WrapperEventPermission for the specified service. * * @param name Name of the event. * @param action The event type or event types to be registered. */ public WrapperUserEventPermission( String name, String action ) { super( name, action ); parseValids( action ); } /** * Return the canonical string representation of the eventTypes. * Always returns present eventTypes in the following order: * start, stop, pause, continue, interrogate. userCode. * * @return The canonical string representation of the eventTypes. */ public String getActions() { String s = ""; for ( int i = 0; i < m_eventArr.size(); i++ ) { if ( i > 0 ) { s = s.concat( "," ); } s = s.concat( (String)m_eventArr.get( i ) ); } return s; } /** * Checks if this WrapperEventPermission object "implies" the * specified permission. *

* More specifically, this method returns true if: *

    *
  • p2 is an instanceof FilePermission, *
  • p2's eventTypes are a proper subset of this object's eventTypes, * and *
  • p2's service name is implied by this object's service name. * For example, "MyApp*" implies "MyApp". *
* * @param p2 the permission to check against. * * @return True if the specified permission is implied by this object. */ public boolean implies( Permission p2 ) { int current, min, max, check, border; String element; check = Integer.parseInt( p2.getActions() ); for ( int i = 0; i < m_eventArr.size(); i++ ) { element = (String)m_eventArr.get( i ); border = element.indexOf( '-' ); if ( border >= 0 ) { min = Integer.parseInt( element.substring( 0, border ) ); max = Integer.parseInt( element.substring( border + 1 ) ); if ( min <= check && check <= max ) { return true; } } else { current = Integer.parseInt( element ); if ( current == check ) { return true; } } } return false; } /** * This method evaluates the passed in permission's action String and stores them in * chunks in an array * * @param action the permission's actions */ private void parseValids( String action ) { int lastValue = 0, currentValue; m_eventArr = new ArrayList(); if ( action.compareTo( "*" ) == 0 ) { m_eventArr.add( new String( EVENT_MIN + "-" + EVENT_MAX ) ); return; } StringTokenizer strok = new StringTokenizer( action.trim(), "," ); while ( strok.hasMoreTokens() ) { String element = strok.nextToken(); if ( element.indexOf( '*' ) >= 0 ) { throw new AccessControlException( WrapperManager.getRes().getString( "can''t define ''*'' inside a sequence." ) ); } int range = element.indexOf( "-" ); if ( range >= 0 ) { if ( range == 0 ) { if ( m_eventArr.size() != 0 ) { throw new AccessControlException( WrapperManager.getRes().getString( "Value {0} has to be first element in sequence.", element ) ); } else { lastValue = Integer.parseInt( element.substring( 1 ) ); if ( lastValue <= EVENT_MIN || lastValue > EVENT_MAX ) { throw new AccessControlException( WrapperManager.getRes().getString( "Value {0} is out of bounds.", new Integer( lastValue ) ) ); } m_eventArr.add( new String( EVENT_MIN + "-" + lastValue ) ); } } else if ( range == element.length() - 1 ) { currentValue = Integer.parseInt( element.substring( 0, element.length() - 1 ) ); if ( currentValue <= EVENT_MIN || currentValue > EVENT_MAX ) { throw new AccessControlException( WrapperManager.getRes().getString( "Value {0} is out of bounds.", new Integer( lastValue ) ) ); } if ( currentValue < lastValue ) { throw new AccessControlException( WrapperManager.getRes().getString( "Value {0} is not sorted.", new Integer( currentValue ) ) ); } lastValue = currentValue; if ( strok.hasMoreTokens() ) { throw new AccessControlException( WrapperManager.getRes().getString( "Value {0} has to be last element in sequence.", element ) ); } m_eventArr.add( currentValue + "-" + EVENT_MAX ); } else { currentValue = Integer.parseInt( element.substring( 0, range ) ); if ( currentValue <= EVENT_MIN || currentValue > EVENT_MAX ) { throw new AccessControlException( WrapperManager.getRes().getString( "Value {0} is out of bounds.", new Integer( lastValue ) ) ); } if ( currentValue < lastValue ) { throw new AccessControlException( WrapperManager.getRes().getString( "Value {0} is not sorted.", new Integer( currentValue ) ) ); } lastValue = currentValue; currentValue = Integer.parseInt( element.substring( range + 1 ) ); if ( currentValue <= EVENT_MIN || currentValue > EVENT_MAX ) { throw new AccessControlException( WrapperManager.getRes().getString( "Value {0} is out of bounds.", new Integer( lastValue ) ) ); } if ( currentValue < lastValue ) { throw new AccessControlException( WrapperManager.getRes().getString( "Value {0} is not sorted.", new Integer( currentValue ) ) ); } m_eventArr.add( lastValue + "-" + currentValue ); lastValue = currentValue; } } else { currentValue = Integer.parseInt( element ); if ( currentValue < lastValue ) { throw new java.security.AccessControlException( WrapperManager.getRes().getString( "Value {0} is not sorted.", new Integer( currentValue ) ) ); } lastValue = currentValue; m_eventArr.add( element ); } } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test2/Jar2Main.java100644 0 0 1711 14333053652 23705 0ustar 0 0 package org.tanukisoftware.wrapper.test2; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.test.JarMain; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class Jar2Main { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main( String[] args ) { System.out.println( "Calling JarMain.main." ); JarMain.main( args ); System.out.println( "Returned from JarMain.main." ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/AbstractActionApp.java100644 0 0 111566 14333053652 25634 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Enumeration; import java.util.Properties; import java.text.SimpleDateFormat; import java.util.Date; import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.WrapperProcess; import org.tanukisoftware.wrapper.WrapperProcessConfig; import org.tanukisoftware.wrapper.WrapperServiceException; import org.tanukisoftware.wrapper.WrapperWin32Service; import org.tanukisoftware.wrapper.event.WrapperControlEvent; import org.tanukisoftware.wrapper.event.WrapperEvent; import org.tanukisoftware.wrapper.event.WrapperEventListener; /** * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public abstract class AbstractActionApp implements WrapperEventListener { private static String c_encoding; private DeadlockPrintStream m_out; private DeadlockPrintStream m_err; private Thread m_runner; private Thread m_consoleRunner; private boolean m_ignoreControlEvents; private boolean m_users; private boolean m_groups; private boolean m_nestedExit; private long m_eventMask = 0xffffffffffffffffL; private int m_slowSeconds = 0; private int m_suspendSeconds = 0; private String m_serviceName = "testWrapper"; private String m_consoleTitle = "Java Service Wrapper"; private String m_childCommand = "ls"; private boolean m_childDetached = true; /*--------------------------------------------------------------- * Static Methods *-------------------------------------------------------------*/ static { // In order to read the output from some processes correctly we need to get the correct encoding. // On some systems, the underlying system encoding is different than the file encoding. c_encoding = System.getProperty( "sun.jnu.encoding" ); if ( c_encoding == null ) { c_encoding = System.getProperty( "file.encoding" ); if ( c_encoding == null ) { // Default to Latin1 c_encoding = "Cp1252"; } } } /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ protected AbstractActionApp() { m_runner = new Thread( "WrapperActionTest_Runner" ) { public void run() { while ( true ) { if ( m_users ) { System.out.println( Main.getRes().getString( "The current user is: {0}", WrapperManager.getUser( m_groups ) ) ); System.out.println( Main.getRes().getString( "The current interactive user is: {0}" , WrapperManager.getInteractiveUser( m_groups ) ) ); } synchronized( AbstractActionApp.class ) { try { AbstractActionApp.class.wait( 5000 ); } catch ( InterruptedException e ) { } } } } }; m_runner.setDaemon( true ); m_runner.start(); } /*--------------------------------------------------------------- * WrapperEventListener Methods *-------------------------------------------------------------*/ /** * Called whenever a WrapperEvent is fired. The exact set of events that a * listener will receive will depend on the mask supplied when * WrapperManager.addWrapperEventListener was called to register the * listener. * * Listener implementations should never assume that they will only receive * events of a particular type. To assure that events added to future * versions of the Wrapper do not cause problems with user code, events * should always be tested with "if ( event instanceof {EventClass} )" * before casting it to a specific event type. * * @param event WrapperEvent which was fired. */ public void fired( WrapperEvent event ) { System.out.println( Main.getRes().getString( "Received event: {0}", event ) ); if ( event instanceof WrapperControlEvent ) { System.out.println( Main.getRes().getString( " Consume and ignore." ) ); ((WrapperControlEvent)event).consume(); } } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ protected boolean ignoreControlEvents() { return m_ignoreControlEvents; } protected boolean isNestedExit() { return m_nestedExit; } protected void setEventMask( long eventMask ) { m_eventMask = eventMask; } protected void setSlowSeconds( int slowSeconds ) { m_slowSeconds = slowSeconds; } protected void setSuspendSeconds( int suspendSeconds ) { m_suspendSeconds = suspendSeconds; } protected void setServiceName( String serviceName ) { m_serviceName = serviceName; } protected void setConsoleTitle( String consoleTitle ) { m_consoleTitle = consoleTitle; } protected void setChildParams( String childCommand, boolean childDetached ) { m_childCommand = childCommand; m_childDetached = childDetached; } protected void prepareSystemOutErr() { boolean streamsSet = false; if ( "true".equals( System.getProperty( "wrapper.use_sun_encoding" ) ) ) { String sunStdoutEncoding = System.getProperty( "sun.stdout.encoding" ); if ( ( sunStdoutEncoding != null ) && !sunStdoutEncoding.equals( System.getProperty( "file.encoding" ) ) ) { /* We need to create the stream using the same encoding as the one used for stdout, else this will lead to encoding issues. */ try { m_out = new DeadlockPrintStream( System.out, false, sunStdoutEncoding ); m_err = new DeadlockPrintStream( System.err, false, sunStdoutEncoding ); streamsSet = true; } catch ( UnsupportedEncodingException e ) { /* This should not happen because we always make sure the encoding exists before launching a JVM. * If any of the above streams failed, we want to fall back to streams that use the same encoding. * This message can be localized because we did not redirect the streams yet. */ System.out.println( Main.getRes().getString( "Failed to set the encoding ''{0}'' when creating a DeadlockPrintStream.\n Make sure the value of sun.stdout.encoding is correct.", sunStdoutEncoding ) ); } } } if ( !streamsSet ) { m_out = new DeadlockPrintStream( System.out ); m_err = new DeadlockPrintStream( System.err ); } System.setOut( m_out ); System.setErr( m_err ); } protected void updateEventListener() { WrapperManager.removeWrapperEventListener( this ); WrapperManager.addWrapperEventListener( this, m_eventMask ); } protected boolean doAction( String action ) { if ( action.equals( "stop0" ) ) { WrapperManager.stop( 0 ); } else if ( action.equals( "stop1" ) ) { WrapperManager.stop( 1 ); } else if ( action.equals( "exit0" ) ) { System.exit( 0 ); } else if ( action.equals( "exit1" ) ) { System.exit( 1 ); } else if ( action.equals( "nestedexit1" ) ) { m_nestedExit = true; WrapperManager.stop( 1 ); } else if ( action.equals( "stopimmediate0" ) ) { WrapperManager.stopImmediate( 0 ); } else if ( action.equals( "stopimmediate1" ) ) { WrapperManager.stopImmediate( 1 ); } else if ( action.equals( "stopandreturn0" ) ) { WrapperManager.stopAndReturn( 0 ); } else if ( action.equals( "halt0" ) ) { Runtime.getRuntime().halt( 0 ); } else if ( action.equals( "halt1" ) ) { Runtime.getRuntime().halt( 1 ); } else if ( action.equals( "restart" ) ) { WrapperManager.restart(); } else if ( action.equals( "restartandreturn" ) ) { WrapperManager.restartAndReturn(); } else if ( action.equals( "access_violation" ) ) { // The bug we used to cause this is not in most modern VMs so this is not shown by default. WrapperManager.accessViolation(); } else if ( action.equals( "access_violation_native" ) ) { WrapperManager.accessViolationNative(); } else if ( action.equals( "exception_native" ) ) { WrapperManager.raiseExceptionNative( 0x12345678 ); } else if ( action.equals( "ff_exception_native" ) ) { WrapperManager.raiseFailFastExceptionNative(); } else if ( action.equals( "appear_hung" ) ) { WrapperManager.appearHung(); } else if ( action.equals( "appear_slow" ) ) { WrapperManager.appearSlow( m_slowSeconds ); } else if ( action.equals( "appear_slow_1" ) ) { WrapperManager.appearSlow( 1 ); } else if ( action.equals( "appear_slow_5" ) ) { WrapperManager.appearSlow( 5 ); } else if ( action.equals( "appear_slow_reset" ) ) { WrapperManager.appearSlow( 0 ); } else if ( action.equals( "deadlock" ) ) { if ( WrapperManager.isStandardEdition() ) { System.out.println( Main.getRes().getString( "Creating a 2-object deadlock...") ); DeadLockBase.create2ObjectDeadlock( false, false ); } else { System.out.println( Main.getRes().getString( "Deadlock checks require the Standard Edition.") ); } } else if ( action.equals( "outofmemory" ) ) { throw new OutOfMemoryError(); } else if ( action.equals( "ignore_events" ) ) { m_ignoreControlEvents = true; } else if ( action.equals( "dump" ) ) { WrapperManager.requestThreadDump(); } else if ( action.equals( "deadlock_out" ) ) { System.out.println( Main.getRes().getString( "Deadlocking System.out and System.err..." ) ); m_out.setDeadlock( true ); m_err.setDeadlock( true ); } else if ( action.equals( "suspend_timeouts" ) ) { if ( WrapperManager.isStandardEdition() ) { WrapperManager.suspendTimeouts( m_suspendSeconds ); } else { System.out.println( Main.getRes().getString( "Suspending/Resuming timeouts requires the Standard Edition.") ); } } else if ( action.equals( "suspend_timeouts_10" ) ) { if ( WrapperManager.isStandardEdition() ) { WrapperManager.suspendTimeouts( 10 ); } else { System.out.println( Main.getRes().getString( "Suspending/Resuming timeouts requires the Standard Edition.") ); } } else if ( action.equals( "suspend_timeouts_60" ) ) { if ( WrapperManager.isStandardEdition() ) { WrapperManager.suspendTimeouts( 60 ); } else { System.out.println( Main.getRes().getString( "Suspending/Resuming timeouts requires the Standard Edition.") ); } } else if ( action.equals( "resume_timeouts" ) ) { if ( WrapperManager.isStandardEdition() ) { WrapperManager.resumeTimeouts(); } else { System.out.println( Main.getRes().getString( "Suspending/Resuming timeouts requires the Standard Edition.") ); } } else if ( action.equals( "users" ) ) { if ( !m_users ) { System.out.println( Main.getRes().getString( "Begin polling the current and interactive users." ) ); m_users = true; } else if ( m_groups ) { System.out.println( Main.getRes().getString("Stop polling for group info." ) ); m_groups = false; } else { System.out.println( Main.getRes().getString("Stop polling the current and interactive users." ) ); m_users = false; } synchronized( AbstractActionApp.class ) { AbstractActionApp.class.notifyAll(); } } else if ( action.equals( "groups" ) ) { if ( ( !m_users ) || ( !m_groups ) ) { System.out.println( Main.getRes().getString( "Begin polling the current and interactive users with group info." ) ); m_users = true; m_groups = true; } else { System.out.println( Main.getRes().getString( "Stop polling for group info." ) ); m_groups = false; } synchronized( AbstractActionApp.class ) { AbstractActionApp.class.notifyAll(); } } else if ( action.equals( "console" ) ) { if ( m_consoleRunner == null ) { m_consoleRunner = new Thread( "console-runner" ) { public void run() { System.out.println( Main.getRes().getString( "Start prompting for actions." ) ); try { BufferedReader r = new BufferedReader(new InputStreamReader(System.in)); String line; try { do { System.out.println( Main.getRes().getString( "Input an action (''help'' for a list of actions):") ); int count = 0; int maxTries = 10; while( true ) { /* On some old JVMs, we may get an "Interrupted system call" exception if a child process is writing some output while tying to read from here. * It would most likely work if we try again though. */ try { line = r.readLine(); break; } catch ( IOException e ) { if ( ( e.toString().indexOf( "Interrupted system call" ) == -1 ) || ( ++count == maxTries ) ) { throw e; } } } if ( line != null ) { if ( !line.equals("") ) { System.out.println(Main.getRes().getString( "Read action: {0}", line ) ); if ( !doAction( line ) ) { if ( !line.equals( "help" ) ) { System.out.println( Main.getRes().getString( "Unknown action: {0}", line ) ); } printActions(); } } else { System.out.println(Main.getRes().getString( "Read action: " ) ); } } else { // EOF System.out.println(Main.getRes().getString( "Stdin was closed." ) ); break; } } while ( true ); } catch ( IOException e ) { e.printStackTrace(); } } finally { System.out.println( Main.getRes().getString( "Stop prompting for actions." ) ); System.out.println(); m_consoleRunner = null; } } }; m_consoleRunner.setDaemon( true ); m_consoleRunner.start(); } } else if ( action.equals( "idle" ) ) { System.out.println( Main.getRes().getString( "Run idle." ) ); m_users = false; m_groups = false; synchronized( AbstractActionApp.class ) { AbstractActionApp.class.notifyAll(); } } else if ( action.equals( "properties" ) ) { System.out.println( Main.getRes().getString( "Dump System Properties:" ) ); Properties props = System.getProperties(); for ( Enumeration en = props.propertyNames(); en.hasMoreElements(); ) { String name = (String)en.nextElement(); System.out.println( " " + name + "=" + props.getProperty( name ) ); } System.out.println(); } else if ( action.equals( "configuration" ) ) { System.out.println( Main.getRes().getString( "Dump Wrapper Properties:" ) ); Properties props = WrapperManager.getProperties(); for ( Enumeration en = props.propertyNames(); en.hasMoreElements(); ) { String name = (String)en.nextElement(); System.out.println( " " + name + "=" + props.getProperty( name ) ); } System.out.println(); } else if ( action.equals( "listener" ) ) { System.out.println( Main.getRes().getString( "Updating Event Listeners:" ) ); updateEventListener(); } else if ( action.equals( "service_list" ) ) { /* for ( int i = 0; i < 1000; i++ ) { WrapperWin32Service[] services = WrapperManager.listServices(); } */ WrapperWin32Service[] services = WrapperManager.listServices(); if ( services == null ) { System.out.println( Main.getRes().getString( "Services not supported by current platform." ) ); } else { System.out.println( Main.getRes().getString( "Registered Services:" ) ); for ( int i = 0; i < services.length; i++ ) { System.out.println( " " + services[i] ); } } } else if ( action.equals( "service_interrogate" ) ) { try { /* for ( int i = 0; i < 10000; i++ ) { WrapperWin32Service service = WrapperManager.sendServiceControlCode( m_serviceName, WrapperManager.SERVICE_CONTROL_CODE_INTERROGATE ); } */ WrapperWin32Service service = WrapperManager.sendServiceControlCode( m_serviceName, WrapperManager.SERVICE_CONTROL_CODE_INTERROGATE ); System.out.println( Main.getRes().getString( "Service after interrogate: {0}", service ) ); } catch ( WrapperServiceException e ) { e.printStackTrace(); } } else if ( action.equals( "service_start" ) ) { try { WrapperWin32Service service = WrapperManager.sendServiceControlCode( m_serviceName, WrapperManager.SERVICE_CONTROL_CODE_START ); System.out.println( Main.getRes().getString( "Service after start: {0}", service ) ); } catch ( WrapperServiceException e ) { e.printStackTrace(); } } else if ( action.equals( "service_stop" ) ) { try { WrapperWin32Service service = WrapperManager.sendServiceControlCode( m_serviceName, WrapperManager.SERVICE_CONTROL_CODE_STOP ); System.out.println( Main.getRes().getString( "Service after stop: {0}", service ) ); } catch ( WrapperServiceException e ) { e.printStackTrace(); } } else if ( action.equals( "service_user" ) ) { try { for ( int i = 128; i < 256; i+=10 ) { WrapperWin32Service service = WrapperManager.sendServiceControlCode( m_serviceName, i ); System.out.println( Main.getRes().getString( "Service after user code {0} : {1}", new Integer( i ), service ) ); } } catch ( WrapperServiceException e ) { e.printStackTrace(); } } else if ( action.equals( "console_title" ) ) { if ( !WrapperManager.isWindows() ) { System.out.println( Main.getRes().getString( "Setting the console title not supported on UNIX platforms." ) ); // The call is fine but it doesn't do anything. } WrapperManager.setConsoleTitle( m_consoleTitle ); } else if ( action.equals( "child_exec" ) ) { // This command is really meant only to be used by the GUI but can also be called from the console if known. doExec( m_childCommand, m_childDetached ); } else if ( action.equals( "gc" ) ) { System.out.println( Main.getRes().getString( "Begin GC..." ) ); System.gc(); System.out.println( Main.getRes().getString( "GC complete." ) ); } else if ( action.equals( "is_professional" ) ) { System.out.println( Main.getRes().getString( "Professional Edition: " ) + WrapperManager.isProfessionalEdition() ); } else if ( action.equals( "is_standard" ) ) { System.out.println( Main.getRes().getString( "Standard Edition: " ) + WrapperManager.isStandardEdition() ); } else if ( action.equals( "current_time" ) ) { SimpleDateFormat df = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss.SSS" ); System.out.println( df.format( new Date() ) + " - " + System.getProperty( "user.timezone" ) ); } else if ( action.startsWith( "exec " ) && ( action.length() > 5 ) ) { String command = action.substring( 5 ); doExec( command, false ); } else if ( action.startsWith( "exec_detached " ) && ( action.length() > 14 ) ) { String command = action.substring( 14 ); doExec( command, true ); } else { // Unknown action return false; } return true; } private static Thread handleInputStream( final InputStream is, final String encoding, String pid, String pipeName ) { final String label = Main.getRes().getString( " Process #{0} {1}", pid, pipeName ); Thread runner = new Thread( "exec_runner_" + pipeName + "_" + pid ) { public void run() { BufferedReader br; String line; try { br = new BufferedReader( new InputStreamReader( is, encoding ) ); try { while ( ( line = br.readLine() ) != null ) { System.out.println( label + ": " + line ); } } finally { br.close(); } } catch ( IOException e ) { e.printStackTrace(); } } }; runner.start(); return runner; } private void doExec( String command, boolean detached ) { try { WrapperProcessConfig processConfig = new WrapperProcessConfig(); processConfig.setDetached( detached ); String type; if ( command.startsWith( "FORK_EXEC " ) ) { processConfig.setStartType( WrapperProcessConfig.FORK_EXEC ); type = "FORK_EXEC"; command = command.substring( 10 ); } else if ( command.startsWith( "POSIX_SPAWN " ) ) { processConfig.setStartType( WrapperProcessConfig.POSIX_SPAWN ); type = "POSIX_SPAWN"; command = command.substring( 12 ); } else if ( command.startsWith( "VFORK_EXEC " ) ) { processConfig.setStartType( WrapperProcessConfig.VFORK_EXEC ); type = "VFORK_EXEC"; command = command.substring( 11 ); } else if ( command.startsWith( "DYNAMIC " ) ) { processConfig.setStartType( WrapperProcessConfig.DYNAMIC ); type = "DYNAMIC"; command = command.substring( 8 ); } else { type = "DYNAMIC"; } if ( detached ) { System.out.println( Main.getRes().getString( "Execute Detached Child Process with type {0}: {1}", type, command ) ); } else { System.out.println( Main.getRes().getString( "Execute Managed Child Process: with type {0}: {1}", type, command ) ); } final WrapperProcess process = WrapperManager.exec( command, processConfig ); final String pid = Integer.toString( process.getPID() ); System.out.println( Main.getRes().getString( " Process #{0} launched.", pid ) ); final Thread outRunner = handleInputStream( process.getInputStream(), c_encoding, pid, "stdout" ); final Thread errRunner = handleInputStream( process.getErrorStream(), c_encoding, pid, "stderr" ); Thread runner = new Thread( "exec_runner_process_" + pid ) { public void run() { try { // Wait for the stdout and stderr reader threads to complete before we say the process completed to avoid confusion. outRunner.join(); errRunner.join(); System.out.println( Main.getRes().getString( " Process #{0} terminated with exitCode={1}", pid, Integer.toString( process.waitFor() ) ) ); } catch ( Throwable t ) { System.out.println( Main.getRes().getString( " Process #{0} unexpected error: {1} ", pid, t.getMessage() ) ); t.printStackTrace(); } } }; runner.start(); } catch ( Throwable t ) { System.out.println( Main.getRes().getString( "Failed to launch child process: {0}", t.getMessage() ) ); t.printStackTrace(); } } /*--------------------------------------------------------------- * Static Methods *-------------------------------------------------------------*/ protected static void printActions() { System.err.println( "" ); System.err.println( Main.getRes().getString( "[ACTIONS]" ) ); System.err.println( Main.getRes().getString( " help : Shows this help message" ) ); System.err.println( Main.getRes().getString( " Actions which should cause the Wrapper to exit cleanly:" ) ); System.err.println( Main.getRes().getString( " stop0 : Calls WrapperManager.stop(0)" ) ); System.err.println( Main.getRes().getString( " exit0 : Calls System.exit(0)" ) ); System.err.println( Main.getRes().getString( " stopimmediate0 : Calls WrapperManager.stopImmediate(0)" ) ); System.err.println( Main.getRes().getString( " stopandreturn0 : Calls WrapperManager.stopAndReturn(0)" ) ); System.err.println( Main.getRes().getString( " Actions which should cause the Wrapper to exit in an error state:" ) ); System.err.println( Main.getRes().getString( " stop1 : Calls WrapperManager.stop(1)" ) ); System.err.println( Main.getRes().getString( " exit1 : Calls System.exit(1)" ) ); System.err.println( Main.getRes().getString( " nestedexit1 : Calls System.exit(1) within WrapperListener.stop(1) callback" ) ); System.err.println( Main.getRes().getString( " stopimmediate1 : Calls WrapperManager.stopImmediate(1)" ) ); System.err.println( Main.getRes().getString( " Actions which should cause the Wrapper to restart the JVM:" ) ); System.err.println( Main.getRes().getString( " access_violation_native : Calls WrapperManager.accessViolationNative()" ) ); if ( WrapperManager.isWindows() ) { System.err.println( Main.getRes().getString( " exception_native : Calls WrapperManager.raiseExceptionNative()" ) ); System.err.println( Main.getRes().getString( " ff_exception_native : Calls WrapperManager.raiseFailFastExceptionNative()" ) ); } System.err.println( Main.getRes().getString( " appear_hung : Calls WrapperManager.appearHung()" ) ); System.err.println( Main.getRes().getString( " halt0 : Calls Runtime.getRuntime().halt(0)" ) ); System.err.println( Main.getRes().getString( " halt1 : Calls Runtime.getRuntime().halt(1)" ) ); System.err.println( Main.getRes().getString( " restart : Calls WrapperManager.restart()" ) ); System.err.println( Main.getRes().getString( " restartandreturn : Calls WrapperManager.restartAndReturn()" ) ); System.err.println( Main.getRes().getString( " Additional Tests:" ) ); System.err.println( Main.getRes().getString( " appear_slow_1 : Calls WrapperManager.appearSlow(1)" ) ); System.err.println( Main.getRes().getString( " appear_slow_5 : Calls WrapperManager.appearSlow(5)" ) ); System.err.println( Main.getRes().getString( " appear_slow_reset : Calls WrapperManager.appearSlow(0) to return to normal" ) ); System.err.println( Main.getRes().getString( " deadlock : Executes some deadlocking code to test deadlock detection" ) ); System.err.println( Main.getRes().getString( " outofmemory : Simulates an OutOfMemoryError being thrown" ) ); System.err.println( Main.getRes().getString( " ignore_events : Makes this application ignore control events." ) ); System.err.println( Main.getRes().getString( " dump : Calls WrapperManager.requestThreadDump()" ) ); System.err.println( Main.getRes().getString( " deadlock_out : Deadlocks the JVM''s System.out and err streams." ) ); System.err.println( Main.getRes().getString( " suspend_timeouts_10 : Calls WrapperManager.suspendTimeouts(10) (Standard, Professional)" ) ); System.err.println( Main.getRes().getString( " suspend_timeouts_60 : Calls WrapperManager.suspendTimeouts(60) (Standard, Professional)" ) ); System.err.println( Main.getRes().getString( " resume_timeouts : Calls WrapperManager.resumeTimeouts() (Standard, Professional)" ) ); System.err.println( Main.getRes().getString( " users : Start polling the current and interactive users." ) ); System.err.println( Main.getRes().getString( " groups : Start polling the current and interactive users with groups." ) ); System.err.println( Main.getRes().getString( " console : Prompt for actions in the console." ) ); System.err.println( Main.getRes().getString( " idle : Do nothing just run in idle mode." ) ); System.err.println( Main.getRes().getString( " properties : Dump all System Properties to the console." ) ); System.err.println( Main.getRes().getString( " configuration : Dump all Wrapper Configuration Properties to the console." ) ); System.err.println( Main.getRes().getString( " gc : Perform a GC sweep." ) ); System.err.println( Main.getRes().getString( " is_professional : Displays whether or not this is a Professional Edition Wrapper." ) ); System.err.println( Main.getRes().getString( " is_standard : Displays whether or not this is at least a Standard Edition Wrapper." ) ); System.err.println( Main.getRes().getString( " current_time : Prints the current time and timezone." ) ); if ( WrapperManager.isProfessionalEdition() ) { System.err.println( Main.getRes().getString( " exec : Executes a managed child process." ) ); System.err.println( Main.getRes().getString( " exec_detached : Executes a detached child process." ) ); } System.err.println( "" ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/BackgroundThreads.java100644 0 0 6070 14333053652 25615 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class BackgroundThreads implements Runnable { private static boolean m_started = false; /*--------------------------------------------------------------- * Runnable Method *-------------------------------------------------------------*/ public void run() { m_started = true; while(true) { System.out.println(Main.getRes().getString( "{0} running", Thread.currentThread().getName() ) ); try { Thread.sleep(500); } catch (InterruptedException e) { } } } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { System.out.println(Main.getRes().getString( "Background Thread Test Running..." ) ); System.out.println(Main.getRes().getString( "Launching background non-daemon threads...") ); BackgroundThreads app = new BackgroundThreads(); for (int i = 0; i < 2; i++) { Thread thread = new Thread(app, "App-Thread-" + i); thread.start(); } // Wait for at least one of the daemon threads to start to make sure // this main method does not exit prematurely and trigger a JVM // shutdown. while ( !m_started ) { try { Thread.sleep( 10 ); } catch ( InterruptedException e ) { // Ignore. } } System.out.println( Main.getRes().getString( "The JVM should now continue to run indefinitely.") ); System.out.println(Main.getRes().getString( "Background Thread Test Main Done..." ) ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/ChildWrapper.java100644 0 0 20354 14333053652 24630 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.WrapperJNIError; import org.tanukisoftware.wrapper.WrapperLicenseError; import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.WrapperProcess; import org.tanukisoftware.wrapper.WrapperProcessConfig; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Random; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class ChildWrapper { private static String c_encoding; /*--------------------------------------------------------------- * Static Methods *-------------------------------------------------------------*/ static { // In order to read the output from some processes correctly we need to get the correct encoding. // On some systems, the underlying system encoding is different than the file encoding. c_encoding = System.getProperty( "sun.jnu.encoding" ); if ( c_encoding == null ) { c_encoding = System.getProperty( "file.encoding" ); if ( c_encoding == null ) { // Default to Latin1 c_encoding = "Cp1252"; } } } private static Thread outputThread( final String context, final Object process, final InputStream is, final String name ) throws IOException { final BufferedReader br = new BufferedReader( new InputStreamReader( is, c_encoding ) ); Thread thread = new Thread( context + "-" + name ) { public void run() { try { try { try { String line; while ( ( line = br.readLine() ) != null ) { System.out.println( name + ": " + line ); } } finally { br.close(); } } catch ( IOException e ) { System.out.println( name + ": Error" ); e.printStackTrace(); } } finally { System.out.println( name + ": End" ); } } }; thread.start(); return thread; } private static void joinThread( Thread thread ) { if ( thread != null ) { try { thread.join(); } catch ( InterruptedException e ) { } } } private static void handleJavaProcess( String command ) throws IOException, InterruptedException { System.out.println( "Test Begin: " + command ); Thread stderrThread; Thread stdoutThread; Process process = Runtime.getRuntime().exec( command ); try { // Dump all stdout stdoutThread = outputThread( "handleJavaProcess", process, process.getInputStream(), "stdout" ); // Dump all stderr stderrThread = outputThread( "handleJavaProcess", process, process.getErrorStream(), "stderr" ); } finally { int exitCode = process.waitFor(); System.out.println( "exitCode: " + exitCode ); } // Wait for the stdout, stderr threads to complete. joinThread( stdoutThread ); joinThread( stderrThread ); System.out.println( "Test End" ); System.out.println(); System.out.println(); } private static void handleWrapperProcess( String command ) throws IOException, InterruptedException { System.out.println( "Test Begin: " + command ); Thread stderrThread; Thread stdoutThread; final WrapperProcess process = WrapperManager.exec( command ); try { // Dump all stdout stdoutThread = outputThread( "handleJavaProcess", process, process.getInputStream(), "stdout" ); // Dump all stderr stderrThread = outputThread( "handleJavaProcess", process, process.getErrorStream(), "stderr" ); } finally { int exitCode = process.waitFor(); System.out.println( "exitCode: " + exitCode ); } // Wait for the stdout, stderr threads to complete. joinThread( stdoutThread ); joinThread( stderrThread ); System.out.println( "Test End" ); System.out.println(); System.out.println(); } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main( String[] args ) { System.out.println( "Communicate with child processes using encoding: " + c_encoding ); try { String wrapperCmdVersion; String wrapperCmdTestWrapper; if ( WrapperManager.isWindows() ) { wrapperCmdVersion = "..\\bin\\wrapper.exe -v"; wrapperCmdTestWrapper = "..\\bin\\wrapper.exe -c ..\\conf\\wrapper.conf -- exit0"; } else { wrapperCmdVersion = "../bin/wrapper -v"; wrapperCmdTestWrapper = "../bin/wrapper -c ../conf/wrapper.conf -- exit0"; } String batCmd = "cmd /c ..\\bin\\TestWrapper.bat exit0"; String batDirect = "..\\bin\\TestWrapper.bat exit0"; System.out.println( Main.getRes().getString( "Runtime.exec test (Version)." ) ); handleJavaProcess( wrapperCmdVersion ); System.out.println( Main.getRes().getString( "Runtime.exec test (TestWrapper)." ) ); handleJavaProcess( wrapperCmdTestWrapper ); System.out.println( Main.getRes().getString( "Runtime.exec test (SimpleWaiter)." ) ); handleJavaProcess( "../test/simplewaiter 99 3" ); System.out.println( Main.getRes().getString( "Runtime.exec test (SimpleWaiter Crash)." ) ); handleJavaProcess( "../test/simplewaiter -crash 99 3" ); if ( WrapperManager.isStandardEdition() ) { System.out.println( Main.getRes().getString( "WrapperManager.exec test (Version)." ) ); handleWrapperProcess( wrapperCmdVersion ); System.out.println( Main.getRes().getString( "WrapperManager.exec test (TestWrapper)." ) ); handleWrapperProcess( wrapperCmdTestWrapper ); System.out.println( Main.getRes().getString( "WrapperManager.exec test (SimpleWaiter)." ) ); handleWrapperProcess( "../test/simplewaiter 99 3" ); System.out.println( Main.getRes().getString( "WrapperManager.exec test (SimpleWaiter Crash)." ) ); handleWrapperProcess( "../test/simplewaiter -crash 99 3" ); } if ( WrapperManager.isWindows() ) { System.out.println( Main.getRes().getString( "Runtime.exec test (Bat with cmd)." ) ); handleJavaProcess( batCmd ); System.out.println( Main.getRes().getString( "Runtime.exec test (Bat direct)." ) ); handleJavaProcess( batDirect ); } } catch ( Exception e ) { e.printStackTrace(); } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/CommandMain.java100644 0 0 6341 14333053652 24407 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.WrapperManager; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class CommandMain { /*--------------------------------------------------------------- * Constructor *-------------------------------------------------------------*/ private CommandMain() { } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { if ( args.length < 1 ) { System.out.println( Main.getRes().getString( "Missing required command." ) ); System.exit( 1 ); return; } String command = args[0].toLowerCase(); if ( command.equals( "halt0" ) ) { System.out.println( Main.getRes().getString( "Calling: " ) + "Runtime.getRuntime().halt( 0 )" ); Runtime.getRuntime().halt( 0 ); return; } else if ( command.equals( "halt1" ) ) { System.out.println( Main.getRes().getString( "Calling: " ) + "Runtime.getRuntime().halt( 1 )" ); Runtime.getRuntime().halt( 1 ); return; } else if ( command.equals( "exit0" ) ) { System.out.println( Main.getRes().getString( "Calling: " ) + "System.exit( 0 )" ); System.exit( 0 ); return; } else if ( command.equals( "exit1" ) ) { System.out.println( Main.getRes().getString( "Calling: " ) + "System.exit( 1 )" ); System.exit( 1 ); return; } else if ( command.equals( "stop0" ) ) { System.out.println( Main.getRes().getString( "Calling: " ) + "WrapperManager.stop( 0 )" ); WrapperManager.stop( 0 ); return; } else if ( command.equals( "stop1" ) ) { System.out.println( Main.getRes().getString( "Calling: " ) + "WrapperManager.stop( 1 )" ); WrapperManager.stop( 1 ); return; } else if ( command.equals( "stopandreturn0" ) ) { System.out.println( Main.getRes().getString( "Calling: " ) + "WrapperManager.stopAndReturn( 0 )" ); WrapperManager.stopAndReturn( 0 ); return; } else if ( command.equals( "stopandreturn1" ) ) { System.out.println( Main.getRes().getString( "Calling: " ) + "WrapperManager.stopAndReturn( 1 )" ); WrapperManager.stopAndReturn( 1 ); return; } else { System.out.println( Main.getRes().getString( "Unexpected command ''{0}''.", command ) ); System.exit( 1 ); return; } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/DaemonThreads.java100644 0 0 6070 14333053652 24741 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class DaemonThreads implements Runnable { private static boolean m_started = false; /*--------------------------------------------------------------- * Runnable Method *-------------------------------------------------------------*/ public void run() { m_started = true; while(true) { System.out.println( Main.getRes().getString( "{0} running", Thread.currentThread().getName() ) ); try { Thread.sleep(500); } catch (InterruptedException e) { } } } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { System.out.println( Main.getRes().getString( "Daemon Thread Test Running..." ) ); System.out.println( Main.getRes().getString( "Launching background daemon threads..." ) ); DaemonThreads app = new DaemonThreads(); for (int i = 0; i < 2; i++) { Thread thread = new Thread(app, "App-Thread-" + i); thread.setDaemon(true); thread.start(); } // Wait for at least one of the daemon threads to start to make sure // this main method does not exit prematurely and trigger a JVM // shutdown. while ( !m_started ) { try { Thread.sleep( 10 ); } catch ( InterruptedException e ) { // Ignore. } } System.out.println( Main.getRes().getString( "The JVM should exit momentarily.") ); System.out.println( Main.getRes().getString( "Daemon Thread Test Main Done...") ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/DeadLockBase.java100644 0 0 16046 14333053652 24510 0ustar 0 0 package org.tanukisoftware.wrapper.test; import java.lang.reflect.InvocationTargetException; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * This is test is designed to simulate various deadlock cases using either * ReentrantLock instances or synchronized objects. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class DeadLockBase { private Integer m_id; private Object m_lock1; private Object m_lock2; private static Class rentrantLockClass; static { try { rentrantLockClass = Class.forName( "java.util.concurrent.locks.ReentrantLock" ); } catch ( ClassNotFoundException e ) { e.printStackTrace(); } } private DeadLockBase( int id, Object lock1, Object lock2 ) { m_id = new Integer( id ); m_lock1 = lock1; m_lock2 = lock2; Thread runner = new Thread( "Locker-" + m_id ) { public void run() { System.out.println( Main.getRes().getString( "Locker-{0}: Started", m_id ) ); try { firstLock(); } catch ( Throwable t ) { t.printStackTrace(); } System.out.println( Main.getRes().getString( "Locker-{0}: Complete", m_id ) ); } }; runner.start(); // Always sleep for a moment to try and keep the test cases running consistently. try { Thread.sleep( 50 ); } catch ( InterruptedException e ) { } } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ private void secondLocked() { System.out.println( Main.getRes().getString( "Locker-{0}: Oops! Locked {1}", m_id, m_lock2.toString() ) ); } private void secondLock() { System.out.println( Main.getRes().getString( "Locker-{0}: Try locking {1}...", m_id, m_lock2.toString() ) ); try { if ( rentrantLockClass.isInstance( m_lock2 ) ) { Object lock2 = m_lock2; rentrantLockClass.getMethod( "lock", (Class[])null ).invoke( lock2, (Object[])null ); try { secondLocked(); } finally { rentrantLockClass.getMethod( "unlock", (Class[])null ).invoke( lock2, (Object[])null ); } } else { synchronized ( m_lock2 ) { secondLocked(); } } } catch ( NoSuchMethodException nsme ) { nsme.printStackTrace(); } catch ( IllegalArgumentException e ) { e.printStackTrace(); } catch ( SecurityException e ) { e.printStackTrace(); } catch ( IllegalAccessException e ) { e.printStackTrace(); } catch ( InvocationTargetException e ) { e.printStackTrace(); } } private void firstLocked() { System.out.println( Main.getRes().getString( "Locker-{0}: Locked {1}", m_id, m_lock1.toString() ) ); try { Thread.sleep( 2000 ); } catch ( InterruptedException e ) { } secondLock(); } private void firstLock() { System.out.println( Main.getRes().getString( "Locker-{0}: Locking {1}", m_id, m_lock1.toString() ) ); if ( rentrantLockClass.isInstance( m_lock1 ) ) { try { Object lock1 = m_lock1; rentrantLockClass.getMethod( "lock", (Class[])null ).invoke( lock1, (Object[])null ); try { firstLocked(); } finally { rentrantLockClass.getMethod( "unlock", (Class[])null ).invoke( lock1, ( Object[] ) null ); } } catch ( NoSuchMethodException nsme ) { nsme.printStackTrace(); } catch ( IllegalArgumentException e ) { e.printStackTrace(); } catch ( SecurityException e ) { e.printStackTrace(); } catch ( IllegalAccessException e ) { e.printStackTrace(); } catch ( InvocationTargetException e ) { e.printStackTrace(); } } else { synchronized ( m_lock1 ) { firstLocked(); } } } /*--------------------------------------------------------------- * Static Methods *-------------------------------------------------------------*/ private static Object createLock( boolean reentrant ) { if ( reentrant ) { try { return rentrantLockClass.newInstance(); } catch ( InstantiationException e ) { e.printStackTrace(); } catch ( IllegalAccessException e ) { e.printStackTrace(); } return null; } else { return new Object(); } } public static void create2ObjectDeadlock( boolean reentrant1, boolean reentrant2 ) { Object lock1 = createLock( reentrant1 ); Object lock2 = createLock( reentrant2 ); new DeadLockBase( 1, lock1, lock2 ); new DeadLockBase( 2, lock2, lock1 ); } public static void create3ObjectDeadlock( boolean reentrant1, boolean reentrant2, boolean reentrant3 ) { Object lock1 = createLock( reentrant1 ); Object lock2 = createLock( reentrant2 ); Object lock3 = createLock( reentrant3 ); new DeadLockBase( 1, lock1, lock2 ); new DeadLockBase( 2, lock2, lock3 ); new DeadLockBase( 3, lock3, lock1 ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/DeadLockMixed.java100644 0 0 7154 14333053652 24664 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.WrapperManager; /** * This is test is designed to simulate a series of deadlock cases using * ReentrantLocks, which will can be detected and restarted by the Standard * Edition. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class DeadLockMixed { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main( String[] args ) { System.out.println( Main.getRes().getString( "Deadlock Tester Running..." ) ); int exitCode = WrapperManager.getJVMId(); switch ( exitCode ) { case 1: System.out.println( Main.getRes().getString( "2-object deadlock (owned, reentrant)." ) ); DeadLockBase.create2ObjectDeadlock( false, true ); break; case 2: System.out.println( Main.getRes().getString( "2-object deadlock (reentrant, owned)." ) ); DeadLockBase.create2ObjectDeadlock( true, false ); break; case 3: System.out.println( Main.getRes().getString( "3-object deadlock (owned, owned, reentrant)." ) ); DeadLockBase.create3ObjectDeadlock( false, false, true ); break; case 4: System.out.println( Main.getRes().getString( "3-object deadlock (owned, reentrant, owned)." ) ); DeadLockBase.create3ObjectDeadlock( false, true, false ); break; case 5: System.out.println( Main.getRes().getString( "3-object deadlock (reentrant, owned, owned)." ) ); DeadLockBase.create3ObjectDeadlock( true, false, false ); break; case 6: System.out.println( Main.getRes().getString( "3-object deadlock (reentrant, owned, reentrant)." ) ); DeadLockBase.create3ObjectDeadlock( true, false, true ); break; case 7: System.out.println( Main.getRes().getString( "3-object deadlock (reentrant, reentrant, owned)." ) ); DeadLockBase.create3ObjectDeadlock( true, true, false ); break; case 8: System.out.println( Main.getRes().getString( "3-object deadlock (owned, reentrant, reentrant)." ) ); DeadLockBase.create3ObjectDeadlock( false, true, true ); break; default: System.out.println( Main.getRes().getString( "Done." ) ); return; } // Always wait a couple seconds to make sure the above threads have time to start. try { Thread.sleep( 2000 ); } catch ( InterruptedException e ) { } System.out.println( Main.getRes().getString( "Threads Launched." ) ); // Always wait long enough for the deadlock to be detected. Expecting a 10 second interval. try { Thread.sleep( 10000 ); } catch ( InterruptedException e ) { } System.out.println( Main.getRes().getString( "Failed to detect a deadlock." ) ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/DeadLockObject.java100644 0 0 5157 14333053652 25025 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.WrapperManager; /** * This is test is designed to simulate a series of deadlock cases using standard * Object synchronization, which will can be detected and restarted by the Standard * Edition. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class DeadLockObject { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main( String[] args ) { System.out.println( Main.getRes().getString( "Deadlock Tester Running..." ) ); int exitCode = WrapperManager.getJVMId(); switch ( exitCode ) { case 1: System.out.println( Main.getRes().getString( "2-object deadlock." ) ); DeadLockBase.create2ObjectDeadlock( false, false ); break; case 2: System.out.println( Main.getRes().getString( "Wait then 2-object deadlock." ) ); try { Thread.sleep( 10000 ); } catch ( InterruptedException e ) { } DeadLockBase.create2ObjectDeadlock( false, false ); break; case 3: System.out.println( Main.getRes().getString( "3-object deadlock." ) ); DeadLockBase.create3ObjectDeadlock( false, false, false ); break; default: System.out.println( Main.getRes().getString( "Done." ) ); return; } // Always wait a couple seconds to make sure the above threads have time to start. try { Thread.sleep( 2000 ); } catch ( InterruptedException e ) { } System.out.println( Main.getRes().getString( "Threads Launched." ) ); // Always wait long enough for the deadlock to be detected. Expecting a 10 second interval. try { Thread.sleep( 10000 ); } catch ( InterruptedException e ) { } System.out.println( Main.getRes().getString( "Failed to detect a deadlock." ) ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/DeadLockReentrantLock.java100644 0 0 5136 14333053652 26367 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.WrapperManager; /** * This is test is designed to simulate a series of deadlock cases using * ReentrantLocks, which will can be detected and restarted by the Standard * Edition. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class DeadLockReentrantLock { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main( String[] args ) { System.out.println( Main.getRes().getString( "Deadlock Tester Running..." ) ); int exitCode = WrapperManager.getJVMId(); switch ( exitCode ) { case 1: System.out.println( Main.getRes().getString( "2-object deadlock." ) ); DeadLockBase.create2ObjectDeadlock( true, true ); break; case 2: System.out.println( Main.getRes().getString( "Wait then 2-object deadlock." ) ); try { Thread.sleep( 10000 ); } catch ( InterruptedException e ) { } DeadLockBase.create2ObjectDeadlock( true, true ); break; case 3: System.out.println( Main.getRes().getString( "3-object deadlock." ) ); DeadLockBase.create3ObjectDeadlock( true, true, true ); break; default: System.out.println( Main.getRes().getString( "Done." ) ); return; } // Always wait a couple seconds to make sure the above threads have time to start. try { Thread.sleep( 2000 ); } catch ( InterruptedException e ) { } System.out.println( Main.getRes().getString( "Threads Launched." ) ); // Always wait long enough for the deadlock to be detected. Expecting a 10 second interval. try { Thread.sleep( 10000 ); } catch ( InterruptedException e ) { } System.out.println( Main.getRes().getString( "Failed to detect a deadlock." ) ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/DeadlockPrintStream.java100644 0 0 10310 14333053652 26132 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.io.PrintStream; /** * A print stream which can be put into a state in which all calls to write * to it will result in the calling threads deadlocking in the call. * Obviously, this class will not be useful to many as it is for tests. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class DeadlockPrintStream extends PrintStream { /** The Wrapped PrintStream. */ private PrintStream m_out; /** True if calling threads should be deadlocked. */ private boolean m_deadlock; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ /** * Creates a new DeadlockPrintStream wrapped around another PrintStream. * * @param out The PrintStream which will be wrapped by this new stream. */ public DeadlockPrintStream( PrintStream out ) { super( out ); m_out = out; } /** * Creates a new DeadlockPrintStream wrapped around another PrintStream. * * @param out The PrintStream which will be wrapped by this new stream. * @param autoFlush Whether the output buffer should be automatically flushed or not. ill be passed to the parent stream. * @param encoding The name of a supported character encoding. Will be passed to the parent stream. * * @throws UnsupportedEncodingException If the named encoding is not supported. */ public DeadlockPrintStream( PrintStream out, boolean autoFlush, String encoding ) throws UnsupportedEncodingException { super( out, autoFlush, encoding ); m_out = out; } /*--------------------------------------------------------------- * PrintStream Methods *-------------------------------------------------------------*/ public void write( int b ) { deadlock(); m_out.write( b ); } public void write( byte[] b ) throws IOException { deadlock(); m_out.write( b ); } public void write( byte[] b, int off, int len ) { deadlock(); m_out.write( b, off, len ); } public void flush() { deadlock(); m_out.flush(); } public void close() { deadlock(); m_out.close(); } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * This call will not return as long as the m_deadLock flag is set. * If it is ever cleared with a call to setDeadlock(), stuck threads * will all be released. */ private void deadlock() { if ( m_deadlock ) { synchronized( this ) { while( m_deadlock ) { try { this.wait(); } catch ( InterruptedException e ) { // Ignore } } } } } /** * Sets or clears the deadlock flag. If set, calls to any other method * of this class will result in the calling thread being deadlocked. * They will be released if the flag is cleared with this method. * * @param deadlock True to set the flag, false to clear it. */ public void setDeadlock( boolean deadlock ) { m_deadlock = deadlock; if ( !m_deadlock ) { synchronized( this ) { // Release any threads that are waiting. this.notifyAll(); } } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/DelayedIORestarter.java100644 0 0 5457 14333053652 25726 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.io.IOException; import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.WrapperPropertyUtil; /** * This test is designed to make sure the Wrapper handles the case where the * JVM is restarted while the Wrapper is blocking due to a long disk IO queue. * Prior to 3.5.11, the Wrapper would sometimes exit before it noticed that * the JVM had wanted to restart. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class DelayedIORestarter { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { // Let everything start up correctly. TestUtils.sleep( 2000 ); int pauseDelay; boolean restart; switch ( WrapperManager.getJVMId() ) { case 1: pauseDelay = 1; restart = true; break; case 2: pauseDelay = 5; restart = true; break; case 3: pauseDelay = 10; restart = true; break; default: pauseDelay = 10; restart = false; break; } System.out.println( Main.getRes().getString( "Asking Wrapper to pause logger for {0} seconds.", new Integer( pauseDelay ) ) ); try { TestUtils.writeWrapperTestCommand( "PAUSE_LOGGER " + pauseDelay ); } catch ( IOException e ) { e.printStackTrace(); } int pollInterval = WrapperPropertyUtil.getIntProperty( "wrapper.commandfile.poll_interval", 5 ) + 1; if ( restart ) { System.out.println( Main.getRes().getString( "Restart JVM after {0} second delay...", new Integer( pollInterval ) ) ); } else { System.out.println( Main.getRes().getString( "Stop JVM after {0} second delay...", new Integer( pollInterval ) ) ); } // Give the Wrapper time to notice the command file. TestUtils.sleep( pollInterval * 1000 ); if ( restart ) { WrapperManager.restart(); } else { WrapperManager.stop( 0 ); } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/EnvironmentVariables.java100644 0 0 12117 14333053652 26377 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ import java.util.Properties; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.IOException; import org.tanukisoftware.wrapper.WrapperManager; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class EnvironmentVariables { private static Properties _env = null; /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { System.out.println("user.language=" + System.getProperty("user.language")); System.out.println("user.region=" + System.getProperty("user.region")); System.out.println(Main.getRes().getString( "Locale=" ) + java.util.Locale.getDefault()); System.out.println(Main.getRes().getString( "Looking for environment variables..." ) ); try { getEnvironmentVariables(); } catch (IOException e) { System.out.println(e.getMessage()); } boolean passed = false; if (_env != null) { System.out.println(); passed = check("ENV_VAR_1", "a"); passed = check("ENV_VAR_2", "b"); passed = check("ENV_VAR_3", "c"); passed = check("ENV_VAR_4", "d"); System.out.println(); } if (passed) { System.out.println( Main.getRes().getString( "Environment variables test passed." ) ); } else { System.out.println( Main.getRes().getString( "Environment variables test FAILED." ) ); } System.out.println( Main.getRes().getString( "Request a JVM restart." ) ); WrapperManager.restart(); } private static boolean check(String variable, String expected) { String actual = _env.getProperty(variable); System.out.print(variable + " = " + actual + ": "); if (expected.equals(actual)) { System.out.println("OK"); return true; } System.out.println(Main.getRes().getString( "FAILED (expected: {0})", expected ) ); return false; } private static void getEnvironmentVariables() throws IOException { String os = System.getProperty("os.name").toLowerCase(); System.out.println(Main.getRes().getString( "Platform is {0}.", os ) ); Process p = null; if (os.indexOf("windows 9") > -1) { p = Runtime.getRuntime().exec("command.com /c set"); } else if (os.indexOf("unix") > -1) { p = Runtime.getRuntime().exec("/bin/env"); } else if ((os.indexOf("nt") > -1) || (os.indexOf("windows 2000") > -1) || (os.indexOf("windows xp") > -1) || (os.indexOf("windows 2003") > -1) ) { p = Runtime.getRuntime().exec("cmd.exe /c set"); } else if (os.indexOf("unix") > -1) { p = Runtime.getRuntime().exec("/bin/env"); } else if ((os.indexOf("linux") > -1) || (os.indexOf("mac os x") > -1) || (os.indexOf("freebsd") > -1)) { p = Runtime.getRuntime().exec("/usr/bin/env"); } if (p == null) { System.out.println(Main.getRes().getString( "Don''t know how to read environment variables on this platform: {0}", os ) ); return; } _env = new Properties(); BufferedReader br=new BufferedReader(new InputStreamReader(p.getInputStream())); String line = null; while ((line = br.readLine()) != null) { int idx = line.indexOf('='); if (idx > -1) { String key = line.substring(0, idx); String value = line.substring(idx + 1); _env.setProperty(key, value); } } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/ExecParallel.java100644 0 0 17613 14333053652 24611 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ // import java.lang.management.ThreadMXBean; // Require Java 5+ // import java.lang.management.ManagementFactory; // Require Java 5+ import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.WrapperProcessConfig; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class ExecParallel { /** The time that we ask the child process to run before exiting on its own. */ private static String CMD; private static String EXECUTABLE; private static final int CHILD_SLEEP_TIME_S = 4; private static final long CHILD_SLEEP_TIME_MS = CHILD_SLEEP_TIME_S * 1000L; // private static ThreadMXBean mbean; // Require Java 5+ // private static volatile long cpuTime; // Require Java 5+ private static String[] testIds = new String[6]; private static long[] totalSysTime = new long[6]; // private static long[] totalCpuTime = new long[6]; // Require Java 5+ private static int seriesIndex = 0; static { if ( WrapperManager.isWindows() ) { EXECUTABLE = "..\\test\\simplewaiter.exe"; CMD = "CMD"; } else { EXECUTABLE = "../test/simplewaiter"; CMD = "Bash"; } // mbean = ManagementFactory.getThreadMXBean(); // Require Java 5+ } private static void doTestInner( String command, String testId, boolean newProcessGroup ) { WrapperProcessConfig wrapperProcessConfig = new WrapperProcessConfig().setNewProcessGroup( newProcessGroup ); try { RuntimeExec.handleWrapperProcess( testId, command, wrapperProcessConfig, 0, false, false, false, RuntimeExec.WAIT_MODE_API, 0, false ); } catch ( Exception e ) { e.printStackTrace(); } } private static void doTest( String testId, boolean newProcessGroup, boolean useCmd, boolean detached ) { if ( !useCmd ) { doTestInner( EXECUTABLE + " 0 " + CHILD_SLEEP_TIME_S, testId, newProcessGroup ); } else if ( !detached ) { if ( WrapperManager.isWindows() ) { doTestInner( "cmd /c \"" + EXECUTABLE + " 0 " + CHILD_SLEEP_TIME_S + "\"", testId, newProcessGroup ); } else { doTestInner( "bash -c \"" + EXECUTABLE + " 0 " + CHILD_SLEEP_TIME_S + "\"", testId, newProcessGroup ); } } else { if ( WrapperManager.isWindows() ) { doTestInner( "cmd /c \"start /B " + EXECUTABLE + " 0 " + CHILD_SLEEP_TIME_S + "\"", testId, newProcessGroup ); } else { doTestInner( "bash -c \"" + EXECUTABLE + " 0 " + CHILD_SLEEP_TIME_S + " &\"", testId, newProcessGroup ); } } } private static void doTestsParallel( final boolean newProcessGroup, final boolean useCmd, final boolean detached ) { String testIdBase; long expectedTime; if ( newProcessGroup ) { testIdBase = "NewProcessGroup|"; } else { testIdBase = ""; } if ( !useCmd ) { testIdBase += "simplewaiter "; expectedTime = CHILD_SLEEP_TIME_MS; } else if ( !detached ) { testIdBase += CMD + "+simplewaiter "; expectedTime = CHILD_SLEEP_TIME_MS; } else { testIdBase += CMD + "+simplewaiter(detached) "; expectedTime = newProcessGroup ? CHILD_SLEEP_TIME_MS : 0; } for (int i = 0; i < 10; i += 2) /* 1 4 16 64 256 */ { int n = (int)Math.pow( 2, i ); final String testId = testIdBase + "(" + n + " threads) :"; RuntimeExec.beginCase( testId ); Thread[] threads = new Thread[n]; // cpuTime = 0; // Require Java 5+ for (int j = 0; j < n; j++) { // mbean = ManagementFactory.getThreadMXBean(); // Require Java 5+ threads[j] = new Thread() { public void run() { doTest( testId, newProcessGroup, useCmd, detached ); // cpuTime += mbean.getThreadCpuTime( Thread.currentThread().getId() ); // Require Java 5+ } }; } long sysTime = System.currentTimeMillis(); for (int j = 0; j < n; j++) { threads[j].start(); } for (int j = 0; j < n; j++) { try { threads[j].join(); } catch ( InterruptedException e ) { } } sysTime = System.currentTimeMillis() - sysTime; // cpuTime /= 1000000; /* ns -> ms */ // Require Java 5+ // RuntimeExec.endCase( testId + " (System time: " + sysTime + "ms, CPU time: " + cpuTime + "ms)", expectedTime ); // Require Java 5+ RuntimeExec.endCase( testId + " (System time: " + sysTime + "ms)", expectedTime ); totalSysTime[seriesIndex] += sysTime; // totalCpuTime[seriesIndex] += cpuTime; // Require Java 5+ } testIds[seriesIndex] = testIdBase; seriesIndex++; } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main( String[] args ) { // Need to initialize the counts. RuntimeExec.c_testsPerformed = 0; RuntimeExec.c_testsFailed = 0; System.out.println( "" ); System.out.println( Main.getRes().getString( "Running test with wrapper.child.wait_signals=" + System.getProperty( "wrapper.child.wait_signals" ) ) ); System.out.println( Main.getRes().getString( "Running test with wrapper.child.wait_signals.max_threads=" + System.getProperty( "wrapper.child.wait_signals.max_threads" ) ) ); System.out.println( Main.getRes().getString( " You may change the values of these properties in the execparallel.conf." ) ); doTestsParallel( false, false, false ); doTestsParallel( false, true, false ); doTestsParallel( false, true, true ); doTestsParallel( true, false, false ); doTestsParallel( true, true, false ); doTestsParallel( true, true, true ); int nbTestsFailed = RuntimeExec.c_testsFailed; int nbTestsPassed = RuntimeExec.c_testsPerformed - nbTestsFailed; System.out.println( "" ); System.out.println( Main.getRes().getString( "[PASSED] {0}", Integer.toString( nbTestsPassed ) ) ); System.out.println( Main.getRes().getString( "[FAILED] {0}", Integer.toString( nbTestsFailed ) ) ); System.out.println( Main.getRes().getString( "--------------------" )); for (int i = 0; i < testIds.length; i++) { // System.out.println( Main.getRes().getString( "{0}: System time: {1}ms, CPU time: {2}ms", testIds[i], Long.toString( totalSysTime[i] ), Long.toString( totalCpuTime[i] ) ) ); // Require Java 5+ System.out.println( Main.getRes().getString( "{0}: System time: {1}ms", testIds[i], Long.toString( totalSysTime[i] ) ) ); } System.out.println( Main.getRes().getString( "--------------------" )); if ( nbTestsFailed > 0 ) { System.exit( 1 ); } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/ExecRead.java100644 0 0 17337 14333053652 23733 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.WrapperProcessConfig; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class ExecRead { private static boolean m_debug = false; /** The time that we ask the child process to run before exiting on its own. */ private static final int CHILD_SLEEP_TIME_S = 2; private static final long CHILD_SLEEP_TIME_MS = CHILD_SLEEP_TIME_S * 1000L; private static void doTest( String command, String testId, boolean readByteByteByByte, boolean newProcessGroup, boolean autoCloseInputStreams, long expectedTimeMs ) { RuntimeExec.beginCase( testId ); try { WrapperProcessConfig wrapperProcessConfig = new WrapperProcessConfig().setNewProcessGroup( newProcessGroup ).setAutoCloseInputStreams( autoCloseInputStreams ); try { RuntimeExec.handleWrapperProcess( testId, command, wrapperProcessConfig, 0, true, false, false, RuntimeExec.WAIT_MODE_MANUAL, 0, m_debug, readByteByteByByte ); } catch ( Exception e ) { e.printStackTrace(); } } finally { RuntimeExec.endCase( testId, expectedTimeMs ); } } private static void doTests( String executable, boolean testStdErr, boolean readByteByteByByte, boolean newProcessGroup, boolean autoCloseInputStreams ) { StringBuffer sb = new StringBuffer(); if ( autoCloseInputStreams ) { sb.append( "AutoClose" ); } else { sb.append( "Blocking " ); } sb.append( " " ); if ( newProcessGroup ) { sb.append( "NewProcessGroup" ); } else { sb.append( "SharedProcessGroup" ); } sb.append( " " ); if ( readByteByteByByte ) { sb.append( "Read " ); } else { sb.append( "Read2" ); } sb.append( " " ); String context = sb.toString(); String stdErrArg = (testStdErr ? "-messagetostderr " : ""); // launching simplewaiter only. Expected to read for CHILD_SLEEP_TIME_MS. doTest( executable + " -message \"hello tanukis!\" " + stdErrArg + "-messageinterval 250 0 " + CHILD_SLEEP_TIME_S, context + "simplewaiter : ", readByteByteByByte, newProcessGroup, autoCloseInputStreams, CHILD_SLEEP_TIME_MS ); if ( !WrapperManager.isWindows() ) { // launching bash as direct child and simplewaiter as a subchild. Bash will exit after simplewaiter completes. Expected to read for CHILD_SLEEP_TIME_MS. doTest( "bash -c \"" + executable + " -message hello\\ tanukis! " + stdErrArg + "-messageinterval 250 0 " + CHILD_SLEEP_TIME_S + "\"", context + "Bash+simplewaiter : ", readByteByteByByte, newProcessGroup, autoCloseInputStreams, CHILD_SLEEP_TIME_MS ); // launching bash as direct child and simplewaiter as a subchild. Bash will exit after simplewaiter completes. Expected to read for CHILD_SLEEP_TIME_MS. doTest( "bash -c \"export FOO=bar;" + executable + " -message hello\\ tanukis! " + stdErrArg + "-messageinterval 250 0 " + CHILD_SLEEP_TIME_S + "\"", context + "Bash+env+simplewaiter : ", readByteByteByByte, newProcessGroup, autoCloseInputStreams, CHILD_SLEEP_TIME_MS ); // launching bash as direct child and simplewaiter as a detached subchild. Bash will exit right away. Expected to stop reading almost immediately if we are in auto-close mode and if we are not managing sub-children as group members. doTest( "bash -c \"" + executable + " -message hello\\ tanukis! " + stdErrArg + "-messageinterval 250 0 " + CHILD_SLEEP_TIME_S + " &\"", context + "Bash+simplewaiter(detached) : ", readByteByteByByte, newProcessGroup, autoCloseInputStreams, ( ( autoCloseInputStreams || WrapperManager.isZOS() ) && !newProcessGroup ) ? 0 : CHILD_SLEEP_TIME_MS ); // launching bash as direct child and simplewaiter as a detached subchild. Bash will exit right away. Expected to stop reading almost immediately if we are in auto-close mode and if we are not managing sub-children as group members. doTest( "bash -c \"export FOO=bar;" + executable + " -message hello\\ tanukis! " + stdErrArg + "-messageinterval 250 0 " + CHILD_SLEEP_TIME_S + " &\"", context + "Bash+env+simplewaiter(detached) : ", readByteByteByByte, newProcessGroup, autoCloseInputStreams, ( ( autoCloseInputStreams || WrapperManager.isZOS() ) && !newProcessGroup ) ? 0 : CHILD_SLEEP_TIME_MS ); } } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main( String[] args ) { // Need to initialize the counts. RuntimeExec.c_testsPerformed = 0; RuntimeExec.c_testsFailed = 0; final String simplewaiter; if ( WrapperManager.isWindows() ) { simplewaiter = "..\\test\\simplewaiter.exe"; } else { simplewaiter = "../test/simplewaiter"; } if ( !WrapperManager.isWindows() ) { if ( ( args.length > 0 ) && "true".equals( args[0] ) ) { System.out.println( Main.getRes().getString( "DEBUG mode enabled." ) ); m_debug = true; } } System.out.println( "" ); System.out.println( Main.getRes().getString( "A bunch of tests will be run. Please check that:\n- Each of the tests that take ~2 secs to execute have standard output messages.\n- Tests that stop immediately should not have stdout messages.\nThe summary of results below only take into account the expected times of execution." ) ); /* test stdout */ doTests( simplewaiter, false, true, false, false ); doTests( simplewaiter, false, true, false, true ); doTests( simplewaiter, false, true, true, false ); doTests( simplewaiter, false, true, true, true ); doTests( simplewaiter, false, false, false, false ); doTests( simplewaiter, false, false, false, true ); doTests( simplewaiter, false, false, true, false ); doTests( simplewaiter, false, false, true, true ); /* test stderr */ doTests( simplewaiter, true, true, false, false ); doTests( simplewaiter, true, true, false, true ); doTests( simplewaiter, true, true, true, false ); doTests( simplewaiter, true, true, true, true ); doTests( simplewaiter, true, false, false, false ); doTests( simplewaiter, true, false, false, true ); doTests( simplewaiter, true, false, true, false ); doTests( simplewaiter, true, false, true, true ); int nbTestsFailed = RuntimeExec.c_testsFailed; int nbTestsPassed = RuntimeExec.c_testsPerformed - nbTestsFailed; System.out.println( "" ); System.out.println( Main.getRes().getString( "[PASSED] {0}", Integer.toString( nbTestsPassed ) ) ); System.out.println( Main.getRes().getString( "[FAILED] {0}", Integer.toString( nbTestsFailed ) ) ); if ( nbTestsFailed > 0 ) { System.exit( 1 ); } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/ExecSignals.java100644 0 0 51730 14333053652 24453 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.io.File; import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.WrapperProcessConfig; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class ExecSignals { private static void doTest( String command, String testId, long timeoutMS, boolean newProcessGroup, int startType, int expectedExitCode, long expectedTimeMs ) { RuntimeExec.beginCase( testId ); try { if ( !WrapperProcessConfig.isSupported( startType ) ) { System.out.println( Main.getRes().getString( "{0} startType {1} not supported", testId, Integer.toString( startType ) ) ); return; } WrapperProcessConfig wrapperProcessConfig = new WrapperProcessConfig().setNewProcessGroup( newProcessGroup ).setAutoCloseInputStreams( m_autoClose ).setStartType( startType ); try { RuntimeExec.handleWrapperProcess( testId, command, wrapperProcessConfig, timeoutMS, true, false, false, RuntimeExec.WAIT_MODE_MANUAL, expectedExitCode, m_debug ); } catch ( Exception e ) { e.printStackTrace(); } } finally { RuntimeExec.endCase( testId, expectedTimeMs ); } } private static boolean m_bashForwardsSignals = false; private static boolean m_debug = false; private static boolean m_autoClose = true; private static final boolean m_powershellPresent = new File( "C:\\Windows\\System32\\WindowsPowerShell" ).exists(); /** Time after launching a child process before it is crashed. */ private static final int CRASH_DELAY_S = 1; private static final long CRASH_DELAY_MS = CRASH_DELAY_S * 1000L; /** Time after launching a child process that we send a TERM or CTRL-C asking it to stop cleanly. */ private static final int CTRL_C_DELAY_S = 2; private static final long CTRL_C_DELAY_MS = CTRL_C_DELAY_S * 1000L; /** The time that we ask the child process to run before exiting on its own. */ private static final int CHILD_SLEEP_TIME_S = 10; private static final long CHILD_SLEEP_TIME_MS = CHILD_SLEEP_TIME_S * 1000L; /** Time the Wrapper will wait after sending a TERM or CTRL-C to the child before giving up and killing the process. */ private static final int CHILD_KILL_TIMEOUT_S = 5; private static final long CHILD_KILL_TIMEOUT_MS = CHILD_KILL_TIMEOUT_S * 1000L; private static void doTests( String executable, boolean newProcessGroup, int startType ) { boolean executableExists = ( new java.io.File( executable ) ).exists(); StringBuffer sb = new StringBuffer(); if ( startType == WrapperProcessConfig.POSIX_SPAWN ) { sb.append( "PosixSpawn" ); } else if ( startType == WrapperProcessConfig.FORK_EXEC ) { sb.append( "Fork" ); } else if ( startType == WrapperProcessConfig.VFORK_EXEC ) { sb.append( "VFork" ); } else { sb.append( "Dynamic" ); } sb.append( " " ); if ( newProcessGroup ) { sb.append( "NewProcessGroup" ); } else { sb.append( "SharedProcessGroup" ); } sb.append( " " ); String context = sb.toString(); // // 1) executable is called directly (no intermediate bash process) // if ( executableExists ) { doTest( executable + " 3 " + CHILD_SLEEP_TIME_S, context + "NoIgnore Destroy " + CTRL_C_DELAY_S + "s : ", CTRL_C_DELAY_MS, newProcessGroup, startType, RuntimeExec.EXIT_CODE_TERM_CTRL_C, CTRL_C_DELAY_MS ); doTest( executable + " -ignoresignals 3 " + CHILD_SLEEP_TIME_S, context + "Ignore Destroy " + CTRL_C_DELAY_S + "s : ", CTRL_C_DELAY_MS, newProcessGroup, startType, RuntimeExec.EXIT_CODE_KILLED, CTRL_C_DELAY_MS + ( WrapperManager.isWindows() ? 0 : CHILD_KILL_TIMEOUT_MS ) ); doTest( executable + " -crash 3 " + CRASH_DELAY_S, context + "NoIgnore Crash : ", 0, newProcessGroup, startType, RuntimeExec.EXIT_CODE_CRASH, CRASH_DELAY_MS ); doTest( executable + " -ignoresignals -crash 3 " + CRASH_DELAY_S, context + "Ignore Crash : ", 0, newProcessGroup, startType, RuntimeExec.EXIT_CODE_CRASH, CRASH_DELAY_MS ); } else { doTest( executable + " 3 " + CHILD_SLEEP_TIME_S, context + "NoIgnore Destroy " + CTRL_C_DELAY_S + "s : ", CTRL_C_DELAY_MS, newProcessGroup, startType, RuntimeExec.EXIT_CODE_COMMAND_NOT_FOUND, 0 ); } // // 2) executable is called via an intermediate bash, batch or powershell process - we need to distinguish Unix and Windows. // if ( WrapperManager.isWindows() ) { // // 2-1) executable is simplewaiter // NOTE: these tests on Windows will run for the full time. simplewaiter is not killed and pipes stay opened until it stops. // Should we add an option to force terminate the full process tree, similar to what we do on UNIX ? // (see TODO comments in destroyChildObject() and callTheReaperPid()) // if ( executableExists ) { // // 2-1-1) simplewaiter with default signal handler // doTest( "cmd /c \"" + executable + " 3 " + CHILD_SLEEP_TIME_S + "\"", context + "CMD Destroy " + CTRL_C_DELAY_S + "s : ", CTRL_C_DELAY_MS, newProcessGroup, startType, RuntimeExec.EXIT_CODE_TERM_CTRL_C, CHILD_SLEEP_TIME_MS ); if ( m_powershellPresent ) { doTest( "powershell.exe \"" + executable + " 3 " + CHILD_SLEEP_TIME_S + "\"", context + "PowerShell Destroy " + CTRL_C_DELAY_S + "s : ", CTRL_C_DELAY_MS, newProcessGroup, startType, RuntimeExec.EXIT_CODE_TERM_CTRL_C, CHILD_SLEEP_TIME_MS ); } // // 2-1-2) simplewaiter with custom signal handler ignoring signals // doTest( "cmd /c \"" + executable + " -ignoresignals 3 " + CHILD_SLEEP_TIME_S + "\"", context + "CMD Destroy " + CTRL_C_DELAY_S + "s : ", CTRL_C_DELAY_MS, newProcessGroup, startType, RuntimeExec.EXIT_CODE_TERM_CTRL_C, CHILD_SLEEP_TIME_MS ); if ( m_powershellPresent ) { doTest( "powershell.exe \"" + executable + " -ignoresignals 3 " + CHILD_SLEEP_TIME_S + "\"", context + "PowerShell Destroy " + CTRL_C_DELAY_S + "s : ", CTRL_C_DELAY_MS, newProcessGroup, startType, RuntimeExec.EXIT_CODE_TERM_CTRL_C, CHILD_SLEEP_TIME_MS ); } } // TODO: create a simplewaiterw (consoless app) which should be able to receive CTRL-C signals - ideally with and without an hidden window receiving WM_CLOSE messages. // // 2-2) executable doesn't exist // else { // Should always complete quickly as the pipes will be closed together with the bash process. doTest( "cmd /c \"" + executable + " 3 " + CHILD_SLEEP_TIME_S + "\"", context + "Bash Destroy " + CTRL_C_DELAY_S + "s : ", CTRL_C_DELAY_MS, newProcessGroup, startType, RuntimeExec.EXIT_CODE_COMMAND_NOT_FOUND, 0 ); } } else { // // 2-1) executable is simplewaiter // if ( executableExists ) { // // 2-1-1) simplewaiter with default signal handler // // // 2-1-1-1) bash runs simplewaiter as a single command. // doTest( "bash -c \"" + executable + " 3 " + CHILD_SLEEP_TIME_S + "\"", context + "Bash Destroy " + CTRL_C_DELAY_S + "s : ", CTRL_C_DELAY_MS, newProcessGroup, startType, RuntimeExec.EXIT_CODE_TERM_CTRL_C, CTRL_C_DELAY_MS ); // // 2-1-1-2) bash runs simplewaiter as part of a series: export FOO=bar; ./simplewaiter ... // NOTE: we observe a different behavior from case 2-1-1-1) but the underneath reason is not clearly understood // if ( !WrapperManager.isZOS() ) { long expectedTimeMs; if ( m_bashForwardsSignals ) // On some systems bash behaves like 2-1-1-1) even when commands are in series. { // bash will get the TERM signal and forwards it. expectedTimeMs = CTRL_C_DELAY_MS; } else if ( newProcessGroup ) { // bash and executable will get the TERM signal and will exit. expectedTimeMs = CTRL_C_DELAY_MS; } else if ( m_autoClose ) { // the read() method will stop blocking when bash process terminates. Shortly after the reaper will close the InputStreams. expectedTimeMs = CTRL_C_DELAY_MS; } else // sharedProcessGroup { // bash will get the TERM signal, but not the executable. The executable is never notified so it will run for the full time. // The test case is expected to take the full amount of time as the output pipes will not be closed when bash is terminated. expectedTimeMs = CHILD_SLEEP_TIME_MS; } doTest( "bash -c \"export FOO=bar;" + executable + " 3 " + CHILD_SLEEP_TIME_S + "\"", context + "Bash+env Destroy " + CTRL_C_DELAY_S + "s : ", CTRL_C_DELAY_MS, newProcessGroup, startType, RuntimeExec.EXIT_CODE_TERM_CTRL_C, expectedTimeMs ); } else { // On zOS, stdout/stderr seem to be only attached to bash, not to the sub-child 'simplewaiter' process. Simplewaiter will be running for CHILD_SLEEP_TIME_S secs, but the pipes will be closed when bash terminates and won't block until simplewaiter completes. // If simplewaiter is launched in a new process group, it will be monitored together with bash and should respond to CTRL-C in the same delay. doTest( "bash -c \"export FOO=bar;" + executable + " 3 " + CHILD_SLEEP_TIME_S + "\"", context + "Bash+env Destroy " + CTRL_C_DELAY_S + "s : ", CTRL_C_DELAY_MS, newProcessGroup, startType, RuntimeExec.EXIT_CODE_TERM_CTRL_C, CTRL_C_DELAY_MS ); } // // 2-1-2) simplewaiter with custom signal handler ignoring signals // // // 2-1-2-1) bash runs simplewaiter as a single command. // NOTE: a TERM signal will not kill bash until simplewaiter exits. So the timeout happens and they are both killed with SIGKILL. We get EXIT_CODE_KILLED. // doTest( "bash -c \"" + executable + " -ignoresignals 3 " + CHILD_SLEEP_TIME_S + "\"", context + "Bash Ignore Destroy " + CTRL_C_DELAY_S + "s : ", CTRL_C_DELAY_MS, newProcessGroup, startType, RuntimeExec.EXIT_CODE_KILLED, CTRL_C_DELAY_MS + CHILD_KILL_TIMEOUT_MS ); doTest( "bash -c \"" + executable + " -crash 3 " + CRASH_DELAY_S + "\"", context + "Bash NoIgnore Crash : ", 0, newProcessGroup, startType, RuntimeExec.EXIT_CODE_CRASH, CRASH_DELAY_MS ); doTest( "bash -c \"" + executable + " -ignoresignals -crash 3 " + CRASH_DELAY_S + "\"", context + "Bash Ignore Crash : ", 0, newProcessGroup, startType, RuntimeExec.EXIT_CODE_CRASH, CRASH_DELAY_MS ); // // 2-1-2-2) bash runs simplewaiter as part of a series: export FOO=bar; ./simplewaiter ... // NOTE: we observe a different behavior from case 2-1-2-1) but the underneath reason is not clearly understood // if ( !WrapperManager.isZOS() ) { long expectedTimeMs; int expectedExitCode; if ( m_bashForwardsSignals ) // On some systems bash behaves like 2-1-1-1) even when commands are in series. { // bash will get the TERM signal and forwards it. // bash will exit when it gets the TERM signal, but the executable will ignore it and continue to wait until it times out and is killed. expectedTimeMs = CTRL_C_DELAY_MS + CHILD_KILL_TIMEOUT_MS; expectedExitCode = RuntimeExec.EXIT_CODE_KILLED; } else { if ( newProcessGroup ) { // bash and executable will get the TERM signal. // bash will exit when it gets the TERM signal, but the executable will ignore it and continue to wait until it times out and is killed. expectedTimeMs = CTRL_C_DELAY_MS + CHILD_KILL_TIMEOUT_MS; } else if ( m_autoClose ) { // the read() method will stop blocking when bash process terminates. Shortly after the reaper will close the InputStreams. expectedTimeMs = CTRL_C_DELAY_MS; } else // sharedProcessGroup { // bash will get the TERM signal, but not the executable. The executable is never notified so it will run for the full time. // The test case is expected to take the full amount of time as the output pipes will not be closed when bash is terminated. expectedTimeMs = CHILD_SLEEP_TIME_MS; } // a TERM signal kills bash and we get EXIT_CODE_TERM_CTRL_C. But the simplewaiter times out and is SIGKILLed. expectedExitCode = RuntimeExec.EXIT_CODE_TERM_CTRL_C; } doTest( "bash -c \"export FOO=bar;" + executable + " -ignoresignals 3 " + CHILD_SLEEP_TIME_S + "\"", context + "Bash+env Ignore Destroy " + CTRL_C_DELAY_S + "s : ", CTRL_C_DELAY_MS, newProcessGroup, startType, expectedExitCode, expectedTimeMs ); } else { // On zOS, stdout/stderr seem to be only attached to bash, not to the sub-child 'simplewaiter' process. Simplewaiter will be running for CHILD_SLEEP_TIME_S secs, but the pipes will be closed when bash terminates and won't block until simplewaiter completes. // If simplewaiter is launched in a new process group, it will be monitored together with bash but will ignore CTRL-C. An additional CHILD_KILL_TIMEOUT_MS delay is expected to happen until the process is terminated. doTest( "bash -c \"export FOO=bar;" + executable + " -ignoresignals 3 " + CHILD_SLEEP_TIME_S + "\"", context + "Bash+env Ignore Destroy " + CTRL_C_DELAY_S + "s : ", CTRL_C_DELAY_MS, newProcessGroup, startType, RuntimeExec.EXIT_CODE_TERM_CTRL_C, newProcessGroup ? CTRL_C_DELAY_MS + CHILD_KILL_TIMEOUT_MS : CTRL_C_DELAY_MS ); } doTest( "bash -c \"export FOO=bar;" + executable + " -crash 3 " + CRASH_DELAY_S + "\"", context + "Bash+env NoIgnore Crash : ", 0, newProcessGroup, startType, RuntimeExec.EXIT_CODE_CRASH, CRASH_DELAY_MS ); doTest( "bash -c \"export FOO=bar;" + executable + " -ignoresignals -crash 3 " + CRASH_DELAY_S + "\"", context + "Bash+env Ignore Crash : ", 0, newProcessGroup, startType, RuntimeExec.EXIT_CODE_CRASH, CRASH_DELAY_MS ); } // // 2-2) executable doesn't exist // else { // Should always complete quickly as the pipes will be closed together with the bash process. doTest( "bash -c \"" + executable + " 3 " + CHILD_SLEEP_TIME_S + "\"", context + "Bash Destroy " + CTRL_C_DELAY_S + "s : ", CTRL_C_DELAY_MS, newProcessGroup, startType, RuntimeExec.EXIT_CODE_COMMAND_NOT_FOUND, 0 ); doTest( "bash -c \"export FOO=bar;" + executable + " 3 " + CHILD_SLEEP_TIME_S + "\"", context + "Bash+env Destroy " + CTRL_C_DELAY_S + "s : ", CTRL_C_DELAY_MS, newProcessGroup, startType, RuntimeExec.EXIT_CODE_COMMAND_NOT_FOUND, 0 ); } } } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main( String[] args ) { // Need to initialize the counts. RuntimeExec.c_testsPerformed = 0; RuntimeExec.c_testsFailed = 0; final String simplewaiter; final String doesnotexist; if ( WrapperManager.isWindows() ) { simplewaiter = "..\\test\\simplewaiter.exe"; doesnotexist = "..\\test\\doesnotexist.exe"; } else { simplewaiter = "../test/simplewaiter"; doesnotexist = "../test/doesnotexist"; } if ( !WrapperManager.isWindows() ) { if ( ( args.length > 0 ) && "true".equals( args[0] ) ) { System.out.println( Main.getRes().getString( "Detected that Bash forwards signals to its child processes." ) ); m_bashForwardsSignals = true; } if ( ( args.length > 1 ) ) { if ( "true".equals( args[1] ) ) { System.out.println( Main.getRes().getString( "Using Auto-close mode to read the pipes." ) ); m_autoClose = true; } else { System.out.println( Main.getRes().getString( "Using Blocking mode to read the pipes." ) ); m_autoClose = false; } } if ( ( args.length > 2 ) && "true".equals( args[2] ) ) { System.out.println( Main.getRes().getString( "DEBUG mode enabled." ) ); m_debug = true; } } doTests( simplewaiter, true, WrapperProcessConfig.DYNAMIC ); doTests( doesnotexist, true, WrapperProcessConfig.DYNAMIC ); if ( !WrapperManager.isWindows() ) { doTests( simplewaiter, false, WrapperProcessConfig.DYNAMIC ); doTests( doesnotexist, false, WrapperProcessConfig.DYNAMIC ); if ( WrapperProcessConfig.isSupported( WrapperProcessConfig.FORK_EXEC ) ) { doTests( simplewaiter, true, WrapperProcessConfig.FORK_EXEC ); doTests( simplewaiter, false, WrapperProcessConfig.FORK_EXEC ); doTests( doesnotexist, true, WrapperProcessConfig.FORK_EXEC ); doTests( doesnotexist, false, WrapperProcessConfig.FORK_EXEC ); } if ( WrapperProcessConfig.isSupported( WrapperProcessConfig.VFORK_EXEC ) ) { doTests( simplewaiter, true, WrapperProcessConfig.VFORK_EXEC ); doTests( simplewaiter, false, WrapperProcessConfig.VFORK_EXEC ); doTests( doesnotexist, true, WrapperProcessConfig.VFORK_EXEC ); doTests( doesnotexist, false, WrapperProcessConfig.VFORK_EXEC ); } if ( WrapperProcessConfig.isSupported( WrapperProcessConfig.POSIX_SPAWN ) ) { doTests( simplewaiter, true, WrapperProcessConfig.POSIX_SPAWN ); doTests( simplewaiter, false, WrapperProcessConfig.POSIX_SPAWN ); doTests( doesnotexist, true, WrapperProcessConfig.POSIX_SPAWN ); doTests( doesnotexist, false, WrapperProcessConfig.POSIX_SPAWN ); } } int nbTestsFailed = RuntimeExec.c_testsFailed; int nbTestsPassed = RuntimeExec.c_testsPerformed - nbTestsFailed; System.out.println( "" ); System.out.println( Main.getRes().getString( "[PASSED] {0}", Integer.toString( nbTestsPassed ) ) ); System.out.println( Main.getRes().getString( "[FAILED] {0}", Integer.toString( nbTestsFailed ) ) ); if ( nbTestsFailed > 0 ) { System.exit( 1 ); } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/ExecThrasher.java100644 0 0 17664 14333053652 24643 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.WrapperJNIError; import org.tanukisoftware.wrapper.WrapperLicenseError; import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.WrapperProcess; import org.tanukisoftware.wrapper.WrapperProcessConfig; import org.tanukisoftware.wrapper.WrapperSystemPropertyUtil; import java.io.BufferedReader; import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.util.Random; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class ExecThrasher { private static String c_simplewaiter; private static String c_encoding; private static String c_startTypeS; private static int c_startType; private static int c_threadCount; /*--------------------------------------------------------------- * Static Methods *-------------------------------------------------------------*/ static { if ( WrapperManager.isWindows() ) { c_simplewaiter = "../test/simplewaiter.exe"; } else { c_simplewaiter = "../test/simplewaiter"; } // In order to read the output from some processes correctly we need to get the correct encoding. // On some systems, the underlying system encoding is different than the file encoding. c_encoding = System.getProperty( "sun.jnu.encoding" ); if ( c_encoding == null ) { c_encoding = System.getProperty( "file.encoding" ); if ( c_encoding == null ) { // Default to Latin1 c_encoding = "Cp1252"; } } // Resolve the start type. c_startTypeS = WrapperSystemPropertyUtil.getStringProperty( ExecThrasher.class.getName() + ".startType", "" ); if ( c_startTypeS.equals( "POSIX_SPAWN" ) ) { c_startType = WrapperProcessConfig.POSIX_SPAWN; } else if ( c_startTypeS.equals( "FORK_EXEC" ) ) { c_startType = WrapperProcessConfig.FORK_EXEC; } else if ( c_startTypeS.equals( "VFORK_EXEC" ) ) { c_startType = WrapperProcessConfig.VFORK_EXEC; } else { c_startType = WrapperProcessConfig.DYNAMIC; c_startTypeS = "DYNAMIC"; } // Resolve the thread count. c_threadCount = WrapperSystemPropertyUtil.getIntProperty( ExecThrasher.class.getName() + ".threadCount", 100 ); } private static void handleInputStream( final InputStream is, final String encoding, final String label ) { Thread runner = new Thread() { public void run() { BufferedReader br; String line; try { br = new BufferedReader( new InputStreamReader( is, encoding ) ); try { while ( ( line = br.readLine() ) != null ) { System.out.println( label + ": " + line ); } } finally { br.close(); } } catch ( IOException e ) { e.printStackTrace(); } System.out.println( label + " EOF" ); } }; runner.start(); } private static void handleWrapperProcessInner( WrapperProcess process, long timeoutMS, int threadId, int processId ) throws IOException { try { handleInputStream( process.getInputStream(), c_encoding, "Thrasher Thread #" + threadId + " Process #" + processId + " stdout" ); handleInputStream( process.getErrorStream(), c_encoding, "Thrasher Thread #" + threadId + " Process #" + processId + " stderr" ); if ( timeoutMS > 0 ) { long start = System.currentTimeMillis(); while ( process.isAlive() && ( System.currentTimeMillis() - start < timeoutMS ) ) { try { Thread.sleep( 100 ); } catch ( InterruptedException e ) { } } if ( process.isAlive() ) { System.out.println( "Thrasher Thread #" + threadId + " Process #" + processId + " Timed out waiting for child. Destroying." ); process.destroy(); } } } finally { try { int exitCode = process.waitFor(); System.out.println( "Thrasher Thread #" + threadId + " Process #" + processId + " exitCode: " + exitCode ); } catch ( InterruptedException e ) { System.out.println( "Thrasher Thread #" + threadId + " Process #" + processId + " Timed out waiting for process to complete." ); } } } private static void handleWrapperProcess( String command, long timeoutMS, int threadId, int processId ) throws IOException { WrapperProcessConfig processConfig = new WrapperProcessConfig(); processConfig.setStartType( c_startType ); WrapperProcess process = WrapperManager.exec( command, processConfig ); handleWrapperProcessInner( process, timeoutMS, threadId, processId ); } private static void thrasher( int threadId ) { // We want to work in a repeatable way. Random rand = new Random( threadId ); System.out.println( "Thrasher Thread #" + threadId + " Begin" ); try { for ( int processId = 0; processId < 1000; processId++ ) { int seconds = rand.nextInt( 10 ); String command = c_simplewaiter + " 0 " + seconds; System.out.println( "Thrasher Thread #" + threadId + " Process #" + processId + " Launch (" + seconds + " seconds)" ); try { handleWrapperProcess( command, ( seconds + 5 ) * 1000, threadId, processId ); } catch ( IOException e ) { System.out.println( "Thrasher Thread #" + threadId + " Process #" + processId + " Launch Failed." ); e.printStackTrace(); } } } finally { System.out.println( "Thrasher Thread #" + threadId + " End" ); } } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main( String[] args ) { System.out.println( "Communicate with child processes using encoding: " + c_encoding ); System.out.println( "Using start type '" + c_startTypeS + "'." ); System.out.println( "Launching " + c_threadCount + " threads..." ); for ( int i = 0; i < c_threadCount; i++ ) { final int threadId = i; Thread thread = new Thread( "ExecThrasher-" + i ) { public void run() { thrasher( threadId ); } }; thread.start(); } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/ExecWorkingDir.java100644 0 0 7537 14333053652 25120 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.io.File; import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.WrapperProcessConfig; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class ExecWorkingDir { private static void doTest( String command, String chDir, int startType, boolean spawnChDir ) { StringBuffer sb = new StringBuffer( "'" + chDir + "' " ); if ( startType == WrapperProcessConfig.POSIX_SPAWN ) { sb.append( "PosixSpawn" ); } else if ( startType == WrapperProcessConfig.FORK_EXEC ) { sb.append( "Fork" ); } else if ( startType == WrapperProcessConfig.VFORK_EXEC ) { sb.append( "VFork" ); } else { sb.append( "Dynamic" ); } sb.append( " " ); if ( spawnChDir ) { sb.append( "(spawnChDir) " ); } String testId = sb.toString(); RuntimeExec.beginCase( testId ); try { if ( !WrapperProcessConfig.isSupported( startType ) ) { System.out.println( Main.getRes().getString( "{0} startType {1} not supported", testId, Integer.toString( startType ) ) ); return; } try { WrapperProcessConfig wrapperProcessConfig = new WrapperProcessConfig().setWorkingDirectory( new File( chDir ) ).setStartType( startType ); RuntimeExec.handleWrapperProcess( testId, command, wrapperProcessConfig, 0, true, false, false, RuntimeExec.WAIT_MODE_API, 0, false ); } catch ( Exception e ) { e.printStackTrace(); } } finally { RuntimeExec.endCase( testId, 0 ); } } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main( String[] args ) { final String command; if ( WrapperManager.isWindows() ) { command = "cmd /c cd"; } else { command = "pwd"; } System.out.println( "" ); System.out.println( Main.getRes().getString( "Please check that each command run in its correct directory." ) ); /* test stdout */ doTest( command, ".", WrapperProcessConfig.DYNAMIC, false ); doTest( command, "..", WrapperProcessConfig.DYNAMIC, false ); if ( !WrapperManager.isWindows() ) { doTest( command, ".", WrapperProcessConfig.POSIX_SPAWN, false ); doTest( command, "..", WrapperProcessConfig.POSIX_SPAWN, false ); doTest( command, ".", WrapperProcessConfig.FORK_EXEC, false ); doTest( command, "..", WrapperProcessConfig.FORK_EXEC, false ); doTest( command, ".", WrapperProcessConfig.VFORK_EXEC, false ); doTest( command, "..", WrapperProcessConfig.VFORK_EXEC, false ); System.setProperty( "wrapper.child.allowCWDOnSpawn", "TRUE" ); doTest( command, ".", WrapperProcessConfig.DYNAMIC, true ); doTest( command, "..", WrapperProcessConfig.DYNAMIC, true ); doTest( command, ".", WrapperProcessConfig.POSIX_SPAWN, true ); doTest( command, "..", WrapperProcessConfig.POSIX_SPAWN, true ); } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/ExecWriteAndRead.java100644 0 0 13301 14333053652 25354 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.WrapperProcessConfig; /** * This class tests the ability to write to the output stream of a subprocess * and to read the input stream of that same process. A comparison is then * made to confirm they both match. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class ExecWriteAndRead { private static boolean m_debug = false; /** The time that we ask the child process to run before exiting on its own. */ private static final int CHILD_SLEEP_TIME_S = 0; private static final long CHILD_SLEEP_TIME_MS = CHILD_SLEEP_TIME_S * 1000L; private static final String CHILD_INPUT_STRING = "some input..." + System.getProperty("line.separator"); private static void doTest( String command, String testId, boolean newProcessGroup, int startType, long expectedTimeMs ) { RuntimeExec.beginCase( testId ); try { WrapperProcessConfig wrapperProcessConfig = new WrapperProcessConfig().setNewProcessGroup( newProcessGroup ).setStartType( startType ); try { RuntimeExec.handleWrapperProcess( testId, command, wrapperProcessConfig, 0, true, false, CHILD_INPUT_STRING, false, RuntimeExec.WAIT_MODE_MANUAL, "(.*)(" + CHILD_INPUT_STRING + ")(.*)", 0, m_debug, false ); } catch ( Exception e ) { e.printStackTrace(); } } finally { RuntimeExec.endCase( testId, expectedTimeMs ); } } private static void doTests( String executable, boolean newProcessGroup, int startType ) { StringBuffer sb = new StringBuffer(); if ( newProcessGroup ) { sb.append( "NewProcessGroup" ); } else { sb.append( "SharedProcessGroup" ); } sb.append( " " ); String context = sb.toString(); // launching simplewaiter only. Expected to read for CHILD_SLEEP_TIME_MS. doTest( executable + " -readinput 0 " + CHILD_SLEEP_TIME_S, context + "simplewaiter : ", newProcessGroup, startType, CHILD_SLEEP_TIME_MS ); if ( !WrapperManager.isWindows() ) { // launching bash as direct child and simplewaiter as a subchild. Bash will exit after simplewaiter completes. Expected to read for CHILD_SLEEP_TIME_MS. doTest( "bash -c \"" + executable + " -readinput 0 " + CHILD_SLEEP_TIME_S + "\"", context + "Bash+simplewaiter : ", newProcessGroup, startType, CHILD_SLEEP_TIME_MS ); // launching bash as direct child and simplewaiter as a subchild. Bash will exit after simplewaiter completes. Expected to read for CHILD_SLEEP_TIME_MS. doTest( "bash -c \"export FOO=bar;" + executable + " -readinput 0 " + CHILD_SLEEP_TIME_S + "\"", context + "Bash+env+simplewaiter : ", newProcessGroup, startType, CHILD_SLEEP_TIME_MS ); } } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main( String[] args ) { // Need to initialize the counts. RuntimeExec.c_testsPerformed = 0; RuntimeExec.c_testsFailed = 0; final String simplewaiter; if ( WrapperManager.isWindows() ) { simplewaiter = "..\\test\\simplewaiter.exe"; } else { simplewaiter = "../test/simplewaiter"; } if ( !WrapperManager.isWindows() ) { if ( ( args.length > 0 ) && "true".equals( args[0] ) ) { System.out.println( Main.getRes().getString( "DEBUG mode enabled." ) ); m_debug = true; } } System.out.println( "" ); System.out.println( Main.getRes().getString( "A bunch of tests will be run. Please check that the input written matches the output printed." ) ); /* test stdin */ if ( WrapperProcessConfig.isSupported( WrapperProcessConfig.FORK_EXEC ) ) { doTests( simplewaiter, true, WrapperProcessConfig.FORK_EXEC ); doTests( simplewaiter, false, WrapperProcessConfig.FORK_EXEC ); } if ( WrapperProcessConfig.isSupported( WrapperProcessConfig.VFORK_EXEC ) ) { doTests( simplewaiter, true, WrapperProcessConfig.VFORK_EXEC ); doTests( simplewaiter, false, WrapperProcessConfig.VFORK_EXEC ); } if ( WrapperProcessConfig.isSupported( WrapperProcessConfig.POSIX_SPAWN ) ) { doTests( simplewaiter, true, WrapperProcessConfig.POSIX_SPAWN ); doTests( simplewaiter, false, WrapperProcessConfig.POSIX_SPAWN ); } int nbTestsFailed = RuntimeExec.c_testsFailed; int nbTestsPassed = RuntimeExec.c_testsPerformed - nbTestsFailed; System.out.println( "" ); System.out.println( Main.getRes().getString( "[PASSED] {0}", Integer.toString( nbTestsPassed ) ) ); System.out.println( Main.getRes().getString( "[FAILED] {0}", Integer.toString( nbTestsFailed ) ) ); if ( nbTestsFailed > 0 ) { System.exit( 1 ); } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/Filter.java100644 0 0 7545 14333053652 23460 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.WrapperManager; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class Filter { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { System.out.println( Main.getRes().getString( "Test the handling of filters." ) ); System.out.println( Main.getRes().getString( "The Wrapper should restart the JVM when it detects either the string:" ) ); System.out.println( Main.getRes().getString( " \"ERR OR\" or \"N ice long restart message.\", both without the" ) ); System.out.println( Main.getRes().getString( " extra space. It should ignore the string: \"NONERROR\". Then" ) ); System.out.println( Main.getRes().getString( " it should exit when it detects the string: \"ALL DONE\", once again" ) ); System.out.println( Main.getRes().getString( " without the space." ) ); System.out.println(); System.out.println( Main.getRes().getString( "The next line should be ignored:" ) ); System.out.println( " NONERROR"); System.out.println(); if (WrapperManager.getJVMId() >= 6) { // Time to shutdown System.out.println( Main.getRes().getString( "The next line should cause the Wrapper to exit:" ) ); System.out.println(" ALLDONE"); } else if (WrapperManager.getJVMId() == 5) { // Perform a restart. System.out.println( Main.getRes().getString( "The next line should cause the Wrapper to restart the JVM:" ) ); System.out.println( " HEAD and a bunch of stuff before the TAIL" ); } else if (WrapperManager.getJVMId() == 4) { // Perform a thread dump and restart. System.out.println( Main.getRes().getString( "The next line should cause the Wrapper to invoke a thread dump and then restart the JVM:" ) ); System.out.println( " DUMP -n- RESTART" ); } else if (WrapperManager.getJVMId() == 3) { // Try a restart with spaces. System.out.println( Main.getRes().getString( "The next line should cause the Wrapper to restart the JVM:" ) ); System.out.println( " Nice long restart message." ); } else { System.out.println( Main.getRes().getString( "The next line should cause the Wrapper to restart the JVM:" ) ); System.out.println(" ERROR"); } System.out.println(); System.out.println( Main.getRes().getString( "The above message should be caught before this line, but this line" ) ); System.out.println( Main.getRes().getString( " will still be visible. Wait for 5 seconds before this thread is" ) ); System.out.println( Main.getRes().getString( " allowed to complete. This prevents the Wrapper from detecting" ) ); System.out.println( Main.getRes().getString( " that the application has completed and exiting normally. The" ) ); System.out.println( Main.getRes().getString( " Wrapper will try to shutdown the JVM cleanly, so it will not exit" ) ); System.out.println( Main.getRes().getString( " until this thread has completed." ) ); try { Thread.sleep(5000); } catch (InterruptedException e) { } System.out.println( Main.getRes().getString( "Main complete." ) ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/GrowingLogOutput.java100644 0 0 5311 14333053652 25517 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.util.Date; import java.text.SimpleDateFormat; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class GrowingLogOutput { /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ private static String lPad( int n, int len, String padding ) { String s = Integer.toString( n ); int len2 = s.length(); if ( len2 < len ) { return padding.substring( 0, len - len2 ) + s; } else { return s; } } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { // This class is not localized so we can run it without the Wrapper code. System.out.println( "Log several lines of output of growing length. This is to massage the internal logging buffers." ); SimpleDateFormat df = new SimpleDateFormat( "HH:mm:ss.SSS" ); // Build up a long base string. We will be sending varying substring instances of this to the output. StringBuffer messageSB = new StringBuffer(); for ( int i = 0; i < 1000; i++ ) { messageSB.append( "ThisIsAVeryLongStringWithoutSpaces." ); // 35 chars } String message = messageSB.toString(); // 35000 chars. int messageLen = message.length(); long allStart = System.currentTimeMillis(); System.out.println( df.format( new Date() ) + " Starting..." ); for ( int subMessageLen = 0; subMessageLen < messageLen; subMessageLen++ ) { String subMessage = message.substring( 0, subMessageLen ); System.out.println( df.format( new Date() ) + " " + lPad( subMessageLen + 20, 6, " " ) + ":" + subMessage ); } long allTime = System.currentTimeMillis() - allStart; System.out.println( df.format( new Date() ) + " Max length should be: " + ( messageLen - 1 + 20 ) ); System.out.println( df.format( new Date() ) + " Total time: " + allTime ); System.out.println( df.format( new Date() ) + " All done." ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/HugeLogOutput.java100644 0 0 5110 14333053652 24770 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.text.SimpleDateFormat; import java.util.Date; import org.tanukisoftware.wrapper.WrapperManager; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class HugeLogOutput { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { // 62 chars long String subStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; // This first test is to check for buffer problems increasing the size of the Wrapper's log buffer. System.out.println( "Print out 10 long lines of increasing length." ); // Now loop and print this to the console 10 times. Log the time before each line. StringBuffer sb = new StringBuffer(); SimpleDateFormat df = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss.SSS" ); for ( int i = 0; i < 10; i++ ) { for ( int j = 0; j < 10; j++ ) { sb.append( subStr ); } String longStr = sb.toString(); System.out.println( df.format( new Date() ) ); System.out.println( longStr ); } // This next test is to check for a speed problem which used to exist with VERY large log lines. // Loop printing out 10 lines of increasing length. System.out.println( "Print out 10 very long lines of output." ); sb = new StringBuffer(); for ( int j = 0; j < 10; j++ ) { // Increase the size of the buffer by 100,006 chars each cyle (62*1613 copies) // After the 10th loop, it will be 1,000,060 chars long. for ( int i = 0; i < 1613; i++ ) { sb.append( subStr ); } String hugeStr = sb.toString(); System.out.println(); System.out.println( "Loop #" + j + " Size: " + hugeStr.length() ); System.out.println( df.format( new Date() ) ); System.out.println( hugeStr ); } System.out.println( "All done." ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/JarMain.java100644 0 0 3512 14333053652 23542 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class JarMain { static { if ( System.getProperty( "JarMain.init.fail" ) != null ) { System.out.println( Main.getRes().getString( "About to throw exception in initializer..." ) ); throw new IllegalStateException( Main.getRes().getString( "This is an intentional error in the initializer." ) ); } } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { if ( args.length > 0 ) { System.out.println( Main.getRes().getString( "Arguments:" ) ); for ( int i = 0; i < args.length; i++ ) { System.out.println( " args[" + i + "]=" + args[i] ); } } System.out.println( Main.getRes().getString( "Loop for 10 seconds." ) ); for ( int i = 0; i < 10; i++ ) { try { Thread.sleep(1000); } catch ( InterruptedException e ) { } System.out.println( Main.getRes().getString( "Counting...{0}", new Integer( i ) ) ); } System.out.println( Main.getRes().getString( "Loop complete." ) ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/JvmEncoding.java100644 0 0 31623 14333053652 24450 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.WrapperManager; import java.io.UnsupportedEncodingException; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class JvmEncoding { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { System.out.println( Main.getRes().getString( "Test the handling of different charsets." ) ); System.out.println(); String encodings[][] = {{"Cp858", "IBM00858"}, {"Cp437", "IBM437"}, {"Cp775", "IBM775"}, {"Cp850", "IBM850"}, {"Cp852", "IBM852"}, {"Cp855", "IBM855"}, {"Cp857", "IBM857"}, {"Cp862", "IBM862"}, {"Cp866", "IBM866"}, {"ISO8859_1", "ISO-8859-1"}, {"ISO8859_2", "ISO-8859-2"}, {"ISO8859_4", "ISO-8859-4"}, {"ISO8859_5", "ISO-8859-5"}, {"ISO8859_7", "ISO-8859-7"}, {"ISO8859_9", "ISO-8859-9"}, {"ISO8859_13", "ISO-8859-13"}, {"ISO8859_15", "ISO-8859-15"}, {"KOI8_R", "KOI8-R"}, {"KOI8_U", "KOI8-U"}, {"ASCII", "US-ASCII"}, {"UTF8", "UTF-8"}, {"UTF-16", "UTF-16"}, {"UnicodeBigUnmarked", "UTF-16BE"}, {"UnicodeLittleUnmarked", "UTF-16LE"}, {"UTF_32", "UTF-32"}, {"UTF_32BE", "UTF-32BE"}, {"UTF_32LE", "UTF-32LE"}, {"UTF_32BE_BOM", "x-UTF-32BE-BOM"}, {"UTF_32LE_BOM", "x-UTF-32LE-BOM"}, {"Cp1250", "windows-1250"}, {"Cp1251", "windows-1251"}, {"Cp1252", "windows-1252"}, {"Cp1253", "windows-1253"}, {"Cp1254", "windows-1254"}, {"Cp1257", "windows-1257"}, {"UnicodeBig", "Not available"}, {"Cp737", "x-IBM737"}, {"Cp874", "x-IBM874"}, {"UnicodeLittle", "x-UTF-16LE-BOM"}, {"Big5", "Big5"}, {"Big5_HKSCS", "Big5-HKSCS"}, {"EUC_JP", "EUC-JP"}, {"EUC_KR", "EUC-KR"}, {"GB18030", "GB18030"}, {"EUC_CN", "GB2312"}, {"GBK", "GBK"}, {"Cp838", "IBM-Thai"}, {"Cp1140", "IBM01140"}, {"Cp1141", "IBM01141"}, {"Cp1142", "IBM01142"}, {"Cp1143", "IBM01143"}, {"Cp1144", "IBM01144"}, {"Cp1145", "IBM01145"}, {"Cp1146", "IBM01146"}, {"Cp1147", "IBM01147"}, {"Cp1148", "IBM01148"}, {"Cp1149", "IBM01149"}, {"Cp037", "IBM037"}, {"Cp1026", "IBM1026"}, {"Cp1047", "IBM1047"}, {"Cp273", "IBM273"}, {"Cp277", "IBM277"}, {"Cp278", "IBM278"}, {"Cp280", "IBM280"}, {"Cp284", "IBM284"}, {"Cp285", "IBM285"}, {"Cp290", "IBM290"}, {"Cp297", "IBM297"}, {"Cp300", "IBM300"}, {"Cp420", "IBM420"}, {"Cp424", "IBM424"}, {"Cp500", "IBM500"}, {"Cp860", "IBM860"}, {"Cp861", "IBM861"}, {"Cp863", "IBM863"}, {"Cp864", "IBM864"}, {"Cp865", "IBM865"}, {"Cp868", "IBM868"}, {"Cp869", "IBM869"}, {"Cp870", "IBM870"}, {"Cp871", "IBM871"}, {"Cp918", "IBM918"}, {"ISO2022CN", "ISO-2022-CN"}, {"ISO2022JP", "ISO-2022-JP"}, {"ISO2022KR", "ISO-2022-KR"}, {"ISO8859_3", "ISO-8859-3"}, {"ISO8859_6", "ISO-8859-6"}, {"ISO8859_8", "ISO-8859-8"}, {"JIS_X0201", "JIS_X0201"}, {"JIS_X0212-1990", "JIS_X0212-1990"}, {"SJIS", "Shift_JIS"}, {"TIS620", "TIS-620"}, {"Cp1255", "windows-1255"}, {"Cp1256", "windows-1256"}, {"Cp1258", "windows-1258"}, {"MS932", "windows-31j"}, {"Big5_Solaris", "x-Big5-Solaris"}, {"EUC_JP_LINUX", "x-euc-jp-linux"}, {"EUC_TW", "x-EUC-TW"}, {"EUC_JP_Solaris", "x-eucJP-Open"}, {"Cp1006", "x-IBM1006"}, {"Cp1025", "x-IBM1025"}, {"Cp1046", "x-IBM1046"}, {"Cp1097", "x-IBM1097"}, {"Cp1098", "x-IBM1098"}, {"Cp1112", "x-IBM1112"}, {"Cp1122", "x-IBM1122"}, {"Cp1123", "x-IBM1123"}, {"Cp1124", "x-IBM1124"}, {"Cp1381", "x-IBM1381"}, {"Cp1383", "x-IBM1383"}, {"Cp33722", "x-IBM33722"}, {"Cp834", "x-IBM834"}, {"Cp856", "x-IBM856"}, {"Cp875", "x-IBM875"}, {"Cp921", "x-IBM921"}, {"Cp922", "x-IBM922"}, {"Cp930", "x-IBM930"}, {"Cp933", "x-IBM933"}, {"Cp935", "x-IBM935"}, {"Cp937", "x-IBM937"}, {"Cp939", "x-IBM939"}, {"Cp942", "x-IBM942"}, {"Cp942C", "x-IBM942C"}, {"Cp943", "x-IBM943"}, {"Cp943C", "x-IBM943C"}, {"Cp948", "x-IBM948"}, {"Cp949", "x-IBM949"}, {"Cp949C", "x-IBM949C"}, {"Cp950", "x-IBM950"}, {"Cp964", "x-IBM964"}, {"Cp970", "x-IBM970"}, {"ISCII91", "x-ISCII91"}, {"ISO2022_CN_CNS", "x-ISO2022-CN-CNS"}, {"ISO2022_CN_GB", "x-ISO2022-CN-GB"}, {"x-iso-8859-11", "x-iso-8859-11"}, {"x-JIS0208", "x-JIS0208"}, {"JISAutoDetect", "x-JISAutoDetect"}, {"x-Johab", "x-Johab"}, {"MacArabic", "x-MacArabic"}, {"MacCentralEurope", "x-MacCentralEurope"}, {"MacCroatian", "x-MacCroatian"}, {"MacCyrillic", "x-MacCyrillic"}, {"MacDingbat", "x-MacDingbat"}, {"MacGreek", "x-MacGreek"}, {"MacHebrew", "x-MacHebrew"}, {"MacIceland", "x-MacIceland"}, {"MacRoman", "x-MacRoman"}, {"MacRomania", "x-MacRomania"}, {"MacSymbol", "x-MacSymbol"}, {"MacThai", "x-MacThai"}, {"MacTurkish", "x-MacTurkish"}, {"MacUkraine", "x-MacUkraine"}, {"MS950_HKSCS", "x-MS950-HKSCS"}, {"MS936", "x-mswin-936"}, {"PCK", "x-PCK"}, {"x-SJIS_0213", "x-SJIS_0213"}, {"Cp50220", "x-windows-50220"}, {"Cp50221", "x-windows-50221"}, {"MS874", "x-windows-874"}, {"MS949", "x-windows-949"}, {"MS950", "x-windows-950"}, {"x-windows-iso2022jp", "x-windows-iso2022jp"}}; String str = "test sentence 123"; int passed = 0; int failed = 0; boolean atLeastOneFailed = false; for ( int i = 0; i < encodings.length; i++ ) { if (checkEncoding(str, encodings[i][0])) { passed++; } else { atLeastOneFailed = true; failed++; } if (checkEncoding(str, encodings[i][0].toUpperCase())) { passed++; } else { atLeastOneFailed = true; failed++; } if (checkEncoding(str, encodings[i][0].toLowerCase())) { passed++; } else { atLeastOneFailed = true; failed++; } if (checkEncoding(str, encodings[i][1])) { passed++; } else { atLeastOneFailed = true; failed++; } if (checkEncoding(str, encodings[i][1].toUpperCase())) { passed++; } else { atLeastOneFailed = true; failed++; } if (checkEncoding(str, encodings[i][1].toLowerCase())) { passed++; } else { atLeastOneFailed = true; failed++; } if (atLeastOneFailed) { System.out.println(); atLeastOneFailed = false; } } if (passed > 0) { System.out.println( Main.getRes().getString( "JVM encoding {0} test(s) passed.", new Integer(passed) )); } if (failed > 0) { System.out.println( Main.getRes().getString( "JVM encoding {0} test(s) FAILED.", new Integer(failed) )); } } private static boolean checkEncoding(String inStr, String encoding) { boolean result; try { byte[] bytes = inStr.getBytes(encoding); String outStr = new String(bytes, encoding); result = inStr.equals(outStr); if (!result) { System.err.println( Main.getRes().getString( "{0} supported but conversion error.", encoding ) ); } else { /*System.out.println( Main.getRes().getString( "{0} supported", encoding ) );*/ } } catch ( UnsupportedEncodingException e ) { System.err.println( Main.getRes().getString( "{0} not supported.", encoding ) ); result = false; } catch ( Exception e ) { System.err.println( Main.getRes().getString( "{0} not supported - {1}", encoding, e ) ); result = false; } return result; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/JvmEncoding2.java100644 0 0 5563 14333053652 24516 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.WrapperManager; import java.io.UnsupportedEncodingException; import java.io.IOException; import java.io.File; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class JvmEncoding2 { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { System.out.println( Main.getRes().getString( "Test the handling of different charsets (file creation and if output is correctly decoded by the Wrapper)." ) ); System.out.println(); String currentEncoding = System.getProperty("file.encoding"); System.out.println(Main.getRes().getString("Current file.encoding: {0}", currentEncoding)); String currentSunEncoding = System.getProperty("sun.stdout.encoding"); System.out.println(Main.getRes().getString("Current sun.stdout.encoding: {0}", currentSunEncoding)); boolean noTest = true; if (WrapperManager.isWindows()) { if (currentEncoding.equals("MS949")) { /* Do not translate this message! Korean characters would corrupt the mo file... */ System.out.println("Test output korean characters: \uD55C\uAD6D\uC5B4"); CreateAndDeleteFile("\uD55C\uAD6D\uC5B4"); noTest = false; } /* Add more languages here */ } if (noTest) { System.out.println(Main.getRes().getString("No test for the current platform and language. Try changing the OS language.")); } } private static boolean CreateAndDeleteFile(String filename) { File file = new File(filename); boolean result = false; try { result = file.createNewFile(); System.out.println(Main.getRes().getString("Successfully created a new file ''{0}'' in the working directory.\nPress any key to delete it.", filename)); System.in.read(); if (!file.delete()) { System.out.println(Main.getRes().getString("Error while deleting the file")); } } catch(IOException ioe) { System.out.println(Main.getRes().getString("Error while creating a new empty file :") + ioe); result = false; } return result; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/LoadedGCOutput.java100644 0 0 3431 14333053652 25044 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class LoadedGCOutput { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { System.out.println( Main.getRes().getString( "This test is to make sure that large amounts of GCs with -verbose:gc\nenabled do not cause extra line feeds in output." ) ); System.out.println(); System.out.println( Main.getRes().getString( "This test will run indefinitely." ) ); try { Thread.sleep( 2000 ); } catch ( InterruptedException e ) { } for ( int i = 0; i < 10; i++ ) { // Start another thread to keep things busy creating lots of objects. Thread runner = new Thread( "LoadedGCOutput-busy-bee-" + i ) { public void run() { while ( true ) { Object test = new Object(); } } }; runner.setDaemon( true ); runner.start(); } while ( true ) { System.gc(); } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/LoadedLogOutput.java100644 0 0 3233 14333053652 25274 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class LoadedLogOutput { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { DateFormat df = new SimpleDateFormat( "yyyyMMdd'T'HHmmssSSS" ); /* // Let the JVM inform the Wrapper that it is started. try { Thread.sleep( 1500 ); } catch ( InterruptedException e ) { } */ long start = System.currentTimeMillis(); long now = start; System.out.println( Main.getRes().getString( "Log as much as possible for 60 seconds..." ) ); int line = 1; while ( now - start < 60000 ) { System.out.println( ( line++ ) + " : " + ( now - start ) + " : " + df.format( new Date( now ) ) + " : ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890" ); now = System.currentTimeMillis(); } System.out.println( Main.getRes().getString( "All done.") ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/LoadedSplitOutput.java100644 0 0 5046 14333053652 25652 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class LoadedSplitOutput { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { int maxDelay = 16384; System.out.println( Main.getRes().getString( "This test is to make sure that the Wrapper handles lines of split output correctly, only adding line feeds when the split delay is longer than the configured wrapper.log.lf_delay.threshold." ) ); System.out.println(); System.out.println( Main.getRes().getString( "This test will loop with delays between 1ms and {0}ms at increasing increments.", new Integer( maxDelay ) ) ); System.out.println(); try { Thread.sleep( 2000 ); } catch ( InterruptedException e ) { } int delay = 1; while ( delay <= maxDelay ) { System.out.print( "Head then delay for " + delay + "ms ..." ); try { Thread.sleep( delay ); } catch ( InterruptedException e ) { } System.out.println( "... Complete the line." ); delay += delay; } System.out.println(); System.out.println( Main.getRes().getString( "Print a progress bar with delays between 1ms and {0}ms at increasing increments.", new Integer( maxDelay ) ) ); System.out.println(); delay = 1; while ( delay <= maxDelay ) { System.out.print( "Start with " + delay + "ms delay." ); for ( int i = 0; i < 10; i++ ) { try { Thread.sleep( delay ); } catch ( InterruptedException e ) { } System.out.print( "." ); } System.out.println( " Done." ); delay += delay; } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/LoadedWrapperListener.java100644 0 0 27323 14333053652 26506 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.Date; import org.tanukisoftware.wrapper.WrapperListener; import org.tanukisoftware.wrapper.WrapperManager; /** * This test was created to test timeout problems under heavily loaded * conditions. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class LoadedWrapperListener implements WrapperListener, Runnable { private String[] m_startMainArgs; private boolean m_mainComplete; private Integer m_mainExitCode; private boolean m_waitTimedOut; /*--------------------------------------------------------------- * Constructor *-------------------------------------------------------------*/ private LoadedWrapperListener() { } /*--------------------------------------------------------------- * WrapperListener Methods *-------------------------------------------------------------*/ /** * The start method is called when the WrapperManager is signaled by the * native wrapper code that it can start its application. This * method call is expected to return, so a new thread should be launched * if necessary. * * @param args List of arguments used to initialize the application. * * @return Any error code if the application should exit on completion * of the start method. If there were no problems then this * method should return null. */ public Integer start( String[] args ) { if ( WrapperManager.isDebugEnabled() ) { System.out.println( Main.getRes().getString( "LoadedWrapperListener: start(args)" ) ); } Thread mainThread = new Thread( this, "LoadedWrapperListenerMain" ); synchronized ( this ) { m_startMainArgs = args; mainThread.start(); // Wait for five seconds to give the application a chance to have failed. try { this.wait( 5000 ); } catch ( InterruptedException e ) { } m_waitTimedOut = true; if ( WrapperManager.isDebugEnabled() ) { System.out.println( Main.getRes().getString( "LoadedWrapperListener: start(args) end. Main Completed={0}, exitCode={1}", new Object[]{ new Boolean( m_mainComplete ), m_mainExitCode } ) ); } return m_mainExitCode; } } /** * Called when the application is shutting down. The Wrapper assumes that * this method will return fairly quickly. If the shutdown code code * could potentially take a long time, then WrapperManager.signalStopping() * should be called to extend the timeout period. If for some reason, * the stop method can not return, then it must call * WrapperManager.stopped() to avoid warning messages from the Wrapper. * * @param exitCode The suggested exit code that will be returned to the OS * when the JVM exits. * * @return The exit code to actually return to the OS. In most cases, this * should just be the value of exitCode, however the user code has * the option of changing the exit code if there are any problems * during shutdown. */ public int stop( int exitCode ) { if ( WrapperManager.isDebugEnabled() ) { System.out.println( Main.getRes().getString( "LoadedWrapperListener: stop({0})", new Integer( exitCode ) ) ); } return exitCode; } /** * Called whenever the native wrapper code traps a system control signal * against the Java process. It is up to the callback to take any actions * necessary. Possible values are: WrapperManager.WRAPPER_CTRL_C_EVENT, * WRAPPER_CTRL_CLOSE_EVENT, WRAPPER_CTRL_LOGOFF_EVENT, or * WRAPPER_CTRL_SHUTDOWN_EVENT * * @param event The system control signal. */ public void controlEvent( int event ) { if ( WrapperManager.isControlledByNativeWrapper() ) { if ( WrapperManager.isDebugEnabled() ) { System.out.println( Main.getRes().getString( "LoadedWrapperListener: controlEvent({0}) Ignored", new Integer( event ) ) ); } // Ignore the event as the native wrapper will handle it. } else { if ( WrapperManager.isDebugEnabled() ) { System.out.println( Main.getRes().getString( "LoadedWrapperListener: controlEvent({0}) Stopping", new Integer(event) ) ); } // Not being run under a wrapper, so this isn't an NT service and should always exit. // Handle the event here. WrapperManager.stop( 0 ); // Will not get here. } } /*--------------------------------------------------------------- * Runnable Methods *-------------------------------------------------------------*/ /** * Runner thread which actually launches the application. */ public void run() { Throwable t = null; try { if ( WrapperManager.isDebugEnabled() ) { System.out.println( Main.getRes().getString( "LoadedWrapperListener: invoking start main method" ) ); } appMain( m_startMainArgs ); if ( WrapperManager.isDebugEnabled() ) { System.out.println( Main.getRes().getString( "LoadedWrapperListener: start main method completed" ) ); } synchronized ( this ) { // Let the start() method know that the main method returned, in case it is // still waiting. m_mainComplete = true; this.notifyAll(); } return; } catch (Throwable e) { t = e; } // If we get here, then an error was thrown. If this happened quickly // enough, the start method should be allowed to shut things down. System.out.println( Main.getRes().getString( "Encountered an error running start main: {0}", t ) ); t.printStackTrace(); synchronized( this ) { if ( m_waitTimedOut ) { // Shut down here. WrapperManager.stop( 1 ); return; // Will not get here. } else { // Let start method handle shutdown. m_mainComplete = true; m_mainExitCode = new Integer( 1 ); this.notifyAll(); return; } } } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ /** * Main method of the actual application. */ private void appMain( String[] args ) { System.out.println( Main.getRes().getString( "App Main Starting." ) ); System.out.println(); // Loop and display 500 long lines of text to place to dump a lot of // output before the CPU starts being loaded down. This will strain // the Wrapper just as the CPU suddenly hpegs at 100%. for ( int i = 0; i < 500; i++ ) { System.out.println( new Date() + Main.getRes().getString( " Pre {0} of output. " , new Integer( i ) ) + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ); } // Start up a thread to thrash the hard disk. Thread diskThrasher = new Thread( "LoadedWrapperListener_DiskThrasher" ) { public void run() { performDiskThrashing(); } }; diskThrasher.start(); // Start up a thread to thrash memory. Thread memoryThrasher = new Thread( "LoadedWrapperListener_MemoryThrasher" ) { public void run() { performMemoryThrashing(); } }; memoryThrasher.start(); // Start up some threads to eat all available CPU for ( int i = 0; i < 4; i++ ) { Thread cpuThrasher = new Thread( "LoadedWrapperListener_CPUThrasher_" + i ) { public void run() { performCPUThrashing(); } }; cpuThrasher.start(); } // Loop and display 5000 long lines of text to place a heavy load on the // JVM output processing code of the Wrapper while the above threads are // eating all available CPU. for ( int i = 0; i < 5000; i++ ) { System.out.println( new Date() + " Row " + i + " of output. " + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ); } System.out.println(); System.out.println( Main.getRes().getString( "App Main Complete." ) ); } private void performDiskThrashing() { while( !m_mainComplete ) { File file = new File( "loadedwrapperlistener.dat" ); try { PrintWriter w = new PrintWriter( new FileWriter( file ) ); try { for ( int i = 0; i < 100; i++ ) { w.println( new Date() + Main.getRes().getString( " Row {0} of output. ", new Integer( i ) ) + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" ); } } finally { w.close(); } } catch ( IOException e ) { e.printStackTrace(); } file.delete(); } } private void performMemoryThrashing() { while( !m_mainComplete ) { // 200MB block of memory byte[][] garbage = new byte[200][]; for ( int i = 0; i < garbage.length; i++ ) { garbage[i] = new byte[1024 * 1024]; } garbage = null; Runtime runtime = Runtime.getRuntime(); long totalMemory = runtime.totalMemory(); long freeMemory = runtime.freeMemory(); System.out.println( Main.getRes().getString( "Total Memory=" ) + totalMemory + ", " + Main.getRes().getString( "Used Memory=" ) + ( totalMemory - freeMemory ) ); } } private void performCPUThrashing() { while( !m_mainComplete ) { // Do nothing, we just want a tight loop. } } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { // Test an initial line feed as a regression test. Must be // the first output from the JVM System.out.println(); System.out.println( "LoadedWrapperListener.main" ); WrapperManager.start( new LoadedWrapperListener(), args ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/LogOutput.java100644 0 0 11315 14333053652 24203 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ import org.tanukisoftware.wrapper.WrapperManager; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class LogOutput { private static void sleep() { try { Thread.sleep(2000); } catch (InterruptedException e) {} } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { System.out.println( Main.getRes().getString( "Test the various log levels...") ); WrapperManager.log(WrapperManager.WRAPPER_LOG_LEVEL_DEBUG, Main.getRes().getString( "Debug output" ) ); WrapperManager.log(WrapperManager.WRAPPER_LOG_LEVEL_INFO, Main.getRes().getString( "Info output" ) ); WrapperManager.log(WrapperManager.WRAPPER_LOG_LEVEL_STATUS, Main.getRes().getString( "Status output" ) ); WrapperManager.log(WrapperManager.WRAPPER_LOG_LEVEL_WARN, Main.getRes().getString( "Warn output" ) ); WrapperManager.log(WrapperManager.WRAPPER_LOG_LEVEL_ERROR, Main.getRes().getString( "Error output" ) ); WrapperManager.log(WrapperManager.WRAPPER_LOG_LEVEL_FATAL, Main.getRes().getString( "Fatal output" ) ); // Let things catch up as the timing of WrapperManager.log output and System.out // output can not be guaranteed. sleep(); System.out.println(Main.getRes().getString( "Put the logger through its paces..." ) ); WrapperManager.log(WrapperManager.WRAPPER_LOG_LEVEL_INFO, Main.getRes().getString( "Special C characters in %s %d % %%" ) ); sleep(); WrapperManager.log(WrapperManager.WRAPPER_LOG_LEVEL_INFO, ""); sleep(); String sa = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; WrapperManager.log(WrapperManager.WRAPPER_LOG_LEVEL_INFO, Main.getRes().getString("Long log messages will be clipped at 4096 bytes when the Wrapper reads them.") ); WrapperManager.log(WrapperManager.WRAPPER_LOG_LEVEL_INFO, ""); WrapperManager.log(WrapperManager.WRAPPER_LOG_LEVEL_INFO, Main.getRes().getString("Output a 62 * 100 + 2 length string.")); StringBuffer sb = new StringBuffer(); for (int i = 0; i < 100; i++) { sb.append(sa); } WrapperManager.log(WrapperManager.WRAPPER_LOG_LEVEL_INFO, "[" + sb + "]" ); sleep(); WrapperManager.log(WrapperManager.WRAPPER_LOG_LEVEL_INFO, Main.getRes().getString("Output 100 62 character strings with line feeds between each one as a single log message.") ); sb = new StringBuffer(); for (int i = 0; i < 100; i++) { sb.append(sa); sb.append("\n"); } WrapperManager.log(WrapperManager.WRAPPER_LOG_LEVEL_INFO, "[" + sb + "]"); sleep(); WrapperManager.log(WrapperManager.WRAPPER_LOG_LEVEL_INFO, Main.getRes().getString("Output 100 62 character strings as individual log message.")); for (int i = 0; i < 100; i++) { WrapperManager.log(WrapperManager.WRAPPER_LOG_LEVEL_INFO, sa); } /* This string should not be translated because it contains Japanese characters. * A conversion error will be raised if a key of a translation file contains non-ascii characters. */ WrapperManager.log(WrapperManager.WRAPPER_LOG_LEVEL_INFO, "Japanese \u300c\u65e5\u672c\u8a9e\u306e\u8a66\u9a13 \u30a2\u30a4\u30a6\u30a8\u30aa\u30ab\u30ad\u30af\u30b1\u30b3\u300d Test"); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/LogStdoutStderr.java100644 0 0 4253 14333053652 25334 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class LogStdoutStderr { private static int m_lineNum = 1; private static int m_outLineNum = 1; private static int m_errLineNum = 1; /*--------------------------------------------------------------- * Static Methods *-------------------------------------------------------------*/ private static void logOut( String message ) { System.out.println( "All:" + (m_lineNum++) + " Out:" + (m_outLineNum++) + " " + message ); } private static void logErr( String message ) { System.err.println( "All:" + (m_lineNum++) + " Err:" + (m_errLineNum++) + " " + message ); } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { System.out.println( Main.getRes().getString( "Logs several lines of output to stdout and stderr." ) ); System.out.println( Main.getRes().getString( "Make sure that all of the line numbers are in order and that they show up in the right places." ) ); logOut( "Test Output" ); logErr( "Test Error" ); for ( int i = 0; i < 100; i++ ) { logOut( "Loop #" + i ); logErr( "Loop #" + i ); } StringBuffer sb = new StringBuffer(); for ( int i = 0; i < 100; i++ ) { sb.append( "abcdefghijklmnopqrstuvwxyz" ); logOut( "Loop #" + i + " " + sb.toString() ); logErr( "Loop #" + i + " " + sb.toString() ); } logOut( "All done." ); logErr( "All done." ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/LongRunningBackgroundThreads.java100644 0 0 6555 14333053652 30006 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ import org.tanukisoftware.wrapper.WrapperManager; /** * Test case which launched several threads and lets them run for 30 seconds. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class LongRunningBackgroundThreads implements Runnable { private volatile int m_threadCount; /*--------------------------------------------------------------- * Runnable Method *-------------------------------------------------------------*/ public void run() { m_threadCount++; int loops = 0; while ( loops < 60 ) { loops++; System.out.println( Main.getRes().getString( "{0} loop #{1}", new Object[]{ Thread.currentThread().getName(), new Integer( loops ) } ) ); try { Thread.sleep(500); } catch ( InterruptedException e ) { } } System.out.println(Main.getRes().getString( "{0} stopping", Thread.currentThread().getName() ) ); m_threadCount--; if ( m_threadCount <= 0 ) { System.out.println( Main.getRes().getString( "The JVM and then the wrapper should exit now.") ); } } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main( String[] args ) { System.out.println( Main.getRes().getString( "Long-running Background Threads Running..." ) ); LongRunningBackgroundThreads app = new LongRunningBackgroundThreads(); for ( int i = 0; i < 2; i++ ) { Thread thread = new Thread( app, "App-Thread-" + i ); thread.start(); } System.out.println( Main.getRes().getString( "Running as a service: {0}", new Boolean( WrapperManager.isLaunchedAsService() ) ) ); System.out.println( Main.getRes().getString( "Controlled by wrapper: {0}", new Boolean( WrapperManager.isControlledByNativeWrapper() ) ) ); System.out.println( Main.getRes().getString( "Long-running Background Threads Main Done...") ) ; } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/LongRunningWithShutdownHook.java100644 0 0 12136 14333053652 27714 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ import org.tanukisoftware.wrapper.WrapperManager; /** * Test case which launched several threads and lets them run for 30 seconds. * In addition a shutdown is registered. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class LongRunningWithShutdownHook implements Runnable { private volatile int m_threadCount; /*--------------------------------------------------------------- * Runnable Method *-------------------------------------------------------------*/ public void run() { m_threadCount++; int loops = 0; while ( loops < 60 ) { loops++; System.out.println( Main.getRes().getString( "{0} loop #{1}", new Object[]{ Thread.currentThread().getName(), new Integer( loops ) } ) ); try { Thread.sleep(500); } catch ( InterruptedException e ) { } } System.out.println(Main.getRes().getString( "{0} stopping", Thread.currentThread().getName() ) ); m_threadCount--; if ( m_threadCount <= 0 ) { System.out.println( Main.getRes().getString( "The JVM and then the wrapper should exit now.") ); } } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main( String[] args ) { System.out.println( Main.getRes().getString( "This test is a combination of the 'Long-running Background Threads' test followed by the execution of shutdown hook." ) ); System.out.println( Main.getRes().getString( "This is to test the wrapper.jvm_exit.timeout property when the Wrapper requests the JVM to stop (e.g with a CTRL+C)." ) ); System.out.println( Main.getRes().getString( "Running as a service: {0}", new Boolean( WrapperManager.isLaunchedAsService() ) ) ); System.out.println( Main.getRes().getString( "Controlled by wrapper: {0}", new Boolean( WrapperManager.isControlledByNativeWrapper() ) ) ); LongRunningBackgroundThreads app = new LongRunningBackgroundThreads(); for ( int i = 0; i < 2; i++ ) { Thread thread = new Thread( app, "App-Thread-" + i ); thread.start(); } // Register a shutdown hook Runtime.getRuntime().addShutdownHook( new Thread() { public void run() { System.out.println( Main.getRes().getString( "Starting shutdown hook. Loop for 25 seconds.") ); System.out.println( Main.getRes().getString( "Should timeout unless this property is set: wrapper.jvm_exit.timeout=30" ) ); long start = System.currentTimeMillis(); while ( System.currentTimeMillis() - start < 25000 ) { try { Thread.sleep( 250 ); } catch ( InterruptedException e ) { // Ignore } } System.out.println( Main.getRes().getString( "Shutdown hook complete. Should exit now." ) ); // Run GC to invoke finalize on unsed objects to test the shutdown process. System.out.println( "GC BEGIN" ); System.gc(); System.out.println( "GC END" ); try { Thread.sleep( 2000 ); } catch ( InterruptedException e ) { // Ignore } System.out.println( "DONE" ); } } ); System.out.println( Main.getRes().getString( "Application complete. Wrapper should stop, invoking the shutdown hooks." ) ); System.out.println(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/Main.java100644 0 0 116124 14333053652 23151 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ import java.awt.BorderLayout; import java.awt.Button; import java.awt.Checkbox; import java.awt.Component; import java.awt.Container; import java.awt.Font; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Label; import java.awt.List; import java.awt.Panel; import java.awt.ScrollPane; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JDialog; import javax.swing.BoxLayout; import javax.swing.border.Border; import javax.swing.border.EmptyBorder; import javax.swing.border.CompoundBorder; import org.tanukisoftware.wrapper.WrapperActionServer; import org.tanukisoftware.wrapper.WrapperListener; import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.WrapperResources; import org.tanukisoftware.wrapper.WrapperSystemPropertyUtil; import org.tanukisoftware.wrapper.event.WrapperEventListener; import org.tanukisoftware.wrapper.event.WrapperSecondInvocationEvent; import org.tanukisoftware.wrapper.event.WrapperServicePauseEvent; import org.tanukisoftware.wrapper.event.WrapperServiceResumeEvent; import org.tanukisoftware.wrapper.event.WrapperEvent; /** * This is a Test / Example program which can be used to test the * main features of the Wrapper. *

* It is also an example of Integration Method #3, where you implement * the WrapperListener interface manually. *

* NOTE that in most cases you will want to use Method #1, using the * WrapperSimpleApp helper class to integrate your application. Please * see the integration * section of the documentation for more details. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class Main extends AbstractActionApp implements WrapperListener { private WrapperActionServer m_actionServer; private MainFrame m_frame; private ActionRunner m_actionRunner; private static WrapperResources m_res; private List m_listenerFlags; private TextField m_slowSeconds; private TextField m_suspendSeconds; private TextField m_serviceName; private TextField m_consoleTitle; private TextField m_childCommand; private Checkbox m_childDetached; private JDialog m_pauseDialog; /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ private Main() { } /*--------------------------------------------------------------- * Inner Classes *-------------------------------------------------------------*/ private class MainFrame extends Frame implements ActionListener, WindowListener { /** * Serial Version UID. */ private static final long serialVersionUID = -3847376282833547574L; private float dpiScaleFactor = 1; MainFrame() { super( getRes().getString( "TestWrapper Example Application" ) ); try { // Rescale the GUI if the dpi awareness is 0 (not aware). Newer versions of Java are dpi aware and will automatically rescale the GUI. if ( WrapperManager.nativeGetDpiAwareness() == 0 ) { // Set the dpi awareness to "per monitor" and get the dpi scale factor. WrapperManager.nativeSetDpiAwareness(2); dpiScaleFactor = (float)WrapperManager.nativeGetDpiScale() / 96; } } catch ( UnsatisfiedLinkError e ) { // This can happen if an old native library is used. Fall through and make sure the scale factor is 1. dpiScaleFactor = 1; } init(); setLocation( 10, 10 ); setSize( (int)(750*dpiScaleFactor), (int)(480*dpiScaleFactor)); setResizable( true ); } private void init() { GridBagLayout gridBag = new GridBagLayout(); GridBagConstraints c = new GridBagConstraints(); Panel panel = new Panel(); panel.setLayout( gridBag ); ScrollPane scrollPane = new ScrollPane(); scrollPane.add( panel ); scrollPane.getHAdjustable().setUnitIncrement( 20 ); scrollPane.getVAdjustable().setUnitIncrement( 20 ); setLayout( new BorderLayout() ); add( scrollPane, BorderLayout.CENTER ); buildCommand( panel, gridBag, c, "Stop(0)", "stop0", getRes().getString( "Calls WrapperManager.stop( 0 ) to shutdown the JVM and Wrapper with a success exit code." ) ); buildCommand( panel, gridBag, c, "Stop(1)", "stop1", getRes().getString( "Calls WrapperManager.stop( 1 ) to shutdown the JVM and Wrapper with a failure exit code." ) ); buildCommand( panel, gridBag, c, "Exit(0)", "exit0", getRes().getString( "Calls System.exit( 0 ) to shutdown the JVM and Wrapper with a success exit code." ) ); buildCommand( panel, gridBag, c, "Exit(1)", "exit1", getRes().getString( "Calls System.exit( 1 ) to shutdown the JVM and Wrapper with a failure exit code." ) ); buildCommand( panel, gridBag, c, "StopImmediate(0)", "stopimmediate0", getRes().getString( "Calls WrapperManager.stopImmediate( 0 ) to immediately shutdown the JVM and Wrapper with a success exit code." ) ); buildCommand( panel, gridBag, c, "StopImmediate(1)", "stopimmediate1", getRes().getString(" Calls WrapperManager.stopImmediate( 1 ) to immediately shutdown the JVM and Wrapper with a failure exit code." ) ); buildCommand( panel, gridBag, c, "StopAndReturn(0)", "stopandreturn0", getRes().getString( "Calls WrapperManager.stopAndReturn( 0 ) to shutdown the JVM and Wrapper with a success exit code." ) ); buildCommand( panel, gridBag, c, "Nested Exit(1)", "nestedexit1", getRes().getString( "Calls System.exit(1) within WrapperListener.stop(1) callback." ) ); buildCommand( panel, gridBag, c, "Halt(0)", "halt0", getRes().getString( "Calls Runtime.getRuntime().halt(0) to kill the JVM, the Wrapper will restart it." ) ); buildCommand( panel, gridBag, c, "Halt(1)", "halt1", getRes().getString( "Calls Runtime.getRuntime().halt(1) to kill the JVM, the Wrapper will restart it." ) ); buildCommand( panel, gridBag, c, "Restart()", "restart", getRes().getString( "Calls WrapperManager.restart() to shutdown the current JVM and start a new one." ) ); buildCommand( panel, gridBag, c, "RestartAndReturn()", "restartandreturn", getRes().getString( "Calls WrapperManager.restartAndReturn() to shutdown the current JVM and start a new one." ) ); buildCommand( panel, gridBag, c, getRes().getString( "Native Access Violation" ), "access_violation_native", getRes().getString( "Causes an access violation using native code, the JVM will crash and be restarted." ) ); if ( WrapperManager.isWindows() ) { buildCommand( panel, gridBag, c, getRes().getString( "Native Exception" ), "exception_native", getRes().getString( "Throw an exception using native code, on Windows, the JVM will crash and be restarted." ) ); buildCommand( panel, gridBag, c, getRes().getString( "Native Fail Fast Exception" ), "ff_exception_native", getRes().getString( "Throw a fail fast exception using native code, on Windows, the JVM will crash and be restarted." ) ); } buildCommand( panel, gridBag, c, getRes().getString( "Simulate JVM Hang" ), "appear_hung", getRes().getString( "Makes the JVM appear to be hung as viewed from the Wrapper, it will be killed and restarted." ) ); m_slowSeconds = new TextField( "0" ); Panel slowPanel = new Panel(); slowPanel.setLayout( new BorderLayout() ); slowPanel.add( new Label( getRes().getString( "Delay Seconds: " ) ), BorderLayout.WEST ); slowPanel.add( m_slowSeconds, BorderLayout.CENTER ); Panel slowPanel2 = new Panel(); slowPanel2.setLayout( new BorderLayout() ); slowPanel2.add( slowPanel, BorderLayout.WEST ); slowPanel2.add( new Label( getRes().getString( "Makes the JVM appear sluggish by being slow to respond to all packet requests from the Wrapper." ) ), BorderLayout.CENTER ); buildCommand( panel, gridBag, c, getRes().getString( "Simulate Slow JVM" ), "appear_slow", slowPanel2 ); buildCommand( panel, gridBag, c, getRes().getString( "Create Deadlock" ), "deadlock", getRes().getString( "Creates two new threads which intentionally go into a deadlock situation. (Standard, Professional)" ) ); buildCommand( panel, gridBag, c, getRes().getString("Simulate Out Of Memory" ), "outofmemory", getRes().getString( "Throws an OutOfMemoryError to demonstrate the Trigger feature." ) ); buildCommand( panel, gridBag, c, getRes().getString( "Ignore Control Events" ), "ignore_events", getRes().getString( "Makes this application ignore control events. It will not shutdown in response to CTRL-C. The Wrapper will still respond." ) ); buildCommand( panel, gridBag, c,getRes().getString( "Request Thread Dump" ), "dump", getRes().getString( "Calls WrapperManager.requestThreadDump() to cause the JVM to dump its current thread state." ) ); buildCommand( panel, gridBag, c, getRes().getString( "System.out Deadlock" ), "deadlock_out", getRes().getString( "Simulates a failure mode where the System.out object has become deadlocked." ) ); m_suspendSeconds = new TextField( "60" ); Panel suspendPanel = new Panel(); suspendPanel.setLayout( new BorderLayout() ); suspendPanel.add( new Label( getRes().getString( "Seconds: " ) ), BorderLayout.WEST ); suspendPanel.add( m_suspendSeconds, BorderLayout.CENTER ); Panel suspendPanel2 = new Panel(); suspendPanel2.setLayout( new BorderLayout() ); suspendPanel2.add( suspendPanel, BorderLayout.WEST ); suspendPanel2.add( new Label( getRes().getString( "Suspend Wrapper timeouts while the JVM is running. (Standard, Professional)" ) ), BorderLayout.CENTER ); buildCommand( panel, gridBag, c, getRes().getString( "Suspend Timeouts" ), "suspend_timeouts", suspendPanel2 ); buildCommand( panel, gridBag, c, getRes().getString( "Resume Timeouts" ), "resume_timeouts", getRes().getString( "Resume Wrapper timeouts. (Standard, Professional)" ) ); buildCommand( panel, gridBag, c, getRes().getString( "Poll Users" ), "users", getRes().getString( "Begins calling WrapperManager.getUser() and getInteractiveUser() to monitor the current and interactive users." ) ); buildCommand( panel, gridBag, c, getRes().getString( "Poll Users with Groups" ), "groups", getRes().getString( "Same as above, but includes information about the user''s groups." ) ); buildCommand( panel, gridBag, c, getRes().getString( "Console" ), "console", getRes().getString( "Prompt for Actions in the console." ) ); buildCommand( panel, gridBag, c, getRes().getString( "Idle" ), "idle", getRes().getString( "Run idly." ) ); buildCommand( panel, gridBag, c, getRes().getString( "Dump Properties" ), "properties", getRes().getString( "Dumps all System Properties to the console." ) ); buildCommand( panel, gridBag, c, getRes().getString( "Dump Configuration" ), "configuration", getRes().getString( "Dumps all Wrapper Configuration Properties to the console." ) ); m_listenerFlags = new List( 2, true ); m_listenerFlags.add( "Service" ); m_listenerFlags.add( "Control" ); m_listenerFlags.add( "Logging" ); m_listenerFlags.add( "Core" ); m_listenerFlags.add( "RemoteControl" ); Panel flagPanel = new Panel(); flagPanel.setLayout( new BorderLayout() ); flagPanel.add( new Label( "Event Flags: " ), BorderLayout.WEST ); flagPanel.add( m_listenerFlags, BorderLayout.CENTER ); flagPanel.setSize( 100, 10 ); Panel flagPanel2 = new Panel(); flagPanel2.setLayout( new BorderLayout() ); flagPanel2.add( flagPanel, BorderLayout.WEST ); buildCommand( panel, gridBag, c, getRes().getString( "Update Event Listener" ), "listener", flagPanel2 ); buildCommand( panel, gridBag, c, getRes().getString( "Service List" ), "service_list", getRes().getString( "Displays a list of registered services on Windows." ) ); m_serviceName = new TextField( "testwrapper" ); Panel servicePanel = new Panel(); servicePanel.setLayout( new BorderLayout() ); servicePanel.add( new Label( getRes().getString( "Interrogate Service. Service name: " ) ), BorderLayout.WEST ); servicePanel.add( m_serviceName, BorderLayout.CENTER ); Panel servicePanel2 = new Panel(); servicePanel2.setLayout( new BorderLayout() ); servicePanel2.add( servicePanel, BorderLayout.WEST ); buildCommand( panel, gridBag, c, getRes().getString( "Service Interrogate" ), "service_interrogate", servicePanel2 ); buildCommand( panel, gridBag, c, getRes().getString( "Service Start" ), "service_start", getRes().getString( "Starts the above service." ) ); buildCommand( panel, gridBag, c, getRes().getString( "Service Stop" ), "service_stop", getRes().getString( "Stops the above service." ) ); buildCommand( panel, gridBag, c, getRes().getString( "Service User Code" ), "service_user", getRes().getString( "Sends a series of user codes to the above service." ) ); m_consoleTitle = new TextField( getRes().getString( "Java Service Wrapper" ) ); Panel titlePanel = new Panel(); titlePanel.setLayout( new BorderLayout() ); titlePanel.add( new Label( getRes().getString( "Console Title: " ) ), BorderLayout.WEST ); titlePanel.add( m_consoleTitle, BorderLayout.CENTER ); Panel titlePanel2 = new Panel(); titlePanel2.setLayout( new BorderLayout() ); titlePanel2.add( titlePanel, BorderLayout.WEST ); buildCommand( panel, gridBag, c, getRes().getString( "Set Console Title" ), "console_title", titlePanel2 ); m_childCommand = new TextField( getRes().getString( "(Please enter command)" ) ); m_childDetached = new Checkbox( getRes().getString( "Detached (Professional)" ), false); Panel childPanel = new Panel(); childPanel.setLayout( new BorderLayout() ); childPanel.add( new Label( getRes().getString( "Command: " ) ), BorderLayout.WEST ); childPanel.add( m_childCommand, BorderLayout.CENTER ); childPanel.add( m_childDetached, BorderLayout.EAST ); Panel childPanel2 = new Panel(); childPanel2.setLayout( new BorderLayout() ); childPanel2.add( childPanel, BorderLayout.WEST ); buildCommand( panel, gridBag, c, getRes().getString( "Execute Child Process" ), "child_exec", childPanel2 ); buildCommand( panel, gridBag, c, getRes().getString( "GC" ), "gc", getRes().getString( "Performs a GC sweep." ) ); buildCommand( panel, gridBag, c, getRes().getString( "Is Professional?" ), "is_professional", getRes().getString( "Prints true if this is a Professional Edition." ) ); buildCommand( panel, gridBag, c, getRes().getString( "Is Standard?" ), "is_standard", getRes().getString( "Prints true if this is a Standard Edition." ) ); buildCommand( panel, gridBag, c, getRes().getString( "Print time" ), "current_time", getRes().getString( "Prints the current time and timezone." ) ); addWindowListener( this ); // By default listen to Service and RemoteControl events. buildEventMask( new String[] { "Service", "RemoteControl" } ); updateEventListener(); } private void buildCommand( Container container, GridBagLayout gridBag, GridBagConstraints c, String label, String command, Object description ) { Button button = new Button( label ); button.setActionCommand( command ); if (dpiScaleFactor != 1) { button.setFont(new Font("Arial", Font.PLAIN, (int)(12 * dpiScaleFactor))); } c.fill = GridBagConstraints.BOTH; c.gridwidth = 1; gridBag.setConstraints( button, c ); container.add( button ); button.addActionListener(this); c.gridwidth = GridBagConstraints.REMAINDER; Component desc; if ( description instanceof String ) { desc = new Label( (String)description ); } else if ( description instanceof Component ) { desc = (Component)description; } else { desc = new Label( description.toString() ); } if (dpiScaleFactor != 1) { desc.setFont(new Font("Arial", Font.PLAIN, (int)(12 * dpiScaleFactor))); } gridBag.setConstraints( desc, c ); container.add( desc ); } private void buildEventMask(String[] flags) { // Create the mask. long mask = 0; for ( int i = 0; i < flags.length; i++ ) { String flag = flags[i]; if ( flag.equals( "Service" ) ) { mask |= WrapperEventListener.EVENT_FLAG_SERVICE; } else if ( flag.equals( "Control" ) ) { mask |= WrapperEventListener.EVENT_FLAG_CONTROL; } else if ( flag.equals( "Logging" ) ) { mask |= WrapperEventListener.EVENT_FLAG_LOGGING; } else if ( flag.equals( "Core" ) ) { mask |= WrapperEventListener.EVENT_FLAG_CORE; } else if ( flag.equals( "RemoteControl" ) ) { mask |= WrapperEventListener.EVENT_FLAG_REMOTE_CONTROL; } } setEventMask( mask ); } /************************************************************************** * ActionListener Methods *************************************************************************/ public void actionPerformed( ActionEvent event ) { String action = event.getActionCommand(); if ( action.equals( "listener" ) ) { String[] flags = m_listenerFlags.getSelectedItems(); buildEventMask(flags); } int slowSeconds; try { slowSeconds = Integer.parseInt( m_slowSeconds.getText() ); } catch ( NumberFormatException e ) { slowSeconds = 0; } m_slowSeconds.setText( Integer.toString( slowSeconds ) ); setSlowSeconds( slowSeconds ); int suspendSeconds; try { suspendSeconds = Integer.parseInt( m_suspendSeconds.getText() ); } catch ( NumberFormatException e ) { suspendSeconds = 0; } m_suspendSeconds.setText( Integer.toString( suspendSeconds ) ); setSuspendSeconds( suspendSeconds ); setServiceName( m_serviceName.getText() ); setConsoleTitle( m_consoleTitle.getText() ); setChildParams( m_childCommand.getText(), m_childDetached.getState() ); Main.this.doAction( action ); } /************************************************************************** * WindowListener Methods *************************************************************************/ public void windowOpened( WindowEvent e ) { } public void windowClosing( WindowEvent e ) { WrapperManager.stopAndReturn( 0 ); } public void windowClosed( WindowEvent e ) { } public void windowIconified( WindowEvent e ) { } public void windowDeiconified( WindowEvent e ) { } public void windowActivated( WindowEvent e ) { } public void windowDeactivated( WindowEvent e ) { } } private class ActionRunner implements Runnable { private String m_action; private boolean m_alive; public ActionRunner(String action) { m_action = action; m_alive = true; } public void run() { // Wait for a second so that the startup will complete. try { Thread.sleep(1000); } catch (InterruptedException e) {} if (!Main.this.doAction(m_action)) { printHelp("\"" + m_action + getRes().getString( "\" is an unknown action." ) ); WrapperManager.stop( 0 ); return; } while (m_alive) { // Idle some try { Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } } } public void endThread( ) { m_alive = false; } } /*--------------------------------------------------------------- * WrapperEventListener Methods *-------------------------------------------------------------*/ /** * Called whenever a WrapperEvent is fired. The exact set of events that a * listener will receive will depend on the mask supplied when * WrapperManager.addWrapperEventListener was called to register the * listener. * * Listener implementations should never assume that they will only receive * events of a particular type. To assure that events added to future * versions of the Wrapper do not cause problems with user code, events * should always be tested with "if ( event instanceof {EventClass} )" * before casting it to a specific event type. * * @param event WrapperEvent which was fired. */ public void fired( WrapperEvent event ) { if (event instanceof WrapperSecondInvocationEvent ) { // bring the window to front bringToFront(m_frame); } if (event instanceof WrapperServicePauseEvent ) { // pause the demo application pause(); } if (event instanceof WrapperServiceResumeEvent ) { // resume the demo application resume(); } } /*--------------------------------------------------------------- * WrapperListener Methods *-------------------------------------------------------------*/ public Integer start( String[] args ) { String command; System.out.println( getRes().getString( "TestWrapper: start()" ) ); prepareSystemOutErr(); if ( args.length <= 0 ) { System.out.println( getRes().getString( "TestWrapper: An action was not specified. Default to \"dialog\". Use \"help\" for list of actions." ) ); command = "dialog"; } else { command = args[0]; } if ( command.equals( "help" ) ) { printHelp( null ); return null; } try { int port = 9999; m_actionServer = new WrapperActionServer( port ); m_actionServer.enableShutdownAction( true ); m_actionServer.enableHaltExpectedAction( true ); m_actionServer.enableRestartAction( true ); m_actionServer.enableThreadDumpAction( true ); m_actionServer.enableHaltUnexpectedAction( true ); m_actionServer.enableAccessViolationAction( true ); m_actionServer.enableAppearHungAction( true ); m_actionServer.start(); System.out.println( getRes().getString( "TestWrapper: ActionServer Enabled. " ) ); System.out.println( getRes().getString( "TestWrapper: Telnet localhost 9999" ) ); System.out.println( getRes().getString( "TestWrapper: Commands: " ) ); System.out.println( getRes().getString( "TestWrapper: S: Shutdown" ) ); System.out.println( getRes().getString( "TestWrapper: H: Expected Halt" ) ); System.out.println( getRes().getString( "TestWrapper: R: Restart" ) ); System.out.println( getRes().getString( "TestWrapper: D: Thread Dump" ) ); System.out.println( getRes().getString( "TestWrapper: U: Unexpected Halt (Simulate crash)" ) ); System.out.println( getRes().getString( "TestWrapper: V: Access Violation (Actual crash)" ) ); System.out.println( getRes().getString( "TestWrapper: G: Make the JVM appear to be hung." ) ); System.out.println( "TestWrapper: " ); } catch ( java.io.IOException e ) { System.out.println( getRes().getString( "TestWrapper: Unable to open the action server socket: {0}", e.getMessage() ) ); System.out.println( "TestWrapper: " ); m_actionServer = null; } if ( command.equals( "dialog" ) ) { System.out.println( getRes().getString( "TestWrapper: Showing dialog..." ) ); try { m_frame = new MainFrame(); m_frame.setVisible( true ); } catch ( java.lang.InternalError e ) { System.out.println( "TestWrapper: " ); System.out.println( getRes().getString( "TestWrapper: ERROR - Unable to display the GUI:" ) ); System.out.println( "TestWrapper: " + e.toString() ); System.out.println( "TestWrapper: " ); System.out.println( getRes().getString( "TestWrapper: Fall back to the \"console\" action." ) ); command = "console"; } catch ( java.awt.AWTError e ) { System.out.println( "TestWrapper: " ); System.out.println( getRes().getString( "TestWrapper: ERROR - Unable to display the GUI:" ) ); System.out.println( "TestWrapper: " + e.toString() ); System.out.println( "TestWrapper: " ); System.out.println( getRes().getString( "TestWrapper: Fall back to the \"console\" action." ) ); command = "console"; } catch ( java.lang.UnsupportedOperationException e ) { // java.awt.HeadlessException does not exist in Java versions prior to 1.4 if ( e.getClass().getName().equals( "java.awt.HeadlessException" ) ) { System.out.println( "TestWrapper: " ); System.out.println( getRes().getString( "TestWrapper: ERROR - Unable to display the GUI:" ) ); System.out.println( "TestWrapper: " + e.toString() ); System.out.println( "TestWrapper: " ); System.out.println( getRes().getString( "TestWrapper: Fall back to the \"console\" action." ) ); command = "console"; } else { throw e; } } } if ( !command.equals( "dialog" ) ) { // * * Start the action thread m_actionRunner = new ActionRunner( command ); Thread actionThread = new Thread( m_actionRunner ); actionThread.start(); } return null; } public int stop( int exitCode ) { System.out.println( getRes().getString( "TestWrapper: stop({0})", new Integer( exitCode ) ) ); if ( m_actionServer != null ) { try { m_actionServer.stop(); } catch ( Exception e ) { System.out.println( getRes().getString( "TestWrapper: Unable to stop the action server: {0}", e.getMessage() ) ); } } if ( m_frame != null ) { if ( !WrapperManager.hasShutdownHookBeenTriggered() ) { m_frame.setVisible( false ); m_frame.dispose(); } m_frame = null; } if ( isNestedExit() ) { System.out.println( getRes().getString( "TestWrapper: calling System.exit({0}) within stop.", String.valueOf(exitCode) ) ); System.exit( exitCode ); } return exitCode; } /** * Pause the demo application. */ private void pause() { // Update the title with the "paused" state. m_frame.setTitle( m_frame.getTitle() + getRes().getString( " (paused)" ) ); // Disable all components of the frame. enableComponents( m_frame, false ); // Show a dialog centered over the frame to indicate that the application was paused and is waiting the resume event. JLabel label1 = new JLabel(); label1.setHorizontalAlignment( javax.swing.SwingConstants.CENTER ); label1.setAlignmentX(Component.CENTER_ALIGNMENT); label1.setText( getRes().getString( "The demo application is paused." ) ); JLabel label2 = new JLabel(); label2.setHorizontalAlignment( javax.swing.SwingConstants.CENTER ); label2.setAlignmentX(Component.CENTER_ALIGNMENT); label2.setText( getRes().getString( "Resume it to make this dialog box disappear." ) ); Border border1 = label1.getBorder(); Border margin1 = new EmptyBorder( 25, 25, 0, 25 ); label1.setBorder( new CompoundBorder(border1, margin1) ); Border border2 = label2.getBorder(); Border margin2 = new EmptyBorder( 0, 25, 30, 25 ); label2.setBorder( new CompoundBorder(border2, margin2) ); JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS)); panel.add( label1 ); panel.add( label2 ); m_pauseDialog = new JDialog( m_frame, m_frame.getTitle(), false ); m_pauseDialog.getContentPane().add( panel ); m_pauseDialog.setResizable( false ); m_pauseDialog.setDefaultCloseOperation( javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE ); m_pauseDialog.pack(); m_pauseDialog.setLocationRelativeTo( m_frame ); m_pauseDialog.setVisible( true ); } /** * Resume the application. */ private void resume() { String title = m_frame.getTitle(); // Update the title to remove the "paused" state. m_frame.setTitle( title.substring( 0, title.indexOf( getRes().getString( " (paused)" ) ) ) ); // Enable all components of the frame. enableComponents( m_frame, true ); // Close the dialog. m_pauseDialog.setDefaultCloseOperation( javax.swing.WindowConstants.DISPOSE_ON_CLOSE ); m_pauseDialog.setVisible(false); m_pauseDialog.dispatchEvent(new WindowEvent(m_pauseDialog, WindowEvent.WINDOW_CLOSING)); } public void controlEvent( int event ) { System.out.println( getRes().getString( "TestWrapper: controlEvent({0})", new Integer( event ) ) ); if ( event == WrapperManager.WRAPPER_CTRL_LOGOFF_EVENT ) { if ( WrapperManager.isLaunchedAsService() || WrapperManager.isIgnoreUserLogoffs() ) { System.out.println( getRes().getString( "TestWrapper: Ignoring logoff event" ) ); // Ignore } else if ( !ignoreControlEvents() ) { WrapperManager.stop( 0 ); } } else if ( event == WrapperManager.WRAPPER_CTRL_C_EVENT ) { if ( !ignoreControlEvents() ) { //WrapperManager.stop(0); // May be called before the runner is started. if (m_actionRunner != null) { m_actionRunner.endThread(); } } } else { if ( !ignoreControlEvents() ) { WrapperManager.stop( 0 ); } } } /*--------------------------------------------------------------- * Static Methods *-------------------------------------------------------------*/ /** * Prints the usage text. * * @param error_msg Error message to write with usage text */ private static void printHelp( String errorMsg ) { System.err.println( getRes().getString( "USAGE" ) ); System.err.println( "" ); System.err.println( getRes().getString( "TestWrapper " ) ); printActions(); System.err.println( getRes().getString( " Interactive:" ) ); System.err.println( getRes().getString( " dialog : Shows the dialog interface" ) ); System.err.println( getRes().getString( "[EXAMPLE]" ) ); System.err.println( getRes().getString( " TestAction access_violation_native " ) ); System.err.println( "" ); if ( errorMsg != null ) { System.err.println( getRes().getString( "ERROR: " ) + errorMsg ); System.err.println( "" ); } System.exit( 1 ); } /** * Request the Resources object for this application. */ public static WrapperResources getRes() { if ( m_res == null ) { // Synchronize and then recheck to avoid this method always synchronizing. synchronized( Main.class ) { if ( m_res == null ) { m_res = WrapperManager.loadWrapperResources( "wrapperTestApp", WrapperSystemPropertyUtil.getStringProperty( "wrapper.lang.folder", "../lang" ) ); } } } return m_res; } /** * Helper method to bring a window to the front */ private boolean bringToFront(MainFrame frame) { try { // use reflection as setAlwaysOnTop() requires java 1.5 java.lang.reflect.Method m = MainFrame.class.getMethod("setAlwaysOnTop", new Class[] { boolean.class } ); m.invoke(frame, new Object[] { new Boolean(true) }); m.invoke(frame, new Object[] { new Boolean(false) }); return true; } catch (Exception e) { return false; } } /** * Helper method to disable or enable all components of a container */ private void enableComponents(Container container, boolean enable) { Component[] components = container.getComponents(); for (int a = 0; a < components.length; a++) { components[a].setEnabled( enable ); if (components[a] instanceof Container) { enableComponents((Container)components[a], enable); } } } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ /** * IMPORTANT: Please read the Javadocs for this class at the top of the * page before you start to use this class as a template for integrating * your own application. This will save you a lot of time. */ public static void main( String[] args ) { System.out.println( getRes().getString( "TestWrapper: Initializing..." )); // System.out.println(wrm.getString("test 0 = {0} String = {1} double = {2} object = {3}", new Object[]{ String.valueOf(99), "test",String.valueOf(34.33), "testob"}) ); // Start the application. If the JVM was launched from the native // Wrapper then the application will wait for the native Wrapper to // call the application's start method. Otherwise the start method // will be called immediately. WrapperManager.start( new Main(), args ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/Memory.java100644 0 0 13376 14333053652 23522 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class Memory implements Runnable { private static Memory c_theInstance; private Thread m_runner; /*--------------------------------------------------------------- * Constructor *-------------------------------------------------------------*/ private Memory() { // Start the runner m_runner = new Thread( this, "runner" ); m_runner.start(); } /*--------------------------------------------------------------- * Runnable Method *-------------------------------------------------------------*/ public void run() { long startTime = System.currentTimeMillis(); long lastTest = startTime; try { File file = new File( "../logs/memory.log" ); System.out.println( Main.getRes().getString( "Writing memory Log to: {0}", file ) ); Writer writer = new FileWriter( file ); try { writer.write( Main.getRes().getString( "--> Starting Memory Log\n" ) ); writer.flush(); while( m_runner != null ) { long now = System.currentTimeMillis(); System.out.println( Main.getRes().getString( "Running for {0}ms...", new Long( now - startTime ) ) ); if ( now - lastTest > 15000 ) { Runtime rt = Runtime.getRuntime(); System.gc(); long totalMemory = rt.totalMemory(); long freeMemory = rt.freeMemory(); long usedMemory = totalMemory - freeMemory; writer.write( Main.getRes().getString( "total memory=" ) + pad( totalMemory, 10 ) + Main.getRes().getString( ", used=" ) + pad( usedMemory, 10 ) + Main.getRes().getString( ", free=" ) + pad( freeMemory, 10 ) + "\n" ); writer.flush(); lastTest = now; } try { Thread.sleep( 250 ); } catch ( InterruptedException e ) { } } writer.write( Main.getRes().getString( "<-- Stopping Memory Log\n" ) ); writer.flush(); } finally { writer.close(); } } catch ( IOException e ) { e.printStackTrace(); } } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ private static final String PADDING = " "; private String pad( long n, int len ) { String s = Long.toString( n ); int sLen = s.length(); if ( sLen < len ) { s = s + PADDING.substring( 0, len - sLen ); } return s; } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { System.out.println( Main.getRes().getString( "Memory Tester Running..." ) ); c_theInstance = new Memory(); // Register a shutdown hook. Runtime.getRuntime().addShutdownHook( new Thread( "shutdown-hook" ) { public void run() { System.out.println(Main.getRes().getString( "Stopping..." ) ); Thread runner = c_theInstance.m_runner; // Tell the main thread to stop. c_theInstance.m_runner = null; // Wait for the thread to actually stop cleanly. try { runner.join(); } catch ( InterruptedException e ) { } System.out.println(Main.getRes().getString( "Stopped." ) ); } } ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/NestedWrapperListener.java100644 0 0 5100 14333053652 26505 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.WrapperListener; /** * This is a simple test to see how the WrapperManager behaves when a main * class designed for Integration Method #3 is called using Integration * Method #2. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class NestedWrapperListener implements WrapperListener { /************************************************************************** * Constructors *************************************************************************/ private NestedWrapperListener() { System.out.println( "NestedWrapperListener()" ); } /************************************************************************** * WrapperListener Methods *************************************************************************/ public Integer start( String[] args ) { System.out.println( "NestedWrapperListener.start()" ); return null; } public int stop( int exitCode ) { System.out.println( "NestedWrapperListener.stop(" + exitCode + ")" ); return exitCode; } public void controlEvent(int event) { System.out.println( "NestedWrapperListener.controlEvent(" + event + ")" ); } /************************************************************************** * Main Method *************************************************************************/ public static void main( String[] args ) { System.out.println( Main.getRes().getString( "NestedWrapperListener.Initializing..." ) ); System.out.println( Main.getRes().getString("An error saying that the WrapperManager has already been started should be displayed and the JVM will exit." )); // Start the application. If the JVM was launched from the native // Wrapper then the application will wait for the native Wrapper to // call the application's start method. Otherwise the start method // will be called immediately. WrapperManager.start( new NestedWrapperListener(), args ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/NoReturn.java100644 0 0 3532 14333053652 23777 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class NoReturn { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { System.out.println(Main.getRes().getString( "Main starting loop that will not return" ) ); while(true) { try { Thread.sleep(1000); } catch (InterruptedException e) { } System.out.println(Main.getRes().getString( "NoReturn.main() running...") ); } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/OnExit.java100644 0 0 4053 14333053652 23430 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.WrapperManager; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class OnExit { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { System.out.println( Main.getRes().getString( "Test the handling of on exit handlers." ) ); System.out.println( Main.getRes().getString( "The Wrapper should restart the JVM when it detects and exit code of " ) ); System.out.println( Main.getRes().getString( " 1, 2, or any code except 3. It will then shutdown if it detects " ) ); System.out.println( Main.getRes().getString( " an exit code of 3." ) ); System.out.println(); int exitCode = WrapperManager.getJVMId(); switch ( exitCode ) { case 1: case 2: System.out.println( Main.getRes().getString( "Stopping the JVM with an exit code of {0},\nthe Wrapper should restart.", new Integer( exitCode ) ) ); break; case 3: System.out.println( Main.getRes().getString( "Stopping the JVM with an exit code of {0},\nthe Wrapper should stop.", new Integer( exitCode ) ) ); break; default: System.out.println( Main.getRes().getString( "The Wrapper should have stopped on the previous exitCode 3." ) ); System.out.println( Main.getRes().getString( "We should not be here." ) ); break; } WrapperManager.stop( exitCode ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/OpenVariousSizeWindows.java100644 0 0 22046 14333053652 26724 0ustar 0 0 package org.tanukisoftware.wrapper.test; import javax.swing.*; import java.awt.*; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ public class OpenVariousSizeWindows { static class MyRunnable implements Runnable { public int width; public int height; public int top = 0; public int left = 0; public boolean createInvisible; public int maximizedState = 0; public int totNumWindows; public int curWindowIndex; public void run() { System.out.println( Main.getRes().getString( "Creating new {0} (width={1}, height={2}, top={3}, left={4}, createInvisible={5})...", new Object[] { ( maximizedState > 0 ? "JFrame" : "JDialog" ), new Integer( width ), new Integer( height ), new Integer( top ), new Integer( left ), ( createInvisible ? "true" : "false" ) } ) ); Window window; if ( maximizedState > 0 ) { window = new JFrame(); ((JFrame)window).setExtendedState( maximizedState ); ((JFrame)window).setTitle( "A window (JFrame) - " + curWindowIndex + " / " + totNumWindows ); } else { // JDialog causes more problem because the second instance may also cause the bug (only the first instance of JFrame will matter) window = new JDialog(); ((JDialog)window).setModal( true ); ((JDialog)window).setDefaultCloseOperation( WindowConstants.DISPOSE_ON_CLOSE ); ((JDialog)window).setTitle( "A window (JDialog) - " + curWindowIndex + " / " + totNumWindows ); } window.setBounds( top, left, width, height ); try { if ( createInvisible ) { window.setVisible( false ); Thread.sleep( 1000 ); } window.setVisible( true ); } catch ( Exception e ) { System.out.println( e ); } } }; public static void main( String[] args ) { System.out.println( "Opening several windows of different size every 4 secs." ); System.out.println( " Make sure that the hidden console doesn't reappear (wrapperw)." ); System.out.println( " You can maximize the windows or move them to see if something happens..." ); System.out.println( " You can use wrapper.app.parameter.2= & wrapper.app.parameter.3= to specify start and end indexes." ); System.out.println( "" ); Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); // the screen height int screenW = (int)screenSize.getWidth(); // the screen width int screenH = (int)screenSize.getHeight(); /* left, top, width, height, createInvisible */ int[][] coords = { /* first windows will be maximized (first both, then horizontally, then vertically). */ /* 00 */ { 0, 0, screenW , screenH, 0}, /* 01 */ { 0, 0, screenW , screenH, 0}, /* 02 */ { 0, 0, screenW , screenH, 0}, /* 03 */ { 0, 0, screenW , screenH, 0}, /* 04 */ { 0, 0, screenW / 2, screenH, 0}, /* 05 */ { 0, 0, screenW , screenH / 2, 0}, /* 06 */ { 0, 0, screenW / 2, screenH / 2, 0}, /* 07 */ { 0, 0, screenW * 2, screenH, 0}, /* 08 */ { 0, 0, screenW , screenH * 2, 0}, /* 09 */ { 0, 0, screenW * 2, screenH * 2, 0}, /* 10 */ { 0, 0, screenW , screenH, 1}, /* 11 */ { 0, 0, screenW / 2, screenH, 1}, /* 12 */ { 0, 0, screenW , screenH / 2, 1}, /* 13 */ { 0, 0, screenW / 2, screenH / 2, 1}, /* 14 */ { 0, 0, screenW * 2, screenH, 1}, /* 15 */ { 0, 0, screenW , screenH * 2, 1}, /* 16 */ { 0, 0, screenW * 2, screenH * 2, 1}, /* 17 */ { screenW / 3, screenH / 3, screenW , screenH, 0}, /* 18 */ { screenW / 3, screenH / 3, screenW / 2, screenH, 0}, /* 19 */ { screenW / 3, screenH / 3, screenW , screenH / 2, 0}, /* 20 */ { screenW / 3, screenH / 3, screenW / 2, screenH / 2, 0}, /* 21 */ { screenW / 3, screenH / 3, screenW * 2, screenH, 0}, /* 22 */ { screenW / 3, screenH / 3, screenW , screenH * 2, 0}, /* 23 */ { screenW / 3, screenH / 3, screenW * 2, screenH * 2, 0}, /* 24 */ { screenW / 3, screenH / 3, screenW , screenH, 1}, /* 25 */ { screenW / 3, screenH / 3, screenW / 2, screenH, 1}, /* 26 */ { screenW / 3, screenH / 3, screenW , screenH / 2, 1}, /* 27 */ { screenW / 3, screenH / 3, screenW / 2, screenH / 2, 1}, /* 28 */ { screenW / 3, screenH / 3, screenW * 2, screenH, 1}, /* 29 */ { screenW / 3, screenH / 3, screenW , screenH * 2, 1}, /* 30 */ { screenW / 3, screenH / 3, screenW * 2, screenH * 2, 1}, /* 31 */ { screenW / 2 - 50, screenH / 2 - 50, screenW , screenH, 0}, /* 32 */ { screenW / 2 - 50, screenH / 2 - 50, screenW / 2, screenH, 0}, /* 33 */ { screenW / 2 - 50, screenH / 2 - 50, screenW , screenH / 2, 0}, /* 34 */ { screenW / 2 - 50, screenH / 2 - 50, screenW / 2, screenH / 2, 0}, /* 35 */ { screenW / 2 - 50, screenH / 2 - 50, screenW * 2, screenH, 0}, /* 36 */ { screenW / 2 - 50, screenH / 2 - 50, screenW , screenH * 2, 0}, /* 37 */ { screenW / 2 - 50, screenH / 2 - 50, screenW * 2, screenH * 2, 0}, /* 38 */ { screenW / 2 - 50, screenH / 2 - 50, screenW , screenH, 1}, /* 39 */ { screenW / 2 - 50, screenH / 2 - 50, screenW / 2, screenH, 1}, /* 40 */ { screenW / 2 - 50, screenH / 2 - 50, screenW , screenH / 2, 1}, /* 41 */ { screenW / 2 - 50, screenH / 2 - 50, screenW / 2, screenH / 2, 1}, /* 42 */ { screenW / 2 - 50, screenH / 2 - 50, screenW * 2, screenH, 1}, /* 43 */ { screenW / 2 - 50, screenH / 2 - 50, screenW , screenH * 2, 1}, /* 44 */ { screenW / 2 - 50, screenH / 2 - 50, screenW * 2, screenH * 2, 1}, }; int start; try { start = args.length > 0 ? Integer.parseInt( args[0] ) : 0; } catch ( NumberFormatException e ) { start = 0; } int end; try { end = args.length > 1 ? Integer.parseInt( args[1] ) : coords.length; } catch ( NumberFormatException e ) { end = coords.length; } MyRunnable r = new OpenVariousSizeWindows.MyRunnable(); r.totNumWindows = end - start + 1; if ( ( start == 0 && end == 0 ) || ( start == 1 && end == 1 ) || ( start == 1 && end == 1 ) ) { System.out.println( Main.getRes().getString( " Create 1 Jframe (it was observed that the first created JFrame can cause the hidden console to reappear).") ); } else if ( start > 3 ) { System.out.println( Main.getRes().getString( " Create {0} JDialog(s) (it was observed that any instance created - not only the first one - can cause the hidden console to reappear).", new Integer( r.totNumWindows ) ) ); } else { System.out.println( Main.getRes().getString( " Create {0} Window(s).", new Integer( r.totNumWindows ) ) ); } System.out.println( "" ); for ( int i = start; i <= end; i++ ) { try { r.createInvisible = ( coords[i][4] != 0 ); if ( i > start ) { Thread.sleep( r.createInvisible ? 3000 : 4000 ); } if ( i == 0 ) { r.maximizedState = JFrame.MAXIMIZED_BOTH; } else if ( i == 1 ) { r.maximizedState = JFrame.MAXIMIZED_HORIZ; } else if ( i == 2 ) { r.maximizedState = JFrame.MAXIMIZED_VERT; } r.top = coords[i][0]; r.left = coords[i][1]; r.width = coords[i][2]; r.height = coords[i][3]; r.curWindowIndex = i - start + 1; SwingUtilities.invokeLater( r ); } catch ( Exception e ) { System.out.println( e ); } } try { Thread.sleep( 4000 ); } catch ( Exception e ) { } System.exit( 0 ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/OutputLoader.java100644 0 0 5071 14333053652 24652 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ import java.io.FileWriter; import java.io.IOException; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class OutputLoader { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { System.out.println( Main.getRes().getString( "Start outputting lots of data.") ); long start = System.currentTimeMillis(); int count = 0; while ((System.currentTimeMillis()) < start + 20000) { System.out.println( Main.getRes().getString( "Testing line Out #{0}", new Integer( ++count ) ) ); System.err.println( Main.getRes().getString( "Testing line Err #{0}", new Integer( ++count ) ) ); } System.out.println( Main.getRes().getString( "Printed {0} lines of output in 20 seconds", new Integer( count ) ) ); // Write the output to a file as well, so we can see the results // when output is disabled. try { FileWriter fw = new FileWriter("OutputLoader.log", true); fw.write( Main.getRes().getString( "Printed {0} lines of output in 20 seconds", new Integer( count ) ) ); fw.close(); } catch (IOException e) { e.printStackTrace(); } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/Passthrough.java100644 0 0 4712 14333053652 24533 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ import org.tanukisoftware.wrapper.WrapperManager; /** * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class Passthrough { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { System.out.println( Main.getRes().getString( "\nTest passthrough parameters...") ); if ( args.length > 0 ) { if ( "start".compareTo(args[0]) == 0 ) { System.out.println( Main.getRes().getString( "\nStarting the application with the following parameter(s):") ); } else if ( "stop".compareTo(args[0]) == 0 ) { System.out.println( Main.getRes().getString( "\nStopping the application with the following parameter(s):") ); } else { printError(); } for (int i = 1; i < args.length; i++) { System.out.println(args[i]); } } else { printError(); } } /** * Just print a general error message */ public static void printError() { System.out.println( Main.getRes().getString( "Invalid parameter...") ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/PercentOutput.java100644 0 0 5340 14333053652 25043 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ /** * Tests Bug #531880 where % characters in the text output were causing * the wrapper to crash. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class PercentOutput { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { System.out.println( Main.getRes().getString( "Starting Test..." ) ); System.out.println( "%"); System.out.println("%%"); System.out.println("%s"); System.out.println("%S"); System.out.println("%d"); System.out.println("\\%s%%"); System.out.println("\\%S%%"); // This is a case from a user crash System.out.println(" and vg.foobar like '%SEARCHKEY=vendorid%'"); // Lots more output mixed with various quotes. // This will test the code that causes the internal buffer to be expanded. for ( int i = 0; i < 100; i++ ) { StringBuffer sb = new StringBuffer(); sb.append( i ); sb.append( ": " ); for ( int j = 0; j < i; j++ ) { sb.append( ":01234567890123456%s90123456789012345%d890123456789012345%S8901234%%789012%S5678901234%p789012345678" ); } sb.append( ":END" ); System.out.println( sb.toString() ); } System.out.println( Main.getRes().getString( "Test Complete..." ) ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/Performance.java100644 0 0 14740 14333053652 24507 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * Can be run without the Wrapper as: * java -classpath lib/wrappertest.jar org.tanukisoftware.wrapper.test.Performance * * NOTE - This class intentionally does not use resources for localization because it * needs to be able to be run without the Wrapper components being loaded. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class Performance { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ private static void initialize() { System.out.println( "Run some loops for a few seconds to give some time for the JVM to get up and running..." ); long now = System.currentTimeMillis(); long count = 1000000000L; double total = 0; for ( long i = 0; i < count; i++ ) { // Take up some time. double x = 10.5 + i; double y = 3.14159 * i; double z = x * y; total += z; } long time = System.currentTimeMillis() - now; System.out.println( " Complete. Total time=" + ( (double)time / 1000 ) + " seconds. (" + total + ")" ); } private static void dumpThreadGroup( ThreadGroup threadGroup, String indent ) { System.out.println( indent + threadGroup.toString() ); Thread[] threads = new Thread[(int)Math.ceil(threadGroup.activeCount() * 1.2)]; threadGroup.enumerate( threads, false ); System.out.println( indent + "Threads:" ); for ( int i = 0; i < threads.length; i++ ) { if ( threads[i] != null ) { System.out.println( indent + " " + threads[i] ); } } ThreadGroup[] threadGroups = new ThreadGroup[(int)Math.ceil(threadGroup.activeGroupCount() * 1.2)]; threadGroup.enumerate( threadGroups, false ); System.out.println( indent + "Thread Groups:" ); for ( int i = 0; i < threadGroups.length; i++ ) { if ( threadGroups[i] != null ) { dumpThreadGroup( threadGroups[i], indent + " " ); } } } private static void dumpThreadInfo() { Thread thread = Thread.currentThread(); System.out.println( "Thread: " + thread ); /* * Thread.getId() was introduced in 1.5, not available in java 1.4 * System.out.println( " Id: " + thread.getId() ); */ System.out.println( " Name: " + thread.getName() ); System.out.println( " Priority: " + thread.getPriority() ); System.out.println( " ThreadGroup Name: " + thread.getThreadGroup().getName() ); System.out.println( " isDaemon: " + thread.isDaemon() ); System.out.println( "All Threads:" ); ThreadGroup topThreadGroup = thread.getThreadGroup(); while ( topThreadGroup.getParent() != null ) { topThreadGroup = topThreadGroup.getParent(); } dumpThreadGroup( topThreadGroup, " " ); } private static void inMemoryLoopsMath() { System.out.println( "Starting in memory loop test (Math)..." ); long now = System.currentTimeMillis(); long count = 10000000000L; double total = 0; for ( long i = 0; i < count; i++ ) { // Do some math work. double x = 10.5 + i; double y = 3.14159 * i; double z = x * y; total += z; } long time = System.currentTimeMillis() - now; System.out.println( " Complete. Total time=" + ( (double)time / 1000 ) + " seconds. (" + ( (double)time * 1000 / count ) + " usec per cycle.) (" + total + ")" ); } private static void inMemoryLoopsString() { System.out.println( "Starting in memory loop test (Strings)..." ); long now = System.currentTimeMillis(); long count = 100000000L; double total = 0; for ( long i = 0; i < count; i++ ) { // Do some string work. This causes the stack to be used because and is thus slower. String a = "This is a test."; String b = "This is the end."; String c = a + i + b; total += c.length(); } long time = System.currentTimeMillis() - now; System.out.println( " Complete. Total time=" + ( (double)time / 1000 ) + " seconds. (" + ( (double)time * 1000 / count ) + " usec per cycle.) (" + total + ")" ); } public static void main(String[] args) { long now = System.currentTimeMillis(); System.out.println( "This test will run a number of tests to check Java performance. It should be run both with and without the Wrapper." ); String fullVersion = System.getProperty( "java.fullversion" ); String vendor = System.getProperty( "java.vm.vendor", "" ); String os = System.getProperty( "os.name", "" ).toLowerCase(); if ( fullVersion == null ) { fullVersion = System.getProperty( "java.runtime.version" ) + " " + System.getProperty( "java.vm.name" ); } System.out.println( "Java Version: " + fullVersion ); initialize(); dumpThreadInfo(); inMemoryLoopsMath(); inMemoryLoopsString(); Thread runner = new Thread( "runner" ) { public void run() { System.out.println( "Do the same tests in a background thread..." ); dumpThreadInfo(); inMemoryLoopsMath(); inMemoryLoopsString(); System.out.println( "Background thread completed." ); } }; runner.start(); try { runner.join(); } catch ( InterruptedException e ) { } long time = System.currentTimeMillis() - now; System.out.println( "Completed in " + ( (double)time / 1000 ) + " seconds. Exiting." ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/PostShutdownGC.java100644 0 0 4424 14333053652 25117 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class PostShutdownGC { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { System.out.println( "This application registers a shutdown hook which will run for 15 seconds after the Wrapper has completely shutdown." ); System.out.println( "It will call GC several times to try and completely get rid of the WrapperManager related code." ); // This test intentionally does NOT use any localization to make sure that there is nothing holding the Wrapper related classes in memory. Runtime.getRuntime().addShutdownHook( new Thread() { public void run() { System.out.println( "Starting shutdown hook. Loop for 15 seconds." ); long start = System.currentTimeMillis(); while ( System.currentTimeMillis() - start < 15000 ) { try { Thread.sleep( 1000 ); } catch ( InterruptedException e ) { // Ignore } // Try to prod the garbage collector into collecting unneeded classes and objects. char[] buffer = new char[10 * 1024 * 1024]; buffer = null; System.gc(); } System.out.println( "Shutdown hook complete. Should exit now." ); } } ); System.out.println( "Application complete. Wrapper should stop, invoking the shutdown hooks." ); System.out.println(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/QuickComplete.java100644 0 0 3267 14333053652 24775 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class QuickComplete { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { System.out.println( Main.getRes().getString( "Quick Complete Running...") ); System.out.println( Main.getRes().getString( "Quick Complete Done...") ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/Restarter.java100644 0 0 13762 14333053652 24224 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.WrapperListener; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class Restarter implements WrapperListener { /************************************************************************** * Constructors *************************************************************************/ private Restarter() { } /************************************************************************** * WrapperListener Methods *************************************************************************/ public Integer start(String[] args) { System.out.println("start()"); // Start up a thread whose job it is to request a restart in 5 seconds. Thread restarter = new Thread("restarter") { public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) {} // Start up a thread whose only job is to dump output to the console. Thread outputter = new Thread("outputter") { public void run() { int counter = 0; while(true) { /* try { Thread.sleep(50); } catch (InterruptedException e) {} */ Thread.yield(); System.out.println( Main.getRes().getString( " outputer line #{0}", new Integer( ++counter ) ) ); System.out.println( " 1) " + Main.getRes().getString( "A long line of test data to cause lots of data to be sent to the console." ) ); System.out.println( " 2) " + Main.getRes().getString( "A long line of test data to cause lots of data to be sent to the console." ) ); System.out.println( " 3) " + Main.getRes().getString( "A long line of test data to cause lots of data to be sent to the console." ) ); System.out.println( " 4) " + Main.getRes().getString( "A long line of test data to cause lots of data to be sent to the console." ) ); System.out.println( " 5) " + Main.getRes().getString( "A long line of test data to cause lots of data to be sent to the console." ) ); System.out.println( " 6) " + Main.getRes().getString( "A long line of test data to cause lots of data to be sent to the console." ) ); System.out.println( " 7) " + Main.getRes().getString( "A long line of test data to cause lots of data to be sent to the console." ) ); System.out.println( " 8) " + Main.getRes().getString( "A long line of test data to cause lots of data to be sent to the console." ) ); System.out.println( " 9) " + Main.getRes().getString( "A long line of test data to cause lots of data to be sent to the console." ) ); System.out.println( " 10) " + Main.getRes().getString( "A long line of test data to cause lots of data to be sent to the console." ) ); System.out.flush(); } } }; //outputter.start(); System.out.println(Main.getRes().getString( "Requesting restart..." ) ); WrapperManager.restart(); } }; restarter.start(); return null; } public int stop(int exitCode) { System.out.println( Main.getRes().getString( "stop({0})", new Integer( exitCode ) ) ); return exitCode; } public void controlEvent(int event) { System.out.println( Main.getRes().getString( "controlEvent({0})", new Integer( event ) ) ); if (event == WrapperManager.WRAPPER_CTRL_C_EVENT) { WrapperManager.stop(0); } } /************************************************************************** * Main Method *************************************************************************/ public static void main(String[] args) { System.out.println( Main.getRes().getString( "Initializing..." ) ); // Start the application. If the JVM was launched from the native // Wrapper then the application will wait for the native Wrapper to // call the application's start method. Otherwise the start method // will be called immediately. WrapperManager.start(new Restarter(), args); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/RuntimeExec.java100644 0 0 140757 14333053652 24526 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.WrapperJNIError; import org.tanukisoftware.wrapper.WrapperLicenseError; import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.WrapperProcess; import org.tanukisoftware.wrapper.WrapperProcessConfig; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.IOException; import java.util.Random; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class RuntimeExec { /* Normal = 0. */ public static final int EXIT_CODE_NORMAL = 0; /* Error = 1. */ public static final int EXIT_CODE_ERROR = 1; /* On UNIX Command not found = 127. */ public static final int EXIT_CODE_COMMAND_NOT_FOUND = ( WrapperManager.isWindows() ? 1 : 127 ); /* On UNIX Signal 15(TERM) + 128 = 143. */ public static final int EXIT_CODE_TERM_CTRL_C = ( WrapperManager.isWindows() ? 1 : 143 ); /* On UNIX Signal 9(KILL) + 128 = 137. */ public static final int EXIT_CODE_KILLED = ( WrapperManager.isWindows() ? 1 : 137 ); /* On Mac Signal 4(CORE) + 128 = 132, other UNIX Signal 11(SEGFAULT) + 128 = 139. */ public static final int EXIT_CODE_CRASH = ( WrapperManager.isWindows() ? -1073741819 : ( WrapperManager.isMacOSX() ? 132 : 139 ) ); static final String c_encoding; static int c_testsFailed = 0; static boolean c_currentTestFailed = false; static int c_testsPerformed = 0; /*--------------------------------------------------------------- * Static Methods *-------------------------------------------------------------*/ static { // In order to read the output from some processes correctly we need to get the correct encoding. // On some systems, the underlying system encoding is different than the file encoding. String encoding = System.getProperty( "sun.jnu.encoding" ); if ( encoding == null ) { encoding = System.getProperty( "file.encoding" ); if ( encoding == null ) { // Default to Latin1 encoding = "Cp1252"; } } c_encoding = encoding; } static Thread handleOutputStream( final OutputStream os, final String encoding, final String text, final String label ) { Thread runner = new Thread() { public void run() { BufferedWriter bw; try { System.out.println( label ); bw = new BufferedWriter( new OutputStreamWriter( os, encoding ) ); try { bw.write( text, 0, text.length() ); } finally { bw.close(); } } catch ( IOException e ) { e.printStackTrace(); } } }; runner.start(); return runner; } static Thread handleInputStream( final InputStream is, final String encoding, final String label, final StringBuffer sb ) { Thread runner = new Thread() { public void run() { BufferedReader br; String line; try { br = new BufferedReader( new InputStreamReader( is, encoding ) ); try { boolean firstLine = true; while ( ( line = br.readLine() ) != null ) { if ( sb != null ) { if ( firstLine ) { firstLine = false; } else { sb.append( System.getProperty("line.separator") ); } sb.append( line ); } System.out.println( label + " " + line ); } } finally { br.close(); } } catch ( IOException e ) { e.printStackTrace(); } } }; runner.start(); return runner; } static Thread handleInputStreamByteByByte( final InputStream is, final String label, final StringBuffer sb ) { Thread runner = new Thread() { public void run() { try { int c; char chr; boolean printLabel = true; while ( ( c = is.read() ) != -1 ) { if ( sb != null ) { sb.append( c ); } chr = (char)c; if ( printLabel ) { System.out.print( label + " " ); printLabel = false; } System.out.print( chr ); if ( chr == '\n' ) { printLabel = true; } } } catch ( IOException e ) { e.printStackTrace(); } } }; runner.start(); return runner; } static void handleJavaProcessInner( String testId, Process process ) throws IOException, InterruptedException { try { handleInputStream( process.getInputStream(), c_encoding, testId + "stdout:", null ); handleInputStream( process.getErrorStream(), c_encoding, testId + "stderr:", null ); } finally { int exitCode = process.waitFor(); System.out.println( Main.getRes().getString( "{0}exitCode: {1}", testId, new Integer( exitCode ) ) ); } } static void handleJavaProcess( String testId, String command ) throws IOException, InterruptedException { System.out.println( Main.getRes().getString( "{0}Runtime.exec command: {1}", testId, command ) ); handleJavaProcessInner( testId, Runtime.getRuntime().exec( command ) ); } static void handleJavaProcess( String testId, String[] command ) throws IOException, InterruptedException { System.out.println( Main.getRes().getString( "{0}Runtime.exec command: {1}", testId, toString( command ) ) ); handleJavaProcessInner( testId, Runtime.getRuntime().exec( command ) ); } static final int WAIT_MODE_NONE = 0; static final int WAIT_MODE_API = 1; static final int WAIT_MODE_MANUAL = 2; static BufferedReader debugStdOut; /** * @param timeoutMS Time to wait before attempting to call process.destroy(), 0 for never. */ private static void handleWrapperProcessInner( String testId, WrapperProcess process, boolean newProcessGroup, long timeoutMS, boolean handleOutErr, boolean closeStdOutErr, String textStdIn, boolean closeStdIn, int waitMode, String outErrRegex, int expectedExitCode, boolean debug, boolean readByteByByte ) throws IOException, InterruptedException { System.out.println( Main.getRes().getString( "{0} Launched PID={1}, PGID={2}", testId, Integer.toString( process.getPID() ), Integer.toString( process.getPGID() ) ) ); if ( debug ) { final long sleepTime = timeoutMS * 2/3; final int pgid = process.getPGID(); new Thread() { public void run() { try { Thread.sleep( sleepTime ); String[] cmd = { "/bin/sh", "-c", "ps -o pid,ppid,pgid,comm | grep -iE '" + pgid + "|pid' | grep -Ev ' sh| ps| grep'" }; Process proc = Runtime.getRuntime().exec( cmd ); debugStdOut = new BufferedReader( new InputStreamReader( proc.getInputStream()) ); } catch ( IOException e ) { } catch ( InterruptedException e ) { } } }.start(); } if ( newProcessGroup ) { // PID should equal PGID. if ( process.getPID() != process.getPGID() ) { System.out.println( Main.getRes().getString( "{0} FAILED! A new process group was requested, but the child process does not have its own group.", testId ) ); c_currentTestFailed = true; } } else { // PID should not equal PGID. if ( process.getPID() == process.getPGID() ) { System.out.println( Main.getRes().getString( "{0} FAILED! A new process group was not requested, but the child process has a new group.", testId ) ); c_currentTestFailed = true; } } Thread stdoutThread = null; Thread stderrThread = null; StringBuffer sbOut = null; StringBuffer sbErr = null; try { if ( textStdIn != null ) { handleOutputStream( process.getStdIn(), c_encoding, textStdIn, testId + "stdin: " + textStdIn ); } if ( handleOutErr ) { if ( outErrRegex != null ) { // create a StringBuffer to collect the output. sbOut = new StringBuffer(); sbErr = new StringBuffer(); } if ( readByteByByte ) { stdoutThread = handleInputStreamByteByByte( process.getStdOut(), testId + "stdout:", sbOut ); stderrThread = handleInputStreamByteByByte( process.getStdErr(), testId + "stderr:", sbErr ); } else { stdoutThread = handleInputStream( process.getStdOut(), c_encoding, testId + "stdout:", sbOut ); stderrThread = handleInputStream( process.getStdErr(), c_encoding, testId + "stderr:", sbErr ); } } else if ( closeStdOutErr ) { process.getStdOut().close(); process.getStdErr().close(); } if ( closeStdIn ) { process.getStdIn().close(); } if ( waitMode == WAIT_MODE_API ) { // We always call waitFor later. } else if ( waitMode == WAIT_MODE_MANUAL ) { if ( timeoutMS > 0 ) { long start = System.currentTimeMillis(); while ( process.isAlive() && ( System.currentTimeMillis() - start < timeoutMS ) ) { try { Thread.sleep( 100 ); } catch ( InterruptedException e ) { } } long time = System.currentTimeMillis() - start; if ( process.isAlive() ) { System.out.println( Main.getRes().getString( "{0}Timed out after {1}ms waiting for child. Destroying.", testId, Long.toString( time ) ) ); process.destroy(); } else if ( System.currentTimeMillis() - start >= timeoutMS ) { // We timed out, so the child should still exist. System.out.println( Main.getRes().getString( "{0}FAILED! Timed out after {1}ms waiting for child. But child is already gone unexpectedly.", testId, Long.toString( time ) ) ); c_currentTestFailed = true; } else { // The child was gone before the timeout. This is normal if the child was expected to fail, but bad if not. System.out.println( Main.getRes().getString( "{0}Child completed within {1}ms, before timeout of {2}ms.", testId, Long.toString( time ), Long.toString( timeoutMS ) ) ); } } } } finally { if ( ( waitMode == WAIT_MODE_API ) || ( waitMode == WAIT_MODE_MANUAL ) ) { int exitCode = process.waitFor(); if ( exitCode == expectedExitCode ) { System.out.println( Main.getRes().getString( "{0}exitCode: {1}", testId, new Integer( exitCode ) ) ); } else { System.out.println( Main.getRes().getString( "{0}FAILED! exitCode: {1} but expected {2}", testId, new Integer( exitCode ), new Integer( expectedExitCode ) ) ); c_currentTestFailed = true; } long start = System.currentTimeMillis(); if ( stdoutThread != null ) { System.out.println( Main.getRes().getString( "{0}waiting for stdout thread...", testId ) ); try { stdoutThread.join(); long time = System.currentTimeMillis() - start; if ( time > 10 ) { System.out.println( Main.getRes().getString( "{0}stdout thread didn't complete for " + time + "ms. after child process exited.", testId ) ); } else { System.out.println( Main.getRes().getString( "{0}stdout thread complete promptly.", testId ) ); } } catch ( InterruptedException e ) { System.out.println( Main.getRes().getString( "{0}wait for stdout thread interrupted.", testId ) ); } } if ( stderrThread != null ) { System.out.println( Main.getRes().getString( "{0}waiting for stderr thread...", testId ) ); try { stderrThread.join(); long time = System.currentTimeMillis() - start; if ( time > 10 ) { System.out.println( Main.getRes().getString( "{0}stderr thread didn't complete for " + time + "ms. after child process exited.", testId ) ); } else { System.out.println( Main.getRes().getString( "{0}stderr thread complete promptly.", testId ) ); } } catch ( InterruptedException e ) { System.out.println( Main.getRes().getString( "{0}wait for stderr thread interrupted.", testId ) ); } } } else { System.out.println( Main.getRes().getString( "{0}leave running...", testId ) ); } if ( ( outErrRegex != null ) && ( sbOut != null ) && ( sbErr != null ) ) { Pattern pattern = Pattern.compile( outErrRegex, Pattern.DOTALL | Pattern.MULTILINE ); if ( pattern.matcher( sbOut ).matches() ) { System.out.println( Main.getRes().getString( "{0}Expected output found in {1}.", testId, "stdout" ) ); } else if ( pattern.matcher( sbErr ).matches() ) { System.out.println( Main.getRes().getString( "{0}Expected output found in {1}.", testId, "stderr" ) ); } else { System.out.println( Main.getRes().getString( "{0}FAILED! Neither stdout nor stderr matched ''{1}''.", testId, outErrRegex ) ); c_currentTestFailed = true; } } if ( debug ) { System.out.println( "----------------DEBUG----------------" ); String s = null; while ( ( s = debugStdOut.readLine() ) != null ) { System.out.println( s ); } System.out.println( "-------------------------------------" ); } } } static void handleWrapperProcess( String testId, String command, WrapperProcessConfig config, long timeoutMS, boolean handleOutErr, boolean closeStdOutErr, String textStdIn, boolean closeStdIn, int waitMode, String outErrRegex, int expectedExitCode, boolean debug, boolean readByteByByte ) throws IOException, InterruptedException { System.out.println( Main.getRes().getString( "{0}WrapperManager.exec command: {1}", testId, command ) ); if ( config == null ) { handleWrapperProcessInner( testId, WrapperManager.exec( command ), true, timeoutMS, handleOutErr, closeStdOutErr, textStdIn, closeStdIn, waitMode, outErrRegex, expectedExitCode, debug, readByteByByte ); } else { handleWrapperProcessInner( testId, WrapperManager.exec( command, config ), config.isNewProcessGroup(), timeoutMS, handleOutErr, closeStdOutErr, textStdIn, closeStdIn, waitMode, outErrRegex, expectedExitCode, debug, readByteByByte ); } } static void handleWrapperProcess( String testId, String command, WrapperProcessConfig config, long timeoutMS, boolean handleOutErr, boolean closeStdOutErr, boolean closeStdIn, int waitMode, int expectedExitCode, boolean debug, boolean readByteByByte ) throws IOException, InterruptedException { handleWrapperProcess( testId, command, config, timeoutMS, handleOutErr, closeStdOutErr, null, closeStdIn, waitMode, null, expectedExitCode, debug, false ); } static void handleWrapperProcess( String testId, String command, WrapperProcessConfig config, long timeoutMS, boolean handleOutErr, boolean closeStdOutErr, boolean closeStdIn, int waitMode, int expectedExitCode, boolean debug ) throws IOException, InterruptedException { handleWrapperProcess( testId, command, config, timeoutMS, handleOutErr, closeStdOutErr, null, closeStdIn, waitMode, null, expectedExitCode, debug, false ); } private static void handleWrapperProcess( String testId, String command ) throws IOException, InterruptedException { handleWrapperProcess( testId, command, null, 0, true, false, false, WAIT_MODE_MANUAL, EXIT_CODE_NORMAL, false ); } private static void handleWrapperProcess( String testId, String[] command, WrapperProcessConfig config, long timeoutMS, boolean handleOutErr, boolean closeStdOutErr, boolean closeStdIn, int waitMode, int expectedExitCode ) throws IOException, InterruptedException { System.out.println( Main.getRes().getString( "{0}WrapperManager.exec command: {1}", testId, toString( command ) ) ); if ( config == null ) { handleWrapperProcessInner( testId, WrapperManager.exec( command ), true, timeoutMS, handleOutErr, closeStdOutErr, null, closeStdIn, waitMode, null, expectedExitCode, false, false ); } else { handleWrapperProcessInner( testId, WrapperManager.exec( command, config ), config.isNewProcessGroup(), timeoutMS, handleOutErr, closeStdOutErr, null, closeStdIn, waitMode, null, expectedExitCode, false, false ); } } private static void handleWrapperProcess( String testId, String[] command ) throws IOException, InterruptedException { handleWrapperProcess( testId, command, null, 0, true, false, false, WAIT_MODE_MANUAL, EXIT_CODE_NORMAL ); } private static long c_start; static void beginCase( String testId ) { c_testsPerformed++; c_currentTestFailed = false; c_start = System.currentTimeMillis(); System.out.println(); System.out.println( Main.getRes().getString( "{0}BEGIN ----------------------------------------", testId ) ); } static void endCase( String testId, long expectedTimeMs ) { long time = System.currentTimeMillis() - c_start; String result; if ( expectedTimeMs >= 0 ) { long minTime = expectedTimeMs; long maxTime = expectedTimeMs + 999L; if ( ( time < minTime ) || ( time > maxTime ) ) { result = " " + Main.getRes().getString( "FAILED! Expected time to be in range of {0} ~ {1}ms.", Long.toString( minTime ), Long.toString( maxTime ) ); c_currentTestFailed = true; } else { result = " " + Main.getRes().getString( "OK." ); } } else { result = ""; } if ( c_currentTestFailed ) { c_testsFailed++; } System.out.println( Main.getRes().getString( "{0}END ---------------------------------------- {1}ms.{2}", testId, Long.toString( time ), result ) ); } static void endCase( String testId ) { // Try to keep all output from the test within the the BEGIN/END block. Most tests capture it correctly however. try { Thread.sleep( 1000 ); } catch ( InterruptedException e ) { } endCase( testId, -1 ); } private static String toString( String[] command ) { StringBuffer sb = new StringBuffer(); sb.append( "{" ); for ( int i = 0; i < command.length; i++ ) { String arg = command[i]; if ( i > 0 ) { sb.append( ", " ); } sb.append( "\"" ); sb.append( arg ); sb.append( "\"" ); } sb.append( "}" ); return sb.toString(); } private static void caseSimpleTestJava( final String simplewaiter ) { String testId = "Simple Java : "; beginCase( testId ); try { try { String command = simplewaiter + " -v \"test 123\" test 123 \"\\\"test\\\""; handleJavaProcess( testId, command ); } catch ( Exception e ) { System.out.println( Main.getRes().getString( "{0}ERROR - Unexpected error:", testId ) ); e.printStackTrace(); c_currentTestFailed = true; } } finally { endCase( testId ); } } private static void caseSimpleTestWrapper( final String simplewaiter ) { String testId = "Simple Wrapper : "; beginCase( testId ); try { try { String command = simplewaiter + " -v \"test 123\" test 123 \"\\\"test\\\""; handleWrapperProcess( testId, command ); } catch ( Exception e ) { System.out.println( Main.getRes().getString( "{0}ERROR - Unexpected error:", testId ) ); e.printStackTrace(); c_currentTestFailed = true; } } finally { endCase( testId ); } } private static void caseSimpleTestJavaAry( final String simplewaiter ) { String testId = "Simple Java (Array) : "; beginCase( testId ); try { try { String[] command = { simplewaiter, "-v", "\"test 123\"", "test 123", "\"\\\"test\\\"\"" }; handleJavaProcess( testId, command ); } catch ( Exception e ) { System.out.println( Main.getRes().getString( "{0}ERROR - Unexpected error:", testId ) ); e.printStackTrace(); c_currentTestFailed = true; } } finally { endCase( testId ); } } private static void caseSimpleTestWrapperAry( final String simplewaiter ) { String testId = "Simple Wrapper (Array) : "; beginCase( testId ); try { try { String[] command = { simplewaiter, "-v", "\"test 123\"", "test 123", "\"\\\"test\\\"\"" }; handleWrapperProcess( testId, command ); } catch ( Exception e ) { System.out.println( Main.getRes().getString( "{0}ERROR - Unexpected error:", testId ) ); e.printStackTrace(); c_currentTestFailed = true; } } finally { endCase( testId ); } } private static void caseLongCommand( final String simplewaiter, int len, boolean expectFailure ) { String testId = "Long Command " + len + ": "; beginCase( testId ); try { try { StringBuffer sb = new StringBuffer(); sb.append( simplewaiter ); sb.append( " -v " ); while ( sb.length() < len - 1 ) { sb.append( "x" ); } // Make the last character a y so we can verify that it is included correctly. sb.append( "y" ); String command = sb.toString(); handleWrapperProcess( testId, command ); if ( expectFailure ) { System.out.println( Main.getRes().getString( "{0}Was looking for a failure, but the long command line passed. This is likely because the limit is larger on this platform.", testId ) ); } } catch ( Exception e ) { if ( expectFailure ) { System.out.println( Main.getRes().getString( "{0}Failed as expected: {1}", testId, e.toString() ) ); } else { System.out.println( Main.getRes().getString( "{0}ERROR - Unexpected error:", testId ) ); e.printStackTrace(); c_currentTestFailed = true; } } } finally { endCase( testId ); } } private static void caseWaitFor( final String simplewaiter ) { String testId = "WaitFor : "; beginCase( testId ); try { try { String command = simplewaiter + " 1 10"; handleWrapperProcess( testId, command, null, 0, true, false, false, WAIT_MODE_API, EXIT_CODE_ERROR, false ); } catch ( Exception e ) { System.out.println( Main.getRes().getString( "{0}ERROR - Unexpected error:", testId ) ); e.printStackTrace(); c_currentTestFailed = true; } } finally { endCase( testId ); } } private static void caseSmallChildProcess( final String simplewaiter ) { String testId = "Simple Wrapper (Array) : "; beginCase( testId ); try { try { String command = simplewaiter + " 65 1"; handleWrapperProcess( testId, command, null, 0, false, false, true, WAIT_MODE_MANUAL, 65, false ); } catch ( InterruptedException e ) { System.out.println( Main.getRes().getString( "{0}ERROR - Unexpected error:", testId ) ); e.printStackTrace(); c_currentTestFailed = true; } catch ( Exception e ) { System.out.println( Main.getRes().getString( "{0}ERROR - Unexpected error:", testId ) ); e.printStackTrace(); c_currentTestFailed = true; } } finally { endCase( testId ); } } private static void caseVFork( final String simplewaiter ) { String testId = "VFork : "; beginCase( testId ); try { if ( !WrapperProcessConfig.isSupported( WrapperProcessConfig.VFORK_EXEC ) ) { System.out.println( Main.getRes().getString( "{0}vfork not supported", testId ) ); } else { System.out.println( Main.getRes().getString( "{0}vfork is supported", testId ) ); try { String command = simplewaiter + " 20 10"; handleWrapperProcess( testId, command, new WrapperProcessConfig().setStartType( WrapperProcessConfig.VFORK_EXEC ), 0, true, false, false, WAIT_MODE_MANUAL, 20, false ); } catch ( Exception e ) { System.out.println( Main.getRes().getString( "{0}ERROR - Unexpected error:", testId ) ); e.printStackTrace(); c_currentTestFailed = true; } } } finally { endCase( testId ); } } private static void casePosixSpawn( final String simplewaiter ) { String testId = "PosixSpawn : "; beginCase( testId ); try { if ( !WrapperProcessConfig.isSupported( WrapperProcessConfig.POSIX_SPAWN ) ) { System.out.println( Main.getRes().getString( "{0}posix spawn not supported", testId ) ); } else { System.out.println( Main.getRes().getString( "{0}posix spawn is supported", testId ) ); try { String command = simplewaiter + " 20 10"; handleWrapperProcess( testId, command, new WrapperProcessConfig().setStartType( WrapperProcessConfig.POSIX_SPAWN ), 0, true, false, false, WAIT_MODE_MANUAL, 20, false ); } catch ( Exception e ) { System.out.println( Main.getRes().getString( "{0}ERROR - Unexpected error:", testId ) ); e.printStackTrace(); c_currentTestFailed = true; } } } finally { endCase( testId ); } } private static void caseEnvSmall( final String simplewaiter ) { String testId = "Environment Small : "; beginCase( testId ); try { try { WrapperProcessConfig config = new WrapperProcessConfig(); java.util.Map environment = config.getEnvironment(); environment.clear(); environment.put( "TEST", "TEST123" ); System.out.println( Main.getRes().getString( "{0}size of Environment map = {1}", testId, new Integer( environment.size() ) ) ); String command = simplewaiter + " -e TEST"; handleWrapperProcess( testId, command, config, 0, true, false, false, WAIT_MODE_MANUAL, EXIT_CODE_NORMAL, false ); } catch ( Exception e ) { System.out.println( Main.getRes().getString( "{0}ERROR - Unexpected error:", testId ) ); e.printStackTrace(); c_currentTestFailed = true; } } finally { endCase( testId ); } } private static void caseEnvLarge( final String simplewaiter, int len, boolean expectFailure ) { String testId = "Environment Large " + len + ": "; beginCase( testId ); try { try { int valueLen = len - 4 - 1; // "TEST=" StringBuffer sb = new StringBuffer(); for ( int i = 0; i < valueLen - 1; i++ ) { sb.append( "X" ); } sb.append( "Y" ); // Make it so we can see the last value. String value = sb.toString(); WrapperProcessConfig config = new WrapperProcessConfig(); java.util.Map environment = config.getEnvironment(); environment.clear(); environment.put( "TEST", value ); System.out.println( Main.getRes().getString( "{0}size of Environment map = {1}", testId, new Integer( environment.size() ) ) ); String command = simplewaiter + " -e TEST"; handleWrapperProcess( testId, command, config, 0, true, false, false, WAIT_MODE_MANUAL, EXIT_CODE_NORMAL, false ); if ( expectFailure ) { System.out.println( Main.getRes().getString( "{0}Was looking for a failure, but the long environment variable passed. This is likely because the limit is larger on this platform.", testId ) ); } } catch ( Exception e ) { if ( expectFailure ) { System.out.println( Main.getRes().getString( "{0}Failed as expected: {1}", testId, e.toString() ) ); } else { System.out.println( Main.getRes().getString( "{0}ERROR - Unexpected error:", testId ) ); e.printStackTrace(); c_currentTestFailed = true; } } } finally { endCase( testId ); } } private static void caseWorkingDir( final String simplewaiter ) { String testId = "Change Working Dir : "; beginCase( testId ); try { if ( WrapperProcessConfig.isSupported( WrapperProcessConfig.FORK_EXEC ) || WrapperProcessConfig.isSupported( WrapperProcessConfig.VFORK_EXEC ) ) { System.out.println( Main.getRes().getString( "{0}changing the working directory is supported", testId ) ); try { WrapperProcessConfig config = new WrapperProcessConfig(); config.setStartType( WrapperProcessConfig.isSupported( WrapperProcessConfig.FORK_EXEC ) ? WrapperProcessConfig.FORK_EXEC : WrapperProcessConfig.VFORK_EXEC ); config.setWorkingDirectory( new File("..") ); String command; if ( WrapperManager.isWindows() ) { command = "cmd.exe /c dir"; } else { command = "ls -l"; } handleWrapperProcess( testId, command, config, 0, true, false, false, WAIT_MODE_MANUAL, EXIT_CODE_NORMAL, false ); } catch ( Exception e ) { System.out.println( Main.getRes().getString( "{0}ERROR - Unexpected error:", testId ) ); e.printStackTrace(); c_currentTestFailed = true; } } else { System.out.println( Main.getRes().getString( "{0}changing the working directory is not supported", testId ) ); } } finally { endCase( testId ); } } /** * Test a short WrapperManager.exec process whose entire lifespan is while another Runtime.exec process is running. */ private static void caseWrapperDuringJava( final String simplewaiter ) { String testId = "Wrapper During Java : "; beginCase( testId ); try { try { String javaCommand = simplewaiter + " 5 10"; String wrapperCommand = simplewaiter + " 6 5"; Process javaProcess = Runtime.getRuntime().exec( javaCommand ); handleInputStream( javaProcess.getInputStream(), c_encoding, testId + "Runtime.exec stdout:", null ); handleInputStream( javaProcess.getErrorStream(), c_encoding, testId + "Runtime.exec stderr:", null ); WrapperProcess wrapperProcess = WrapperManager.exec( wrapperCommand ); handleInputStream( wrapperProcess.getStdOut(), c_encoding, testId + "WrapperManager.exec stdout:", null ); handleInputStream( wrapperProcess.getStdErr(), c_encoding, testId + "WrapperManager.exec stderr:", null ); System.out.println( Main.getRes().getString( "{0}WrapperManager.exec exitCode: {1}", testId, new Integer( wrapperProcess.waitFor() ) ) ); System.out.println( Main.getRes().getString( "{0}Runtime.exec exitCode: {1}", testId, new Integer( javaProcess.waitFor() ) ) ); } catch ( Exception e ) { System.out.println( Main.getRes().getString( "{0}ERROR - Unexpected error:", testId ) ); e.printStackTrace(); c_currentTestFailed = true; } } finally { endCase( testId ); } } /** * Test a short Runtime.exec process whose entire lifespan is while another WrapperManager.exec process is running. */ private static void caseJavaDuringWrapper( final String simplewaiter ) { String testId = "Java During Wrapper : "; beginCase( testId ); try { try { String wrapperCommand = simplewaiter + " 5 10"; String javaCommand = simplewaiter + " 6 5"; WrapperProcess wrapperProcess = WrapperManager.exec( wrapperCommand ); handleInputStream( wrapperProcess.getStdOut(), c_encoding, testId + "WrapperManager.exec stdout:", null ); handleInputStream( wrapperProcess.getStdErr(), c_encoding, testId + "WrapperManager.exec stderr:", null ); Process javaProcess = Runtime.getRuntime().exec( javaCommand ); handleInputStream( javaProcess.getInputStream(), c_encoding, testId + "Runtime.exec stdout:", null ); handleInputStream( javaProcess.getErrorStream(), c_encoding, testId + "Runtime.exec stderr:", null ); System.out.println( Main.getRes().getString( "{0}Runtime.exec exitCode: {1}", testId, new Integer( javaProcess.waitFor() ) ) ); System.out.println( Main.getRes().getString( "{0}WrapperManager.exec exitCode: {1}", testId, new Integer( wrapperProcess.waitFor() ) ) ); } catch ( Exception e ) { System.out.println( Main.getRes().getString( "{0}ERROR - Unexpected error:", testId ) ); e.printStackTrace(); c_currentTestFailed = true; } } finally { endCase( testId ); } } private static void caseInvalid( final String simplewaiter ) { String testId = "Invalid : "; beginCase( testId ); try { try { String command = "invalid"; handleWrapperProcess( testId, command, null, 0, true, false, false, WAIT_MODE_MANUAL, EXIT_CODE_COMMAND_NOT_FOUND, false ); System.out.println( Main.getRes().getString( "{0}ERROR! Did not fail as expected.", testId ) ); c_currentTestFailed = true; } catch ( Exception e ) { System.out.println( Main.getRes().getString( "{0}Failed as expected.", testId ) ); } } finally { endCase( testId ); } } private static void caseTimeoutShort( final String simplewaiter ) { String testId = "Timeout Short : "; beginCase( testId ); try { try { String command = simplewaiter + " 0 5"; handleWrapperProcess( testId, command, null, 10000, true, false, false, WAIT_MODE_MANUAL, EXIT_CODE_NORMAL, false ); } catch ( Exception e ) { System.out.println( Main.getRes().getString( "{0}ERROR - Unexpected error:", testId ) ); e.printStackTrace(); c_currentTestFailed = true; } } finally { endCase( testId ); } } private static void caseTimeoutLong( final String simplewaiter ) { String testId = "Timeout Long : "; beginCase( testId ); try { try { String command = simplewaiter + " 0 30"; handleWrapperProcess( testId, command, null, 10000, true, false, false, WAIT_MODE_MANUAL, EXIT_CODE_TERM_CTRL_C, false ); } catch ( Exception e ) { System.out.println( Main.getRes().getString( "{0}ERROR - Unexpected error:", testId ) ); e.printStackTrace(); c_currentTestFailed = true; } } finally { endCase( testId ); } } private static boolean caseLeaveRunning( final String simplewaiter ) { String testId = "Leave Running : "; beginCase( testId ); try { try { String command = simplewaiter + " 1 600"; handleWrapperProcess( testId, command, null, 0, true, false, false, WAIT_MODE_NONE, EXIT_CODE_ERROR, false ); return false; } catch ( WrapperJNIError e ) { System.out.println( Main.getRes().getString( "{0}Unable to launch child process because JNI library unavailable. Normal on shutdown.", testId ) ); return true; } catch ( Exception e ) { System.out.println( Main.getRes().getString( "{0}ERROR - Unexpected error:", testId ) ); e.printStackTrace(); c_currentTestFailed = true; return true; } } finally { endCase( testId ); } } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main( String[] args ) { // Need to initialize the counts. RuntimeExec.c_testsPerformed = 0; RuntimeExec.c_testsFailed = 0; final String simplewaiter; if ( WrapperManager.isWindows() ) { simplewaiter = "../test/simplewaiter.exe"; } else { simplewaiter = "../test/simplewaiter"; } System.out.println( "Communicate with child processes using encoding: " + c_encoding ); Random rand = new Random(); System.out.println( Main.getRes().getString( "Is DYNAMIC supported? A:" ) + WrapperProcessConfig.isSupported( WrapperProcessConfig.DYNAMIC ) ); System.out.println( Main.getRes().getString( "Is FORK_EXEC supported? A:" ) + WrapperProcessConfig.isSupported( WrapperProcessConfig.FORK_EXEC ) ); System.out.println( Main.getRes().getString( "Is VFORK_EXEC supported? A:" ) + WrapperProcessConfig.isSupported( WrapperProcessConfig.VFORK_EXEC ) ); System.out.println( Main.getRes().getString( "Is POSIX_SPAWN supported? A:" ) + WrapperProcessConfig.isSupported( WrapperProcessConfig.POSIX_SPAWN ) ); caseSimpleTestJava( simplewaiter ); caseSimpleTestWrapper( simplewaiter ); caseSimpleTestJavaAry( simplewaiter ); caseSimpleTestWrapperAry( simplewaiter ); caseLongCommand( simplewaiter, 32766, false ); caseLongCommand( simplewaiter, 32767, true ); caseWaitFor( simplewaiter ); caseSmallChildProcess( simplewaiter ); caseVFork( simplewaiter ); casePosixSpawn( simplewaiter ); caseEnvSmall( simplewaiter ); caseEnvLarge( simplewaiter, 32767, false ); caseEnvLarge( simplewaiter, 32768, true ); caseWorkingDir( simplewaiter ); caseWrapperDuringJava( simplewaiter ); caseJavaDuringWrapper( simplewaiter ); caseInvalid( simplewaiter ); caseTimeoutShort( simplewaiter ); caseTimeoutLong( simplewaiter ); // This test should be the last as it relies on the Wrapper shutting it down. caseLeaveRunning( simplewaiter ); int nbTestsPassed = c_testsPerformed - c_testsFailed; System.out.println( "" ); System.out.println( "(Test results for current JVM #" + WrapperManager.getJVMId() + ")" ); System.out.println( Main.getRes().getString( "[PASSED] {0}", Integer.toString( nbTestsPassed ) ) ); System.out.println( Main.getRes().getString( "[FAILED] {0}", Integer.toString( c_testsFailed ) ) ); System.out.println(); if ( WrapperManager.getJVMId() == 1 ) { // First invocation. System.out.println( Main.getRes().getString( "All Done. Restarting..." ) ); WrapperManager.restart(); } else { // Second invocation. // Register a long shutdownhook which will cause the Wrapper to timeout and kill the JVM. System.out.println( Main.getRes().getString( "All Done. Registering long shutdown hook and stopping.\nWrapper should timeout and kill the JVM, cleaning up all processes in the process." ) ); Runtime.getRuntime().addShutdownHook( new Thread() { public void run() { System.out.println( Main.getRes().getString( "Starting shutdown hook. Loop for 25 seconds.") ); System.out.println( Main.getRes().getString( "Should timeout unless this property is set: wrapper.jvm_exit.timeout=30" ) ); long start = System.currentTimeMillis(); boolean failed = false; while ( System.currentTimeMillis() - start < 25000 ) { if ( !failed ) { failed = caseLeaveRunning( simplewaiter ); System.out.println( Main.getRes().getString( "Launched child...") ); } try { Thread.sleep( 250 ); } catch ( InterruptedException e ) { // Ignore } } System.out.println( Main.getRes().getString( "Shutdown hook complete. Should exit now." ) ); } } ); if ( c_testsFailed > 0 ) { System.exit( 1 ); } else { System.exit( 0 ); } } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/ShutdownHook.java100644 0 0 6704 14333053652 24663 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html * * * Portions of the Software have been derived from source code * developed by Silver Egg Technology under the following license: * * Copyright (c) 2001 Silver Egg Technology * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, * copy, modify, merge, publish, distribute, sub-license, and/or * sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. */ /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class ShutdownHook { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { System.out.println( Main.getRes().getString( "This application registers a shutdown hook which") ); System.out.println( Main.getRes().getString( "should be executed after the JVM has told the Wrapper") ); System.out.println( Main.getRes().getString( "it is exiting." ) ); System.out.println( Main.getRes().getString( "This is to test the wrapper.jvm_exit.timeout property" ) ); Runtime.getRuntime().addShutdownHook( new Thread() { public void run() { System.out.println( Main.getRes().getString( "Starting shutdown hook. Loop for 25 seconds.") ); System.out.println( Main.getRes().getString( "Should timeout unless this property is set: wrapper.jvm_exit.timeout=30" ) ); long start = System.currentTimeMillis(); while ( System.currentTimeMillis() - start < 25000 ) { try { Thread.sleep( 250 ); } catch ( InterruptedException e ) { // Ignore } } System.out.println( Main.getRes().getString( "Shutdown hook complete. Should exit now." ) ); // Run GC to invoke finalize on unsed objects to test the shutdown process. System.out.println( "GC BEGIN" ); System.gc(); System.out.println( "GC END" ); try { Thread.sleep( 2000 ); } catch ( InterruptedException e ) { // Ignore } System.out.println( "DONE" ); } } ); System.out.println( Main.getRes().getString( "Application complete. Wrapper should stop, invoking the shutdown hooks." ) ); System.out.println(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/ShutdownLock.java100644 0 0 4711 14333053652 24647 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.WrapperShuttingDownException; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class ShutdownLock { /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { Thread daemon = new Thread() { public void run() { System.out.println( Main.getRes().getString( "Daemon thread started." ) ); System.out.println( Main.getRes().getString( "Requesting a shutdown lock." ) ); try { WrapperManager.requestShutdownLock(); } catch ( WrapperShuttingDownException e ) { System.out.println( e ); } System.out.println( Main.getRes().getString( "Waiting for 20 seconds." ) ); try { Thread.sleep( 20000 ); } catch ( InterruptedException e ) { } System.out.println( Main.getRes().getString( "Freeing up shutdown lock." ) ); WrapperManager.releaseShutdownLock(); System.out.println( Main.getRes().getString( "Daemon thread completed." ) ); } }; daemon.setDaemon( true ); System.out.println( Main.getRes().getString( "Starting daemon thread." ) ); daemon.start(); try { Thread.sleep( 5000 ); } catch ( InterruptedException e ) { } System.out.println( Main.getRes().getString( "Application complete. Wrapper should stop when shutdown lock is released.") ); System.out.println(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/SimpleWrapperListener.java100644 0 0 4677 14333053652 26536 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.WrapperListener; /** * This is a very simple test of how a main class using Integration Method #3 * works. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class SimpleWrapperListener implements WrapperListener { /************************************************************************** * Constructors *************************************************************************/ private SimpleWrapperListener() { System.out.println( "SimpleWrapperListener()" ); } /************************************************************************** * WrapperListener Methods *************************************************************************/ public Integer start( String[] args ) { System.out.println( "SimpleWrapperListener.start()" ); return null; } public int stop( int exitCode ) { System.out.println( "SimpleWrapperListener.stop(" + exitCode + ")" ); return exitCode; } public void controlEvent(int event) { System.out.println( "SimpleWrapperListener.controlEvent(" + event + ")" ); } /************************************************************************** * Main Method *************************************************************************/ public static void main( String[] args ) { System.out.println( Main.getRes().getString( "SimpleWrapperListener.Initializing..." ) ); System.out.println( Main.getRes().getString( "This test should simply ''start()'' and ''stop()''." ) ); // Start the application. If the JVM was launched from the native // Wrapper then the application will wait for the native Wrapper to // call the application's start method. Otherwise the start method // will be called immediately. WrapperManager.start( new SimpleWrapperListener(), args ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/SlowStop.java100644 0 0 5214 14333053652 24014 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.WrapperListener; /** * This test is to make sure the Wrapper handles applications whose stop method * take too much time to complete. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class SlowStop implements WrapperListener { /************************************************************************** * Constructors *************************************************************************/ private SlowStop() { } /************************************************************************** * WrapperListener Methods *************************************************************************/ public Integer start( String[] args ) { System.out.println( "start()" ); return null; } public int stop( int exitCode ) { System.out.println( "stop(" + exitCode + ")" ); System.out.println( Main.getRes().getString( "This stop method will wait for 45 seconds before continuing." ) ); System.out.println( Main.getRes().getString( "The Wrapper will timeout unless wrapper.shutdown.timeout is set to a larger value." ) ); try { Thread.sleep( 45000 ); } catch ( InterruptedException e ) { } System.out.println( Main.getRes().getString( "Stop complete." ) ); return exitCode; } public void controlEvent( int event ) { System.out.println( "controlEvent(" + event + ")" ); } /************************************************************************** * Main Method *************************************************************************/ public static void main( String[] args ) { System.out.println( Main.getRes().getString( "Initializing..." ) ); // Start the application. If the JVM was launched from the native // Wrapper then the application will wait for the native Wrapper to // call the application's start method. Otherwise the start method // will be called immediately. WrapperManager.start( new SlowStop(), args ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/StopWhileStarting.java100644 0 0 7241 14333053652 25656 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import org.tanukisoftware.wrapper.WrapperManager; import org.tanukisoftware.wrapper.WrapperListener; /** * This test is to make sure the Wrapper works correctly when stop or restart is * called before the start method has completed. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class StopWhileStarting implements WrapperListener { /************************************************************************** * Constructors *************************************************************************/ private StopWhileStarting() { } /************************************************************************** * WrapperListener Methods *************************************************************************/ public Integer start(String[] args) { System.out.println("start()"); switch ( WrapperManager.getJVMId() ) { case 1: System.out.println( Main.getRes().getString( "start() request restart" ) ); WrapperManager.restart(); break; case 2: System.out.println( Main.getRes().getString( "start() request halt(0)" ) ); Runtime.getRuntime().halt( 0 ); break; case 3: System.out.println( Main.getRes().getString( "start() request System.exit(99). Will restart due to on_exit configuration" ) ); System.exit( 99 ); break; case 4: System.out.println( Main.getRes().getString( "start() returns exit code 99. Will restart due to on_exit configuration" ) ); return new Integer( 99 ); case 5: System.out.println( Main.getRes().getString( "start(). Please press ctrl+c" ) ); try { Thread.sleep( 30000 ); } catch ( InterruptedException e ) { } break; default: System.out.println( Main.getRes().getString( "start() request stop(0)") ); WrapperManager.stop( 0 ); break; } System.out.println( Main.getRes().getString( "start() END - Should not get here.") ); return null; } public int stop(int exitCode) { System.out.println("stop(" + exitCode + ")"); return exitCode; } public void controlEvent(int event) { System.out.println("controlEvent(" + event + ")"); if (event == WrapperManager.WRAPPER_CTRL_C_EVENT) { WrapperManager.stop(0); } } /************************************************************************** * Main Method *************************************************************************/ public static void main(String[] args) { System.out.println( Main.getRes().getString( "Initializing..." ) ); // Start the application. If the JVM was launched from the native // Wrapper then the application will wait for the native Wrapper to // call the application's start method. Otherwise the start method // will be called immediately. WrapperManager.start(new StopWhileStarting(), args); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/SystemProperty.java100644 0 0 4015 14333053652 25251 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ /** * This test is to make sure that property values set in the wrapper config file * are handled and passed into the JVM as expected. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class SystemProperty { private static int m_exitCode = 0; /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main( String[] args ) { testProperty( "VAR1", "abc" ); testProperty( "VAR2", "\\" ); testProperty( "VAR3", "\"" ); testProperty( "VAR4", "abc" ); testProperty( "VAR5", "\\" ); testProperty( "VAR6", "\\\\" ); testProperty( "VAR7", "\"" ); System.out.println( Main.getRes().getString( "Main complete.") ); System.exit( m_exitCode ); } private static void testProperty( String name, String expectedValue ) { System.out.println( Main.getRes().getString( "Testing system property: {0}", name ) ); System.out.println( Main.getRes().getString( " Expected:{0}", expectedValue ) ); String value = System.getProperty( name ); System.out.println( Main.getRes().getString( " Value :{0}", value ) ); if ( expectedValue.equals( value ) ) { System.out.println( Main.getRes().getString( " OK" ) ); } else { System.out.println( Main.getRes().getString( " FAILED!!!" ) ); m_exitCode = 1; } System.out.println(); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/TestUtils.java100644 0 0 15431 14333053652 24204 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.Writer; import org.tanukisoftware.wrapper.WrapperPropertyUtil; /** * This test is designed to make sure the Wrapper handles the case where the * JVM is restarted while the Wrapper is blocking due to a long disk IO queue. * Prior to 3.5.11, the Wrapper would sometimes exit before it noticed that * the JVM had wanted to restart. * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class TestUtils { /*--------------------------------------------------------------- * Constructors *-------------------------------------------------------------*/ private TestUtils() { } /*--------------------------------------------------------------- * Static Methods *-------------------------------------------------------------*/ /** * Sleep for the specified time without any exceptions. * * @param ms Time to sleep. */ public static void sleep( long ms ) { try { Thread.sleep( ms ); } catch ( InterruptedException e ) { } } /** * Writes the specified text to a file. This method is not an efficient * way to write multiple lines of text as the file is opened and closed * each time this is called. It can be useful when it is necessary to * quickly write out some text however. * * @param file File to be written to. * @param text Text to be written to the file. May contain multiple lines. * No additional line feeds will be written out so it is * important to include them if needed. * @param encoding Encoding to use when writing the text. Null implies * that the system default will be used. * @param append If true and the file already exists then the text will be * appended to the end of the file. If false then a new file * will always be created. * * @throws IOException If there are any problems writing to the file. */ public static void writeTextFile( File file, String text, String encoding, boolean append ) throws IOException { OutputStream fos = new FileOutputStream( file, append ); try { Writer osw; if ( encoding == null ) { osw = new OutputStreamWriter( fos ); } else { osw = new OutputStreamWriter( fos, encoding ); } BufferedWriter writer = new BufferedWriter( osw ); try { writer.write( text ); } finally { writer.close(); } } finally { fos.close(); } } /** * Writes the specified text to a file. * * @param file File to be written to. * @param text Text to be written to the file. May contain multiple lines. * No additional line feeds will be written out so it is * important to include them if needed. * * @throws IOException If there are any problems writing to the file. */ public static void writeTextFile( File file, String text ) throws IOException { writeTextFile( file, text, "UTF-8", false ); } /** * Writes a command to the Wrapper command file. * * If the file already exists then a warning will be logged as the file will * be overwritten. The Wrapper polls for the file at regular intervals and * this can happen if two command are written in rapid succession. If two * commands must be written then they should be written to the same file at * the same time to avoid any synchronization problems between the Wrapper * and this process. * * @param command Command to write to the configured Wrapper command file. * * @throws IOException If there are any problems writing to the file. * @throws IllegalStateException If the wrapper.commandfile property has not * been defined. */ public static void writeWrapperCommand( String command ) throws IOException, IllegalStateException { String commandFilename = WrapperPropertyUtil.getStringProperty( "wrapper.commandfile", null ); if ( commandFilename == null ) { throw new IllegalStateException( "The wrapper.commandfile property has not been configured." ); } File commandFile = new File( commandFilename ); if ( commandFile.exists() ) { System.out.println( Main.getRes().getString( "WARNING - Command file already exists when trying to write a new command: {0}", commandFile.toString() ) ); } writeTextFile( commandFile, command ); } /** * Writes a test command to the Wrapper command file. * * If the file already exists then a warning will be logged as the file will * be overwritten. The Wrapper polls for the file at regular intervals and * this can happen if two command are written in rapid succession. If two * commands must be written then they should be written to the same file at * the same time to avoid any synchronization problems between the Wrapper * and this process. * * @param command Command to write to the configured Wrapper command file. * * @throws IOException If there are any problems writing to the file. * @throws IllegalStateException If the wrapper.commandfile property has not * been defined or the wrapper.command.enable_tests * property has not been set to true. */ public static void writeWrapperTestCommand( String command ) throws IOException, IllegalStateException { boolean enableTests = WrapperPropertyUtil.getBooleanProperty( "wrapper.commandfile.enable_tests", false ); if ( !enableTests ) { throw new IllegalStateException( "The wrapper.command.enable_tests property has not been set to true." ); } writeWrapperCommand( command ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/TimedLogOutput.java100644 0 0 6546 14333053652 25160 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.util.Random; /** * * * @author Tanuki Software Development Team <support@tanukisoftware.com> */ public class TimedLogOutput { /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ private static String lPad( int n, int len, String padding ) { String s = Integer.toString( n ); int len2 = s.length(); if ( len2 < len ) { return padding.substring( 0, len - len2 ) + s; } else { return s; } } /*--------------------------------------------------------------- * Main Method *-------------------------------------------------------------*/ public static void main(String[] args) { // This class is not localized so we can run it without the Wrapper code. System.out.println( "Log lots of output of varying length with varying delays between entries. Each line shows the time it took to log the previous entry. This is done in a repeatable random series." ); // Build up a long base string. We will be sending varying substring instances of this to the output. StringBuffer messageSB = new StringBuffer(); for ( int i = 0; i < 1000; i++ ) { messageSB.append( "ThisIsAVeryLongStringWithoutSpaces." ); // 35 chars } String message = messageSB.toString(); // 35000 chars. int messageLen = message.length(); // Always seed the random generator so we get the same results for every run. Random rand = new Random( 0 ); long allStart = System.currentTimeMillis(); System.out.println( "Starting..." ); long prevTime = 0; for ( int i = 0; i < 1000; i++ ) { int subMessageLen = (int)( rand.nextFloat() * (messageLen - 1) ); String subMessage = message.substring( 0, subMessageLen ); String line = "#" + lPad( i, 6, "0000000000" ) + ":" + lPad( (int)prevTime, 6, " " ) + "ms:" + lPad( subMessageLen, 6, " " ) + ":" + subMessage; long lineStart = System.currentTimeMillis(); System.out.println( line ); long lineTime = System.currentTimeMillis() - lineStart; prevTime = lineTime; // Only sleep on 10% of the lines, then on those do a random lengthed sleep. if ( rand.nextFloat() < 0.10f ) { long sleepTime = (long)( rand.nextFloat() * 50 ); try { Thread.sleep( sleepTime ); } catch ( InterruptedException e ) { } } } long allTime = System.currentTimeMillis() - allStart; System.out.println( "Total time: " + allTime ); System.out.println( "All done." ); } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/WrapperPrintArgs.java100644 0 0 1374 14333053652 25477 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ public class WrapperPrintArgs { public static void main( String[] args ) { System.out.println( "Dump all Application Arguments:" ); System.out.println( " argv=" + args.length ); for ( int i = 0; i < args.length; i++ ) { System.out.println( " args[" + i + "]=" + args[i] ); } } } wrapper_3.5.51_src/src/java/org/tanukisoftware/wrapper/test/WrapperPrintParam.java100644 0 0 6706 14333053652 25647 0ustar 0 0 package org.tanukisoftware.wrapper.test; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the proprietary information of Tanuki Software. * You shall use it only in accordance with the terms of the * license agreement you entered into with Tanuki Software. * http://wrapper.tanukisoftware.com/doc/english/licenseOverview.html */ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.List; import java.util.ListIterator; import java.util.Arrays; public class WrapperPrintParam { public static void main( String[] args ) { System.out.println( "Dump all JVM parameters using RuntimeMXBean:" ); System.out.println( " (There is a bug in Java that was fixed in 1.7 which causes all parameters being displayed below to be split into different arguments when spaces are encountered.)" ); try { Class cRuntimeMXBean = Class.forName( "java.lang.management.RuntimeMXBean" ); Class cManagementFactory = Class.forName( "java.lang.management.ManagementFactory" ); Method mGetRuntimeMXBean = cManagementFactory.getMethod( "getRuntimeMXBean", (Class[])null ); Method mGetInputArguments = cRuntimeMXBean.getMethod( "getInputArguments", (Class[])null ); Object runtimemxBean = mGetRuntimeMXBean.invoke( null, ( Object[] ) null ); List jvm_args = ( List ) mGetInputArguments.invoke( runtimemxBean, ( Object[] ) null ); System.out.println( jvm_args.size() + " JVM Parameter(s):" ); for ( ListIterator i = jvm_args.listIterator(); i.hasNext(); ) { String arg = (String)i.next(); System.out.println( " " + arg ); } List app_args = Arrays.asList( args ); System.out.println( app_args.size() + " Application Parameter(s):" ); for ( ListIterator i = app_args.listIterator(); i.hasNext(); ) { String arg = ( String ) i.next(); System.out.println( " " + arg ); } System.out.println(); System.out.println( "Resulting System Properties:" ); for ( ListIterator i = jvm_args.listIterator(); i.hasNext(); ) { String arg = (String)i.next(); if ( arg.startsWith( "-D" ) ) { int pos = arg.indexOf( '=' ); if ( pos >= 0 ) { String name = arg.substring( 2, pos ); System.out.println( " " + name + "=" + System.getProperty( name ) ); } } } } catch ( ClassNotFoundException e ) { e.printStackTrace(); } catch ( IllegalArgumentException e ) { e.printStackTrace(); } catch ( IllegalAccessException e ) { e.printStackTrace(); } catch ( InvocationTargetException e ) { e.printStackTrace(); } catch ( SecurityException e ) { e.printStackTrace(); } catch ( NoSuchMethodException e ) { e.printStackTrace(); } } } wrapper_3.5.51_src/src/test/common/matchOutput.in100644 0 0 3531 14333053647 17262 0ustar 0 0 #! /bin/sh #----------------------------------------------------------------------------- #------ FUNCTIONS FOR TESTS THAT PASS WHEN A CERTAIN OUTPUTS MATCH ------- #----------------------------------------------------------------------------- BASENAME=`basename "$0"` TEMPFILE="$BASENAME.tmp" printHeader() { echo "-----------------------------------------------------------------------------" echo $1 echo "" shift for p in "$@" do echo " $p" done echo "" echo " Expected output: $EXPECTED_OUTPUT" if [ -n "$EXPECTED_OUTPUT2" ] ; then echo " Expected output 2: $EXPECTED_OUTPUT2" fi echo "-----------------------------------------------------------------------------" echo "" } printResult() { result1=`grep "$EXPECTED_OUTPUT" TEMPFILE` if [ -n "$EXPECTED_OUTPUT2" ] ; then result2=`grep "$EXPECTED_OUTPUT2" TEMPFILE` fi if [ "X$result1" != "X" ] || [ -n "$EXPECTED_OUTPUT2" -a "X$result2" != "X" ] ; then printf "\033[32m=> TEST PASSED!\033[39m\n" PASSED_TESTS_COUNT=`expr $PASSED_TESTS_COUNT + 1` else printf "\033[31m=> TEST FAILED!\033[39m\n" FAILED_TESTS_COUNT=`expr $FAILED_TESTS_COUNT + 1` fi rm TEMPFILE echo "" } runTest() { ARGS="" for p in "$@"; do # Ignore arguments that are not properties case "$p" in wrapper.*) ARGS="$ARGS $p" ;; esac done ARGS="$ARGS " # use 2>&1 to merge stdout and stderr (we need to flush on each message to keep the order) # use tee command to redirect both to a temp file and the console ../bin/wrapper -c ../test/$BASENAME.conf wrapper.console.flush=TRUE $ARGS -- console 2>&1 | tee TEMPFILE } runTestFull() { printHeader "$@" shift runTest "$@" printResult } wrapper_3.5.51_src/src/test/common/report.in100644 0 0 1552 14333053647 16261 0ustar 0 0 #! /bin/sh #----------------------------------------------------------------------------- #--------- FUNCTIONS USED TO BUILD THE REPORT OF A SET OF TESTS ---------- #----------------------------------------------------------------------------- FAILED_TESTS_COUNT=0 PASSED_TESTS_COUNT=0 printReport() { TOTAL_TESTS_COUNT=`expr $PASSED_TESTS_COUNT + $FAILED_TESTS_COUNT` echo "" echo "=============================================================================" echo "$TOTAL_TESTS_COUNT tests run:" if [ $PASSED_TESTS_COUNT -gt 0 ] ; then printf " \033[32m=> $PASSED_TESTS_COUNT tests passed!\033[39m\n" fi if [ $FAILED_TESTS_COUNT -gt 0 ] ; then printf " \033[31m=> $FAILED_TESTS_COUNT tests failed!\033[39m\n" fi echo "=============================================================================" echo "" } wrapper_3.5.51_src/src/test/common/resourcelimit.in100644 0 0 6662 14333053647 17643 0ustar 0 0 #! /bin/sh #----------------------------------------------------------------------------- #------------------ FUNCTIONS FOR RESOURCE LIMIT TESTS ------------------- #----------------------------------------------------------------------------- . common/report . common/matchOutput setup() { L=$1 ULIMIT_DESC=$2 # 'ulimit' may have different outputs depending on the platform and whether it is run with Shell or Bash. # get the current soft limit CURRENT_SOFT=`ulimit -S -$L` if [ "X$CURRENT_SOFT" = "X" ] ; then echo "Unable to get the current soft limit for $ULIMIT_DESC." exit 1 fi # get the current hard limit CURRENT_HARD=`ulimit -H -$L` if [ "X$CURRENT_HARD" = "X" ] ; then echo "Unable to get the current hard limit for $ULIMIT_DESC." exit 1 fi # on macosx, the hard limit may be initialized to 'unlimited' but if this value # is decreased, it is not possible to restore it to 'unlimited' later on. # We will resolve below if it is possible or not to set the limit to 'unlimited'. if [ "$CURRENT_SOFT" = "unlimited" ] ; then CURRENT_SOFT=1000 ulimit -S -$L $CURRENT_SOFT # retrieve the value from the result of the ulimit command in case the limit was resolved to a different value CURRENT_SOFT=`ulimit -S -$L` if [ "$CURRENT_SOFT" = "unlimited" ] ; then # on z/OS (maybe other platforms as well?) the limits for '-d' are always unlimited echo "The soft and hard limits of this resource are always unlimited on this platform." exit 0 fi fi if [ "$CURRENT_HARD" = "unlimited" ] ; then CURRENT_HARD=`expr $CURRENT_SOFT + 1000` ulimit -H -$L $CURRENT_HARD # retrieve the value from the result of the ulimit command in case the limit was resolved to a different value CURRENT_HARD=`ulimit -H -$L` fi # check if we have sufficient permissions to raise the hard limit # it is not enough to check the return code of ulimit. For example, on FreeBSD, the function will return 0 even if the limit could not be set to unlimited. UNLIMITED_ALLOWED=FALSE TEMP_HARD=`expr $CURRENT_HARD + 1` ulimit -H -$L $TEMP_HARD TEMP_HARD2=`ulimit -H -$L` if [ "$TEMP_HARD" = "$TEMP_HARD2" ] ; then IS_ROOT=TRUE echo "Running the script with sufficient permissions to raise the hard limit." if [ $UNLIMITED_ALLOWED != TRUE ] ; then TEMP_HARD="unlimited" # check if we can raise up to unlimited (depending on the system some resource can't be unlimited) ulimit -H -$L $TEMP_HARD TEMP_HARD2=`ulimit -H -$L` if [ "$TEMP_HARD" = "$TEMP_HARD2" ] ; then UNLIMITED_ALLOWED=TRUE echo "The hard limit can be raised to unlimited." else UNLIMITED_ALLOWED=FALSE echo "The hard limit cannot be raised to unlimited." fi fi else IS_ROOT=FALSE echo "Running the script without permissions to raise the hard limit." fi # restore the hard limit ulimit -H -$L $CURRENT_HARD } printUlimitHeader() { TITLE=$1 CURRENT_LIMITS="Current soft limit=$CURRENT_SOFT | Current hard limit=$CURRENT_HARD" shift printHeader "$TITLE" "$CURRENT_LIMITS" "" "$@" } runUlimitTestFull() { printUlimitHeader "$@" shift runTest "$@" printResult } wrapper_3.5.51_src/src/test/org/tanukisoftware/wrapper/WrapperManagerSystemTimeTestCase.java100644 0 0 11115 14333053652 27711 0ustar 0 0 package org.tanukisoftware.wrapper; /* * Copyright (c) 1999, 2022 Tanuki Software, Ltd. * http://www.tanukisoftware.com * All rights reserved. * * This software is the confidential and proprietary information * of Tanuki Software. ("Confidential Information"). You shall * not disclose such Confidential Information and shall use it * only in accordance with the terms of the license agreement you * entered into with Tanuki Software. */ import junit.framework.TestCase; /** * Tests the conversion of system time into ticks. * * @author Leif Mortenson */ public class WrapperManagerSystemTimeTestCase extends TestCase { private final int TICK_MS = 100; /*--------------------------------------------------------------- * Constructor *-------------------------------------------------------------*/ public WrapperManagerSystemTimeTestCase( String name ) { super( name ); } /*--------------------------------------------------------------- * Methods *-------------------------------------------------------------*/ private int getSystemTicks( long time ) { // Calculate a tick count using the current system time. The // conversion from a long in ms, to an int in TICK_MS increments // will result in data loss, but the loss of bits and resulting // overflow is expected and Ok. return (int)( time / TICK_MS ); } private long getTickAge( int start, int end ) { // Important to cast the first value so that negative values are correctly // cast to negative long values. return (long)( end - start ) * TICK_MS; } public void doTest( long time, int expectedTicks, int negTicks, int posTicks ) { int ticks = getSystemTicks( time ); assertEquals( "getSystemTicks( " + time + " ) failed", expectedTicks, ticks ); long posAge = getTickAge( posTicks, expectedTicks ); assertEquals( "getTickAge( " + posTicks + ", " + expectedTicks + " )", 1000L, posAge ); long negAge = getTickAge( negTicks, expectedTicks ); assertEquals( "getTickAge( " + negTicks + ", " + expectedTicks + " )", -1000L, negAge ); } /*--------------------------------------------------------------- * Test Cases *-------------------------------------------------------------*/ public void testSimple() { doTest( 0L, 0, 10, -10 ); doTest( 1L, 0, 10, -10 ); doTest( 2L, 0, 10, -10 ); doTest( 99L, 0, 10, -10 ); doTest( 100L, 1, 11, -9 ); doTest( 101L, 1, 11, -9 ); doTest( 199L, 1, 11, -9 ); doTest( 200L, 2, 12, -8 ); } public void testOverflow() { doTest( 214748363700L, 2147483637, 2147483647, 2147483627 ); doTest( 214748363800L, 2147483638, -2147483648, 2147483628 ); doTest( 214748363900L, 2147483639, -2147483647, 2147483629 ); doTest( 214748364000L, 2147483640, -2147483646, 2147483630 ); doTest( 214748364100L, 2147483641, -2147483645, 2147483631 ); doTest( 214748364200L, 2147483642, -2147483644, 2147483632 ); doTest( 214748364300L, 2147483643, -2147483643, 2147483633 ); doTest( 214748364400L, 2147483644, -2147483642, 2147483634 ); doTest( 214748364500L, 2147483645, -2147483641, 2147483635 ); doTest( 214748364600L, 2147483646, -2147483640, 2147483636 ); doTest( 214748364700L, 2147483647, -2147483639, 2147483637 ); doTest( 214748364800L, -2147483648, -2147483638, 2147483638 ); doTest( 214748364900L, -2147483647, -2147483637, 2147483639 ); doTest( 214748365000L, -2147483646, -2147483636, 2147483640 ); doTest( 214748365100L, -2147483645, -2147483635, 2147483641 ); doTest( 214748365200L, -2147483644, -2147483634, 2147483642 ); doTest( 214748365300L, -2147483643, -2147483633, 2147483643 ); doTest( 214748365400L, -2147483642, -2147483632, 2147483644 ); doTest( 214748365500L, -2147483641, -2147483631, 2147483645 ); doTest( 214748365600L, -2147483640, -2147483630, 2147483646 ); doTest( 214748365700L, -2147483639, -2147483629, 2147483647 ); doTest( 214748365800L, -2147483638, -2147483628, -2147483648 ); doTest( 429496729300L, -3, 7, -13 ); doTest( 429496729400L, -2, 8, -12 ); doTest( 429496729500L, -1, 9, -11 ); doTest( 429496729600L, 0, 10, -10 ); doTest( 429496729700L, 1, 11, -9 ); doTest( 429496729800L, 2, 12, -8 ); doTest( 429496729900L, 3, 13, -7 ); } } wrapper_3.5.51_src/src/test/resourcelimit_data.in100644 0 0 35063 14333053647 17361 0ustar 0 0 #! /bin/sh #----------------------------------------------------------------------------- #--------------- TESTS - wrapper.ulimit.data.* properties ---------------- #----------------------------------------------------------------------------- . common/resourcelimit setup "d" "the data segment size" #----------------------------------------------------------------------------- # Test 1-a DATA_SOFT=`expr $CURRENT_HARD - 1` EXPECTED_OUTPUT="Data segment size limits: $DATA_SOFT (soft), $CURRENT_HARD (hard)" runUlimitTestFull "Test 1-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "#wrapper.ulimit.data.hard=" "wrapper.ulimit.data.strict=FALSE" # Test 1-b EXPECTED_OUTPUT="Data segment size limits: $DATA_SOFT (soft), $CURRENT_HARD (hard)" runUlimitTestFull "Test 1-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "#wrapper.ulimit.data.hard=" "wrapper.ulimit.data.strict=TRUE" # Test 2-a DATA_SOFT=`expr $CURRENT_HARD + 1` EXPECTED_OUTPUT="Data segment size limits: $CURRENT_HARD (soft), $CURRENT_HARD (hard)" runUlimitTestFull "Test 2-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "#wrapper.ulimit.data.hard=" "wrapper.ulimit.data.strict=FALSE" # Test 2-b EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 2-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "#wrapper.ulimit.data.hard=" "wrapper.ulimit.data.strict=TRUE" # Test 3-a DATA_HARD=`expr $CURRENT_HARD - 1` if [ $CURRENT_SOFT -gt $DATA_HARD ] ; then EXPECTED_OUTPUT="Data segment size limits: $DATA_HARD (soft), $DATA_HARD (hard)" else EXPECTED_OUTPUT="Data segment size limits: $CURRENT_SOFT (soft), $DATA_HARD (hard)" fi runUlimitTestFull "Test 3-a" "#wrapper.ulimit.data.soft=" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 3-b if [ $CURRENT_SOFT -gt $DATA_HARD ] ; then EXPECTED_OUTPUT="The Wrapper will stop" else EXPECTED_OUTPUT="Data segment size limits: $CURRENT_SOFT (soft), $DATA_HARD (hard)" fi runUlimitTestFull "Test 3-b" "#wrapper.ulimit.data.soft=" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Test 4-a DATA_HARD=`expr $CURRENT_HARD + 1` if [ $IS_ROOT = TRUE ] ; then EXPECTED_OUTPUT="Data segment size limits: $CURRENT_SOFT (soft), $DATA_HARD (hard)" else EXPECTED_OUTPUT="Data segment size limits: $CURRENT_SOFT (soft), $CURRENT_HARD (hard)" fi runUlimitTestFull "Test 4-a" "#wrapper.ulimit.data.soft=" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 4-b if [ $IS_ROOT = TRUE ] ; then EXPECTED_OUTPUT="Data segment size limits: $CURRENT_SOFT (soft), $DATA_HARD (hard)" else EXPECTED_OUTPUT="The Wrapper will stop" fi runUlimitTestFull "Test 4-b" "#wrapper.ulimit.data.soft=" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Test 5-a DATA_HARD=`expr $CURRENT_SOFT - 1` EXPECTED_OUTPUT="Data segment size limits: $DATA_HARD (soft), $DATA_HARD (hard)" runUlimitTestFull "Test 5-a" "#wrapper.ulimit.data.soft=" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 5-b EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 5-b" "#wrapper.ulimit.data.soft=" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Test 6-a DATA_HARD=`expr $CURRENT_HARD - 1` DATA_SOFT=`expr $CURRENT_HARD - 2` EXPECTED_OUTPUT="Data segment size limits: $DATA_SOFT (soft), $DATA_HARD (hard)" runUlimitTestFull "Test 6-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 6-b EXPECTED_OUTPUT="Data segment size limits: $DATA_SOFT (soft), $DATA_HARD (hard)" runUlimitTestFull "Test 6-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Test 7-a DATA_HARD=`expr $CURRENT_HARD + 1` DATA_SOFT=`expr $CURRENT_HARD - 2` if [ $IS_ROOT = TRUE ] ; then EXPECTED_OUTPUT="Data segment size limits: $DATA_SOFT (soft), $DATA_HARD (hard)" else EXPECTED_OUTPUT="Data segment size limits: $DATA_SOFT (soft), $CURRENT_HARD (hard)" fi runUlimitTestFull "Test 7-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 7-b if [ $IS_ROOT = TRUE ] ; then EXPECTED_OUTPUT="Data segment size limits: $DATA_SOFT (soft), $DATA_HARD (hard)" else EXPECTED_OUTPUT="The Wrapper will stop" fi runUlimitTestFull "Test 7-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Test 8-a DATA_HARD=`expr $CURRENT_HARD - 2` DATA_SOFT=`expr $CURRENT_HARD - 1` EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 8-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 8-b EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 8-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Test 9-a DATA_HARD=`expr $CURRENT_HARD + 1` DATA_SOFT=`expr $CURRENT_HARD + 2` EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 9-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 9-b EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 9-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Test 10-a DATA_HARD="unlimited" if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Data segment size limits: $CURRENT_SOFT (soft), $DATA_HARD (hard)" else EXPECTED_OUTPUT="Data segment size limits: $CURRENT_SOFT (soft), $CURRENT_HARD (hard)" fi runUlimitTestFull "Test 10-a" "#wrapper.ulimit.data.soft=" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 10-b if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Data segment size limits: $CURRENT_SOFT (soft), $DATA_HARD (hard)" else EXPECTED_OUTPUT="The Wrapper will stop" fi runUlimitTestFull "Test 10-b" "#wrapper.ulimit.data.soft=" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Test 11-a DATA_SOFT="unlimited" EXPECTED_OUTPUT="Data segment size limits: $CURRENT_HARD (soft), $CURRENT_HARD (hard)" runUlimitTestFull "Test 11-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "#wrapper.ulimit.data.hard=" "wrapper.ulimit.data.strict=FALSE" # Test 11-b EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 11-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "#wrapper.ulimit.data.hard=" "wrapper.ulimit.data.strict=TRUE" # Test 12-a DATA_SOFT="unlimited" DATA_HARD="unlimited" if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Data segment size limits: $DATA_SOFT (soft), $DATA_HARD (hard)" else EXPECTED_OUTPUT="Data segment size limits: $CURRENT_HARD (soft), $CURRENT_HARD (hard)" fi runUlimitTestFull "Test 12-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 12-b if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Data segment size limits: $DATA_SOFT (soft), $DATA_HARD (hard)" else EXPECTED_OUTPUT="The Wrapper will stop" fi runUlimitTestFull "Test 12-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Test 13-a DATA_SOFT=`expr $CURRENT_HARD - 1` DATA_HARD="unlimited" if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Data segment size limits: $DATA_SOFT (soft), $DATA_HARD (hard)" else EXPECTED_OUTPUT="Data segment size limits: $DATA_SOFT (soft), $CURRENT_HARD (hard)" fi runUlimitTestFull "Test 13-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 13-b if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Data segment size limits: $DATA_SOFT (soft), $DATA_HARD (hard)" else EXPECTED_OUTPUT="The Wrapper will stop" fi runUlimitTestFull "Test 13-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Test 14-a DATA_SOFT=`expr $CURRENT_HARD + 1` DATA_HARD="unlimited" if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Data segment size limits: $DATA_SOFT (soft), $DATA_HARD (hard)" else EXPECTED_OUTPUT="Data segment size limits: $CURRENT_HARD (soft), $CURRENT_HARD (hard)" fi runUlimitTestFull "Test 14-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 14-b if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Data segment size limits: $DATA_SOFT (soft), $DATA_HARD (hard)" else EXPECTED_OUTPUT="The Wrapper will stop" fi runUlimitTestFull "Test 14-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Test 15-a DATA_SOFT="2147483646" DATA_HARD="2147483646" EXPECTED_OUTPUT="Data segment size limits: $CURRENT_HARD (soft), $CURRENT_HARD (hard)" if [ $IS_ROOT = TRUE ] ; then # Some platforms will allow to raise the limits to such high values while other wont EXPECTED_OUTPUT2="Data segment size limits: $DATA_SOFT (soft), $DATA_HARD (hard)" fi runUlimitTestFull "Test 15-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 15-b EXPECTED_OUTPUT="The Wrapper will stop" if [ $IS_ROOT = TRUE ] ; then # Some platforms will allow to raise the limits to such high values while other wont EXPECTED_OUTPUT2="Data segment size limits: $DATA_SOFT (soft), $DATA_HARD (hard)" fi runUlimitTestFull "Test 15-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Clear EXPECTED_OUTPUT2 EXPECTED_OUTPUT2= # Test 16-a DATA_SOFT="current" DATA_HARD=`expr $CURRENT_HARD - 1` if [ $CURRENT_SOFT -gt $DATA_HARD ] ; then EXPECTED_OUTPUT="Data segment size limits: $DATA_HARD (soft), $DATA_HARD (hard)" else EXPECTED_OUTPUT="Data segment size limits: $CURRENT_SOFT (soft), $DATA_HARD (hard)" fi runUlimitTestFull "Test 16-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 16-b if [ $CURRENT_SOFT -gt $DATA_HARD ] ; then EXPECTED_OUTPUT="The Wrapper will stop" else EXPECTED_OUTPUT="Data segment size limits: $CURRENT_SOFT (soft), $DATA_HARD (hard)" fi runUlimitTestFull "Test 16-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Test 17-a DATA_SOFT="foo" DATA_HARD="foo" EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 17-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 17-b EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 17-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Test 18-a DATA_SOFT="foo" DATA_HARD="foo" EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 18-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 18-b EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 18-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Test 19-a DATA_SOFT="-1" DATA_HARD="-1" EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 19-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 19-b EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 19-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Test 20-a DATA_SOFT="HARD" EXPECTED_OUTPUT="Data segment size limits: $CURRENT_HARD (soft), $CURRENT_HARD (hard)" runUlimitTestFull "Test 20-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "#wrapper.ulimit.data.hard=" "wrapper.ulimit.data.strict=FALSE" # Test 20-b EXPECTED_OUTPUT="Data segment size limits: $CURRENT_HARD (soft), $CURRENT_HARD (hard)" runUlimitTestFull "Test 20-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "#wrapper.ulimit.data.hard=" "wrapper.ulimit.data.strict=TRUE" # Test 21-a DATA_SOFT="HARD" DATA_HARD=`expr $CURRENT_HARD - 1` EXPECTED_OUTPUT="Data segment size limits: $DATA_HARD (soft), $DATA_HARD (hard)" runUlimitTestFull "Test 21-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 21-b EXPECTED_OUTPUT="Data segment size limits: $DATA_HARD (soft), $DATA_HARD (hard)" runUlimitTestFull "Test 21-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Test 22-a DATA_SOFT="HARD" DATA_HARD=`expr $CURRENT_HARD + 1` if [ $IS_ROOT = TRUE ] ; then EXPECTED_OUTPUT="Data segment size limits: $DATA_HARD (soft), $DATA_HARD (hard)" else EXPECTED_OUTPUT="Data segment size limits: $CURRENT_HARD (soft), $CURRENT_HARD (hard)" fi runUlimitTestFull "Test 22-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 22-b if [ $IS_ROOT = TRUE ] ; then EXPECTED_OUTPUT="Data segment size limits: $DATA_HARD (soft), $DATA_HARD (hard)" else EXPECTED_OUTPUT="The Wrapper will stop" fi runUlimitTestFull "Test 22-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Test 23-a DATA_SOFT="HARD" DATA_HARD="unlimited" if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Data segment size limits: $DATA_HARD (soft), $DATA_HARD (hard)" else EXPECTED_OUTPUT="Data segment size limits: $CURRENT_HARD (soft), $CURRENT_HARD (hard)" fi runUlimitTestFull "Test 23-a" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 23-b if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Data segment size limits: $DATA_HARD (soft), $DATA_HARD (hard)" else EXPECTED_OUTPUT="The Wrapper will stop" fi runUlimitTestFull "Test 23-b" "wrapper.ulimit.data.soft=$DATA_SOFT" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" # Test 24-a DATA_HARD="HARD" EXPECTED_OUTPUT="Data segment size limits: $CURRENT_SOFT (soft), $CURRENT_HARD (hard)" runUlimitTestFull "Test 24-a" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=FALSE" # Test 24-b EXPECTED_OUTPUT="Data segment size limits: $CURRENT_SOFT (soft), $CURRENT_HARD (hard)" runUlimitTestFull "Test 24-b" "wrapper.ulimit.data.hard=$DATA_HARD" "wrapper.ulimit.data.strict=TRUE" printReport wrapper_3.5.51_src/src/test/resourcelimit_nofile.in100644 0 0 37472 14333053647 17732 0ustar 0 0 #! /bin/sh #----------------------------------------------------------------------------- #-------------- TESTS - wrapper.ulimit.nofile.* properties --------------- #----------------------------------------------------------------------------- . common/resourcelimit setup "n" "the number of file descriptors" #----------------------------------------------------------------------------- # Test 1-a NOFILE_SOFT=`expr $CURRENT_HARD - 1` EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_SOFT (soft), $CURRENT_HARD (hard)" runUlimitTestFull "Test 1-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "#wrapper.ulimit.nofile.hard=" "wrapper.ulimit.nofile.strict=FALSE" # Test 1-b EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_SOFT (soft), $CURRENT_HARD (hard)" runUlimitTestFull "Test 1-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "#wrapper.ulimit.nofile.hard=" "wrapper.ulimit.nofile.strict=TRUE" # Test 2-a NOFILE_SOFT=`expr $CURRENT_HARD + 1` EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_HARD (soft), $CURRENT_HARD (hard)" runUlimitTestFull "Test 2-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "#wrapper.ulimit.nofile.hard=" "wrapper.ulimit.nofile.strict=FALSE" # Test 2-b EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 2-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "#wrapper.ulimit.nofile.hard=" "wrapper.ulimit.nofile.strict=TRUE" # Test 3-a NOFILE_HARD=`expr $CURRENT_HARD - 1` if [ $CURRENT_SOFT -gt $NOFILE_HARD ] ; then EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_HARD (soft), $NOFILE_HARD (hard)" else EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_SOFT (soft), $NOFILE_HARD (hard)" fi runUlimitTestFull "Test 3-a" "#wrapper.ulimit.nofile.soft=" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 3-b if [ $CURRENT_SOFT -gt $NOFILE_HARD ] ; then EXPECTED_OUTPUT="The Wrapper will stop" else EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_SOFT (soft), $NOFILE_HARD (hard)" fi runUlimitTestFull "Test 3-b" "#wrapper.ulimit.nofile.soft=" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Test 4-a NOFILE_HARD=`expr $CURRENT_HARD + 1` if [ $IS_ROOT = TRUE ] ; then EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_SOFT (soft), $NOFILE_HARD (hard)" else EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_SOFT (soft), $CURRENT_HARD (hard)" fi runUlimitTestFull "Test 4-a" "#wrapper.ulimit.nofile.soft=" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 4-b if [ $IS_ROOT = TRUE ] ; then EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_SOFT (soft), $NOFILE_HARD (hard)" else EXPECTED_OUTPUT="The Wrapper will stop" fi runUlimitTestFull "Test 4-b" "#wrapper.ulimit.nofile.soft=" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Test 5-a NOFILE_HARD=`expr $CURRENT_SOFT - 1` EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_HARD (soft), $NOFILE_HARD (hard)" runUlimitTestFull "Test 5-a" "#wrapper.ulimit.nofile.soft=" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 5-b EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 5-b" "#wrapper.ulimit.nofile.soft=" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Test 6-a NOFILE_HARD=`expr $CURRENT_HARD - 1` NOFILE_SOFT=`expr $CURRENT_HARD - 2` EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_SOFT (soft), $NOFILE_HARD (hard)" runUlimitTestFull "Test 6-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 6-b EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_SOFT (soft), $NOFILE_HARD (hard)" runUlimitTestFull "Test 6-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Test 7-a NOFILE_HARD=`expr $CURRENT_HARD + 1` NOFILE_SOFT=`expr $CURRENT_HARD - 2` if [ $IS_ROOT = TRUE ] ; then EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_SOFT (soft), $NOFILE_HARD (hard)" else EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_SOFT (soft), $CURRENT_HARD (hard)" fi runUlimitTestFull "Test 7-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 7-b if [ $IS_ROOT = TRUE ] ; then EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_SOFT (soft), $NOFILE_HARD (hard)" else EXPECTED_OUTPUT="The Wrapper will stop" fi runUlimitTestFull "Test 7-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Test 8-a NOFILE_HARD=`expr $CURRENT_HARD - 2` NOFILE_SOFT=`expr $CURRENT_HARD - 1` EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 8-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 8-b EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 8-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Test 9-a NOFILE_HARD=`expr $CURRENT_HARD + 1` NOFILE_SOFT=`expr $CURRENT_HARD + 2` EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 9-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 9-b EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 9-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Test 10-a NOFILE_HARD="unlimited" if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_SOFT (soft), $NOFILE_HARD (hard)" else EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_SOFT (soft), $CURRENT_HARD (hard)" fi runUlimitTestFull "Test 10-a" "#wrapper.ulimit.nofile.soft=" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 10-b if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_SOFT (soft), $NOFILE_HARD (hard)" else EXPECTED_OUTPUT="The Wrapper will stop" fi runUlimitTestFull "Test 10-b" "#wrapper.ulimit.nofile.soft=" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Test 11-a NOFILE_SOFT="unlimited" EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_HARD (soft), $CURRENT_HARD (hard)" runUlimitTestFull "Test 11-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "#wrapper.ulimit.nofile.hard=" "wrapper.ulimit.nofile.strict=FALSE" # Test 11-b EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 11-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "#wrapper.ulimit.nofile.hard=" "wrapper.ulimit.nofile.strict=TRUE" # Test 12-a NOFILE_SOFT="unlimited" NOFILE_HARD="unlimited" if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_SOFT (soft), $NOFILE_HARD (hard)" else EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_HARD (soft), $CURRENT_HARD (hard)" fi runUlimitTestFull "Test 12-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 12-b if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_SOFT (soft), $NOFILE_HARD (hard)" else EXPECTED_OUTPUT="The Wrapper will stop" fi runUlimitTestFull "Test 12-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Test 13-a NOFILE_SOFT=`expr $CURRENT_HARD - 1` NOFILE_HARD="unlimited" if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_SOFT (soft), $NOFILE_HARD (hard)" else EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_SOFT (soft), $CURRENT_HARD (hard)" fi runUlimitTestFull "Test 13-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 13-b if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_SOFT (soft), $NOFILE_HARD (hard)" else EXPECTED_OUTPUT="The Wrapper will stop" fi runUlimitTestFull "Test 13-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Test 14-a NOFILE_SOFT=`expr $CURRENT_HARD + 1` NOFILE_HARD="unlimited" if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_SOFT (soft), $NOFILE_HARD (hard)" else EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_HARD (soft), $CURRENT_HARD (hard)" fi runUlimitTestFull "Test 14-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 14-b if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_SOFT (soft), $NOFILE_HARD (hard)" else EXPECTED_OUTPUT="The Wrapper will stop" fi runUlimitTestFull "Test 14-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Test 15-a NOFILE_SOFT="2147483646" NOFILE_HARD="2147483646" EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_HARD (soft), $CURRENT_HARD (hard)" if [ $IS_ROOT = TRUE ] ; then # Some platforms will allow to raise the limits to such high values while other wont EXPECTED_OUTPUT2="Number of open file descriptors limits: $NOFILE_SOFT (soft), $NOFILE_HARD (hard)" fi runUlimitTestFull "Test 15-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 15-b EXPECTED_OUTPUT="The Wrapper will stop" if [ $IS_ROOT = TRUE ] ; then # Some platforms will allow to raise the limits to such high values while other wont EXPECTED_OUTPUT2="Number of open file descriptors limits: $NOFILE_SOFT (soft), $NOFILE_HARD (hard)" fi runUlimitTestFull "Test 15-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Clear EXPECTED_OUTPUT2 EXPECTED_OUTPUT2= # Test 16-a NOFILE_SOFT="current" NOFILE_HARD=`expr $CURRENT_HARD - 1` if [ $CURRENT_SOFT -gt $NOFILE_HARD ] ; then EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_HARD (soft), $NOFILE_HARD (hard)" else EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_SOFT (soft), $NOFILE_HARD (hard)" fi runUlimitTestFull "Test 16-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 16-b if [ $CURRENT_SOFT -gt $NOFILE_HARD ] ; then EXPECTED_OUTPUT="The Wrapper will stop" else EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_SOFT (soft), $NOFILE_HARD (hard)" fi runUlimitTestFull "Test 16-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Test 17-a NOFILE_SOFT="foo" NOFILE_HARD="foo" EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 17-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 17-b EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 17-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Test 18-a NOFILE_SOFT="foo" NOFILE_HARD="foo" EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 18-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 18-b EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 18-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Test 19-a NOFILE_SOFT="-1" NOFILE_HARD="-1" EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 19-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 19-b EXPECTED_OUTPUT="The Wrapper will stop" runUlimitTestFull "Test 19-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Test 20-a NOFILE_SOFT="HARD" EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_HARD (soft), $CURRENT_HARD (hard)" runUlimitTestFull "Test 20-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "#wrapper.ulimit.nofile.hard=" "wrapper.ulimit.nofile.strict=FALSE" # Test 20-b EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_HARD (soft), $CURRENT_HARD (hard)" runUlimitTestFull "Test 20-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "#wrapper.ulimit.nofile.hard=" "wrapper.ulimit.nofile.strict=TRUE" # Test 21-a NOFILE_SOFT="HARD" NOFILE_HARD=`expr $CURRENT_HARD - 1` EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_HARD (soft), $NOFILE_HARD (hard)" runUlimitTestFull "Test 21-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 21-b EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_HARD (soft), $NOFILE_HARD (hard)" runUlimitTestFull "Test 21-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Test 22-a NOFILE_SOFT="HARD" NOFILE_HARD=`expr $CURRENT_HARD + 1` if [ $IS_ROOT = TRUE ] ; then EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_HARD (soft), $NOFILE_HARD (hard)" else EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_HARD (soft), $CURRENT_HARD (hard)" fi runUlimitTestFull "Test 22-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 22-b if [ $IS_ROOT = TRUE ] ; then EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_HARD (soft), $NOFILE_HARD (hard)" else EXPECTED_OUTPUT="The Wrapper will stop" fi runUlimitTestFull "Test 22-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Test 23-a NOFILE_SOFT="HARD" NOFILE_HARD="unlimited" if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_HARD (soft), $NOFILE_HARD (hard)" else EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_HARD (soft), $CURRENT_HARD (hard)" fi runUlimitTestFull "Test 23-a" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 23-b if [ $IS_ROOT = TRUE -a $UNLIMITED_ALLOWED = TRUE ] ; then EXPECTED_OUTPUT="Number of open file descriptors limits: $NOFILE_HARD (soft), $NOFILE_HARD (hard)" else EXPECTED_OUTPUT="The Wrapper will stop" fi runUlimitTestFull "Test 23-b" "wrapper.ulimit.nofile.soft=$NOFILE_SOFT" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" # Test 24-a NOFILE_HARD="HARD" EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_SOFT (soft), $CURRENT_HARD (hard)" runUlimitTestFull "Test 24-a" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=FALSE" # Test 24-b EXPECTED_OUTPUT="Number of open file descriptors limits: $CURRENT_SOFT (soft), $CURRENT_HARD (hard)" runUlimitTestFull "Test 24-b" "wrapper.ulimit.nofile.hard=$NOFILE_HARD" "wrapper.ulimit.nofile.strict=TRUE" printReport