passenger-5.0.30/000755 000765 000024 00000000000 12233035540 014206 5ustar00honglistaff000000 000000 passenger-5.0.30/.editorconfig000644 000765 000024 00000004053 12233035540 016665 0ustar00honglistaff000000 000000 # http://EditorConfig.org root = true [*] end_of_line = lf charset = utf-8 [bin/*] indent_style=space indent_size=2 trim_trailing_whitespace = true [doc/*.txt] indent_style=tab indent_size=4 [doc/*/*.txt] indent_style=tab indent_size=4 [*.cpp] indent_style = tab indent_size = 4 trim_trailing_whitespace = true insert_final_newline = true [*.c] indent_style = tab indent_size = 4 trim_trailing_whitespace = true insert_final_newline = true [*.h] indent_style = tab indent_size = 4 trim_trailing_whitespace = true insert_final_newline = true [*.hpp] indent_style = tab indent_size = 4 trim_trailing_whitespace = true insert_final_newline = true [*.rb] indent_style = space indent_size = 2 trim_trailing_whitespace = true [*.cxxcodebuilder] indent_style = space indent_size = 2 trim_trailing_whitespace = true [config.ru] indent_style = space indent_size = 2 trim_trailing_whitespace = true [Rakefile] indent_style = space indent_size = 2 trim_trailing_whitespace = true [Gemfile] indent_style = space indent_size = 2 trim_trailing_whitespace = true [*.js] indent_style = tab indent_size = 4 trim_trailing_whitespace = true [src/nginx_module/*.c] indent_style = space indent_size = 4 [src/nginx_module/*.h] indent_style = space indent_size = 4 [src/cxx_supportlib/vendor-modified/libev/*.c] indent_style = space indent_size = 2 trim_trailing_whitespace = false [src/cxx_supportlib/vendor-modified/libev/*.h] indent_style = space indent_size = 2 trim_trailing_whitespace = false [src/cxx_supportlib/vendor-copy/libuv/src/*.c] indent_style = space indent_size = 2 trim_trailing_whitespace = false [src/cxx_supportlib/vendor-copy/libuv/src/*.h] indent_style = space indent_size = 2 trim_trailing_whitespace = false [src/cxx_supportlib/vendor-copy/libuv/src/unix/*.c] indent_style = space indent_size = 2 trim_trailing_whitespace = false [src/cxx_supportlib/vendor-copy/libuv/src/unix/*.h] indent_style = space indent_size = 2 trim_trailing_whitespace = false [resources/templates/standalone/config.erb] indent_style = space indent_size = 4 trim_trailing_whitespace = true passenger-5.0.30/bin/000755 000765 000024 00000000000 12233035540 014756 5ustar00honglistaff000000 000000 passenger-5.0.30/build/000755 000765 000024 00000000000 12233035540 015305 5ustar00honglistaff000000 000000 passenger-5.0.30/CHANGELOG000644 000765 000024 00000415570 12233035540 015434 0ustar00honglistaff000000 000000 Release 5.0.30 -------------- * Changes mbuf block size from 512 to 4096 bytes to better fit modern requests and significantly speed up disk buffering. * [Nginx] Fixes PCRE checksum after the preferred version update in 5.0.29 (contributed by: clemensg). * [Apache] Fixes buffer limit crash on large file upload (when core disk buffer can't keep up with client for some time), and limits per-client buffer memory usage to 130 KB. Closes GH-1620. * Fixes potential hang when an UnseekableSocket gets serialized to json. Closes GH-1838. Release 5.0.29 -------------- * Fixes the FreeBSD build breaking due to the `-ldl` flag introduced by the LVE integration patch (5.0.28). Closes GH-1805. * Fixes per-application interpreter override (ruby, node, python) being ignored in mass deployment mode. Closes GH-1818. * Fixes incomplete refactor from 5.0.27 that could, under specific conditions, lead to a Passenger crash. Closes GH-1794. * [Apache] Remove unused code that caused a crash in configurations with thousands of VirtualHost entries. Closes GH-1676. * [Nginx] Fixes use of invalid logfile name (memory already released) in backup log redirection code. Possibly related to GH-1774. * [Nginx] The preferred Nginx version is now 1.10.1 (previously 1.10.0). * [Nginx] The preferred PCRE version is now 8.39 (previously 8.34). * [Standalone] Passenger Standalone now supports /dev/stdout and /dev/stderr as log file path (via `--log-file` or Passengerfile.json). This is especially useful in Docker containers. In previous versions logging to those paths did not work, resulting in nothing getting logged at all. Release 5.0.28 -------------- * Finalizes the fix (5.0.26) for the `rails server` command integration to prevent "missing on_event" errors. Closes GH-1768. * Fixes missing -fPIC in Nginx dynamic module compilation (5.0.26) on Linux (rewrite of a patch by Andrei Belov). Closes GH-1793. * Fixes memory leak that could occur whenever more than 1024 concurrent requests are handled (more likely since the higher concurrency support options from 5.0.24). Closes GH-1797. * Integrates with CloudLinux LVE and CageFS (security checks and a new option PassengerLveMinUid). Thanks to Oleksiy Shchukin from CloudLinux Inc. for contributing this. * Fixes the Nginx build when the PCRE library is not available (such as when compiling with `--without-http_rewrite_module`). Closes GH-1796. * Extends `passenger-memory-stats` filter to show the instance dir toucher too (as well as the core in valgrind debug runs). * Changes the default for friendly error pages to "off" unless the environment is set to "development", rather than "on" unless "staging" or "production". Closes GH-1782. * [Nginx] The preferred Nginx version is now 1.10.0 (previously 1.8.1). Release 5.0.27 -------------- * Fixes encoding issue for Ruby apps that resulted in a 0-byte response body. This occurred when the Ruby native support lib was not used and the app outputted an encoding that doesn't mix with UTF-8 (like UTF-16). Closes GH-1763. * Fixes Passenger Core and application processes staying on the Watchdogs OOM score (unkillable) when user switching is set to off. Closes GH-1631. * Supports Debian GNU/kFreeBSD build. Based on contribution by stevenc99. * Switches a number of places in the Passenger Core over to using the monotonic clock instead of the wallclock for robustness against clock time-stepping. * Slightly improves out-of-memory detection in some subroutines. * Fixes incomplete libuv upgrade: some build files were not autoregenerated during the upgrade from 1.5.0 to 1.8.0 in the previous release. * Warnings about 502 responses that are caused by applications aborting their output while the client is no longer connected (e.g. due to half-close event, reported since 5.0.26) are now reduced to debug level. * Fixes automatic compilation of Ruby's native_support library in case Passenger was installed through Debian or RPM packages. Closes GH-1778. * Fixes memory leak when buffering large request/response bodies to disk (which happens as soon as the 100 KB memory buffer is full). * Fixes crash if an application spawn fails and a non-UTF8 character appears in the spawn output. Closes GH-1601. * Updates the `rails server` command integration (from 5.0.25) to prevent "missing on_event" errors. Closes GH-1768. Update: not all required code made it to the release, the final fix is delivered in 5.0.28. * [Union Station] Fixes a crash that occurs if all of the following conditions are met: 1) Union Station support is enabled, 2) the client sent at least one header containing the empty string, 3) the application responds with a 4xx or 5xx status. Closes GH-1776. Release 5.0.26 -------------- * `passenger-status --show=server` now reports the speed at which new requests are accepted. * `passenger-status --show=server` now reports `last_data_send_time` and `last_data_receive_time` which can be used to troubleshoot long-running requests (for example, to see if a websocket heartbeat is stuck). * Passenger now reports TCP half-closing events to Node.js and Meteor applications, which allows them to detect request body and WebSocket closes without having to send data to the client. * Fixes outputting Content-Length and Transfer-Encoding headers on HEAD requests for Ruby apps. These headers were omitted in previous versions on HEAD requests. * Bumps the default socket backlog size from 1024 to 2048. * Upgrades libuv to version 1.8.0. * When using our RPM packages, system SELinux policy upgrades no longer break the Passenger SELinux policy. Closes GH-1663. * [Apache] Fixes compilation against Apache installations which include `-pie` in CFLAGS. Closes GH-1756. * [Nginx, Standalone] Bumps default Nginx worker_connections from 1024 to 4096 (effectively 2048 because of internal reverse proxy) * [Nginx, Standalone] Introduces the option `core_file_descriptor_ulimit` and `app_file_descriptor_ulimit`, for setting the file descriptor ulimits of the Passenger core and the application, respectively. * [Nginx] Passenger can now be [compiled as an Nginx dynamic module](https://www.phusionpassenger.com/library/install/nginx/install_as_nginx_module.html#dynamic-module). Thanks to Ruslan Ermilov from NGINX Inc for contributing this. * [Standalone] Prints a warning when an unsupported configuration option in Passengerfile.json is set. * [Standalone] Fixes "address already in use" errors when using the builtin engine. * [Enterprise] The rolling restart feature now waits until the old process is completely gone (drained its request queue, process exited) before proceeding with rolling restarting the next process. This results in friendlier resource usage during rolling restart. * [Union Station] Fixes custom logging time arguments getting overwritten by current time for Ruby apps (so some sub-blocks like "framework request processing" appeared shorter than they were). This could happen since the switch to monotonic clock in 5.0.22. Release 5.0.25 -------------- * Integrates into the `rails server` command. Please learn more at [the Passenger + Rails integration documentation](https://www.phusionpassenger.com/library/dev/ruby/rails_integration.html). * Adds explicit support for Action Cable. Please learn more at the [Passenger Library](https://www.phusionpassenger.com/library/dev/ruby/rails_integration.html#action_cable). * Removes packages for Ubuntu 15.04 Vivid and Debian 6. Ubuntu 15.04 and Debian 6 are still supported, we just don't supply packages for them anymore. If you are an Ubuntu 15.04 or Debian 6 user and you want to use Passenger >= 5.0.25, then please upgrade your distribution, or install Passenger from RubyGems/tarball. * Fixes a potential crash due to memory corruption in code for `passenger-config reopen-logs`. * Fixes a potential crash in the large (inbound/outbound) file buffering code. * Fixes a crash that occurs when using Nginx + HTTPS + Sub-requests. Closes GH-1724. * Fixes a crash that occurs when using Nginx + syslog and a logfile for Passenger. Also fixes edge cases where the Nginx logpath would override the Passenger logpath. Closes GH-1514 (again). * [Union Station] Fixes a potential crash due to a wrong limit on snprintf (introduced in 5.0.24 by GH-1633). Closes GH-1744. * [Union Station] Fixes Union Station Node.js request introspection to allow for application.use method chaining. Closes GH-1745. * [Union Station] Fixes information about sinks sometimes missing from `passenger-status --show=union_station`. * [Union Station] When one or more Union Station gateways are suffering from technical difficulties, the Union Station support code now tries more quickly to reestablish the connection. * [Standalone] Don't reject the value 0 (meaning no limit) for `--max-request-queue-size`. Closes GH-1743. * [Standalone] Makes the `--address` option work more reliably if the passed hostname may resolve to multiple addresses. For example, if you pass `--address localhost` then previous versions could fail because Passenger thinks it's an IPv6 address (::1) while Nginx thinks it's an IPv4 address (127.0.0.1). Hostname resolution is now done in a consistent manner. * [Standalone] Adds the `--unlimited-concurrency-path` configuration option. * [Standalone] Adds IPv6 support to the builtin engine. Release 5.0.24 -------------- * Fixes a crash when the new `force_max_concurrent_requests_per_process` option (5.0.22) was used for non-Node.js apps (e.g. Ruby). Closes GH-1720. * Fixes Solaris compilation. This was a regression due to the patch for GH-1643 in 5.0.22. Closes GH-1694, GH-1701. * Logs for [Union Station](https://www.unionstationapp.com) provide more information about request queueing. Closes GH-1633. * Also log HTTP headers to Union Station for HTTP 4xx responses (extends the header logging for HTTP 5xx that was added in 5.0.22) * Fixes cases where compilation failure of (optional) native utils was not reported. * On Ruby, no longer traps SIGEXIT. This fixes erroneously setting `$ERROR_INFO` in `at_exit` callbacks. Closes GH-1730. * Fixes a wrong loop exit condition that could cause a deadlock with 100% CPU usage by Passenger core. Closes GH-1709, GH-1732. * Adds `socket_backlog` option to configure the Passenger Core socket backlog. For use with e.g. "Resource temporarily unavailable while connecting to upstream" errors. Closes GH-1726. * [Nginx] The preferred Nginx version is now 1.8.1 (previously 1.8.0). * [Standalone] Fixes the default value of the `load_shell_envvars` option. It's supposed to be disabled by default, but due to a typo it was enabled by default. Release 5.0.23 -------------- * Fixes the request acceptor error handling timeout. When an error occurs while Passenger is accepting a request (for example, when Passenger has run out of file descriptors), Passenger is supposed to wait for 3 seconds before trying again. Because of a typo, Passenger actually waited 3 milliseconds. * [Enterprise] Fixed a regression in the Passenger Standalone Nginx config template that breaks the Mass Deployment feature. * The mime type for serving static XHTML files is updated. We no longer use the mobile profile, so it is recognized by desktop browsers. Closes GH-1695. * Improves error messages about Ruby native support to indicate the optional nature. Passenger is able to operate even without the native support extension, but that wasn't clear enough to some users, causing them to think of the old messages as errors. * [Standalone, Nginx] When using the new `abort_websockets_on_process_shutdown` configuration option, Passenger waited for the app to close without signaling it that shutdown was in progress. Node.js apps now get a SIGINT. Closes GH-1702. * With friendly error pages off Passenger would still show a trace (referencing only Passenger code) for unusual spawn errors. This has been changed to a generic error message. Closes GH-1704. Release 5.0.22 -------------- * Fixes a header collision vulnerability (CVE-2015-7519, medium severity). Note that this fix involves filtering request headers containing underscores. Please see our blog for detailed vulnerability description and advisory. Thanks to the SUSE security team for reporting this issue. * [Apache] Fixes compatibility with Apache 2.4.17's mod_autoindex. Fix contributed by Eric Covener. Closes GH-1642. * [Standalone] Passenger Standalone now [accepts configuration options from environment variables](https://www.phusionpassenger.com/library/config/standalone/intro.html). This makes using Passenger Standalone significantly easier on Heroku or on systems that follow the 12-factor principle. Closes GH-1661. * [Standalone] The Nginx configuration template has been cleaned up. It is now significantly easier to edit the Nginx configuration template without breaking compatibility with future versions. * [Standalone] The `passenger start` command now performs a sanity check on the internally generated Nginx configuration file and advises you accordingly when there is a problem. * [Standalone] The `passenger status` and `passenger stop` commands now respect Passengerfile.json. Closes GH-1593. * [Standalone] Passenger Standalone on Solaris now properly tails the application log file. * [Standalone] Fixes a problem with Passenger Standalone's builtin engine exiting at startup when run on Solaris. * [Standalone] `passenger start` now accepts the `--envvar` command line option for passing environment variables to the application. * [Standalone] `passenger start` now accepts the `--memory-limit` configuration option. * [Standalone] `passenger start` now accepts the `--max-request-queue-size` configuration option. * [Standalone] `passenger start` now accepts the `--debug-nginx-config` configuration option. This option allows you to view the Nginx configuration file that Passenger Standalone generates internally. * [Standalone, Nginx] Introduces a new configuration option: `abort_websockets_on_process_shutdown`. By default, when Passenger shuts down or restarts an application process, it will abort associated Websocket connections. This option allows you to disable that behavior. Closes GH-1686. * Introduces a new configuration option: `force_max_concurrent_requests_per_process`. This option is mostly useful for making dynamic process scaling work in Node.js and Meteor apps. * Various administration tools, such as `passenger-status`, no longer raise an flock EBADF error on Solaris. Closes GH-1643. * The `passenger-config reopen-logs` command, when used in combination with Passenger Standalone and the Nginx engine, now also instructs Nginx to reopen its log files. Closes GH-1674. * Fixes Passenger erroneously adding a `Content-Length` or `Transfer-Encoding` header to Ruby HTTP 204 No Content responses. Closes GH-1595. * Fixes Union Station logging of Rack response body actions. * The `passenger-config restart-app` command, when given `--ignore-app-not-running`, now properly exits with a zero status when one or more applications are running, but none of them belonging to the invoking user. Closes GH-1655. * The `passenger-config validate-install` command no longer prints false warnings about duplicate Passenger installs on systems that use RBenv. Closes GH-1627. * Fixes race conditions in the automatic building of the Ruby native support extension. Closes GH-1570. * [Enterprise] Fixes compatibility with byebug 7.0. Closes GH-1662. * Support Union Station logging for Node.js applications, with Express/MongoDB automatically supported. * The Ruby Union Station hooks no longer abort with a fatal error when the application does not call the Union Station initializer method during startup. The error is now only logged. * In case of an error response (HTTP 5xx), Union Station logging will also contain request headers. * The Union Station hooks are now more resilient against environment variable problems. Release 5.0.21 -------------- * Properly handles Ruby applications that output the `Content-Length` and `Transfer-Encoding` headers in non-standard casing, e.g. `Content-length`. Closes GH-1517. * Fixes Ruby application loading incompatibilities caused by the use of absolute paths. Closes GH-1596. * Fixes OpenSSL detection problems on OS X 10.11 El Capitan. OS X 10.11 no longer includes OpenSSL headers, so Passenger will suggest and use OpenSSL from Homebrew. Closes GH-1630. * Introduces the [secure HTTP headers](https://www.phusionpassenger.com/library/indepth/meteor/secure_http_headers.html) feature for Node.js and Meteor apps. This mechanism allows Passenger to send per-request information to the application, while guaranteeing that this information is not spoofed by the client. * Per-request Apache environment variables are now passed to Node.js and Meteor apps through the [`!~Passenger-Envvars`](https://www.phusionpassenger.com/library/indepth/nodejs/apache_per_request_envvars.html) secure header. * Fixes some unintentional caching of request-specific environment variables. Closes GH-1479. * For Node.js applications, Passenger now calls `process.emit('message', 'shutdown')` whenever Passenger shuts down an application process. This is the same hook as used by PM2, allowing applications which use the PM2 graceful shutdown mechanism to be run on Passenger without changes. * [Enterprise] Fixes a bug in passenger-irb where printing strings larger than 64 KB would cause it to crash. * [Enterprise] Fixes the `passenger-config restart-app` command so that it performs a non-rolling-restart unless `--rolling-restart` is given as command line option, as per the documentation. Previously, `passenger-config restart-app` without `--rolling-restart` would perform a rolling restart if rolling restarts are configured in the configuration file, but this contradicted documented behavior. Closes GH-1634. Release 5.0.20 -------------- * Fixes memory management bugs in Union Station support. * Improves the error handling in Union Station support. * `passenger-config validate-install` now properly handles CR characters in Apache configuration files. Release 5.0.19 -------------- * Fixes an encoding crash in `passenger-memory-stats` on OS X in case one or more processes are running on the system with names containing UTF-8 characters. Closes GH-1603. * [Ruby] Fixes handling of HTTP 205 responses, which would cause client connections to freeze. * Improves Union Station data collection: more Rack I/O events are now logged. The time taken to write out and to close the Rack response body are now logged. * Improves Union Station data sending: errors are now logged more clearly, and DNS errors are now handled more robustly. * Improves Union Station troubleshooting: errors can now be diagnosed by running `passenger-status --show=union_station`. * Refactors the Union Station Ruby hook code. They have been extracted to external gems. However, they are still bundled with Passenger for ease of use. Release 5.0.18 -------------- * Fixes more memory corruption issues in the palloc subsystem. * Fixes memory corruption issues in the Passenger core that may occur if the application sets many response headers. The issue was caused by an off-by-one bug. Release 5.0.17 -------------- * Adds packages for Ubuntu 15.10 "Wily", even though Ubuntu 15.10 hasn't been released yet. * Fixes some memory corruption issues in the palloc subsystem. Closes GH-1587. * Fixes the Node.js `PhusionPassenger.on('exit')` event. This event worked if you restart the app or detach an application process, but not if you stop Passenger. * Fixes support for `passenger_pre_start` URLs that contain very long authentication strings. This was caused by the fact that our Base64 encoder generated unexpected newlines. * [Standalone] Improves application prestarting. Application prestarting is now available in combination with the 'builtin' engine, and now works when SSL is used. Release 5.0.16 -------------- * Allows independent configuration of Union Station gateway address, port and certificate. Closes GH-1543. * Supports seek() such that body.rewind works when using Rack middleware that uses Zlib::GzipReader (e.g. for compressed requests). Closes GH-1553. * [Apache] Improves detection of Apache configuration file problems. Closes GH-1577. * [Enterprise] Fixes installation of the Passenger Enterprise Apache module on Debian Testing. * Fixes logging of HTTP response code for Union Station. This regression was introduced by Passenger 5. Closes GH-1581. * Adds a new subcommand `passenger-config about support-binaries-dir`. * Fixes a regression in the Node.js loader with regard to custom startup files. This bug was introduced in 5.0.14. Closes GH-1557 (again). * Fixes a crash when a Ruby application is accessed through a sub-URI and a root virtual host at the same time. Release 5.0.15 -------------- * Support SHA256 digests for the Rails asset pipeline, as used by Sprockets 3.x. * Support for JRuby 9.0.0.0. Closes GH-1562. * Fixes some bugs in Union Station support, which causes some data (such as controller information and exceptions) to not be logged. * The old Users Guides have been deprecated in favor of the [Passenger Library](https://www.phusionpassenger.com/library/). The Users Guides now redirect to appropriate sections in the Passenger Library. Release 5.0.14 -------------- * [Standalone] Relative path handling has been improved. In previous versions, relative paths were not handled in a consistent manner. Relative paths are now handled consistently according to the following rules: - If a relative path is given via a command line option, then it is relative to the current working directory. - If a relative path is given via Passengerfile.json, then it is relative to Passengerfile.json. Closes GH-1557. * [Standalone] The `--disable-turbocaching` now works with the Nginx engine. Release 5.0.13 -------------- * The `passenger-config restart-app` command now supports the option `--ignore-passenger-not-running`. If this option is given, the command will exit normally instead of exiting with an error, if Passenger is not running. This option is useful in deployments involving Passenger Standalone. In an initial deployment, Passenger Standalone may not yet be running. Passing this option allows you to ignore that issue. * SELinux policy issues in the RPMs have been fixed. * [Apache] `passenger-config reopen-logs` didn't work on Apache unless you explicitly set `PassengerLogFile`. This has now been fixed. * [Standalone] Due to some internal refactorings, the Passenger Standalone Nginx configuration template has changed. If you used a custom Nginx configuration template, please merge our latest changes into it. Release 5.0.12 -------------- * [Enterprise] Fixed passenger-irb. It was broken in 5.0.10 because of the change that made using admin commands without sudo possible. Release 5.0.11 -------------- * In 5.0.10, admin tools such as `passenger-status` and `passenger-config restart-app` display an authorization error if they are run without sudo, while at the same time Passenger isn't serving any applications. Since this is confusing, they have now been modified to display a more appropriate error message. * Fixes a bug in the RPMs that prevent admin tools such as `passenger-status` and `passenger-config restart-app` from working when they are invoked without root privileges. * Fixes a bug on OS X that prevent admin tools such as `passenger-status` and `passenger-config restart-app` from detecting Passenger instance directories when they are invoked without root privileges. Closes GH-1535. * Fixes a bug that causes Passenger not to work if the HOME environment variable is not set. * Fixes compatibility with non-Rails Ruby apps that require the actionview gem. Closes GH-1547. * Fixes some non-fatal "permission denied" error that may occasionally occur if user switching is turned off. Closes GH-1541. * Relative values for the `pid_file` and `log_file` options in Passengerfile.json are now supported. * If Passengerfile.json contains a syntax error, Passenger Standalone now correctly prints an error message instead of crashing. * Sending a SIGABRT signal to a Ruby process now properly makes it terminate. * The `passenger-config restart-app` command now accepts `.` as parameter, which it will interpreter as "restart the app in the current working directory". Closes GH-1386. * [Apache] Setting `PassengerLogLevel` no longer redirects Apache's own stderr to that log file. Closes GH-1373. * [Standalone] Passenger Standalone's Nginx engine now includes the RealIP module. Closes GH-1389. * [Standalone] The `--max-preloader-idle-time` option has been added. Release 5.0.10 -------------- * It is now possible to run `passenger-status`, `passenger-config restart-app` and other admin commands without using sudo. When run without sudo, these admin commands will allow you to operate on apps and processes that are owned by the user that invoked the admin command. Closes GH-1392. * Fixes a crash introduces in 5.0.9 due to not properly initializing a variable. Closes GH-1530. * The `passenger-config reopen-logs` command now works by instructing the Watchdog process to reopen the log file, while instructing the other Passenger processes to re-inerhit the log file from the Watchdog instead of trying to reopen the log file on their own. This makes log file reopening more robust. Closes GH-1452. * `passenger-config restart-app` no longer leaves the terminal in a state with black background. Closes GH-1526. * `passenger-config admin-command` has been renamed to `passenger-config api-call` in order to avoid confusion with any potential admin interfaces that we will introduce in the future. * If Union Station support is enabled, process and system metrics weren't being sent correctly to Union Station. This has been fixed. * [Enterprise] Fixes the fact that the Passenger Enterprise RPM didn't correctly set SELinux permissions on its own files. * [Apache] passenger-install-apache2-module no longer aborts with an error if the Apache configuration file contains errors. Closes GH-1525. * [Apache] Fixes a typo that would cause passenger-install-apache2-module to crash on Red Hat and CentOS systems on which the SELinux command line tools are not installed. Closes GH-1527. Release 5.0.9 ------------- * The casing of original headers as generated by the application are now preserved, instead of being downcased. This fixes compatibility issues with broken HTTP clients. Closes GH-1436. * Internal refactoring: we've replaced libeio with libuv. This makes some of our code simpler. Closes GH-1428. * When the passenger-status tool tries to cleanup a stale instance directory, it will no longer abort with an error when it fails to do that. It will now merely print a warning. Fixes [StackOverflow question 30354732](http://stackoverflow.com/questions/30354732/cap-aborted-capistrano-aborts-rails-deploy-while-attempting-to-chown-tmp-p/30357100#30357100). * Fixes compilation problems on Solaris. * The Ruby handler has been made more robust. Previously, it was possible for applications to corrupt connections by returning incorrect Rack responses. This may cause connections to get stuck. The Rack handler has been hardened to ensure that connections will never get corrupted or stuck. Closes GH-1512. * The Ruby handler now closes the Rack response body even when the socket connection is hijacked by the application. The Rack specification is unclear about what to do in this case, and different Ruby app servers do different things. We have found that by closing the body object anyway, we maximize compatibility with existing Rack middlewares and apps, such as Rack::Lock. Background information about this issue can be found at https://github.com/ngauthier/tubesock/issues/10#issuecomment-72539461. * Fixes a crash that could occur if some HTTP request headers are present, but have the empty value. Closes GH-1524. * Fixes a permission problem that prevents the web server from communicating with Passenger when user switching is off. Closes GH-1520. * Fixes a few small one-time memory leaks in the Passenger agent. This wraps up the workitems discovered in valgrind runs on earlier versions. * Fixes use of uninitialized metrics. This could happen for a brief moment after spawning. * [Apache] If you pass the `--apxs2-path` parameter to `passenger-install-apache2-module`, and the apxs2 path that you specified is not in PATH, then the installer would think that Apache installation is broken. This has been fixed. * [Apache] A `Connection: close` header that was used for internal communication between Passenger processes was being leaked to the client, which breaks HTTP keep-alive connections. This has been fixed. Closes GH-1516. * [Nginx] The preferred Nginx version is now 1.8.0. It was previously 1.6.3. * [Nginx] Passenger now passes to the application the raw URI as sent by the client, as long as Nginx didn't modify the URI (e.g. as part of rewrite rules). This means that escaped slashes (%2F) in the URI now work correctly and out-of-the-box as long as there are no applicable rewrite rules. * [Nginx] Fixes that crash that would occur if Nginx is configured to log to syslog. And to prevent log messages from disappearing into a black hole, Passenger will now ask you to set `passenger_log_file` if Nginx is configured to log to syslog. Closes GH-1514. * [Standalone] Prevents an existing instance from being shut down if starting a new instance fails. Release 5.0.8 ------------- * We now supply Debian 8 and Ubuntu 15.04 packages. Closes GH-1494 and GH-1400. * We now supply Red Hat 6, Red Hat 7, CentOS 6 and CentOS 7 packages. * We no longer supply Ubuntu 10.04 packages because Ubuntu 10.04 is no longer supported by Canonical. * Fixes a Passenger crash (SIGSEGV) that occurs occasionally when out-of-band garbage collection is enabled. Closes GH-1469. * Fixes a Passenger crash (SIGSEGV) that occurs occasionally with redirects to relative URLs. Closes GH-1513. * Fixes cases when Passenger shuts down more processes than is allowed by the `min_instances` limit. Closes GH-1500. * Fixes "Bad Gateway" errors that would occur when an application sets the X-Sendfile or X-Accel-Redirect header, together with a non-empty response body. Closes GH-1498. * Fixes the fact that Passenger agent processes don't lower their privilege when user switching is turned off. * Fixes autodetection of Apache on Gentoo. Closes GH-1510. * Fixes compilation problems on Solaris. Closes GH-1508. * [Standalone] Adds the `--pool-idle-time` command line parameter. * [Standalone] Adds the `--auto` command line parameter for running non-interactively. This supresses prompts. Closes GH-1511. Release 5.0.7 ------------- * Supports changed way of specifying settings for (non-bundled) Meteor apps. Closes GH-1403. * Fixes an integer-to-string conversion bug in the code responsible for buffering chunked request bodies. This bug could cause the PassengerAgent to crash due to an exception. Thanks to Marcus Rückert of SUSE for reporting this. * Request-specific environment variables are no longer cached. This fixes a number of issues, such as Shibboleth not working properly and conflicts between HTTPS and non-HTTPS virtual hosts. Closes GH-1472. * Fixes a memory corruption bug that would be triggered when using `passenger_base_uri`. The memory corruption bug resided in the code for resolving symlinks. Closes GH-1388. * Re-introduced signal catchers during shutdown, to allow clean shutdown in Foreman. Closes GH-1454. * `passenger-status --show=xml` no longer outputs the non-XML header by default. This fixes a regression as reported in a comment in GH-1136. * Passenger now prefers to load Rack and Bundler from RubyGems instead of from `vendor_ruby`. This solves some issues with Rack and Bundler on Debian systems. Closes GH-1480 and GH-1478. * The turbocache no longer caches responses that contain the `X-Sendfile` or the `X-Accel-Redirect` header. * The preferred Nginx version has been upgraded to 1.6.3. * The logging agent no longer aborts with an error if one of the Passenger root directory's parent directories is not world-executable. Closes GH-1487. * [Standalone] It is now possible to configure the Ruby, Node.js and Python executable to use in Passenger Standalone through the command line options --ruby, --nodejs and --python. Closes GH-1442. * [Standalone] Running `passenger start --engine=builtin --daemonize` would fail with a timeout error. This has been fixed. * [Standalone] Running `passenger start --nginx-version=XXX` would crash. This has been fixed. Closes GH-1490. * [Apache] Fixed some issues with X-Sendfile. Closes GH-1376. * [Apache] If the installer fails to autodetect Apache while the installer is running as a normal user, it will now ask you to give it root privileges. Closes GH-1289. * [Apache] The installer now validates your Apache configuration file to check for common problems. The validator can also be accessed separately by running `passenger-config validate-install --validate-apache2`. * [Nginx] Introduces the `passenger_read_timeout` option for rare cases when server needs more than the default 10 minute timeout. Contributed by pkmiec. Closes [GH-PR-34](https://github.com/phusion/passenger/pull/34). * [Nginx] The Nginx module now looks for index.html if the path ends in / so that it works intuitively, without needing to use try_files. * Fixes wrong memory address display in crash dumps. Thanks to thoughtpolice for pointing it out. * Fixes an ugly backtrace that would be shown if an invalid request is made to an application process using the private HTTP interface. Contributed by jbergler. Closes GH-1311. * Various documentation improvements. Closes [GH-PR-1332](https://github.com/phusion/passenger/pull/1332), [GH-PR-1354](https://github.com/phusion/passenger/pull/1354), [GH-PR-1216](https://github.com/phusion/passenger/pull/1216), [GH-PR-1385](https://github.com/phusion/passenger/pull/1385), [GH-PR-1302](https://github.com/phusion/passenger/pull/1302). Release 5.0.6 ------------- * The turbocache no longer caches responses for which the Cache-Control header contains "no-cache". Please note that "no-cache" does not mean "do not cache this response". Instead, it means "any caching servers may only serve the cached response after validating it". Since the turbocache does not support validation, we've chosen to skip caching instead. Coincidentally, this change "fixes" problems with applications that erroneously use "no-cache" as a flag for "do not cache this response". What these applications should actually use is "no-store". We recommend the developers of such applications to change their caching headers in this manner, because even if Passenger doesn't unintentionally cache the response, any intermediate proxies that visitors are behind may still cache the response. * Fixes a number of memory leaks. Memory was leaked upon processing a request with multiple headers, upon processing a response with multiple headers, and upon processing a response with Set-Cookie headers. Every time such a request or response was processed, 512 bytes of memory was leaked due to improperly dereferencing relevant memory buffers. Closes GH-1455. * Fixes various bugs related to Union Station data collection. Union Station is our upcoming application analytics and performance monitoring SaaS platform. It is opt-in: no data is collected unless you turn the feature on. * Fixes a Union Station-related file descriptor leak. Closes GH-1439. * Fixes some bugs w.r.t. use of uninitialized memory. * More informative error message if a support binary is not found, including a resolution hint. Closes GH-1395. * [Apache] `SetEnv` variables are now passed as Rack/CGI/request variables. This was also the case in Passenger 4, but not in Passenger 5.0.0-5.0.5. We've restored the old behavior because the behavior in 5.0.0-5.0.5 breaks certain Apache modules such as Shibboleth. Closes GH-1446. * [Standalone] PID and log files now correctly created if user specifies relative path. Release 5.0.5 ------------- * Fixes various crashes due to use of uninitialized memory. One such crash is documented in GH-1431. * Fixes a connection stall in the Apache module. Closes GH-1425. * Fixes a potential read-past-buffer bug in string-to-integer conversion routines. Thanks to dcb314 for spotting this. Closes GH-1441. * Fixes a compilation problem on Solaris. This problem was caused by the fact that `tm_gmtoff` is not supported on that platform. Closes GH-1435. * There is now an API endpoint for force disconnecting a client: `passenger-config admin-command DELETE /server/.json`. Closes GH-1246. * Fixes some file descriptor leaks. These leaks were caused by the fact that keep-alive connections with application processes were not being closed properly. Closes GH-1439. * In order to more easily debug future file descriptor leaks, we've introduced the `PassengerFileDescriptorLogFile` (Apache) and `passenger_file_descriptor_log_file` (Nginx) config options. This allows Passenger to log all file descriptor open/close activity to a specific log file. * The `PassengerDebugLogFile` (Apache) and `passenger_debug_log_file` (Nginx) configuration options have been renamed to `PassengerLogFile` and `passenger_log_file`, respectively. The old name is support supported for backward compatibility reasons. * [Enterprise] Fixes a bug in Flying Passenger's `--instance-registry-dir` command line parameter. This command line parameter didn't do anything. * [Enterprise] The Flying Passenger daemon no longer supports the `--max-preloader-idle-time` config option. This is because the config option never worked. The correct way to set the max preloader idle time is through the Nginx config option, but this was wrongly documented, so the documentation has been fixed. Release 5.0.4 ------------- * Fixes a compilation problem introduced in 5.0.3. Release 5.0.3 ------------- * [Standalone] When using the builtin engine, `passenger start` may crash during startup due to an initialization race condition. This has been fixed. * [Enterprise] Fixes a bug in passenger-irb. Running passenger-irb without a PID parameter worked, but running it with a PID parameter didn't. * Fixes an integer overflow that resulted in a file descriptor leak and stalled client connections. Closes GH-1412. * Truncates Passenger source code paths in logs (to 3 chars) to reduce redundant info. Closes GH-1383. * Fixes invalid JSON output for non-finite double values (e.g. from the HTTP JSON API). Closes GH-1408. * All hooks now set the `PASSENGER_HOOK_NAME` environment variable. This variable is set to the name of the hook that is being called. * The Ruby handler no longer tries to call #force_encoding on response body strings, which fixes an incompatibility with apps/libraries that return frozen body strings. Closes GH-1414. * If the Ruby handler crashes while processing a Rack response body, it will now no longer stall the connection. * Fixes env.SERVER_PORT containing 80 instead of 443 when using https on default port. Closes GH-1421. * We now handle errors in the `poll()` system call better. This might fix some crashes during shutdown which manifest on FreeBSD. Release 5.0.2 ------------- * Fixes a connection freeze that could occur when processing large responses. This would manifest itself under the error message "This website is under heavy load" or "Request queue is full, returning an error". Closes GH-1404. * Debian and Ubuntu packages have been reintroduced. * When `passenger-config restart-app` is run interactively, if Passenger is not serving any applications, then the command now prints an error message instead of showing a menu with only a "Cancel" option. * Fixes a compilation problem on FreeBSD 10 (contributed by: clemensg). Closes GH-1401. * [Standalone] Fixes a crash that would occur if you use the `--ctl` parameter. * [Enterprise] The `--max-request-time` option has been added to Passenger Standalone. * [Enterprise] The `max_request_time_reached` hook has been introduced. This hook allows you to run diagnostics on a process that that took too long to respond to a request. Release 5.0.1 ------------- * The `passenger-config restart-app` command is now more user friendly. When run in a terminal, it will show an interactive menu, allowing you to select the app to restart. Closes GH-1387. * Fixed a crash bug in the handling of sticky session cookies. * Log failed program in error message, not its command line (contributed by: paisleyrob). Closes GH-1397. * [Nginx] Fixes cases in which Passenger overrides the Nginx handler function even when it shouldn't, for example when Passenger is disabled. Closes GH-1393. * [Enterprise] The `sticky_sessions` and `envvars` options in Passengerfile.json is now also supported in mass deployment mode. Release 5.0.0 release candidate 2 --------------------------------- * Fixes an installation problem with the Ruby gem due to incorrect Makefile generation. Closes GH-1382. * More helpful message when request queue is full. Closes GH-1375. Release 5.0.0 release candidate 1 --------------------------------- * Fixed Date headers not being formatted in the GMT timezone. Closes GH-1367. * Fixed Passengerfile.json/passenger-standalone.json not being properly loaded in Passenger Standalone. * Fixed support for sticky sessions. * Fixed an infinite loop if the ApplicationPool garbage collector fails due to an exception. Closes GH-1360. * Fixed Passenger Standalone exiting prematurely when the HelperAgent crashes. Exiting prematurely is not supposed to happen because the watchdog will restart the HelperAgent. Closes GH-1339. * Fixed a crash that occurs when using a non-standard startup file value. Closes GH-1378. * When dumping system metrics during error page generation, the `passenger-config` command is now invoked under the same Ruby interpreter as the app, instead of the one in PATH. Closes GH-1381. * When a Ruby process crashes due to an uncaught exception, this fact is now properly logged. * Specifying 0 for the `max_pool_size` config option no longer results in a crash. Closes GH-1334. * The timeouts when downloading Passenger Standalone binaries and source files are now customizable. Closes GH-1295. * The `envvars` option is now supported in Passengerfile.json, for passing environment variables to the application. Closes GH-1377. * Introduced `hook_queue_full_error` for request queue overflows. Closes GH-1358. * [Ruby] Fixed handling of "transfer-encoding chunked" response bodies which contain zero-sized chunks. * [Nginx] It is no longer necessary to re-specify `passenger_enabled` in `location` contexts. Closes GH-1338. * [Enterprise] Fixed a bug in mass deployment reloading. * [Enterprise] Fixed a bug in mass deployment daemonization. * [Enterprise] Fixed passenger-irb. Closes GH-1350. * [Enterprise] The mass deployment mode now supports the `app_type` and `startup_file` configuration options in Passengerfile.json/passenger-standalone.json. Closes GH-1366. Release 5.0.0 beta 3 -------------------- * The turbocache has received major updates and fixes based on excellent feedback Chris Heald and the community. First, several bugs w.r.t. the handling of caching headers have been fixed. Second, the turbocache has become slightly more conservative for security reasons. In previous versions, default cacheable responses (as defined by RFC 7234) were cached unless caching headers tell us not to. Now, default cacheable responses are only cached if caching headers explicitly tell us to. This change was introduced because there are many applications that set incorrect caching headers on private responses. This new behavior is currently not configurable, but there are plans to make it configurable in 5.0.0 release candidate 1. * Introduced a new configuration option, `passenger_response_buffer_high_watermark` (Nginx) and `PassengerResponseBufferHighWatermark` (Apache), for configuring the behavior of the response buffering system. Closes GH-1300. * Fixed more cookie handling issues. Closes GH-1310. * Fixed various WebSocket issues. Closes GH-1306. * Fixed some crashes caused by race conditions. Closes GH-1326. * Fixed issues with handling POST data. Closes GH-1331. * Fixed some issues on Heroku. Closes GH-1329. * Fixed some integer overflows. Fix contributed by Go Maeda. Closes GH-1357. * Fixed the `passenger-status --show=union_station` command. Closes GH-1336. * Nginx versions earlier than 1.6 are no longer supported. * Improved state introspection. Release 5.0.0 beta 2 -------------------- * Fixed handling of multiple Set-Cookie headers. Closes GH-1296. * `passenger-config system-metrics` now works properly if the agent is installed in ~/.passenger. Closes GH-1304. * Documentation enhancements by Igor Vuk. Closes GH-1318. * Fixed some crasher bugs. * [Standalone] User switching is now correctly disabled. * [Standalone] Fixed the `--thread-count` parameter. * [Apache] IPs set by mod_remoteip are now respected. Closes GH-1284. * [Apache] Fixed support for gzipped chunked responses. Closes GH-1309. Release 5.0.0 beta 1 -------------------- Version 5.0.0 beta 1 contains major changes. It's mostly compatible with version 4, but there are a few minor breakages, which are described below. Major changes and notable breakages are: * Performance has been much improved. This is thanks to months of optimization work. You can learn more at www.rubyraptor.org. * We've published a [server optimization guide](https://www.phusionpassenger.com/documentation/ServerOptimizationGuide.html) for those who are interested in tuning Phusion Passenger. * Support for Rails 1.2 - 2.2 has been removed, for performance reasons. Rails 2.3 is still supported. * Phusion Passenger now supports integrated HTTP caching, which we call turbocaching. If your app sets the right HTTP headers then Phusion Passenger can tremendously accelerate your app. It is enabled by default, but you can disable it with `--disable-turbocaching` (Standalone), `PassengerTurbocaching off` (Apache), or 'passenger_turbocaching off' (Nginx). * Touching restart.txt will no longer restart your app immediately. This is because, for performance reasons, the stat throttle rate now defaults to 10. You can still get back the old behavior by setting `PassengerStatThrottleRate 0` (Apache) or `passenger_stat_throttle_rate 0` (Nginx), but this is not encouraged. Instead, we encourage you to use the `passenger-config restart-app` tool to initiate restarts, which has immediate effect. * Websockets are now properly disconnected on application restarts. * The Phusion Passneger log levels have been completely revamped. If you were setting a log level before (e.g. through `passenger_log_level`), please read the latest documentation to learn about the new log levels. * If you use out-of-band garbage collection, beware that the `X-Passenger-Request-OOB-Work` header has now been renamed to `!~Request-OOB-Work`. * When using Rack's full socket hijacking, you must now output an HTTP status line. * [Nginx] The `passenger_set_cgi_param` option has been removed and replaced by `passenger_set_header` and `passenger_env_var`. * [Nginx] `passenger_show_version_in_header` is now only valid in the `http` context. * [Apache] The `PassengerStatThrottleRate` option is now global. Minor changes: * The minimum required Nginx version is now 1.6.0. * The instance directory is now touched every hour instead of every 6 hours. This should hopefully prevent more problems with /tmp cleaner daemons. * Applications are not grouped not only on the application root path, but also on the environment. For example, this allows you to run the same app in both production and staging mode, with only a single directory, without further configuration. Closes GH-664. * The `passenger_temp_dir` option (Nginx) and the `PassengerTempDir` option (Apache) have been replaced by two config options. On Nginx they are `passenger_instance_registry_dir` and `passenger_data_buffer_dir`. On Apache they are `PassengerInstanceRegistryDir` and `PassengerDataBufferDir`. On Apache, `PassengerUploadBufferDir` has been replaced by `PassengerDataBufferDir`. * Command line tools no longer respect the `PASSENGER_TEMP_DIR` environment variable. Use `PASSENGER_INSTANCE_REGISTRY_DIR` instead. * `passenger-status --show=requests` has been deprecated in favor of `passenger-status --show=connections`. * Using the SIGUSR1 signal to restart a Ruby app without dropping connections, is no longer supported. Instead, use `passenger-config detach-process`. * Introduced the `passenger-config reopen-logs` command, which instructs all Phusion Passenger agent processes to reopen their log files. You should call this after having rotated the web server logs. * [Standalone] The Phusion Passenger Standalone config template has changed. Users are encouraged to update it. * [Standalone] `passenger-standalone.json` has been renamed to `Passengerfile.json`. * [Standalone] `passenger-standalone.json`/`Passengerfile.json` no longer overrides command line options. Instead, command line options now have the highest priority. Release 4.0.60 -------------- Note that 4.0.60 is a source-only maintenance release. There will not be any binaries, Debian or RPM packages for this release. * Adds OS X El Capitan support. * Updates preferred Nginx version from 1.6.2 to 1.6.3. * Fixes a header collision vulnerability (CVE-2015-7519, medium severity). Please see our blog for detailed vulnerability description and advisory. Thanks to the SUSE security team for reporting this issue. * Fixes the password protection of internal Phusion Passenger processes. For security reasons, Phusion Passenger limits access to internal processes, by using Unix file permissions and randomly generated passwords that only authorized internal processes know. It turns out that this password wasn't set correctly, which has now been fixed. There was no security vulnerability, because the file permissions already provide sufficient security. The password only serves as an extra layer of security just in case there is a problem with the former. This issue is not at all related to any application-level security or application-level passwords. Any database passwords, keys, or secrets used and generated by applications have got nothing to do with the nature of this issue. This issue only relates to some randomly generated passwords that Passenger uses internally, for its internal operations. Release 4.0.59 -------------- * [Enterprise] Fixed support for free-style Node.js apps. Release 4.0.58 -------------- * [Enterprise] Fixed a bug in the Debian packages which caused Flying Passenger to break when used with non-system Rubies. * The Debian packages no longer require Ruby 1.9. Closes GH-1353. Release 4.0.57 -------------- * Fixed a native extension compatibility problem with Ruby 2.2. Closes [ruby-core:67152](https://bugs.ruby-lang.org/issues/10656). * Fixed compatibility with Nginx 1.7.9. Closes GH-1335. Release 4.0.56 -------------- * Fixed a file descriptor leak that manifests when an error page is shown. Contributed by Paul Bonaud, closes GH-1325. * Improved Node.js request load balancing. Closes GH-1322. Thanks to Charles Vallières for the analysis. Release 4.0.55 -------------- * Supports Ruby 2.2. Closes GH-1314. * Fixed Linux OS name detection. Release 4.0.54 -------------- * Contains a licensing-related hot fix for Enterprise customers. Release 4.0.53 -------------- * Upgraded the preferred Nginx version to 1.6.2. * Improved RVM gemset autodetection. * Fixed some Ruby 2.2 compatibility issues. Release 4.0.52 -------------- * Fixed a null termination bug when autodetecting application types. * Node.js apps can now also trigger the inverse port binding mechanism by passing `'/passenger'` as argument. This was introduced in order to be able to support the Hapi.js framework. Please read http://stackoverflow.com/questions/20645231/phusion-passenger-error-http-server-listen-was-called-more-than-once/20645549 for more information regarding Hapi.js support. * It is now possible to abort Node.js WebSocket connections upon application restart. Please refer to https://github.com/phusion/passenger/wiki/Phusion-Passenger:-Node.js-tutorial#restarting_apps_that_serve_long_running_connections for more information. Closes GH-1200. * Passenger Standalone no longer automatically resolves symlinks in its paths. * `passenger-config system-metrics` no longer crashes when the system clock is set to a time in the past. Closes GH-1276. * `passenger-status`, `passenger-memory-stats`, `passenger-install-apache2-module` and `passenger-install-nginx-module` no longer output ANSI color codes by default when STDOUT is not a TTY. Closes GH-487. * `passenger-install-nginx-module --auto` is now all that's necessary to make it fully non-interactive. It is no longer necessary to provide all the answers through command line parameters. Closes GH-852. * Minor contribution by Alessandro Lenzen. Release 4.0.50 -------------- * Fixed a potential heap corruption bug. * Added Union Station support for Rails 4.1. Release 4.0.49 -------------- * Upgraded the preferred Nginx version to 1.6.1. * Fixed a crash that may be triggered by the `passenger_max_requests` feature. * Introduced the `spawn_failed` hook, which is called when an application process fails to spawn. You could use this hook to setup an error notification system. Closes GH-1252. * Fonts, RSS and XML are now gzip-compressed by default in Phusion Passenger Standalone. Thanks to Jacob Elder. Closes GH-1254. * Fixed some user and group information lookup issues. Closes GH-1253. * Fixed some request handling crashes. Closes GH-1250. * Fixed some compilation problems on Gentoo. Closes GH-1261. * Fixed some compilation problems on Solaris. Closes GH-1260. Release 4.0.48 -------------- * Fixed a race condition while determining what user an application should be executed as. This bug could lead to applications being run as the wrong user. Closes GH-1241. * [Standalone] Improved autodetection of Rails asset pipeline files. This prevents Standalone from incorrectly setting caching headers on non-asset pipeline files. Closes GH-1225. * Fixed compilation problems on CentOS 5. Thanks to J. Smith. Closes GH-1247. * Fixed compilation problems on OpenBSD. * Fixed compatibility with Ruby 1.8.5. Release 4.0.47 -------------- * [Enterprise] Fixed a bug in Flying Passenger's `--max-preloader-idle-time` option. Release 4.0.46 -------------- * Further improved Node.js and Socket.io compatibility. * Sticky session cookies have been made more reliable. * Fixed WebSocket upgrade issues on Firefox. Closes GH-1232. * The Python application loader now inserts the application root into `sys.path`. The fact that this was not done previously caused a lot of confusion amongst Python users, who wondered why their `passenger_wsgi.py` could not import any modules from the same directory. * Fixed a compatibility problem with Django, which could cause Django apps to freeze indefinitely. Closes GH-1215. * Logging of application spawning errors has been much improved. Full details about the error, such as environment variables, are saved to a private log file. In the past, these details were only viewable in the browser. This change also fixes a bug on Phusion Passenger Enterprise, where enabling Deployment Error Resistance causes error messages to get lost. Closes GH-1021 and GH-1175. * Fixed a regression in Node.js support. When a Node.js app is deployed on a HTTPS host, the `X-Forwarded-Proto` header wasn't set in 4.0.45. Closes GH-1231. * Passenger Standalone no longer, by default, loads shell startup files before loading the application. This is because Passenger Standalone is often invoked from the shell anyway. Indeed, loading shell startup files again can interfere with any environment variables already set in the invoking shell. You can still tell Passenger Standalone to load shell startup files by passing `--load-shell-envvars`. Passenger for Apache and Passenger for Nginx still load shell startup files by default. * Passenger Standalone now works properly when the HOME environment variable isn't set. Closes GH-713. * Passenger Standalone's `package-runtime` command has been removed. It has been broken for a while and has nowadays been obsolete by our automatic [binary generation system](https://github.com/phusion/passenger_autobuilder). Closes GH-1133. * The `passenger_startup_file` option now also works on Python apps. Closes GH-1233. * If you are a [Union Station](https://www.unionstationapp.com) customer, then Phusion Passenger will now also log application spawning errors to Union Station. This data isn't shown in the Union Station interface yet, but it will be implemented in the future. * Fixed compilation problems on OmniOS and OpenIndiana. Closes GH-1212. * Fixed compilation problems when Nginx is configured with OpenResty. Thanks to Yichun Zhang. Closes GH-1226. * Fixed Nginx HTTP POST failures on ARM platforms. Thanks to nocelic for the fix. Closes GH-1151. * Documentation contributions by Tim Bishop and Tugdual de Kerviler. * Minor Nginx bug fix by Feng Gu. Closes GH-1235. Release 4.0.45 -------------- * Major improvements in Node.js and Meteor compatibility. Older Phusion Passenger versions implemented Node.js support by emulating Node.js' HTTP library. This approach was found to be unsustainable, so we've abandoned that approach and replaced it with a much simpler approach that does not involve emulating the HTTP library. * Introduced support for sticky sessions. Sticky sessions are useful -- or even required -- for apps that store state inside process memory. Prominent examples include SockJS, Socket.io, faye-websocket and Meteor. Sticky sessions are required to make the aforementioned examples work in multi-process scenarios. By introducing sticky sessions support, we've much improved WebSocket support and support for the aforementioned libraries and frameworks. * Due to user demand, GET requests with request bodies are once again supported. Support for these kinds of requests was removed in 4.0.42 in an attempt to increase the strictness and robustness of our request handling code. It has been determined that GET requests with request bodies can be adequately supported without degrading robustness in Phusion Passenger. However, GET requests with both request bodies and WebSocket upgrade headers are unsupported. Fixes issue #1092. * [Enterprise] The [Flying Passenger](http://www.modrails.com/documentation/Users%20guide%20Apache.html#flying_passenger) feature is now also available on Apache. * Fixed some issues with RVM mixed mode support, issue #1121. * Fixed Passenger Standalone complaining about not finding PassengerHelperAgent during startup. * Fixed various minor issues such as #1190 and #1197. * The download timeout for passenger-install-nginx-module has been increased. Patch by 亀田 義裕. Release 4.0.44 -------------- * The issue tracker has now been moved from Google Code to Github. Before version 4.0.44 (May 29 2014, commit 3dd0964c9f4), all issue numbers referred to Google Code. From now on, all issue numbers will refer to Github Issues. * Fixed compilation problems on OS X Lion and OS X Mountain Lion. * On Ruby, fixed `nil` being frozen on accident in some cases. See issue #1192. Release 4.0.43 -------------- * Introduced a new command `passenger-config list-instances`, which prints all running Phusion Passenger instances. * Introduced a new command `passenger-config system-metrics, which displays metrics about the system such as the total CPU and memory usage. * Fixed some compilation problems caused by the compiler capability autodetector. * System metrics such as total CPU usage and memory usage, are now sent to [Union Station](https://www.unionstationapp.com) in preparation for future features. Release 4.0.42 -------------- * [Nginx] Upgraded the preferred Nginx version to 1.6.0. * [Nginx] Fixed compatibility with Nginx 1.7.0. * [Standalone] The MIME type for .woff files has been changed to application/font-woff. Fixes issue #1071. * There are now APT packages for Ubuntu 14.04. At the same time, packages for Ubuntu 13.10 have been abandoned. * Introduced a new command, `passenger-config build-native-support`, for ensuring that the native_support library for the current Ruby interpreter is built. This is useful in system provisioning scripts. * For security reasons, friendly error pages (those black/purple pages that shows the error message, backtrace and environment variable dump when an application fails to start) are now disabled by default when the application environment is set to 'staging' or 'production'. Fixes issue #1063. * Fixed some compilation warnings on Ubuntu 14.04. * Fixed some compatibility problems with Rake 10.2.0 and later. See [Rake issue 274](https://github.com/jimweirich/rake/issues/274). * Improved error handling in [Union Station](https://www.unionstationapp.com) support. * Data is now sent to Union Station on a more frequent basis, in order to make new data show up more quickly. * Information about the code revision is now sent to Union Station, which will be used in the upcoming deployment tracking feature in Union Station 2. Release 4.0.41 -------------- * Fixed some issues with printing UTF-8 log files on Heroku. * Added a new flag `--ignore-app-not-running` to `passenger-config restart-app`. When this flag is given, `passenger-config restart-app` will exit successfully when the specified application is not running, instead of exiting with an error. * Our precompiled Passenger Standalone binaries have been upgraded to use OpenSSL 1.0.1g, which fixes [the OpenSSL Heartbleed vulnerability](http://heartbleed.com/). Users who are using Passenger Standalone with SSL enabled are vulnerable, and should upgrade immediately. Users who do not use Passenger Standalone, users who use Passenger Standalone without SSL, or users who use Passenger Standalone with SSL behind another SSL-enabled reverse proxy, are not vulnerable. Release 4.0.40 -------------- * Upgraded preferred Nginx version to 1.4.7. This Nginx version fixes a buffer overflow. Users are strongly urged to upgrade Nginx as soon as possible. Release 4.0.39 -------------- * Fixed a crash that could happen if the client disconnects while a chunked response is being sent. Fixes issue #1062. * In Phusion Passenger Standalone, it is now possible to customize the Nginx configuration file on Heroku. It is now also possible to permanently apply changes to the Nginx configuration file, surviving upgrades. Please refer to the "Advanced configuration" section of the Phusion Passenger Standalone manual for more information. * The programming language selection menu in passenger-install-apache2-module and passenger-install-nginx-module only works on terminals that support UTF-8 and that have a UTF-8 capable font. To cater to users who cannot meet these requirements (e.g. PuTTY users using any of the default Windows fonts), it is now possible to switch the menu to a plain text mode by pressing '!'. Fixes issue #1066. * Fixed printing UTF-8 characters in log files in Phusion Passenger Standalone. * It is now possible to dump live backtraces of Python apps through the 'SIGABRT' signal. * Fixed closing of file descriptors on OS X 10.9. * Fixed compilation problems with Apple Clang 503.0.38 on OS X. * Fixed compilation of native_support on Rubinius. Release 4.0.38 -------------- * Added support for the new Ruby 2.1.0 out-of-band garbage collector. This can much improve garbage collection performance, and drastically reduce request times. * Fixed a symlink-related security vulnerability. Urgency: low Scope: local exploit Summary: writing files to arbitrary directory by hijacking temp directories Affected versions: 4.0.37 Fixed versions: 4.0.38 CVE-2014-1832 Description: This issue is related to CVE-2014-1831 (the security issue as mentioned in the 4.0.37 release notes). The previous fix was incomplete, and still has a (albeit smaller) small attack time window in between two filesystem checks. This attack window is now gone. * Passenger Standalone is now compatible with IPv6. * Fixed some compilation problems on Solaris. See issue #1047. * passenger-install-apache2-module and passenger-install-nginx-module now automatically run in `--auto` mode if stdin is not a TTY. Fixes issue #1030. * Fixed an issue with non-bundled Meteor apps not correctly running in production mode. * The `PassengerPreStart` option is now compatible with IPv6 server sockets. * When running Python WSGI apps, `wsgi.run_once` is now set to False. This should improve the performance of certain apps and frameworks. * When handling HTTP requests with chunked transfer encoding, the 'Transfer-Encoding' header is no longer passed to the application. This is because the web server already buffers and dechunks the request body. * Fixed a possible hang in Phusion Passenger for Nginx when Nginx is instructed to reload or reopen log files. Thanks to Feng Gu, [pull request #97](https://github.com/phusion/passenger/pull/97). * The preferred Nginx version has been upgraded to 1.4.6. * Fixed a problem with running passenger-install-apache2-module and passenger-install-nginx-module on JRuby. They were not able to accept any terminal input after displaying the programming language menu. Release 4.0.37 -------------- * Improved Node.js compatibility. Calling on() on the request object now returns the request object itself. This fixes some issues with Express, Connect and Formidable. Furthermore, some WebSocket-related issues have been fixed. * Improved Meteor support. Meteor application processes are now shut down quicker. Previously, they linger around for 5 seconds while waiting for all connections to terminate, but that didn't work well because WebSocket connections were kept open indefinitely. Also, some WebSocket-related issues have been fixed. * Introduced a new tool `passenger-config detach-process` for gracefully detaching an application process from the process pool. Has a similar effect to killing the application process directly with `kill `, but killing directly may cause the HTTP client to see an error, while using this command guarantees that clients see no errors. * Fixed a crash that occurs when an application fails to spawn, but the HTTP client disconnects before the error page is generated. Fixes issue #1028. * Fixed a symlink-related security vulnerability. Urgency: low Scope: local exploit Summary: writing files to arbitrary directory by hijacking temp directories Affected versions: 4.0.5 and later Fixed versions: 4.0.37 CVE-2014-1831 Description: Phusion Passenger creates a "server instance directory" in /tmp during startup, which is a temporary directory that Phusion Passenger uses to store working files. This directory is deleted after Phusion Passenger exits. For various technical reasons, this directory must have a semi-predictable filename. If a local attacker can predict this filename, and precreates a symlink with the same filename that points to an arbitrary directory with mode 755, owner root and group root, then the attacker will succeed in making Phusion Passenger write files and create subdirectories inside that target directory. The following files/subdirectories are created: * control_process.pid * generation-X, where X is a number. If you happen to have a file inside the target directory called `control_process.pid`, then that file's contents are overwritten. These files and directories are deleted during Phusion Passenger exit. The target directory itself is not deleted, nor are any other contents inside the target directory, although the symlink is. Thanks go to Jakub Wilk for discovering this issue. Release 4.0.36 -------------- * [Enterprise] Fixed some Mass Deployment bugs. * [Enterprise] Fixed a bug that causes an application group to be put into Deployment Error Resistance Mode if rolling restarting fails while deployment error resistance is off. Deployment Error Resistance Mode is now only activated if it's explicitly turned on. * Passenger Standalone now gzips JSON responses. * Fixed some cases in which Passenger Standalone does not to properly cleanup its temporary files. Release 4.0.35 -------------- * Fixed some unit tests. Release 4.0.34 -------------- * The Node.js loader code now sets the `isApplicationLoader` attribute on the bootstrapping module. This provides a way for apps and frameworks that check for `module.parent` to check whether the current file is loaded by Phusion Passenger, or by other software that work in a similar way. This change has been introduced to solve a compatibility issue with CompoundJS. CompoundJS users should modify their server.js, and change the following: if (!module.parent) { to: if (!module.parent || module.parent.isApplicationLoader) { * Improved support for Meteor in development mode. Terminating Phusion Passenger now leaves less garbage Meteor processes behind. * It is now possible to disable the usage of the Ruby native extension by setting the environment variable `PASSENGER_USE_RUBY_NATIVE_SUPPORT=0`. * Fixed incorrect detection of the Apache MPM on Ubuntu 13.10. * When using RVM, if you set PassengerRuby/passenger_ruby to the raw Ruby binary instead of the wrapper script, Phusion Passenger will now print an error. * Added support for RVM >= 1.25 wrapper scripts. * Fixed loading passenger_native_support on Ruby 1.9.2. * The Union Station analytics code now works even without native_support. * Fixed `passenger-install-apache2-module` and `passenger-install-nginx-module` in Homebrew. * Binaries are now downloaded from an Amazon S3 mirror if the main binary server is unavailable. * And finally, although this isn't really a change in 4.0.34, it should be noted. In version 4.0.33 we changed the way Phusion Passenger's own Ruby source files are loaded, in order to fix some Debian and RPM packaging issues. The following doesn't work anymore: require 'phusion_passenger/foo' Instead, it should become: PhusionPassenger.require_passenger_lib 'foo' However, we overlooked the fact that this change breaks Ruby apps which use our Out-of-Band GC feature, because such apps had to call `require 'phusion_passenger/rack/out_of_band_gc'`. Unfortunately we're not able to maintain compatibility without reintroducing the Debian and RPM packaging issues. Users should modify the following: require 'phusion_passenger/rack/out_of_band_gc' to: if PhusionPassenger.respond_to?(:require_passenger_lib) # Phusion Passenger >= 4.0.33 PhusionPassenger.require_passenger_lib 'rack/out_of_band_gc' else # Phusion Passenger < 4.0.33 require 'phusion_passenger/rack/out_of_band_gc' end Release 4.0.33 -------------- * Fixed a compatibility problem in passenger-install-apache2-module with Ruby 1.8. The language selection menu didn't work properly. Release 4.0.32 -------------- * Fixed compatibility problems with old Ruby versions that didn't include RubyGems. Release 4.0.31 -------------- * Introduced a new tool: `passenger-config restart-app`. With this command you can initiate an application restart without touching restart.txt. Unlike touching restart.txt, this tool initiates the restart immediately instead of on the next request. * Fixed some problems in process spawning and request handling. * Fixed some problems with the handling of HTTP chunked transfer encoding bodies. These problems only occurred in Ruby. * Fixed the HelperAgent, upon shutdown, not correctly waiting 5 seconds until all clients have disconnected. Fixes issue #884. * Fixed compilation problems on FreeBSD. * Fixed some C++ strict aliasing problems. * Fixed some problems with spawning applications that print messages without newline during startup. Fixes issue #1039. * Fixed potential hangs on JRuby when Ctrl-C is used to shutdown the server. Fixes issue #1035. * When Phusion Passenger is installed through the Debian package, passenger-install-apache2-module now checks whether the Apache module package (libapache2-mod-passenger) is properly installed, and installs it using apt-get if it's not installed. Fixes issue #1031. * The `passenger-status --show=xml` command no longer prints the non-XML preamble, such as the version number and the time. Fixes issue #1037. * The Ruby native extension check whether it's loaded against the right Ruby version, to prevent problems when people upgrade Ruby without recompiling their native extensions. * Various other minor Debian packaging improvements. Release 4.0.30 -------------- * Fixed wrong autogeneration of HTTP Date header. If the web app does not supply a Date header, then Passenger will add one. Unfortunately due to the use of the wrong format string, December 30 2013 is formatted as December 30 2014. As a result, cookies that expire before 2014 would expire on December 30 2013 and December 31 2013. Details can be found at [Github pull request 93](https://github.com/phusion/passenger/pull/93). This issue only affects Phusion Passenger for Nginx and Phusion Passenger Standalone, and does not affect Phusion Passenger for Apache. You can work around this problem in your application by setting a Date header. For example, in Rails you can do: before_filter { response.date = Time.now.utc } Many thanks to Jeff Michael Dean (zilkey) and many others for bringing this to our attention and for providing workarounds and feedback. Release 4.0.29 -------------- * Fixed a compilation problem on OS X Mavericks. Release 4.0.28 -------------- * Introduced a workaround for a GCC 4.6 bug. This bug could cause Phusion Passsenger to crash during startup. Affected operating systems include Ubuntu 12.04 and Amazon Linux 2013.09.01, though not every machine with this OS installed exhibits the problem. See issue #902. * Improved Node.js support: the Sails framework is now supported. * Improved Node.js support: the streams2 API is now supported. * Introduced support for hooks, allowing users to easily extend Phusion Passenger's behavior. * Fixed a bug in the `passenger start -R` option. It was broken because of a change introduced in 4.0.25. * Fixed a bug in PassengerMaxInstancesPerApp. Fixes issue #1016. * Fixed compilation problems on Solaris. * Fixed an encoding problem in the Apache autodetection code. Fixes issue #1026. * The Debian packages no longer depend on libruby. * Application stdout and stderr are now printed without normal Phusion Passenger debugging information, making them easier to read. Release 4.0.27 -------------- * [Apache] Fixed a bug in the Apache module which could lock up the Apache process or thread. This is a regression introduced in version 4.0.24. * Node.js application processes now have friendly process titles. Release 4.0.26 -------------- * Introduced the `PassengerBufferUpload` option for Apache. This option allows one to disable upload buffering, e.g. in order to be able to track upload progress. * [Nginx] The `HTTPS` variable is now set correctly for HTTPS connections, even without setting `ssl on`. Fixes issue #401. * [Standalone] It is now possible to listen on both a normal HTTP and an HTTPS port. * [Enterprise] The `passenger-status` tool now displays rolling restart status. Release 4.0.25 -------------- * The `PassengerAppEnv`/`passenger_app_env`/`--environment` option now also sets NODE_ENV, so that Node.js frameworks like Connect can properly respond to the environment. * Fixed a bug in our Debian/Ubuntu packages causing `passenger-install-nginx-module` not to be able to compile Nginx. * Arbitrary Node.js application structures are now supported. * [Nginx] Introduced the `passenger_restart_dir` option. * [Nginx] Upgraded preferred Nginx version to 1.4.4 because of CVE-2013-4547. Release 4.0.24 -------------- * Introduced the `PassengerNodejs` (Apache) and `passenger_nodejs` (Nginx) configuration options. * [Apache] Introduced the `PassengerErrorOverride` option, so that HTTP error responses generated by applications can be intercepted by Apache and customized using the `ErrorDocument` directive. * [Standalone] It is now possible to specify some configuration options in a configuration file `passenger-standalone.json`. When Passenger Standalone is used in Mass Deployment mode, this configuration file can be used to customize settings on a per-application basis. * [Enterprise] Fixed a potential crash when a rolling restart is triggered while a process is already shutting down. * [Enterprise] Fixed Mass Deployment support for Node.js and Meteor. Release 4.0.23 -------------- * Fixed compilation problems on GCC 4.8.2 (e.g. Arch Linux 2013-10-27). * Fixed a compatibility problem with Solaris /usr/ccs/bin/make: issue #999. * Support for the Meteor Javascript framework has been open sourced. Release 4.0.22 -------------- * [Enterprised] Fixed compilation problems on OS X Mavericks. Release 4.0.21 -------------- * [Nginx] Upgraded the preferred Nginx version to 1.4.3. * Node.js support has been open sourced. * Prelimenary OS X Mavericks support. * Work around an Apache packaging bug in CentOS 5. * Various user friendliness improvements in the documentation and the installers. * Fixed a bug in the always_restart.txt support. Phusion Passenger was looking for it in the wrong directory. * Many Solaris and Sun Studio compatibility fixes. Special thanks to "mark" for his extensive assistance. * [Standalone] The --temp-dir command line option has been introduced. Release 4.0.20 -------------- * Fixed a bug in Phusion Passenger Standalone's daemon mode. When in daemon mode, the Nginx temporary directory was deleted prematurely, causing some POST requests to fail. This was a regression that was introduced in 4.0.15 as part of an optimization. * Fixed compilation problems on Solaris 10 with Sun Studio 12.3. * Improved detection of RVM problems. * It is now possible to log the request method to Union Station. * Introduced a new option, `PassengerLoadShellEnvvars` (Apache) and `passenger_load_shell_envvars` (Nginx). This allows enabling or disabling the loading of bashrc before spawning the application. * [Enterprise] Fixed a packaging problem which caused the flying-passenger executable not to be properly included in the bin path. * [Enterprise] Fixed a race condition which sometimes causes the Flying Passenger socket to be deleted after a restart. Fixes issue #939. * [Enterprise] The `byebug` gem is now supported for debugging on Ruby 2.0. The byebug gem requires a patch before this works: https://github.com/deivid-rodriguez/byebug/pull/29 Release 4.0.19 -------------- * Fixed a problem with response buffering. Application processes are now properly marked available for request processing immediately after they're done sending the response, instead of after having sent the entire response to the client. * The "processed" counter in `passenger-status` is now bumped after the process has handled a request, not at the beginning. * [Enterprise] Fixed an off-by-one bug in the `passenger_max_processes` setting. Release 4.0.18 -------------- * The Enterprise variant of Phusion Passenger Standalone now supports customizing the concurrency model and thread count from the command line. * On Nginx, the Enterprise license is now only checked if Phusion Passenger is enabled in Nginx. This allows you to deploy Nginx binaries, that have Phusion Passenger Enterprise compiled in, to servers that are not actually running Phusion Passenger Enterprise. * Fixed a performance bug in the Union Station support code. In certain cases where a lot of data must be sent to Union Station, the code is now over 100 times faster. * `passenger-status --show=union_station` now displays all clients that are connected to the LoggingAgent. * Added a workaround for Heroku so that exited processes are properly detected as such. * When using Phusion Passenger Standalone with Foreman, pressing Ctrl-C in Foreman no longer results in runaway Nginx processes. * Fixed backtraces in the Apache module. Release 4.0.17 -------------- * Fixed compilation problems on GCC 4.8 systems, such as Arch Linux 2013.04. Fixes issue #941. * Fixed some deprecation warnings when compiling the Ruby native extension on Ruby 2.0.0. * Fixed some Union Station-related stability issues. Release 4.0.16 -------------- * Allow Phusion Passenger to work properly on systems where the user's GID does not have a proper entry in /etc/group, such as Heroku. Release 4.0.15 -------------- * Out-of-band work has been much improved. The number of processes which may perform out-of-band work concurrently has been limited to 1. Furthermore, processes which are performing out-of-band work are now included in the max pool size constraint calculation. However, this means that in order to use out-of-band work, you need to have at least 2 application processes running. Out-of-band work will never be triggered if you just have 1 process. Partially fixes issue #892. * Phusion Passenger now displays an error message to clients if too many requests are queued up. By default, "too many" is 100. You may customize this with `PassengerMaxRequestQueueSize` (Apache) or `passenger_max_request_queue_size` (Nginx). * A new configuration option, `PassengerStartTimeout` (Apache) and `passenger_start_timeout` (Nginx), has been added. This option allows you to specify a timeout for application startup. The startup timeout has exited since version 4.0.0, but before version 4.0.15 it was hardcoded at a value of 90 seconds. Now it is customizable. Fixes issue #936. * [Enterprise] The `PassengerMaxRequestTime`/`passenger_max_request_time` feature is now available for Python and Node.js as well, and is no longer limited to just Ruby. Fixes issue #938. * [Nginx] Introduced a configuration option `passenger_intercept_errors`, which decides if Nginx will intercept responses with HTTP status codes of 400 and higher. Its effect is similar to `proxy_intercept_errors`. * [Standalone] Memory usage optimization: when `passenger start` is run with `--daemonize`, the frontend exits after starting the Nginx core. This saves ~20 MB of memory per `passenger start` instance. * [Standalone] Phusion Passenger Standalone is now also packaged in the Debian packages. * [Standalone] Fix a problem with the `passenger stop` command on Ruby 1.8.7. The 'thread' library was not properly required, causing a crash. * [Standalone] There is now builtin support for SSL. * Fix a crash when multiple `passenger_pass_header` directives are set. Fixes issue #934. * Permissions on the server instance directory are now explicitly set with chmod, so that permissions are correct on systems with a non-default umask. Fixes issue #928. * Fix permission problems when running `passenger start` with `--user`. * `passenger-config --detect-apache2` now correctly detects the eror log filename on Amazon Linux. Fixes issue #933. * An environment variable `PASSENGER_THREAD_LOCAL_STORAGE` has been added to the build system for forcefully disabling the use of thread-local storage within the Phusion Passenger codebase. This flag useful on systems that have broken support for thread-local storage, despite passing our build system's check for proper thread-local storage support. At the time of writing, one user has reported that Ubuntu 12.04 32-bit has broken thread-local storage report although neither the reporter nor us were able to reproduce the problem on any other systems running Ubuntu 12.04 32-bit. Note that this flag has no effect on non-Phusion Passenger code. Fixes issue #937. * It is now possible to preprocess events before they are sent to Union Station. This is useful for removing confidential data as demonstrated in this example `config/initializers/passenger.rb` file: if defined?(PhusionPassenger) event_preprocessor = lambda do |e| e.payload[:sql].gsub!("secret","PASSWORD") if e.payload[:sql] end PhusionPassenger.install_framework_extensions!(:event_preprocessor => event_preprocessor) end Release 4.0.14 -------------- * Fixed a bug in Passenger Standalone's source compiler, for the specific case when the downloaded Nginx binary doesn't work, and compilation of the Nginx binary did not succeed the first time (e.g. because of missing dependencies). * Precompiled Ruby native extensions are now automatically downloaded. Release 4.0.13 -------------- * Updated preferred Nginx version to 1.4.2. * Worked around the fact that FreeBSD 9.1 has a broken C++ runtime. Patch contributed by David Keller. * Autogenerated HTTP Date headers are now in UTC instead of local time. This could cause cookies to have the wrong expiration time. Fixes issue #913. * Fixed compatibility problems with Ruby 1.8.6 (issue #924). * Introduced a tool, `passenger-config --detect-apache2`, which autodetects all Apache installations on the system along with their parameters (which apachectl command to run, which log file to read, which config file to edit). The tool advises users about how to use that specific Apache installation. Useful if the user has multiple Apache installations but don't know about it, or when the user doesn't know how to work with multiple Apache installations. * Added an API for better Rack socket hijacking support. * Added a hidden configuration option for customizing the application start timeout. A proper configuration option will be introduced in the future. * Added autodetection support for Amazon Linux. * Fixed process metrics collection on some operating systems. Some systems' 'ps' command expect no space between -p and the list of PIDs. Release 4.0.10 -------------- * Fixed a crash in PassengerWatchdog which occurs on some OS X systems. * Fixed exception reporting to Union Station. * Improved documentation. Release 4.0.9 ------------- * [Enterprise] Fixed a problem with passenger-irb. Release 4.0.8 ------------- * Fixed a problem with graceful web server restarts. When you gracefully restart the web server, it would cause Phusion Passenger internal sockets to be deleted, thus causing Phusion Passenger to go down. This problem was introduced in 4.0.6 during the attempt to fix issue #910. * The PassengerRestartDir/passenger_restart_dir now accepts relative filenames again, just like in Phusion Passenger 3.x. Patch contributed by Ryan Schwartz. * Documentation updates contributed by Gokulnath Manakkattil. * [Enterprise] Fixed a license key checking issue on some operating systems, such as CentOS 6. Release 4.0.7 ------------- * There was a regression in 4.0.6 that sometimes prevents PassengerLoggingAgent from starting up. Unfortunately this slipped our release testing. This regression has been fixed and we've updated our test suite to check for these kinds of regressions. Release 4.0.6 ------------- * Fixed a potential 100% CPU lock up in the crash handler, which only occurs on OS X. Fixes issue #908. * Fixed a crash in request handling, when certain events are trigger after the client has already disconnected. Fixes issue #889. * Phusion Passenger will no longer crash when the Phusion Passenger native_support Ruby extension cannot be compiled, e.g. because the Ruby development headers are not installed or because the current user has no permission to save the native extension file. Fixes issue #890. * Fixed OS X 10.9 support. Fixes issue #906. * Removed dependency on bash, so that Phusion Passenger works out of the box on BSD platforms without installing/configuring bash. Fixes issue #911. * Fix 'PassengerPoolIdleTime 0' not being respected correctly. Issue #904. * Admin tools improvement: it is now possible to see all currently running requests by invoking `passenger-status --show=requests`. * A new feature called Flying Passenger allows you to decouple the life time of Phusion Passenger from the web server, so that both can be restarted indepedently from each other. Please refer to http://blog.phusion.nl/2013/07/03/technology-preview-introducing-flying-passenger/ for an introduction. * [Apache] Fixed compatibility with Apache pipe logging. Previously this would cause Phusion Passenger to lock up with 100% CPU during Apache restart. * [Nginx] The Nginx configure script now checks whether 'ruby' is in $PATH. Previously, if 'ruby' is not in $PATH, then the compilation process fails with an obscure error. * [Nginx] passenger-install-nginx-module now works properly even when Phusion Passenger is installed through the Debian packages. Before, the installer would tell you to install Phusion Passenger through the gem or tarball instead. * [Enterprise] Added pretty printing helpers to the Live IRB Console. * Fixed permissions on a subdirectory in the server instance directory. The server instance directory is a temporary directory that Phusion Passenger uses to store working files, and is deleted after Phusion Passenger exits. A subdirectory inside it is world-writable (but not world-readable) and is used for storing Unix domain sockets created by different apps, which may run as different users. These sockets had long random filenames to prevent them from being guessed. However because of a typo, this subdirectory was created with the setuid bit, when it should have sticky bit (to prevent existing files from being deleted or renamed by a user that doesn't own the file). This has now been fixed. * If the server instance directory already exists, it will now be removed first in order get correct directory permissions. If the directory still exists after removal, Phusion Passenger aborts to avoid writing to a directory with unexpected permissions. Fixes issue #910. * The installer now checks whether the system has enough virtual memory, and prints a helpful warning if it doesn't. * Linux/AArch64 compatibility fixes. Patch contributed by Dirk Mueller. * Improved documentation. Release 4.0.5 ------------- * [Standalone] Fixed a regression that prevented Passenger Standalone from starting. Fixes issue #899. * Fixed security vulnerability CVE-2013-2119. Urgency: low Scope: local exploit Summary: denial of service and arbitrary code execution by hijacking temp files Affected versions: all versions Fixed versions: 3.0.21 and 4.0.5 Description: Phusion Passenger's code did not always create temporary files and directories in a secure manner. Temporary files and directories were sometimes created with a predictable filename. A local attacker can pre-create temporary files, resulting in a denial of service. In addition, this vulnerability allows a local attacker to run arbitrary code as another user, by hijacking temporary files. By pre-creating certain temporary files with certain permissions, attackers can prevent Passenger Standalone from starting (denial of service). By pre-creating certain temporary files with certain other permissions, attackers can trick `passenger start` and the build system (which is invoked by `passenger-install-apache2-module`/`passenger-install-nginx-module`) to run arbitrary code. The user that the code is run as, is equal to the user that ran `passenger start` or the build system. Attacks of this nature have to be timed exactly right. The attacker must overwrite the file contents right after Phusion Passenger has created the file contents, but right before the file is used. In the context of `passenger start`, the vulnerable window begins right after Passenger Standalone has created the Nginx config file, and ends when Nginx has read the config file. Once Nginx has started and initialized, the system is no longer vulnerable. `passenger stop` and other Passenger Standalone commands besides `start` are not vulnerable. In the context of the build system, the vulnerable window begins when `passenger-install-apache2-module`/`passenger-install-nginx-module` prints its first dependency checking message, and ends when it prints the first compiler command. Only the `passenger start` command, the `passenger-install-apache2-module` command and the `passenger-install-nginx-module` commands are vulnerable. Phusion Passenger for Apache and Phusion Passenger for Nginx (once they are installed) are not vulnerable. Fixed versions: 3.0.21 and 4.0.5 have been released to address this issue. Workaround: You can use this workaround if you are unable to upgrade. Before invoking any Phusion Passenger command, set the `TMPDIR` environment variable to a directory that is not world-writable. Special care must be taken when you use sudo: sudo resets all environment variables, so you should either invoke sudo with `-E`, or you must set the environment variable after gaining root privileges with sudo. Release 4.0.4 ------------- * Fixed autodetection of noexec-mount /tmp directory. Fixes issue #850 and issue #625. * Fixed a WSGI bug. wsgi.input was a file object opened in text mode, but should be opened in binary mode. Fixes issue #881. * Fixed a potential crash in Out-of-Band Work. Fixes issue #894. * Fixed a potential crash in rolling restarting, which only occurs if a process was also being spawned at the same time. Fixes issue #896. * [Apache] The RailsBaseURI and RackBaseURI directives have been unified. For a long time, RailsBaseURI told Phusion Passenger that the given sub-URI belongs to a **Rails 2** application. Attempt to use this directive with Rails 3 or with Rack applications would result in an error. Because this confused users, RailsBaseURI and RackBaseURI have now been unified and can now be used interchangably. Phusion Passenger will automatically detect what kind of application it is. The Nginx version already worked like this. Fixes issue #882. * [Standalone] The Passenger Standalone temp directory and PassengerWatchdog server instance directory have been unified. PassengerWatchdog already automatically updates the timestamps of all files in its server instance directory every 6 hours to prevent /tmp cleaners from deleting the directory. Therefore this unification prevents the Passenger Standalone temp directory to be deleted by /tmp cleaners as well. Fixes issue #654. * [Standalone] types_hash_max_size has been increased from 1024 to 2048. This solves a problem that causes Nginx not to start on some platforms. Contributed by Jan-Willem Koelewijn. Release 4.0.3 ------------- * Better protection is now provided against application processes that are stuck and refuse to shut down cleanly. Since version 4.0.0, Phusion Passenger already forcefully shuts down all processes during web server shutdown. In addition to this, 4.0.3 now also forcefully shuts down processes that take more than 1 minute to shut down, even outside the context of web server shutdowns. This feature does not, however, protect against requests that take too long. Use PassengerMaxRequestTime (Apache) or passenger_max_request_time (Nginx) for that. * Fixed a crash in the HelperAgent which results in frequent process restarts in some traffic patterns. Fixes issue #862. * Fixed a problem that prevents processes from being spawned correctly if the user's bashrc changes working directory. Fixes issue #851. * passenger-status now also displays CPU usage. * The installer now checks for checksums when automatically downloading PCRE and Nginx. Contributed by Joshua Lund. * An error is now printed when trying to daemonize Phusion Passenger Standalone on Ruby implementations that don't support forking. Contributed by Benjamin Fleischer. * Although Phusion Passenger already supported JRuby, *installing* Phusion Passenger with JRuby was not possible. This has been fixed. * Various other minor bug fixes. Release 4.0.2 ------------- * Bumped the preferred Nginx version to 1.4.1 because of a critical Nginx security vulnerability, CVE-2013-2028. Users are advised to upgrade immediately. Release 4.0.1 ------------- * Fixed a crasher bug in the Deployment Error Resistance feature. * Fixed a bug in PassengerDefaultUser and PassengerDefaultGroup. * Fixed a bug which could cause application processes to exit before they've finished their request. * Fixed some small file descriptor leaks. * Bumped the preferred Nginx version to 1.4.0. * Editing the Phusion Passenger Standalone Nginx config template is no longer discouraged. * Improved documentation. Release 4.0.0 release candidate 6 --------------------------------- * WebSocket support on Nginx. Requires Nginx >= 1.3.15. * Improved RVM support. * Performance optimizations. * Various bug fixes. Release 4.0.0 release candidate 5 --------------------------------- * The default config snippet for Apache has changed! It must now contain a `PassengerDefaultRuby` option. The installer has been updated to output this option. The `PassengerRuby` option still exists, but it's only used for configuring different Ruby interpreters in different contexts. Please refer to the manual for more information. * We now provide GPG digital signatures for all file releases by Phusion. More information can be found in the manual. * `passenger-status` now displays process memory usage and time when it was last used. The latter fixes issue #853. * Exceptions in Rack application objects are now caught to prevent application processes from exiting. * The `passenger-config` tool now supports the `--ruby-command` argument, which helps the user with figuring out the correct Ruby command to use in case s/he wants to use multiple Ruby interpreters. The manual has also been updated to mention this tool. * Fixed streaming responses on Apache. * Worked around an OS X Unix domain socket bug. Fixes issue #854. * Out-of-Band Garbage Collection now works properly when the application has disabled garbage collection. Fixes issue #859. * Fixed support for /usr/bin/python on OS X. Fixes issue #855. * Fixed looping-without-sleeping in the ApplicationPool garbage collector if PassengerPoolIdleTime is set to 0. Fixes issue #858. * Fixed some process memory usage measurement bugs. * Fixed process memory usage measurement on NetBSD. Fixes issue #736. * Fixed a file descriptor leak in the Out-of-Band Work feature. Fixes issue #864. * The PassengerPreStart helper script now uses the default Ruby interpreter specified in the web server configuration, and no longer requires a `ruby` command to be in `$PATH`. * Updated preferred PCRE version to 8.32. * Worked around some RVM bugs. * The ngx_http_stub_status_module is now enabled by default. * Performance optimizations. Release 4.0.0 release candidate 4 --------------------------------- * Fixed compilation on systems where /tmp is mounted noexec. * Fixed some memory corruption bugs. * Improved debugging messages. * Phusion Passenger Standalone now sets underscores_in_headers. Fixes issue #708. * Fixed some process spawning compatibility problems, as reported in issue #842. * The Python WSGI loader now correctly shuts down client sockets even when there are child processes that keep the socket open. * A new configuration option PassengerPython (Apache) and passenger_python (Nginx) has been added so that users can customize the Python interpreter on a per-application basis. Fixes issue #852. * The Apache module now supports file uploads larger than 2 GB when on 32-bit systems. Fixes issue #838. * The Nginx version now supports the `passenger_temp_dir` option. * Environment variables set in the Nginx configuration file (through the `env` config option) are now correctly passed to all application processes. Fixes issue #371. * Fixed support for RVM mixed mode installations. Fixes issue #828. * Phusion Passenger now outputs the Date HTTP header in case the application didn't already do that (and was violating the HTTP spec). Fixes issue #485. * Phusion Passenger now checks whether /dev/urandom isn't broken. Fixes issue #516. Release 3.9.5 (4.0.0 release candidate 3) ----------------------------------------- * Fixed Rake autodetection. Release 3.9.4 (4.0.0 release candidate 2) ----------------------------------------- * More bug fixes. * More documentation updates. * Better crash diagnostics. Release 3.9.3 (4.0.0 release candidate 1) ----------------------------------------- * The Nginx version now supports the `passenger_app_root` configuration option. * The Enterprise memory limiting feature has been extended to work with non-Ruby applications as well. * Application processes that have been killed are now automatically detected within 5 seconds. Previously Phusion Passenger needed to send a request to the process before detecting that it's gone. This change means that when you kill a process by sending it a signal, Phusion Passenger will automatically respawn it within 5 seconds (provided that the process limit settings allow respawning). * Phusion Passenger Standalone's HTTP client body limit has been raised from 50 MB to 1 GB. * Python 3 support has been added. * The build system has been made compatible with JRuby and Ruby 2.0. * The installers now print a lot more information about detected system settings so that the user can see whether something has been wrongly detected. * Some performance optimizations. These involve further extending the zero-copy architecture, and the use of hash table maps instead of binary tree maps. * Many potential crasher and freezer bugs have been fixed. * Error diagnostics have been further improved. * Many documentation improvements. Release 3.9.2 (4.0.0 beta 2) ---------------------------- * New feature: JRuby and Rubinius support. * New feature: Out of Band Work. * Sending SIGBART to a Ruby process will now trigger the same behavior as SIGQUIT - that is, it will print a backtrace. This is necessary for proper JRuby support because JRuby cannot catch SIGQUIT. * Rolling restarts and depoyment error resistance are now also available in Phusion Passenger Standalone in the Enterprise version. * System call failure simulation framework. * Improved crash reporting. * Many documentation improvements. * Many bug fixes. Release 3.9.1 (4.0.0 beta 1) ---------------------------- This is the first beta of Phusion Passenger 4. The changes are numerous. * Support for multiple Ruby versions. * The internals now use evented I/O. * Real-time response buffering. * Improved zero-copy architecture. * Rewritten ApplicationPool and process spawning subsystem. * Multithreading within Ruby apps (Phusion Passenger Enterprise only). * Python WSGI support lifted to "beta" status. * More protection against stuck processes. * Automatically picks up environment variables from your bashrc. * Allows setting environment variables directly in Apache. * Automatic asset pipeline support in Standalone. * Deleting restart.txt no longer triggers a restart. * More stable Union Station support. * Many internal robustness improvements. * Better relocatability without wasting space. Release 3.0.21 -------------- * Rebootstrapped the libev configure to fix compilation problems on Solaris 11. * Fixed support for RVM mixed mode installations. Fixes issue #828. * Fixed encoding problems in Phusion Passenger Standalone. * Changed preferred Nginx version to 1.2.9. * Catch exceptions raised by Rack application objects. * Fix for CVE-2013-2119. Details can be found in the announcement for version 4.0.5. * Version 3.0.20 was pulled because its fixes were incomplete. Release 3.0.19 -------------- * Nginx security fix: do not display Nginx version when server_tokens are off. * Fixed compilation problems on some systems. * Fixed some Union Station-related bugs. Release 3.0.18 -------------- * Fixed compilation problems on Fedora 17. * Fixed Union Station compatibility with Rails 3.2. * Phusion Passenger Enterprise Standalone now supports rolling restarts and deployment error resistance. Release 3.0.17 -------------- * Fixed a Ruby 1.9 encoding-related bug in the memory measurer. (Phusion Passenger Enterprise) * Fixed OOM adjustment bugs on Linux. * Fixed compilation problems on Fedora 18 and 19. * Fixed compilation problems on SunOS. * Fixed compilation problems on AIX. Contribution by Perry Smith. * Fixed various compilation warnings. * Upgraded preferred Nginx version to 1.2.3. 3.0.16 was an unofficial hotfix release, and so its announcement had been skipped. Release 3.0.15 -------------- * Updated documentation. * Updated website links. Release 3.0.14 -------------- * [Apache] Fixed a long-standing mod_rewrite-related problem. Some mod_rewrite rules would not work, but it depends on the exact mod_rewrite configuration so it would work for some people but not for others. Issue #563. Thanks a lot to cedricmaion for providing information on the nature of the bug and to peter.nash55 for providing a VM that allowed us to reproduce the problem. * [Nginx] Preferred Nginx version to 1.2.2. The previously preferred version was 1.2.1. * Cleared some confusing terminology in the documentation. * Fixed some Ruby 1.9 encoding problems. Release 3.0.13 -------------- * [Nginx] Preferred Nginx version upgraded to 1.2.1. * Fixed compilation problems on FreeBSD 6.4. Fixes issue #766. * Fixed compilation problems on GCC >= 4.6. * Fixed compilation problems on OpenIndiana and Solaris 11. Fixes issue #742. * Union Station-related bug fixes. * Sending the soft termination signal twice to application processes no longer makes them crash. Patch contributed by Ian Ehlert. Release 3.0.12 -------------- * [Apache] Support Apache 2.4. The event MPM is now also supported. * [Nginx] Preferred Nginx version upgraded to 1.0.15. * [Nginx] Preferred PCRE version upgraded to 8.30. * [Nginx] Fixed compatibility with Nginx < 1.0.10. * [Nginx] Nginx is now installed with http_gzip_static_module by default. * [Nginx] Fixed a memory disclosure security problem. The issue is documented at http://www.nginx.org/en/security_advisories.html and affects more modules than just Phusion Passenger. Users are advised to upgrade as soon as possible. Patch submitted by Gregory Potamianos. * [Nginx] passenger_show_version_in_header now hides the Phusion Passenger version number from the 'Server:' header too. Patch submitted by Gregory Potamianos. * Fixed a /proc deprecation warning on Linux kernel >= 3.0. Release 3.0.11 -------------- * Fixed a compilation problem on platforms without alloca.h, such as FreeBSD 7. * Improved performance and solved some warnings on Xen systems by compiling with `-mno-tls-direct-seg-refs`. Patch contributed by Michał Pokrywka. Release 3.0.10 -------------- * [Nginx] Dropped support for Nginx versions older than 1.0.0 * [Nginx] Fixed support for Nginx 1.1.4+ * [Nginx, Standalone] Upgraded default Nginx version to 1.0.10 The previously default version was 1.0.5. * [Nginx] New option passenger_max_requests This is equivalent to the PassengerMaxRequests option in the Apache version: Phusion Passenger will automatically shutdown a worker process once it has processed the specified number of requests. Contributed by Paul Kmiec. * [Apache] New option PassengerBufferResponse The Apache version did not buffer responses. This could block the Ruby worker process in case of slow clients. We now enable response buffering by default. It can be turned off through this option. Feature contributed by Ryo Onodera. * Fixed remaining Ruby 1.9.3 compatibility problems We already supported Ruby 1.9.3 since 3.0.8, but due to bugs in Ruby 1.9.3's build system Phusion Passenger would fail to detect Ruby 1.9.3 features on some systems. Fixes issue #714. * Fixed a bug in PassengerPreStart A regression was introduced in 3.0.8, causing the prespawn script to connect to the host name instead of to 127.0.0.1. Fix contributed by Andy Allan. * Fixed compatibility with GCC 4.6 Affected systems include Ubuntu 11.10. * Fixed various compilation problems. * Fixed some Ruby 1.9 encoding problems. * Fixed some Ruby 1.9.3 deprecation warnings. Release 3.0.9 ------------- * [Nginx] Fixed a NULL pointer crash that occurs on HTTP/1.0 requests when the Host header isn't given. * Fixed deprecation warnings on RubyGems >= 1.6. * Improved Union Station support stability. Release 3.0.8 ------------- * [Nginx] Upgraded preferred Nginx version to 1.0.5. * [Nginx] Fixed various compilation problems on various platforms. * [Nginx] We now ensure that SERVER_NAME is equal to HTTP_HOST without the port part. This is needed for Rack compliance. By default Nginx sets SERVER_NAME to whatever is specified in the server_name directive, but that's not necessarily the correct value. This fixes, for example, the use of the 'map' statement in config.ru. * [Nginx] Added the options passenger_buffer_size, passenger_buffers and passenger_busy_buffers_size. These options are similar to proxy_module's similarly named options. You can use these to e.g. increase the maximum header size limit. * [Nginx] passenger_pre_start now supports virtual hosts that listen on Unix domain sockets. * [Apache] Fixed the pcre.h compilation problem. * [Standalone] Fixed 'passenger stop'. It didn't work properly because it kept waiting for 'tail' to exit. We now properly terminate 'tail' as well. * Fixed compatibility with Rake 0.9. * Fixed various Ruby 1.9 compatibility issues. * Various documentation improvements. * New Union Station filter language features. It now supports status codes and response times. Please refer to https://engage.unionstationapp.com/help#filtering for more information. Release 3.0.7 ------------- * Fixed a bug passenger-install-apache2-module. It could crash on some systems due to a typo in the code. * Upgraded preferred Nginx version to 1.0.0. * Phusion Passenger Standalone now pre-starts application processes at startup instead of doing that at the first request. * When sending data to Union Station, the HTTP status code is now also logged. * Various Union Station-related stability improvements. * The Linux OOM killer was previously erroneously disabled for all Phusion Passenger processes, including application processes. The intention was to only disable it for the Watchdog. This has been fixed, and the Watchdog is now the only process for which the OOM killer is disabled. * Fixed some compilation problems on OpenBSD. * Due to a typo, the dependency on file-tail was not entirely removed in 3.0.6. This has now been fixed. Release 3.0.6 ------------- * Fixed various compilation problems such as XCode 4 support and OpenBSD support. * Fixed various Union Station-related stability issues. * Fixed an issue with host name detection on certain platforms. * Improved error logging in various parts. * The dependency on the file-tail library has been removed. * During installation, check whether /tmp is mounted with 'noexec'. Phusion Passenger's installer relies on /tmp *not* being mounted with 'noexec'. If it is then the installer will now show a helpful error message instead of bailing out in a confusing manner. Users can now tell the installer to use a different directory for storing temporary files by customizing the $TMPDIR environment variable. * Phusion Passenger Standalone can now run Rackup files that are not named 'config.ru'. The filename can be passed through the command line using the -R option. Release 3.0.5 ------------- * [Apache] Fixed Union Station process statistics collection Union Station users that are using Apache may notice that no process information show up in Union Station. This is because of a bug in Phusion Passenger's Apache version, which has now been fixed. * [Apache] PassengerAnalytics has been renamed to UnionStationSupport This option has been renamed for consistency reasons. * [Nginx] passenger_analytics has been renamed to union_station_support This option has been renamed for consistency reasons. * Fixed Union Station data sending on older libcurl versions Some Union Station users have reported that their data don't show up. Upon investigation this turned out to be a compatibility with older libcurl versions. Affected systems include all RHEL 5 based systems, such as RHEL 5.5 and CentOS 5.5. We've now fixed compatibility with older libcurl versions. * Added support for the Union Station filter language This language can be used to limit the kind of data that's sent to Union Station. Please read https://engage.unionstationapp.com/help#filtering for details. * Fixed a PassengerMaxPoolSize/passenger_max_pool_size violation bug People who host a lot of different applications on Phusion Passenger may notice that it sometimes spawns more processes than is allowed by PassengerMaxPoolSize/passenger_max_pool_size. This has been fixed. Release 3.0.4 ------------- * [Apache] Changed mod_dir workaround hook priority Phusion Passenger temporarily disables mod_dir on all Phusion Passenger-handled requests in order to avoid conflicts. In order to do this it registers some Apache hooks with the APR_HOOK_MIDDLE priority, but it turned out that this breaks some other modules like mod_python. The hook priority has been changed to APR_HOOK_LAST to match mod_dir's hook priorities. Issue reported by Jay Freeman. * Added support for Union Station: http://www.unionstationapp.com/ * Some error messages have been improved. Release 3.0.3 ------------- * [Nginx] Preferred Nginx version upgraded to 0.8.54 The previous preferred version was 0.8.53. * PATH_INFO and REQUEST_URI now contain the original escaped URI Phusion Passenger passes the URI, as reported by Apache/Nginx, to application processes through the PATH_INFO and REQUEST_URI variables. These variables are supposed to contain the original, unescaped URI, e.g. /clubs/%C3%BC. Both Apache and Nginx thought that it would be a good idea to unescape the URI before passing it to modules like Phusion Passenger, thereby causing PATH_INFO and REQUEST_URI to contain the unescaped URI, e.g. /clubs/ü. This causes all sorts of encoding problems. We now manually re-escape the URI when setting PATH_INFO and REQUEST_URI. Issue #404. * The installer no longer detects directories as potential commands Previously the installer would look in $PATH for everything that's executable, including directories. If one has /usr/lib in $PATH and a directory /usr/lib/gcc exists then the installer would recognize /usr/lib/gcc as the compiler. We now explicitly check whether the item is also a file. * PseudoIO now responds to #to_io Phusion Passenger sets STDERR to a PseudoIO object in order to capture anything written to STDERR during application startup. This breaks some libraries which expect STDERR to respond to #to_io. This has now been fixed. Issue #607. * Fixed various other minor bugs See the git commit log for details. Release 3.0.2 ------------- * [Nginx] Fixed compilation problems The Nginx compilation process was broken due to not correctly reverting the working directory of the Nginx configure script. This has been fixed: issue #595. * [Nginx] Fixed crash if passenger_root refers to a nonexistant directory Issue #599. * Fixed compilation problems on NetBSD There was a typo in a NetBSD-specific fcntl() call. It also turns out that NetBSD doesn't support some ISO C99 math functions like llroundl(); this has been worked around by using other functions. Issue #593. * Fixed file descriptor closing issues on FreeBSD Phusion Passenger child processes didn't correct close file descriptors on FreeBSD because it queries /dev/fd to do that. On FreeBSD /dev/fd only returns meaningful results if fdescfs is mounted, which it isn't by default. Issue #597. Release 3.0.1 ------------- * MUCH faster compilation We've applied code aggregation techniques, allowing Phusion Passenger to be compiled much quicker now. For example, compiling the Nginx component (not Nginx itself) on a MacBook Pro now takes only 29 seconds instead of 51 seconds, an improvement of 75%! Compiling the Apache module on a slower Dell Inspiron now takes 39 seconds instead of 1 minute 22 seconds, or 110% faster! * Fixed malfunction after web server restart On Linux systems that have a non-standard filesystem on /tmp, Phusion Passenger could malfunction after restarting the web server because of a bug that's only triggered on certain filesystems. Issue #569. * Boost upgraded to version 1.44.0. We were on 1.42.0. * Much improved startup error messages Phusion Passenger performs many extensive checks during startup to ensure integrity. However the error message in some situation could be vague. These startup error messages have now been improved dramatically, so that if something goes wrong during startup you will now more likely know why. * Curl < 7.12.1 is now supported The previous version fails to compile with Curl versions earlier than 7.12.1. Issue #556. * passenger-make-enterprisey fixed This is the command that people can run after donating. It allows people to slightly modify Phusion Passenger's display name as a joke. In 3.0.0 it was broken because of a typo. This has been fixed. * Removed passenger-stress-test This tool was used during the early life of Phusion Passenger for stress testing websites. Its performance has never been very good and there are much better tools for stress testing, so this tool has now been removed. * [Apache] RailsEnv and RackEnv configuration options are now equivalent In previous versions, RailsEnv only had effect on Rails 1 and Rails 2 apps while RackEnv only had effect on Rack apps. Because Rails 3 apps are considered Rack apps, setting RailsEnv had no effect on Rails 3 apps. Because this is confusing to users, we've now made RailsEnv and RackEnv equivalent. Issue #579. * [Nginx] Fixed compilation problems on systems with unpowerful shells Most notably Solaris. Its default shell does not support some basic constructs that we used in the Nginx configure script. * [Nginx] Upgraded default Nginx version to to 0.8.53 The previous default was 0.8.52. * [Nginx] passenger_enabled now only accepts 'on' or 'off' values Previously it would recognize any value not equal to 'on' as meaning 'off'. This caused confusion among users who thought they could also specify 'true', so we now throw a proper error if the value is unrecognized. Fixes issue #583. Release 3.0.0 ------------- This is a major release with many changes. Please read our blog for details. Release 2.2.15 -------------- * [Apache] Fixed incorrect temp dir cleanup by passenger-status On some systems, running passenger-status could print the following message: *** Cleaning stale folder /tmp/passenger.1234 ...after which Phusion Passenger breaks because that directory is necessary for it to function properly. The cause of this problem has been found and has been fixed. * [Apache] Fixed some upload handling problems Previous versions of Phusion Passenger check whether the size of the received upload data matches the contents of the Content-Length header as received by the client. It turns out that there could be a mismatch e.g. because of mod_deflate input compression, so we can't trust Content-Length anyway and we're being too strict. The check has now been removed. * [Nginx] Fixed compilation issues with Nginx >= 0.7.66 Thanks to Potamianos Gregory for reporting this issue. Issue #500. * [Nginx] Default Nginx version changed to 0.7.67 The previous default version was 0.7.65. * Fixed more Bundler problems Previous versions of Phusion Passenger would preload some popular libraries such as mysql and sqlite3 in order to utilize copy-on-write optimizations better. However this behavior conflicts with Bundler so we've removed it. Release 2.2.14 -------------- * Added support for Rubinius Patch contributed by Evan Phoenix. * Fixed a mistake in the SIGQUIT backtrace message. Patch contributed by Christoffer Sawicki. * [Nginx] Fix a localtime() crash on FreeBSD This was caused by insufficient stack space for threads. Issue #499. Release 2.2.13 -------------- * Fixed some Rails 3 compatibility issues that were recently introduced. * Fixed a typo that causes config/setup_load_paths.rb not to be loaded correctly. Release 2.2.12 -------------- * Improved Bundler support. Previous versions might not be able to correctly load gems bundled by Bundler. We've also documented how our Bundler support works and how to override our support if you need special behavior. Please refer to the Phusion Passenger Users Guide, section "Bundler support". * Worked around some user account handling bugs in Ruby. Issue #192. * Fixed some Ruby 1.9 tempfile.rb compatibility problems. * Fixed some compilation problems on some ARM Linux platforms. * [Apache] Suppress bogus mod_xsendfile-related error messages. When mod_xsendfile is being used, Phusion Passenger might print bogus error messages like "EPIPE" or "Apache stopped forwarding the backend's response" to the log file. These messages are normal, are harmless and can be safely ignored, but they pollute the log file. So in this release we've added code to suppress these messages when mod_xsendfile is being used. Issue #474. * [Nginx] Fixed "passenger_user_switching off" permission problems If Nginx is running as root and passenger_user_switching is turned off, then Phusion Passenger would fail to initialize because of a permission problem. This has been fixed. Issue #458. * [Nginx] Nginx >= 0.8.38 is now supported. Thanks to Sergey A. Osokin for reporting the problem. * [Nginx] passenger-install-nginx-module upgraded It now defaults to installing Nginx 0.7.65 instead of 0.7.64. Release 2.2.11 -------------- * This release fixes a regression that appeared in 2.2.10 which only affects Apache. When under high load, Apache might freeze and stop responding to requests. It is caused by a race condition which is why it escaped our last release testing. This problem does not affect Nginx; you only have to upgrade if you're using Apache. http://groups.google.com/group/phusion-passenger/t/d5bb2f17c8446ea0 Release 2.2.10 -------------- * Fixed some Bundler compatibility problems. * Fixed some file descriptor passing problems, which previously could lead to mysterious crashes. * Fixed some compilation problems on newer GCC versions. Issue #430. * Support #size method in rack.input. Release 2.2.9 ------------- * Fixed compatibility with Rails 3. Actually, previous Phusion Passenger releases were already compatible with Rails 3, depending on the spawn method that would be invoked. Here's the story: Since Phusion Passenger 2.2.8, when the file config.ru exists, Phusion Passenger will treat the app as a Rack app, not as a Rails app. This is in contrast to earlier versions which gave Rails detection more priority than Rack detection. Phusion Passenger loads Rack apps and Rails apps in different ways. The Rails loader was not compatible with Rails 3, which is what we've fixed in this release. That said, a Rails 3 app would have worked out-of-the-box on Phusion Passenger 2.2.8 as well because Rails 3 apps include a config.ru file by default, causing Phusion Passenger 2.2.8 to use the Rack loader. Earlier versions of Phusion Passenger would just completely bail out because they'd use the Rails loader. That said, with 2.2.9 there are still some caveats: - Smart spawning (the mechanism with which REE's 33% memory reduction is implemented) is *not* supported for Rack apps. This means that if you want to utilize smart spawning with Rails 3, then you should remove your config.ru file. - Rails 3 depends on Rack 1.1.0. You must have Rack 1.1.0 installed as a gem, even if you've bundled it with the gem bundler. This is because Phusion Passenger itself depends on Rack. Both of these caveats are temporary. We have plans to solve both of these properly in the future. * What's up with the Gem Bundler? There has been some reports that Phusion Passenger is not compatible with Yehuda Katz's gem bundler (http://github.com/wycats/bundler). This might have been true for an earlier version of the gem bundler, but the latest version seems to work fine. Please note that you need to insert the following snippet in config/preinitializer.rb, as instructed by the gem bundler's README: require "#{RAILS_ROOT}/vendor/gems/environment" The Rails::Boot monkey patching code as posted at http://yehudakatz.com/2009/11/03/using-the-new-gem-bundler-today/ does not seem to be required anymore. * Fixed support for ActiveRecord subclasses that connect to another database. ActiveRecord subclasses that connect to a database other than the default one did not have their connection correctly cleared after forking. This can result in weird errors along the lines of "Lost connection to MySQL server during query". Issue #429. * [Nginx] Fixed PCRE URL. passenger-install-nginx-module downloads PCRE 7.8 if PCRE is not already installed. However PCRE 7.8 has been removed from their FTP server, so we've updated the URL to point to the latest version, 8.0. Release 2.2.8 ------------- * [Nginx] Fixed some signal handling problems. Restarting Nginx on OS X with SIGHUP can sometimes take a long time or even fail completely. This is because of some signal handling problems, which have now been fixed. * [Nginx] Added OpenSSL as dependency. OpenSSL is required in order to install Nginx, but this was not checked by passenger-install-nginx-module. As a result, passenger-install-nginx-module fails on e.g. out-of-the-box Ubuntu installations until the user manually installs OpenSSL. Issue #422. * [Nginx] Fixed support for internal redirects and subrequests. It is now possible to, for example, point X-Accel-Redirects to Phusion Passenger-served URLs. Patch contributed by W. Andrew Loe III: issue #433. * [Apache] Fixed a GnuTLS compatibility issue. mod_gnutls can cause Phusion Passenger to crash because of an unchecked NULL pointer. This problem has now been fixed: issue #391. * Fixed thread creation issue on Intel Itanium platforms. This fixes issue #427. * Fixed compilation problems on Linux running on the Renesas SH4 CPU. Patch contributed by iwamatsu: issue #428. * The Rack library has been unvendored. The original reason for vendoring was to work around broken Rails applications that explicitly specify Rack as a gem dependency. We've found a better workaround that does not require vendoring Rack. This also fixes a compatibility problem with Rails 3, because Rails 3 depends on a newer Rack version than the one we had vendored. Issue #432. * Fixed compatibility with Ruby 1.9.1 patchlevel >= 152 Ruby 1.9.1 patchlevel >= 152 has a bug in its tempfile library. If you've seen an error message along the lines of *** Exception IOError in Passenger RequestHandler (closed stream) then this is a Ruby bug at work. This bug has been fixed in Ruby 1.9.2, but Ruby 1.9.1 still contains this bug. We've added a workaround so that the bug is not triggered with this Ruby version. Issue #432. Release 2.2.7 ------------- * Removed forgotten debugging code in passenger-install-apache2-module, which caused it not to compile anything. Release 2.2.6 ------------- * Some /tmp cleaner programs such as tmpwatch try to remove subdirectories in /tmp/passenger.xxx after a while because they think those subdirectories are unused. This could cause Phusion Passenger to malfunction, requiring a web server restart. Measures have now been taken to prevent those tmp cleaner programs from removing anything in /tmp/passenger.xxx. Issue #365. * When autodetecting the application type, Rack is now given more priority than Rails. This allows one to drop a config.ru file in a Rails directory and have it detected as a Rack application instead of a Rails application. Patch contributed by Sam Pohlenz: issue #338. * The default socket backlog has been increased from 'SOMAXCONN' (which is 128 on most platforms) to 1024. This should fix most 'helper_server.sock failed: Resource temporarily unavailable' errors. * Fixed compilation problems on Solaris. Issue #369 and issue #379. * Fixed crashes on PowerPC. * Some Ruby 1.9 compatibility fixes. Issue #398. * The installer now displays correct dependency installation instructions for Mandriva Linux. * [Apache] The location of the 'apxs' and 'apr-config' commands can now also be passed to the installer through the --apxs-path and --apr-config-path parameters, in addition to the $APXS2 and $APR_CONFIG environment variables. Issue #3. * [Nginx] Various problems that only occur on 64-bit platforms have been fixed. * [Nginx] The installer now installs Nginx 0.7.64 by default. Release 2.2.5 ------------- * [Apache] Small file uploads are now buffered; fixes potential DoS attack Phusion Passenger buffers large file uploads to temp files so that it doesn't block applications while an upload is in progress, but it sent small uploads directly to the application without buffering it. This could result in a potential DoS attack: the client can send many small, incomplete file uploads to the server, and this would block all application processes until a timeout occurs. In order to solve this problem, Phusion Passenger now buffers small file uploads in memory. Bug #356. * [Apache] Fixed support for mod_rewrite passthrough rules Mod_rewrite passthrough rules were not properly supported because of a bug fix for supporting encoded slashes (%2f) in URLs. Unfortunately, due to bugs/limitations in Apache, we can support either encoded slashes or mod_rewrite passthrough rules, but not both; supporting one will break the other. Support for mod_rewrite passthrough rules is now enabled by default; that is, support for encoded slashes is disabled by default. A new configuration option, "PassengerAllowEncodedSlashes", has been added. Turning this option on will enable support for encoded slashes and disable support for mod_rewrite passthrough rules. Issue #113 and issue #230. * [Apache] Added a configuration option for resolving symlinks in the document root path Phusion Passenger 2.2.0 and higher no longer resolves symlinks in the document root path in order to properly support Capistrano-style directory structures. The exact behavior is documented in the Users Guide, section "How Phusion Passenger detects whether a virtual host is a web application". However, some people relied on the old behavior. A new configuration option, PassengerResolveSymlinksInDocumentRoot, has been added to allow reverting back to the old behavior. Patch contributed by Locaweb (http://www.locaweb.com.br/). * [Apache] mod_env variables are now also passed through CGI environment headers Prior to version 2.2.3, environment variables set by mod_env are passed to the application as CGI environment headers, not through Ruby's ENV variable. In the last release we introduced support for setting ENV environment variables with mod_env, and got rid of the code for setting CGI environment headers. It turns out that some people relied on the old behavior, we so now environment variables set with mod_env are set in both ENV and in the CGI environment. Fixes bug #335. * [Apache] Fixed compilation problems on some Linux systems with older versions of Apache If you used to see compilation errors like this: ext/apache2/Configuration.cpp:554: error: expected primary-expression before '.' token then this version should compile properly. * [Apache] Fixed I/O timeouts for communication with backend processes Got rid of the code for enforcing I/O timeouts when reading from or writing to a backend process. This caused more problems than it solved. * [Nginx] Support for streaming responses (e.g. Comet or HTTP push) Buffering of backend responses is now disabled. This fixes support for streaming responses, something which the Apache version has supported for a while now. One can generate streaming responses in Ruby on Rails like this: render :text => lambda { |response, output| 10_000.times do |i| output.write("hello #{i}!\n") end } * [Nginx] Installer now installs Nginx 0.7.61 by default Previously it installed 0.6.37 by default. * [Nginx] Fixed the installer's --extra-configure-flags flag when combined with --auto-download Arguments passed to --extra-configure-flags were not being passed to the Nginx configure script when --auto-download is given. This has been fixed: bug #349. * [Nginx] Fixed unnecessary download of PCRE The installer now checks whether PCRE is installed in /opt/local (e.g. MacPorts) as well before concluding that it isn't installed and going ahead with downloading PCRE. * Fixed STDERR capturing While spawning an application, Phusion Passenger captures any output written to STDERR so that it can show them later if the application failed to start. This turns out to be much more difficult than expected, with all kinds of corner cases that can mess up this feature. For example, if the Rails log file is not writable, then this can cause Rails to crash with a bizarre and unhelpful error message whenever it tries to write to STDERR: /!\ FAILSAFE /!\ Thu Aug 20 14:58:39 +1000 2009 Status: 500 Internal Server Error undefined method `[]' for nil:NilClass Some applications reopen STDERR to a log file. This didn't work. Of all of these problems have been fixed now. (Bug #332) * Fixed some bugs in application sources preloading Rails >= 2.2 already preloads the application sources, in which case Phusion Passenger wasn't supposed to perform it's own preloading, but the Rails >= 2.2 detection code was bugged. This has been fixed. Rails < 2.2 doesn't preload the application sources by itself, but there should be a certain order with which the sources are preloaded, otherwise preloading could fail in some applications. We now enforce a specific load order: first models, then controllers, then helpers. Bug #359. * Fixed a few bugs in WSGI compliance PATH_INFO is supposed to be set to the request URI, but without the query string and without the base URI. This has been fixed: bug #360. * Fixed some Ruby 1.9-specific crashes caused by encoding issues. Bug #354. * Fixed loading of config/environment.rb on Ruby 1.9.2, because Ruby 1.9.2 no longer has "." in the default load path. Patch by metaljastix, issue #368. * The Users Guide for Apache now mentions something about correct permissions for application directories. * Fixed compilation problems on IA-64 (bug #118). We also reduced the stack sizes for the threads by half, so Phusion Passenger should use even less virtual memory now. * Fixed compilation problems on Linux systems with ARM CPU. * Fixed a few compatibility problems with 64-bit OpenBSD. * Fixed a few typos and minor bugs. Older releases -------------- Please consult the blog posts on http://old.blog.phusion.nl/ for the information about older releases. passenger-5.0.30/configure000755 000765 000024 00000000437 12233035540 016121 0ustar00honglistaff000000 000000 #!/bin/sh echo "This is not how you install Phusion Passenger! Please run one of:" echo echo " ./bin/passenger-install-apache2-module" echo " ./bin/passenger-install-nginx-module" echo echo "When unsure, please consult the manual (see 'doc' directory)." echo echo "*** ABORTED" exit 1 passenger-5.0.30/CONTRIBUTING.md000644 000765 000024 00000042365 12233035540 016451 0ustar00honglistaff000000 000000 # Contributors Guide **Table of contents** * [Filing bug reports](#file_bugs) * [Contributing documentation](#contrib_docs) * [Contributing by bug triaging](#contrib_triag) * [Contributing community support](#contrib_support) * [Contributing code](#contrib_code) * [Developer QuickStart](#dev_quickstart) * [Design and Architecture](#design_and_architecture) * [Code Walkthrough](#code_walkthrough) * [Compilation and build system](#build_system) * [Running the unit tests](#unit_tests) * [Directory structure](#dir_structure) * [C++ coding style](#cxx_coding_style) * [Ruby coding style](#ruby_coding_style) * [Systems programming fundamentals](#systems_programming_fundamentals) * [Further reading](#further_reading) * [Git structure](#git_structure) Thank you for your interest in Phusion Passenger. Phusion Passenger is open source so your contributions are very welcome. Although we also provide a [commercial version](https://www.phusionpassenger.com/enterprise) and [commercial support](https://www.phusionpassenger.com/commercial_support), the core remains open source and we remain committed to keep it that way. This guide gives you an overview of the ways with which you can contribute, as well as contribution guidelines. You can contribute in one of the following areas: * Filing bugs. * Bug triage. * Documentation (user documentation, developer documentation, contributor documentation). * Community support. * Code. We require contributors to sign our [contributor agreement](http://www.phusion.nl/forms/contributor_agreement) before we can merge their patches. Please submit patches in the form of a Github pull request or as a patch on the [bug tracker](https://github.com/phusion/passenger/issues). Pull requests are preferred and generally get more attention because Github has better email notifications and better discussion capabilities. You should also install required developer tools. The following command will install everything you need: rake test:install_deps If your system requires gems to be installed with root privileges, run: rake test:install_deps SUDO=1 ## Filing bug reports When filing a bug report, please ensure that you include the following information: * What steps will reproduce the problem? * What is the expected output? What do you see instead? * What version of Phusion Passenger are you using? * Which version of Ruby, Rails, Node.js or Meteor are you using? On what operating system? ## Contributing documentation All good software should have good documentation, and we take this very seriously. However writing and maintaing quality documentation is not an easy task. If you are not skilled in C++ or programming, then writing documentation is the easiest way to contribute. Most documentation can be located in the `doc` directory, and are either written in Markdown or in Asciidoc format. They can be compiled to HTML with `rake doc`. You need [Mizuho](https://github.com/FooBarWidget/mizuho) to compile Asciidoc and [BlueCloth](http://deveiate.org/projects/BlueCloth) to compile Markdown. Both gems are automatically installed as part of the Phusion Passenger developer tools. ## Contributing by bug triaging Users [file bug reports](https://github.com/phusion/passenger/issues) on a regular basis, but not all bug reports are legit,contain sufficient information, are equally important, etc. By helping with bug triaging you make the lives of the core developers a lot easier. To start contributing, please submit a comment on any bug report that needs triaging. This comment should contain triaging instructions, e.g. whether a report should be considered duplicate. If you contribute regularly we'll give you moderator access to the bug tracker so that you can apply triaging labels directly. Here are some of the things that you should look for: * Some reports are duplicates of each other, i.e. they report the same issue. You should mark them as duplicate and note the ID of the original report. * Some reported problems are caused by the reporter's machine or the reporter's application. You should explain to them what the problem actually is, that it's not caused by Phusion Passenger, and then close the report. * Some reports need more information. At the very least, we need specific instructions on how to reproduce the problem. You should ask the reporter to provide more information. Some reporters reply slowly or not at all. If some time has passed, you should remind the reporter about the request for more information. But if too much time has passed and the issue cannot be reproduced, you should close the report and mark it as "Stale". * Some bug reports seem to be limited to one reporter, and it does not seem that other people suffer from the same problem. These are reports that need _confirmation_. You can help by trying to reproduce the problem and confirming the existance of the problem. * Some reports are important, but have been neglected for too long. Although the core developers try to minimize the number of times this happens, sometimes it happens anyway because they're so busy. You should actively ping the core developers and remind them about it. Or better: try to actively find contributors who can help solving the issue. **Always be polite to bug reporters.** Not all reporters are fluent in English, and not everybody may be tech-savvy. But we ask you for your patience and tolerance on this. We want to stimulate a positive and ejoyable environment. ## Contributing community support You can contribute by answering support questions on the [community discussion forum](http://groups.google.com/group/phusion-passenger) or on [Stack Overflow](http://stackoverflow.com/search?q=passenger). ## Contributing code Phusion Passenger is mostly written in C++, but the build system and various small helper scripts are in Ruby. The loaders for each supported language is written in the respective language. The source code is filled with inline comments, so look there if you want to understand how things work. ### Developer QuickStart _Watch the Developer QuickStart screencast_ We provide an easy and convenient development environment that contributors can use. Learn more at the [Developer QuickStart](https://github.com/phusion/passenger/blob/master/doc/DeveloperQuickstart.md). ### Design and Architecture Phusion Passenger's design and architecture is documented in detail in the [Design & Architecture](https://www.phusionpassenger.com/documentation/Design%20and%20Architecture.html) document. ### Code Walkthrough We have [a video](http://vimeo.com/phusionnl/review/98027409/03ba678684) which walks you through the Phusion Passenger codebase, showing you step-by-step how things fit together. It complements the [Design & Architecture](https://www.phusionpassenger.com/documentation/Design%20and%20Architecture.html) document. ### Compilation and build system `passenger-install-apache2-module` and `passenger-install-nginx-module` are actually user-friendly wrappers around the build system. The build system is written in Rake, and most of it can be found in the `build/` directory. Run the following command to compile everything: rake apache2 rake nginx It is recommended that you install ccache and set the `USE_CCACHE=1` environment variable. The build system will then automatically wrap all compiler calls in ccache, significantly improving recompilation times. ### Running the unit tests The tests depend on the Phusion Passenger developer tools. If you're not using our [Vagrant environment](https://github.com/phusion/passenger/blob/master/doc/DeveloperQuickstart.md), you need to make sure they're installed: rake test:install_deps You also need to setup the file `test/config.json`. You can find an example in `test/config.json.example`. Run all tests: rake test Run only the unit tests for the C++ components: rake test:cxx rake test:oxt The `test:cxx` unit test suite contains many different test groups. You can run a specific one by setting the environment variable `GROUPS` to a comma-delimited list of group names, e.g.: rake test:cxx GROUPS='ApplicationPool2_PoolTest,UtilsTest' You can also run just a single test within a suite. Pass the relevant test number like this: rake test:cxx GROUPS='ApplicationPool2_PoolTest:82' You can also run the C++ tests in GDB or Valgrind. We have a useful GDB config file in `test/gdbinit.example`. You should copy it to `test/.gdbinit` and edit it. rake test:cxx GDB=1 rake test:cxx VALGRIND=1 Run just the unit tests for the Ruby components: rake test:ruby Run just the integration tests: rake test:integration # All integration tests. rake test:integration:apache2 # Just integration tests for Apache 2. rake test:integration:nginx # Just integration tests for Nginx. Note that some tests, such as the ones that test privilege lowering, require root privileges. Those will only be run if Rake is run as root. ### Directory structure The most important directories are: * `src/ruby_suppportlib`
The source code for Ruby parts of Phusion Passenger. * `src/ruby_native_extension`
Native extension for Ruby. Phusion Passenger uses the functions in this extension for optimizing certain operations, but Phusion Passenger can also function without this extension. * `src/apache2_module`
Apache 2-specific source code. * `src/nginx_module`
Nginx-specific source code. * `src/cxx_supportlib`
Support code shared between all C++ components. * `src/agent`
Source code of the PassengerAgent executable. The agent can be started in multiple modes. * The Watchdog is the main Phusion Passenger process. It starts the Passenger core and the UstRouter, and restarts them when they crash. It also cleans everything up upon shut down. * The Core performs most of the heavy lifting. It parses requests, spawns application processes, forwards requests to the correct process and forwards application responses back to the web server. * The UstRouter processes Union Station data and sends them to the Union Station server. * `bin`
User executables. * `helper-scripts`
Scripts used during runtime, but not directly executed by the user. All the loaders - applications which are responsible for loading an application written in a certain language and hooking it up to Phusion Passenger - are in this directory. * `doc`
Various documentation. * `test`
Unit tests and integration tests. * `test/support`
Support/utility code, used in the tests. * `test/stub`
Stubbing and mocking code, used in the tests. Less important directories: * `src/vendor-modified/boost`
A stripped-down and customized version of the [Boost C++ library](http://www.boost.org). * `src/oxt`
The "OS eXtensions for boosT" library, which provides various important functionality necessary for writing robust server software. It provides things like support for interruptable system calls and portable backtraces for C++. Boost was modified to make use of the functionality provided by OXT. * `dev`
Tools for Phusion Passenger developers. Not used during production. * `resources`
Various non-executable resource files, used during production. * `packaging/debian`
Debian packaging files. * `packaging/rpm`
RPM packaging files. * `man`
Man pages. * `build`
Source code of the build system. ### C++ coding style * Use 4-space tabs for indentation. * Wrap at approximately 80 characters. This is a recommendation, not a hard guideline. You can exceed it if you think it makes things more readable, but try to minimize it. * Use camelCasing for function names, variables, class/struct members and parameters: void frobnicate(); void deleteFile(const char *filename, bool syncHardDisk); int fooBar; Use PascalCasing for classes, structs and namespaces: class ApplicationPool { struct HashFunction { namespace Passenger { * `if` and `while` statements must always have their body enclosed by brackets: if (foo) { ... } Not: if (foo) ... * When it comes to `if`, `while`, `class` and other keywords, put a space before and after the opening and closing parentheses: if (foo) { while (foo) { case (foo) { Not: if(foo){ while (foo) { * You should generally put brackets on the same line as the statement: if (foo) { ... } while (bar) { ... } However, if the main statement is so long that it does not fit on a single line, then the bracket should start at the next line: if (very very long expression && another very very long expression) { ... } * Do not put a space before the opening parenthesis when calling functions. foo(1, 2, 3); Not: foo (1, 2, 3); * Seperate arguments and parts of expressions by spaces: foo(1, 2, foo == bar, 5 + 6); if (foo && bar) { Not: foo(1,2, foo==bar,5+6); if (foo&&bar) { * When declaring functions, puts as much on the same line as possible: void foo(int x, int y); When the declaration becomes too long, wrap at the beginning of an argument and indent with a tab: void aLongMethod(double longArgument, double longArgument2, double longArgument3); If the declaration already starts at a large indentation level (e.g. in a class) and the function has many arguments, or if the names are all very long, then it may be a good idea to wrap at each argument to make the declaration more readable: class Foo { void aLongLongLongLongMethod(shared_ptr sharedFooInstance, shared_ptr myBarFactory, GenerationDir::Entry directoryEntry); * When defining functions outside class declarations, put the return type and any function attributes on a different line than the function name. Put the opening bracket on the same line as the function name. static __attribute__((visibility("hidden"))) void foo() { ... } void Group::onSessionClose() { ... } But don't do that if the function is part of a class declarations: class Foo { void foo() { ... } }; Other than the aforementioned rules, function definitions follow the same rules as function declarations. ### Ruby coding style The usual Ruby coding style applies. That is, 2 spaces for indenting. ### Systems programming fundamentals Large parts of Phusion Passenger are written in C++. You can find a free C++ tutorial at [cplusplus.com](http://www.cplusplus.com/doc/tutorial/). Phusion Passenger heavily utilizes POSIX, the API that is in use by all Unix systems. The POSIX API is heavily used for: * Filesystem operations. * Process management. * Sockets. A good and comprehensive, but rather large source for learning POSIX is the [POSIX Programmer's Guide](ftp://92.42.8.18/pub/doc/books/OReilly_-_POSIX_Programmers_Guide.pdf) by Donald A. Lewine. You can find smaller but less comprehensive documents all over the Internet. In particular, you will want to familiarize yourself with [fork-exec](http://en.wikipedia.org/wiki/Fork-exec), the standard process creation pattern in Unix. ### Further reading * [Coding Tips and Pitfalls](https://github.com/phusion/passenger/blob/master/doc/CodingTipsAndPitfalls.md) ### Git structure The **master** branch is the main development branch, containing the latest and greatest code that was tested and accepted for inclusion into passenger (usually merged in from loose development branches that are deleted afterwards). This branch may not be stable enough yet for production. Branches like **stable-4.0**, **stable-5.0** are production quality branches (split off from master) for major versions. Each production branch has tags for minor versions, whereby **tag x.0.1** represents the first production-ready version on a branch (there may be some release candidates before that). For example: branch stable-5.0, tagged 5.0.1 is the first release of the 5.0 line that is ready for production. In general we apply fixes to the respective stable branch and merge these into the master, so it is easiest if you submit pull requests to the stable branches (unless of course you are working with the unstable master). Conversely, new features always go to the master and are then cherrypicked from one or more branches.passenger-5.0.30/CONTRIBUTORS000644 000765 000024 00000002611 12233035540 016066 0ustar00honglistaff000000 000000 _why Adam Duke Alessandro Lenzen Alex Osborne Alex Tomlins Aman Gupta Andre Ferraz Andre Nathan Andrei Belov Andy Allan Benjamin Fleischer Bernd Ahlers Camden Narzt Chad Fowler Chris Walquist Christoffer Sawicki Clemens Gruber Cody Russell cyclotron3k Damien Le Berrigaud Dan Peterson Danial Pearce Daniel Knoppel (Phusion) Dave Parfitt David Keller David Sissitka Dirk Mueller Dmitry Galinsky Dylan Vaughn Eric Covener Erik Ogan Evan Phoenix Fedor Sumkin flygoast Gaspard Bucher Goffert van Gool (Phusion) Gokulnath Manakkattil Gregory Potamianos Hongli Lai (Phusion) Ian Ehlert Igor Vuk isaac Isaac Reuben J Smith J.W. Koelewijn Jacob Elder Jacob Harris James Miller Jan Berkel Jason Cannon jastix Jay Freeman (saurik) jbergler John Dewey John Leach Joshua Lund jpatterson Jude Nagurney Kenneth Powers Luuk Hendriks (Phusion) MAEDA Go Magnus Holm Michal Papis Michał Pokrywka Mike Mike Boone Morton Jonuschat Nathaniel Bibler Neil Wilson Ninh Bui (Phusion) Oleksiy Shchukin Pat Downey Paul B Paul Kmiec Pepijn Looije Perry Smith Philip M. Gollucci Radagaisus Redmar Kerkhoff remi Richard Michael Rob Paisley Robin Bowes Ruslan Ermilov (NGINX Inc) Ryan Schwartz Ryo Onodera Saimon Moore Sam Pohlenz Sean Wilkinson Sebastian Delmont Slippy Douglas Stephen Bannasch Steven Chamberlain Tim Bishop Tim Carey-Smith Tinco Andringa (Phusion) Tugdual de Kerviler W. Andrew Loe III Weyert de Boer Yichun Zhang 亀田 義裕 passenger-5.0.30/dev/000755 000765 000024 00000000000 12233035540 014764 5ustar00honglistaff000000 000000 passenger-5.0.30/doc/000755 000765 000024 00000000000 12233035540 014753 5ustar00honglistaff000000 000000 passenger-5.0.30/INSTALL.md000644 000765 000024 00000000533 12233035540 015637 0ustar00honglistaff000000 000000 # Installing Phusion Passenger Please read README.md for installation instructions. If you're having trouble installing Phusion Passenger, please refer to [the documentation](https://www.phusionpassenger.com/). Documentation and support resources are also available on [the website](https://www.phusionpassenger.com/documentation_and_support). passenger-5.0.30/LICENSE000644 000765 000024 00000002055 12233035540 015215 0ustar00honglistaff000000 000000 Copyright (c) 2010-2015 Phusion Holding B.V. 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, sublicense, 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 NONINFRINGEMENT. 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. passenger-5.0.30/man/000755 000765 000024 00000000000 12233035540 014761 5ustar00honglistaff000000 000000 passenger-5.0.30/npm-shrinkwrap.json000644 000765 000024 00000070572 12233035540 020074 0ustar00honglistaff000000 000000 { "name": "passenger", "version": "0.0.0", "dependencies": { "express": { "version": "3.21.2", "from": "express@^3.4.8", "resolved": "https://registry.npmjs.org/express/-/express-3.21.2.tgz", "dependencies": { "basic-auth": { "version": "1.0.3", "from": "basic-auth@~1.0.3", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-1.0.3.tgz" }, "connect": { "version": "2.30.2", "from": "connect@2.30.2", "resolved": "https://registry.npmjs.org/connect/-/connect-2.30.2.tgz", "dependencies": { "basic-auth-connect": { "version": "1.0.0", "from": "basic-auth-connect@1.0.0", "resolved": "https://registry.npmjs.org/basic-auth-connect/-/basic-auth-connect-1.0.0.tgz" }, "body-parser": { "version": "1.13.3", "from": "body-parser@~1.13.3", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.13.3.tgz", "dependencies": { "iconv-lite": { "version": "0.4.11", "from": "iconv-lite@0.4.11", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.11.tgz" }, "on-finished": { "version": "2.3.0", "from": "on-finished@~2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "dependencies": { "ee-first": { "version": "1.1.1", "from": "ee-first@1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" } } }, "raw-body": { "version": "2.1.4", "from": "raw-body@~2.1.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.1.4.tgz", "dependencies": { "iconv-lite": { "version": "0.4.12", "from": "iconv-lite@0.4.12", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.12.tgz" }, "unpipe": { "version": "1.0.0", "from": "unpipe@1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" } } } } }, "bytes": { "version": "2.1.0", "from": "bytes@2.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-2.1.0.tgz" }, "cookie-parser": { "version": "1.3.5", "from": "cookie-parser@~1.3.5", "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.3.5.tgz" }, "compression": { "version": "1.5.2", "from": "compression@~1.5.2", "resolved": "https://registry.npmjs.org/compression/-/compression-1.5.2.tgz", "dependencies": { "accepts": { "version": "1.2.13", "from": "accepts@~1.2.12", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.2.13.tgz", "dependencies": { "mime-types": { "version": "2.1.7", "from": "mime-types@~2.1.4", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.7.tgz", "dependencies": { "mime-db": { "version": "1.19.0", "from": "mime-db@~1.19.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.19.0.tgz" } } }, "negotiator": { "version": "0.5.3", "from": "negotiator@0.5.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.5.3.tgz" } } }, "compressible": { "version": "2.0.6", "from": "compressible@~2.0.5", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.6.tgz", "dependencies": { "mime-db": { "version": "1.20.0", "from": "mime-db@>= 1.19.0 < 2", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.20.0.tgz" } } } } }, "connect-timeout": { "version": "1.6.2", "from": "connect-timeout@~1.6.2", "resolved": "https://registry.npmjs.org/connect-timeout/-/connect-timeout-1.6.2.tgz", "dependencies": { "ms": { "version": "0.7.1", "from": "ms@0.7.1", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" } } }, "csurf": { "version": "1.8.3", "from": "csurf@~1.8.3", "resolved": "https://registry.npmjs.org/csurf/-/csurf-1.8.3.tgz", "dependencies": { "csrf": { "version": "3.0.0", "from": "csrf@~3.0.0", "resolved": "https://registry.npmjs.org/csrf/-/csrf-3.0.0.tgz", "dependencies": { "base64-url": { "version": "1.2.1", "from": "base64-url@1.2.1", "resolved": "https://registry.npmjs.org/base64-url/-/base64-url-1.2.1.tgz" }, "rndm": { "version": "1.1.1", "from": "rndm@~1.1.0", "resolved": "https://registry.npmjs.org/rndm/-/rndm-1.1.1.tgz" }, "scmp": { "version": "1.0.0", "from": "scmp@1.0.0", "resolved": "https://registry.npmjs.org/scmp/-/scmp-1.0.0.tgz" }, "uid-safe": { "version": "2.0.0", "from": "uid-safe@~2.0.0", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.0.0.tgz" } } } } }, "errorhandler": { "version": "1.4.2", "from": "errorhandler@~1.4.2", "resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.4.2.tgz", "dependencies": { "accepts": { "version": "1.2.13", "from": "accepts@~1.2.12", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.2.13.tgz", "dependencies": { "mime-types": { "version": "2.1.7", "from": "mime-types@~2.1.4", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.7.tgz", "dependencies": { "mime-db": { "version": "1.19.0", "from": "mime-db@~1.19.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.19.0.tgz" } } }, "negotiator": { "version": "0.5.3", "from": "negotiator@0.5.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.5.3.tgz" } } } } }, "express-session": { "version": "1.11.3", "from": "express-session@~1.11.3", "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.11.3.tgz", "dependencies": { "crc": { "version": "3.3.0", "from": "crc@3.3.0", "resolved": "https://registry.npmjs.org/crc/-/crc-3.3.0.tgz" }, "uid-safe": { "version": "2.0.0", "from": "uid-safe@~2.0.0", "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.0.0.tgz", "dependencies": { "base64-url": { "version": "1.2.1", "from": "base64-url@1.2.1", "resolved": "https://registry.npmjs.org/base64-url/-/base64-url-1.2.1.tgz" } } } } }, "finalhandler": { "version": "0.4.0", "from": "finalhandler@0.4.0", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.4.0.tgz", "dependencies": { "on-finished": { "version": "2.3.0", "from": "on-finished@~2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "dependencies": { "ee-first": { "version": "1.1.1", "from": "ee-first@1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" } } }, "unpipe": { "version": "1.0.0", "from": "unpipe@~1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" } } }, "http-errors": { "version": "1.3.1", "from": "http-errors@~1.3.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", "dependencies": { "inherits": { "version": "2.0.1", "from": "inherits@~2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" }, "statuses": { "version": "1.2.1", "from": "statuses@1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz" } } }, "method-override": { "version": "2.3.5", "from": "method-override@~2.3.5", "resolved": "https://registry.npmjs.org/method-override/-/method-override-2.3.5.tgz" }, "morgan": { "version": "1.6.1", "from": "morgan@~1.6.1", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.6.1.tgz", "dependencies": { "on-finished": { "version": "2.3.0", "from": "on-finished@~2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "dependencies": { "ee-first": { "version": "1.1.1", "from": "ee-first@1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" } } } } }, "multiparty": { "version": "3.3.2", "from": "multiparty@3.3.2", "resolved": "https://registry.npmjs.org/multiparty/-/multiparty-3.3.2.tgz", "dependencies": { "readable-stream": { "version": "1.1.13", "from": "readable-stream@~1.1.9", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.13.tgz", "dependencies": { "core-util-is": { "version": "1.0.1", "from": "core-util-is@~1.0.0", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.1.tgz" }, "isarray": { "version": "0.0.1", "from": "isarray@0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz" }, "string_decoder": { "version": "0.10.31", "from": "string_decoder@~0.10.x", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" }, "inherits": { "version": "2.0.1", "from": "inherits@~2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } }, "stream-counter": { "version": "0.2.0", "from": "stream-counter@~0.2.0", "resolved": "https://registry.npmjs.org/stream-counter/-/stream-counter-0.2.0.tgz" } } }, "on-headers": { "version": "1.0.1", "from": "on-headers@~1.0.0", "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz" }, "pause": { "version": "0.1.0", "from": "pause@0.1.0", "resolved": "https://registry.npmjs.org/pause/-/pause-0.1.0.tgz" }, "qs": { "version": "4.0.0", "from": "qs@4.0.0", "resolved": "https://registry.npmjs.org/qs/-/qs-4.0.0.tgz" }, "response-time": { "version": "2.3.1", "from": "response-time@~2.3.1", "resolved": "https://registry.npmjs.org/response-time/-/response-time-2.3.1.tgz" }, "serve-favicon": { "version": "2.3.0", "from": "serve-favicon@~2.3.0", "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.3.0.tgz", "dependencies": { "ms": { "version": "0.7.1", "from": "ms@0.7.1", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" } } }, "serve-index": { "version": "1.7.2", "from": "serve-index@~1.7.2", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.7.2.tgz", "dependencies": { "accepts": { "version": "1.2.13", "from": "accepts@~1.2.12", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.2.13.tgz", "dependencies": { "negotiator": { "version": "0.5.3", "from": "negotiator@0.5.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.5.3.tgz" } } }, "batch": { "version": "0.5.2", "from": "batch@0.5.2", "resolved": "https://registry.npmjs.org/batch/-/batch-0.5.2.tgz" }, "mime-types": { "version": "2.1.7", "from": "mime-types@~2.1.4", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.7.tgz", "dependencies": { "mime-db": { "version": "1.19.0", "from": "mime-db@~1.19.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.19.0.tgz" } } } } }, "serve-static": { "version": "1.10.0", "from": "serve-static@~1.10.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.10.0.tgz" }, "type-is": { "version": "1.6.9", "from": "type-is@~1.6.6", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.9.tgz", "dependencies": { "media-typer": { "version": "0.3.0", "from": "media-typer@0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz" }, "mime-types": { "version": "2.1.7", "from": "mime-types@~2.1.7", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.7.tgz", "dependencies": { "mime-db": { "version": "1.19.0", "from": "mime-db@~1.19.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.19.0.tgz" } } } } }, "vhost": { "version": "3.0.2", "from": "vhost@~3.0.1", "resolved": "https://registry.npmjs.org/vhost/-/vhost-3.0.2.tgz" } } }, "content-disposition": { "version": "0.5.0", "from": "content-disposition@0.5.0", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.0.tgz" }, "content-type": { "version": "1.0.1", "from": "content-type@~1.0.1", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.1.tgz" }, "commander": { "version": "2.6.0", "from": "commander@2.6.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.6.0.tgz" }, "cookie": { "version": "0.1.3", "from": "cookie@0.1.3", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.3.tgz" }, "cookie-signature": { "version": "1.0.6", "from": "cookie-signature@1.0.6", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz" }, "debug": { "version": "2.2.0", "from": "debug@~2.2.0", "resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz", "dependencies": { "ms": { "version": "0.7.1", "from": "ms@0.7.1", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" } } }, "depd": { "version": "1.0.1", "from": "depd@~1.0.1", "resolved": "https://registry.npmjs.org/depd/-/depd-1.0.1.tgz" }, "escape-html": { "version": "1.0.2", "from": "escape-html@1.0.2", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.2.tgz" }, "etag": { "version": "1.7.0", "from": "etag@~1.7.0", "resolved": "https://registry.npmjs.org/etag/-/etag-1.7.0.tgz" }, "fresh": { "version": "0.3.0", "from": "fresh@0.3.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.3.0.tgz" }, "merge-descriptors": { "version": "1.0.0", "from": "merge-descriptors@1.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.0.tgz" }, "methods": { "version": "1.1.1", "from": "methods@~1.1.1", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.1.tgz" }, "mkdirp": { "version": "0.5.1", "from": "mkdirp@0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "dependencies": { "minimist": { "version": "0.0.8", "from": "minimist@0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } }, "parseurl": { "version": "1.3.0", "from": "parseurl@~1.3.0", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.0.tgz" }, "proxy-addr": { "version": "1.0.8", "from": "proxy-addr@~1.0.8", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.8.tgz", "dependencies": { "forwarded": { "version": "0.1.0", "from": "forwarded@~0.1.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz" }, "ipaddr.js": { "version": "1.0.1", "from": "ipaddr.js@1.0.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.0.1.tgz" } } }, "range-parser": { "version": "1.0.3", "from": "range-parser@~1.0.2", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.3.tgz" }, "send": { "version": "0.13.0", "from": "send@0.13.0", "resolved": "https://registry.npmjs.org/send/-/send-0.13.0.tgz", "dependencies": { "destroy": { "version": "1.0.3", "from": "destroy@1.0.3", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.3.tgz" }, "http-errors": { "version": "1.3.1", "from": "http-errors@~1.3.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.3.1.tgz", "dependencies": { "inherits": { "version": "2.0.1", "from": "inherits@~2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } }, "mime": { "version": "1.3.4", "from": "mime@1.3.4", "resolved": "https://registry.npmjs.org/mime/-/mime-1.3.4.tgz" }, "ms": { "version": "0.7.1", "from": "ms@0.7.1", "resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz" }, "on-finished": { "version": "2.3.0", "from": "on-finished@~2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "dependencies": { "ee-first": { "version": "1.1.1", "from": "ee-first@1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" } } }, "statuses": { "version": "1.2.1", "from": "statuses@~1.2.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.2.1.tgz" } } }, "utils-merge": { "version": "1.0.0", "from": "utils-merge@1.0.0", "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz" }, "vary": { "version": "1.0.1", "from": "vary@~1.0.1", "resolved": "https://registry.npmjs.org/vary/-/vary-1.0.1.tgz" } } }, "mocha": { "version": "1.21.5", "from": "mocha@^1.15.1", "resolved": "https://registry.npmjs.org/mocha/-/mocha-1.21.5.tgz", "dependencies": { "commander": { "version": "2.3.0", "from": "commander@2.3.0", "resolved": "https://registry.npmjs.org/commander/-/commander-2.3.0.tgz" }, "debug": { "version": "2.0.0", "from": "debug@2.0.0", "resolved": "https://registry.npmjs.org/debug/-/debug-2.0.0.tgz", "dependencies": { "ms": { "version": "0.6.2", "from": "ms@0.6.2", "resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz" } } }, "diff": { "version": "1.0.8", "from": "diff@1.0.8", "resolved": "https://registry.npmjs.org/diff/-/diff-1.0.8.tgz" }, "escape-string-regexp": { "version": "1.0.2", "from": "escape-string-regexp@1.0.2", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz" }, "glob": { "version": "3.2.3", "from": "glob@3.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.3.tgz", "dependencies": { "minimatch": { "version": "0.2.14", "from": "minimatch@~0.2.11", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", "dependencies": { "lru-cache": { "version": "2.7.0", "from": "lru-cache@2", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.0.tgz" }, "sigmund": { "version": "1.0.1", "from": "sigmund@~1.0.0", "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz" } } }, "graceful-fs": { "version": "2.0.3", "from": "graceful-fs@~2.0.0", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-2.0.3.tgz" }, "inherits": { "version": "2.0.1", "from": "inherits@2", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } }, "growl": { "version": "1.8.1", "from": "growl@1.8.1", "resolved": "https://registry.npmjs.org/growl/-/growl-1.8.1.tgz" }, "jade": { "version": "0.26.3", "from": "jade@0.26.3", "resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz", "dependencies": { "commander": { "version": "0.6.1", "from": "commander@0.6.1", "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz" }, "mkdirp": { "version": "0.3.0", "from": "mkdirp@0.3.0", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz" } } }, "mkdirp": { "version": "0.5.0", "from": "mkdirp@0.5.0", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", "dependencies": { "minimist": { "version": "0.0.8", "from": "minimist@0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz" } } } } }, "should": { "version": "2.1.1", "from": "should@^2.0.1", "resolved": "https://registry.npmjs.org/should/-/should-2.1.1.tgz" }, "sinon": { "version": "1.17.2", "from": "sinon@^1.7.3", "resolved": "https://registry.npmjs.org/sinon/-/sinon-1.17.2.tgz", "dependencies": { "formatio": { "version": "1.1.1", "from": "formatio@1.1.1", "resolved": "https://registry.npmjs.org/formatio/-/formatio-1.1.1.tgz" }, "util": { "version": "0.10.3", "from": "util@>=0.10.3 <1", "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "dependencies": { "inherits": { "version": "2.0.1", "from": "inherits@2.0.1", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz" } } }, "lolex": { "version": "1.3.2", "from": "lolex@1.3.2", "resolved": "https://registry.npmjs.org/lolex/-/lolex-1.3.2.tgz" }, "samsam": { "version": "1.1.2", "from": "samsam@1.1.2", "resolved": "https://registry.npmjs.org/samsam/-/samsam-1.1.2.tgz" } } } } } passenger-5.0.30/package.json000644 000765 000024 00000001402 12233035540 016471 0ustar00honglistaff000000 000000 { "__comment__": [ "Indicate Passenger dependencies on Node modules here, and use it to regenerate npm-shrinkwrap.json via 'npm shrinkwrap'." ], "name": "passenger", "version": "0.0.0", "description": "A fast and robust web server and application server for Ruby, Python and Node.js", "main": "index.js", "dependencies": { "should": "^2.0.1", "sinon": "^1.7.3", "express": "^3.4.8", "mocha": "^1.15.1" }, "scripts": { "test": "rake test:node" }, "repository": { "type": "git", "url": "git://github.com/phusion/passenger.git" }, "author": "Phusion ", "license": "MIT", "bugs": { "url": "https://github.com/phusion/passenger/issues" }, "homepage": "https://www.phusionpassenger.com" } passenger-5.0.30/passenger.gemspec000644 000765 000024 00000002362 12233035540 017545 0ustar00honglistaff000000 000000 source_root = File.expand_path(File.dirname(__FILE__)) $LOAD_PATH.unshift("#{source_root}/src/ruby_supportlib") require 'phusion_passenger' PhusionPassenger.locate_directories PhusionPassenger.require_passenger_lib 'packaging' Gem::Specification.new do |s| s.platform = Gem::Platform::RUBY s.homepage = "https://www.phusionpassenger.com/" s.summary = "A fast and robust web server and application server for Ruby, Python and Node.js" s.name = PhusionPassenger::PACKAGE_NAME s.version = PhusionPassenger::VERSION_STRING s.rubyforge_project = "passenger" s.author = "Phusion - http://www.phusion.nl/" s.email = "software-signing@phusion.nl" s.require_paths = ["src/ruby_supportlib"] s.add_dependency 'rake', '>= 0.8.1' s.add_dependency 'rack' s.files = Dir[*PhusionPassenger::Packaging::GLOB] - Dir[*PhusionPassenger::Packaging::EXCLUDE_GLOB] s.executables = PhusionPassenger::Packaging::USER_EXECUTABLES + PhusionPassenger::Packaging::SUPER_USER_EXECUTABLES s.description = "A modern web server and application server for Ruby, Python and Node.js, " + "optimized for performance, low memory usage and ease of use." if ENV['OFFICIAL_RELEASE'] s.extensions = ["src/helper-scripts/download_binaries/extconf.rb"] end end passenger-5.0.30/Rakefile000644 000765 000024 00000004646 12233035540 015665 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (C) 2008-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. require 'pathname' SOURCE_ROOT = Pathname.new(File.expand_path(File.dirname(__FILE__))) $LOAD_PATH.unshift(SOURCE_ROOT.to_s) $LOAD_PATH.unshift("#{SOURCE_ROOT}/src/ruby_supportlib") # Clean Bundler environment variables, preserve Rake environment variables. # Otherwise all Ruby commands will take slightly longer to start, which messes up # timing-sensitive tests like those in the C++ test suite. if defined?(Bundler) clean_env = nil Bundler.with_clean_env do clean_env = ENV.to_hash end ENV.replace(clean_env) ARGV.each do |arg| if arg =~ /^(\w+)=(.*)$/m ENV[$1] = $2 end end end require("#{SOURCE_ROOT}/config") if File.exist?("#{SOURCE_ROOT}/config.rb") require 'build/basics' if boolean_option('ONLY_RUBY') require 'build/ruby_extension' else require 'build/ruby_extension' require 'build/common_library' require 'build/agent' require 'build/apache2' require 'build/nginx' require 'build/documentation' require 'build/packaging' require 'build/test_basics' require 'build/oxt_tests' require 'build/cxx_tests' require 'build/ruby_tests' require 'build/node_tests' require 'build/integration_tests' require 'build/misc' end #### Default tasks task :default do abort "Please type one of:\n" + " rake apache2\n" + " rake nginx" end desc "Remove compiled files" task :clean => 'clean:cache' do if OUTPUT_DIR == "buildout/" sh "rm -rf buildout" end end task 'common:clean' => 'clean:cache' task 'clean:cache' do sh "rm -rf #{OUTPUT_DIR}cache" end desc "Remove all generated files" task :clobber passenger-5.0.30/README.md000644 000765 000024 00000006034 12233035540 015470 0ustar00honglistaff000000 000000 # Phusion Passenger: a fast and robust web server and application server for Ruby, Python and Node.js [Phusion Passenger™](https://www.phusionpassenger.com/) is a web server and application server, designed to be fast, robust and lightweight. It takes a lot of complexity out of deploying web apps, adds powerful enterprise-grade features that are useful in production, and makes administration much easier and less complex. Phusion Passenger supports Ruby, Python, Node.js and Meteor, and is being used by high-profile companies such as **Apple, Pixar, New York Times, AirBnB, Juniper** etc as well as [over 350.000 websites](http://trends.builtwith.com/Web-Server/Phusion-Passenger). What makes it so fast and reliable is its **C++** core, its **zero-copy** architecture, its **watchdog** system and its **hybrid** evented, multi-threaded and multi-process design.
Phusion Passenger used in Game of Thrones Ascent **Learn more:** [Website](https://www.phusionpassenger.com/) | [Documentation](https://www.phusionpassenger.com/documentation_and_support) | [Support resources](https://www.phusionpassenger.com/documentation_and_support) | [Github](https://github.com/phusion/passenger) | [Twitter](https://twitter.com/phusion_nl) | [Blog](http://blog.phusion.nl/)
Phusion Passenger
## Installation Please follow [the installation instructions on the website](https://www.phusionpassenger.com/download). ### Installing the source directly from git If you mean to install the latest version of Passenger directly from this git repository, then you should run one of the following commands. Installing from the git repository is basically the same as the tarball installation method, as [described in the manual](https://www.phusionpassenger.com/library/install/), with one exception: you need to clone git submodules: git submodule update --init --recursive After that, run one of the following: ./bin/passenger-install-apache2-module -OR- ./bin/passenger-install-nginx-module -OR- # From your application directory ~/path-to-passenger/bin/passenger start For troubleshooting, configuration and tips, please also refer to the above documentation. For further support, please refer to [the Phusion Passenger support page](https://www.phusionpassenger.com/support). Ruby users can also build a gem from the Git repository and install the gem. gem build passenger.gemspec gem install passenger-x.x.x.gem ## Further reading * The `doc/` directory. * [Contributors Guide](https://github.com/phusion/passenger/blob/master/CONTRIBUTING.md) * https://www.phusionpassenger.com/support ## Legal "Passenger", "Phusion Passenger" and "Union Station" are registered trademarks of Phusion Holding B.V. passenger-5.0.30/resources/000755 000765 000024 00000000000 12233035540 016220 5ustar00honglistaff000000 000000 passenger-5.0.30/src/000755 000765 000024 00000000000 12233035540 014775 5ustar00honglistaff000000 000000 passenger-5.0.30/src/agent/000755 000765 000024 00000000000 12233035540 016073 5ustar00honglistaff000000 000000 passenger-5.0.30/src/apache2_module/000755 000765 000024 00000000000 12233035540 017645 5ustar00honglistaff000000 000000 passenger-5.0.30/src/cxx_supportlib/000755 000765 000024 00000000000 12233035540 020062 5ustar00honglistaff000000 000000 passenger-5.0.30/src/helper-scripts/000755 000765 000024 00000000000 12233035540 017741 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nginx_module/000755 000765 000024 00000000000 12233035540 017465 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/000755 000765 000024 00000000000 12233035540 020542 5ustar00honglistaff000000 000000 passenger-5.0.30/src/README.md000644 000765 000024 00000001143 12233035540 016253 0ustar00honglistaff000000 000000 This directory contains the Passenger main source code. There are source files written in various programming languages, including C++, Ruby and Javascript. Browse to a subdirectory for a README which explains that particular subdirectory. If you aren't familiar with the Passenger codebase then we encourage you to read these first: * [Contributors Guide](https://github.com/phusion/passenger/blob/master/CONTRIBUTING.md) * [Design & Architecture](https://www.phusionpassenger.com/documentation/Design%20and%20Architecture.html) * [Code walkthrough](http://vimeo.com/phusionnl/review/98027409/03ba678684) passenger-5.0.30/src/ruby_native_extension/000755 000765 000024 00000000000 12233035540 021420 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/000755 000765 000024 00000000000 12233035540 020241 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/000755 000765 000024 00000000000 12233035540 023775 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger.rb000644 000765 000024 00000026074 12233035540 024333 0ustar00honglistaff000000 000000 # encoding: utf-8 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module PhusionPassenger FILE_LOCATION = File.expand_path(__FILE__) ###### Names and version numbers ###### PACKAGE_NAME = 'passenger' # Run 'rake src/cxx_supportlib/Constants.h' after changing this number. VERSION_STRING = '5.0.30' PREFERRED_NGINX_VERSION = '1.10.1' NGINX_SHA256_CHECKSUM = '1fd35846566485e03c0e318989561c135c598323ff349c503a6c14826487a801' PREFERRED_PCRE_VERSION = '8.39' PCRE_SHA256_CHECKSUM = 'ccdf7e788769838f8285b3ee672ed573358202305ee361cfec7a4a4fb005bbc7' STANDALONE_INTERFACE_VERSION = 1 ###### Directories ###### GLOBAL_NAMESPACE_DIRNAME_ = "passenger" # Subdirectory under $HOME to use for storing stuff. USER_NAMESPACE_DIRNAME_ = ".passenger" # The name for the /etc/apache2/mods-available/*.{load,conf} file. APACHE2_MODULE_CONF_NAME = "passenger" # Directories in which to look for plugins. PLUGIN_DIRS = [ "/usr/share/#{GLOBAL_NAMESPACE_DIRNAME_}/plugins", "/usr/local/share/#{GLOBAL_NAMESPACE_DIRNAME_}/plugins", "~/#{USER_NAMESPACE_DIRNAME_}/plugins" ] REQUIRED_LOCATIONS_INI_FIELDS = [ # User-invoked commands :bin_dir, # Support binaries :support_binaries_dir, # Library files like libboost_oxt.a and various .o files :lib_dir, # Scripts not directly invoked by users :helper_scripts_dir, # Various non-executable resources :resources_dir, # C header files, necessary for compiling Nginx :include_dir, # Documentation :doc_dir, # Ruby support libraries :ruby_libdir, # Node.js support libraries :node_libdir, # Path to the compiled Apache module :apache2_module_path, # Directory containing the source code of our Ruby extension :ruby_extension_source_dir, # Directory containing the source code of our Nginx module :nginx_module_source_dir ].freeze OPTIONAL_LOCATIONS_INI_FIELDS = [ # Directory which contains the main Phusion Passenger Rakefile. Only # available when originally packaged, :build_system_dir, # Directory in which downloaded Phusion Passenger binaries are cached. :download_cache_dir ].freeze # Follows the logic of src/cxx_supportlib/ResourceLocator.h, so don't forget to modify that too. def self.locate_directories(install_spec = nil) @install_spec = install_spec || infer_install_spec if @install_spec && File.file?(@install_spec) filename = @install_spec options = parse_ini_file(filename) @custom_packaged = true @packaging_method = get_option(filename, options, 'packaging_method') REQUIRED_LOCATIONS_INI_FIELDS.each do |field| value = get_option(filename, options, field.to_s) value.freeze unless value.nil? instance_variable_set("@#{field}", value) end OPTIONAL_LOCATIONS_INI_FIELDS.each do |field| value = get_option(filename, options, field.to_s, false) value.freeze unless value.nil? instance_variable_set("@#{field}", value) end else source_root = File.dirname(File.dirname(File.dirname(FILE_LOCATION))) @install_spec = source_root @custom_packaged = false @bin_dir = "#{source_root}/bin".freeze @support_binaries_dir = "#{source_root}/buildout/support-binaries".freeze @lib_dir = "#{source_root}/buildout".freeze @helper_scripts_dir = "#{source_root}/src/helper-scripts".freeze @resources_dir = "#{source_root}/resources".freeze @include_dir = "#{source_root}/src".freeze @doc_dir = "#{source_root}/doc".freeze @ruby_libdir = File.dirname(FILE_LOCATION).freeze @node_libdir = "#{source_root}/src/nodejs_supportlib".freeze @apache2_module_path = "#{source_root}/buildout/apache2/mod_passenger.so".freeze @ruby_extension_source_dir = "#{source_root}/src/ruby_native_extension".freeze @nginx_module_source_dir = "#{source_root}/src/nginx_module".freeze @download_cache_dir = "#{source_root}/download_cache".freeze @build_system_dir = source_root.dup.freeze REQUIRED_LOCATIONS_INI_FIELDS.each do |field| if instance_variable_get("@#{field}").nil? raise "BUG: @#{field} not set" end end end end # Returns whether this Phusion Passenger installation is in the 'originally packaged' # configuration (as opposed to the 'custom packaged' configuration. def self.originally_packaged? return !@custom_packaged end def self.custom_packaged? return @custom_packaged end # If Phusion Passenger is custom packaged, returns which packaging # method was used. Can be 'deb', 'rpm', 'homebrew', 'test' # or 'unknown'. def self.packaging_method return @packaging_method end def self.packaging_method_description case packaging_method when "deb" "Debian packages" when "rpm" "RPM packages" when "homebrew" "Homebrew" else "gem or tarball" end end # Whether the current Phusion Passenger installation is installed # from a release package, e.g. an official gem or official tarball. # Retruns false if e.g. the gem was built by the user, or if this # install is from a git repository. def self.installed_from_release_package? File.exist?("#{resources_dir}/release.txt") end # The installation specification string, as passed to #locate_directories. def self.install_spec return @install_spec end # Generate getters for the directory types in locations.ini. getters_code = "" (REQUIRED_LOCATIONS_INI_FIELDS + OPTIONAL_LOCATIONS_INI_FIELDS).each do |field| getters_code << %Q{ def self.#{field} return @#{field} end } end eval(getters_code, binding, __FILE__, __LINE__) def self.user_support_binaries_dir return "#{home_dir}/#{USER_NAMESPACE_DIRNAME_}/support-binaries/#{VERSION_STRING}" end def self.find_support_binary(name) all_support_binary_dirs = [ support_binaries_dir, user_support_binaries_dir ] all_support_binary_dirs.each do |dir| result = "#{dir}/#{name}" if File.exist?(result) return result end end return nil end ###### Other resource locations ###### def self.binaries_sites [ { :url => "https://oss-binaries.phusionpassenger.com/binaries/passenger/by_release".freeze }, { :url => "https://s3.amazonaws.com/phusion-passenger/binaries/passenger/by_release".freeze } ] end # Instead of calling `require 'phusion_passenger/foo'`, you should call # `PhusionPassenger.require_passenger_lib 'foo'`. This is because when Phusion # Passenger is natively packaged, it may still be run with arbitrary Ruby # interpreters. Adding ruby_libdir to $LOAD_PATH is then dangerous because ruby_libdir # may be the distribution's Ruby's vendor_ruby directory, which may be incompatible # with the active Ruby interpreter. This method looks up the exact filename directly. # # Using this method also has two more advantages: # # 1. It is immune to Bundler's load path mangling code. # 2. It is faster than plan require() because it doesn't need to # scan the entire load path. def self.require_passenger_lib(name) require("#{ruby_libdir}/phusion_passenger/#{name}") end # Forward define public_api.rb methods for code that tries to hook # Passenger events before an app is spawned (such as mongoid) def self.on_event(name, &block) # The definition in `public_api.rb` will override this implementation when # a Ruby application is spawned. nil end def self.call_event(name, *args) # The definition in `public_api.rb` will override this implementation when # a Ruby application is spawned. nil end private def self.infer_install_spec filename = ENV['PASSENGER_LOCATION_CONFIGURATION_FILE'] return filename if filename && !filename.empty? filename = File.dirname(FILE_LOCATION) + "/phusion_passenger/locations.ini" return filename if filename && File.exist?(filename) require 'etc' if !defined?(Etc) begin home_dir = Etc.getpwuid(Process.uid).dir rescue ArgumentError # Unknown user. home_dir = ENV['HOME'] end if home_dir && !home_dir.empty? filename = "#{home_dir}/#{USER_NAMESPACE_DIRNAME_}/locations.ini" return filename if File.exist?(filename) end filename = "/etc/#{GLOBAL_NAMESPACE_DIRNAME_}/locations.ini" return filename if File.exist?(filename) return nil end def self.parse_ini_file(filename) options = {} in_locations_section = false File.open(filename, 'r') do |f| while !f.eof? line = f.readline line.strip! next if line.empty? if line =~ /\A\[(.+)\]\Z/ in_locations_section = $1 == 'locations' elsif in_locations_section && line =~ /=/ key, value = line.split(/ *= */, 2) options[key.freeze] = value.freeze end end end return options end def self.get_option(filename, options, key, required = true) value = options[key] if value return value elsif required raise "Option '#{key}' missing in file '#{filename}'" else return nil end end def self.get_bool_option(filename, options, key) value = get_option(filename, options, key) return value == 'yes' || value == 'true' || value == 'on' || value == '1' end # The HOME environment variable is often unreliable, because for # example `sudo` preserves it. That's why we don't respect it by # default. def self.home_dir(respect_home_env = false) if respect_home_env home = ENV['HOME'].to_s end if home.nil? || home.empty? require 'etc' if !defined?(Etc) home = Etc.getpwuid(Process.uid).dir end return home end end if !defined?(PhusionPassenger::VERSION_STRING) passenger-5.0.30/src/ruby_supportlib/phusion_passenger/abstract_installer.rb000644 000765 000024 00000032343 12233035540 030207 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'console_text_template' PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/operating_system' PhusionPassenger.require_passenger_lib 'utils/ansi_colors' PhusionPassenger.require_passenger_lib 'utils/download' require 'fileutils' require 'logger' require 'etc' # IMPORTANT: do not directly or indirectly require native_support; we can't compile # it yet until we have a compiler, and installers usually check whether a compiler # is installed. module PhusionPassenger # Abstract base class for text mode installers. Used by # passenger-install-apache2-module and passenger-install-nginx-module. # # Subclasses must at least implement the #run_steps method which handles # the installation itself. # # Usage: # # installer = ConcereteInstallerClass.new(options...) # installer.run class AbstractInstaller PASSENGER_WEBSITE = "https://www.phusionpassenger.com" PASSENGER_LIBRARY_URL = "https://www.phusionpassenger.com/library/" PHUSION_WEBSITE = "www.phusion.nl" # Create an AbstractInstaller. All options will be stored as instance # variables, for example: # # installer = AbstractInstaller.new(:foo => "bar") # installer.instance_variable_get(:"@foo") # => "bar" def initialize(options = {}) @stdout = STDOUT @stderr = STDERR @auto = !STDIN.tty? @colors = Utils::AnsiColors.new(options[:colorize] || :auto) options.each_pair do |key, value| instance_variable_set(:"@#{key}", value) end end # Start the installation by calling the #install! method. def run before_install run_steps return true rescue Abort puts return false rescue SignalException, SystemExit raise rescue PlatformInfo::RuntimeError => e new_screen puts "An error occurred" puts puts e.message exit 1 rescue Exception => e show_support_options_for_installer_bug(e) exit 2 ensure after_install end protected class Abort < StandardError end class CommandError < Abort end def interactive? return !@auto end def non_interactive? return !interactive? end def before_install if STDOUT.respond_to?(:set_encoding) STDOUT.set_encoding("UTF-8") end STDOUT.write(@colors.default_terminal_color) STDOUT.flush end def after_install STDOUT.write(@colors.reset) STDOUT.flush end def install_doc_url "https://www.phusionpassenger.com/library/install/" end def troubleshooting_doc_url "https://www.phusionpassenger.com/library/admin/troubleshooting/" end def dependencies return [[], []] end def check_dependencies(show_new_screen = true) new_screen if show_new_screen puts "Checking for required software..." puts PhusionPassenger.require_passenger_lib 'platform_info/depcheck' specs, ids = dependencies runner = PlatformInfo::Depcheck::ConsoleRunner.new specs.each do |spec| PlatformInfo::Depcheck.load(spec) end ids.each do |id| runner.add(id) end if runner.check_all return true else puts puts "Some required software is not installed." puts "But don't worry, this installer will tell you how to install them.\n" puts "Press Enter to continue, or Ctrl-C to abort." if PhusionPassenger.originally_packaged? wait else wait(10) end line puts puts "Installation instructions for required software" puts runner.missing_dependencies.each do |dep| puts " * To install #{dep.name}:" puts " #{dep.install_instructions}" puts end puts "If the aforementioned instructions didn't solve your problem, then please take" puts "a look at our documentation for troubleshooting tips:" puts puts " #{install_doc_url}" puts " #{troubleshooting_doc_url}" return false end end def check_whether_os_is_broken # No known broken OSes at the moment. end def check_gem_install_permission_problems return true if PhusionPassenger.custom_packaged? begin require 'rubygems' rescue LoadError return true end if Process.uid != 0 && PhusionPassenger.build_system_dir =~ /^#{Regexp.escape home_dir}\// && PhusionPassenger.build_system_dir =~ /^#{Regexp.escape Gem.dir}\// && File.stat(PhusionPassenger.build_system_dir).uid == 0 new_screen render_template 'installer_common/gem_install_permission_problems' return false else return true end end def check_directory_accessible_by_web_server return true if PhusionPassenger.custom_packaged? inaccessible_directories = [] list_parent_directories(PhusionPassenger.build_system_dir).each do |path| if !world_executable?(path) inaccessible_directories << path end end if !inaccessible_directories.empty? new_screen render_template 'installer_common/world_inaccessible_directories', :directories => inaccessible_directories wait end end def check_whether_system_has_enough_ram(required = 1024) begin meminfo = File.read("/proc/meminfo") if meminfo =~ /^MemTotal: *(\d+) kB$/ ram_mb = $1.to_i / 1024 if meminfo =~ /^SwapTotal: *(\d+) kB$/ swap_mb = $1.to_i / 1024 else swap_mb = 0 end end rescue Errno::ENOENT, Errno::EACCES # Don't do anything on systems without memory information. ram_mb = nil swap_mb = nil end if ram_mb && swap_mb && ram_mb + swap_mb < required new_screen render_template 'installer_common/low_amount_of_memory_warning', :required => required, :current => ram_mb + swap_mb, :ram => ram_mb, :swap => swap_mb, :install_doc_url => install_doc_url wait end end def show_support_options_for_installer_bug(e) # We do not use template rendering here. Since we've determined that there's # a bug, *anything* may be broken, so we use the safest codepath to ensure that # the user sees the proper messages. begin line @stderr.puts "*** EXCEPTION: #{e} (#{e.class})\n " + e.backtrace.join("\n ") new_screen puts 'Oops, something went wrong :-(' puts puts "We're sorry, but it looks like this installer ran into an unexpected problem.\n" + "Please visit the following website for support. We'll do our best to help you.\n\n" + " #{SUPPORT_URL}\n\n" + "When submitting a support inquiry, please copy and paste the entire installer\n" + "output." rescue Exception => e2 # Raise original exception so that it doesn't get lost. raise e end end def use_stderr old_stdout = @stdout begin @stdout = @stderr yield ensure @stdout = old_stdout end end def print(text) @stdout.write(@colors.ansi_colorize(text)) @stdout.flush end def puts(text = nil) if text @stdout.puts(@colors.ansi_colorize(text.to_s)) else @stdout.puts end @stdout.flush end def puts_error(text) @stderr.puts(@colors.ansi_colorize("#{text}")) @stderr.flush end def render_template(name, options = {}) options.merge!(:colors => @colors) puts ConsoleTextTemplate.new({ :file => name }, options).result end def new_screen puts line puts end def line puts "--------------------------------------------" end def prompt(message, default_value = nil) done = false while !done print "#{message}: " if non_interactive? && default_value puts default_value return default_value end begin result = STDIN.readline rescue EOFError exit 2 end result.strip! if result.empty? if default_value result = default_value done = true else done = !block_given? || yield(result) end else done = !block_given? || yield(result) end end return result rescue Interrupt raise Abort end def prompt_confirmation(message) result = prompt("#{message} [y/n]") do |value| if value.downcase == 'y' || value.downcase == 'n' true else puts_error "Invalid input '#{value}'; please enter either 'y' or 'n'." false end end return result.downcase == 'y' rescue Interrupt raise Abort end def prompt_confirmation_with_default(message, default) if default default_str = "[Y/n]" else default_str = "[y/N]" end result = prompt("#{message} #{default_str}") do |value| if value.downcase == 'y' || value.downcase == 'n' true elsif value.empty? true else puts_error "Invalid input '#{value}'; please enter either 'y' or 'n'." false end end if result.empty? return default else return result.downcase == 'y' end rescue Interrupt raise Abort end def wait(timeout = nil) if interactive? if timeout require 'timeout' unless defined?(Timeout) begin Timeout.timeout(timeout) do STDIN.readline end rescue Timeout::Error # Do nothing. end else STDIN.readline end end rescue Interrupt raise Abort end def home_dir return PhusionPassenger.home_dir end def sh(*args) puts "# #{args.join(' ')}" result = system(*args) if result return true elsif $?.signaled? && $?.termsig == Signal.list["INT"] raise Interrupt else return false end end def sh!(*args) if !sh(*args) puts_error "*** Command failed: #{args.join(' ')}" raise CommandError end end def rake(*args) PhusionPassenger.require_passenger_lib 'platform_info/ruby' if !PlatformInfo.rake_command puts_error 'Cannot find Rake.' raise Abort end sh("#{PlatformInfo.rake_command} #{args.join(' ')}") end def rake!(*args) PhusionPassenger.require_passenger_lib 'platform_info/ruby' if !PlatformInfo.rake_command puts_error 'Cannot find Rake.' raise Abort end sh!("#{PlatformInfo.rake_command} #{args.join(' ')}") end def download(url, output, options = {}) options[:logger] ||= begin logger = Logger.new(STDOUT) logger.level = Logger::WARN logger.formatter = proc { |severity, datetime, progname, msg| "*** #{msg}\n" } logger end return PhusionPassenger::Utils::Download.download(url, output, options) end def list_parent_directories(dir) dirs = [] components = File.expand_path(dir).split(File::SEPARATOR) components.shift # Remove leading / components.size.times do |i| dirs << File::SEPARATOR + components[0 .. i].join(File::SEPARATOR) end return dirs.reverse end def world_executable?(dir) begin stat = File.stat(dir) rescue Errno::EACCESS return false end return stat.mode & 0000001 != 0 end end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/admin_tools/000755 000765 000024 00000000000 12233035540 026305 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/admin_tools.rb000644 000765 000024 00000003437 12233035540 026641 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module PhusionPassenger module AdminTools def self.tmpdir ["PASSENGER_INSTANCE_REGISTRY_DIR", "TMPDIR"].each do |name| if ENV.has_key?(name) && !ENV[name].empty? return ENV[name] end end return "/tmp" end def self.process_is_alive?(pid) begin Process.kill(0, pid) return true rescue Errno::ESRCH return false rescue SystemCallError => e return true end end end # module AdminTools end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/apache2/000755 000765 000024 00000000000 12233035540 025300 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/common_library.rb000644 000765 000024 00000023770 12233035540 027347 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2012-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. # This file lists all the Phusion Passenger C++ support library files and # contains code for calculating how to compile and how to link them into # executables. It's used by the build system (build/*.rb) and by # src/ruby_supportlib/phusion_passenger/config/nginx_engine_compiler.rb class CommonLibraryBuilder include Rake::DSL if defined?(Rake::DSL) attr_reader :all_components, :selected_components, :output_dir def initialize(&block) @all_components = {} @all_ordered_components = [] @selected_components = {} @namespace = "common" if defined?(COMMON_OUTPUT_DIR) @output_dir = COMMON_OUTPUT_DIR + "libpassenger_common" else @output_dir = "." end instance_eval(&block) if block end def initialize_copy(other) [:all_components, :all_ordered_components, :selected_components, :namespace, :output_dir].each do |name| var_name = "@#{name}" instance_variable_set(var_name, other.instance_variable_get(var_name).dup) end end def define_component(object_name, options) @all_components[object_name] = options @all_ordered_components << object_name @selected_components[object_name] = options end def only(*selector) dup.send(:only!, *selector) end def exclude(*selector) dup.send(:exclude!, *selector) end def set_namespace(namespace) dup.send(:set_namespace!, namespace) end def set_output_dir(dir) dup.send(:set_output_dir!, dir) end def link_objects result = [] selected_categories.each do |category| object_names = selected_objects_belonging_to_category(category) result.concat(object_filenames_for(object_names)) end result end def link_objects_as_string link_objects.join(' ') end def enable_optimizations!(lto = false) @default_optimization_level = "-O" if lto @default_extra_optimization_flags = "-flto" end end def define_tasks(extra_compiler_flags = nil) group_all_components_by_category.each_pair do |category, object_names| define_category_tasks(category, object_names, extra_compiler_flags) end task("#{@namespace}:clean") do sh "rm -rf #{@output_dir}" end self end private def define_category_tasks(category, object_names, extra_compiler_flags) object_names.each do |object_name| define_object_compilation_task(object_name, extra_compiler_flags) end object_filenames = object_filenames_for(object_names) task "#{@namespace}:clean" do sh "rm -f #{object_filenames.join(' ')}" end end def define_object_compilation_task(object_name, extra_compiler_flags) options = @all_components[object_name] source_file = locate_source_file(options[:source]) object_file = "#{@output_dir}/#{object_name}" case options[:optimize] when :light optimization_level = "-O" when true, :heavy optimization_level = "-O2" when :very_heavy optimization_level = "-O3" when nil optimization_level = @default_optimization_level else raise "Unknown optimization level #{options[:optimize]}" end optimize = "#{optimization_level} #{@default_extra_optimization_flags}".strip if options[:strict_aliasing] == false # and not nil optimize = "#{optimize} -fno-strict-aliasing" # Disable link-time optimization so that we can no-strict-aliasing # works: http://stackoverflow.com/a/25765338/20816 optimize.sub!(/-flto/, "") end define_c_or_cxx_object_compilation_task( object_file, source_file, :include_paths => CXX_SUPPORTLIB_INCLUDE_PATHS, :flags => [ LIBEV_CFLAGS, LIBUV_CFLAGS, optimize, extra_compiler_flags ] ) end def set_namespace!(namespace) @namespace = namespace return self end def set_output_dir!(dir) @output_dir = dir return self end def only!(*selector) new_components = apply_selector(*selector) @selected_components = new_components return self end def exclude!(*selector) apply_selector(*selector).each_key do |object_name| @selected_components.delete(object_name) end return self end def apply_selector(*selector) result = {} selector = [selector].flatten selector.each do |condition| @selected_components.each do |object_name, options| if component_satisfies_condition?(object_name, options, condition) result[object_name] = options end end end return result end def component_satisfies_condition?(object_name, options, condition) case condition when Symbol return condition == :all || options[:category] == condition when String return object_name == condition else raise ArgumentError, "Invalid condition #{condition.inspect}" end end def selected_categories categories = {} @selected_components.each_value do |options| categories[options[:category]] = true end return categories.keys end def category_complete?(category) expected = 0 actual = 0 @all_components.each_value do |options| if options[:category] == category expected += 1 end end @selected_components.each_value do |options| if options[:category] == category actual += 1 end end return expected == actual end def selected_objects_belonging_to_category(category) result = [] @selected_components.each_pair do |object_name, options| if options[:category] == category result << object_name end end return result end def object_filenames_for(object_names) return object_names.map { |name| "#{@output_dir}/#{name}" } end def group_all_components_by_category categories = {} @all_ordered_components.each do |object_name| options = @all_components[object_name] category = options[:category] categories[category] ||= [] categories[category] << object_name end return categories end def locate_source_file(path) "src/cxx_supportlib/#{path}" end end COMMON_LIBRARY = CommonLibraryBuilder.new do define_component 'Logging.o', :source => 'Logging.cpp', :category => :base, :optimize => :light define_component 'Exceptions.o', :source => 'Exceptions.cpp', :category => :base define_component 'Utils/SystemTime.o', :source => 'Utils/SystemTime.cpp', :category => :base define_component 'Utils/StrIntUtils.o', :source => 'Utils/StrIntUtils.cpp', :category => :base, :optimize => :very_heavy define_component 'Utils/StrIntUtilsNoStrictAliasing.o', :source => 'Utils/StrIntUtilsNoStrictAliasing.cpp', :category => :base, # Compiling with -O3 causes segfaults on RHEL 6 :optimize => :heavy, :strict_aliasing => false define_component 'Utils/IOUtils.o', :source => 'Utils/IOUtils.cpp', :optimize => :light, :category => :base define_component 'Utils.o', :source => 'Utils.cpp', :category => :base define_component 'Utils/CachedFileStat.o', :source => 'Utils/CachedFileStat.cpp', :category => :other define_component 'Utils/LargeFiles.o', :source => 'Utils/LargeFiles.cpp', :category => :other define_component 'WatchdogLauncher.o', :source => 'WatchdogLauncher.cpp', :category => :other define_component 'MemoryKit/mbuf.o', :source => 'MemoryKit/mbuf.cpp', :category => :other, :optimize => true define_component 'MemoryKit/palloc.o', :source => 'MemoryKit/palloc.cpp', :category => :other, :optimize => true define_component 'ServerKit/http_parser.o', :source => 'ServerKit/http_parser.cpp', :category => :other, :optimize => :very_heavy define_component 'ServerKit/Implementation.o', :source => 'ServerKit/Implementation.cpp', :category => :other, :optimize => true define_component 'DataStructures/LString.o', :source => 'DataStructures/LString.cpp', :category => :other define_component 'Utils/Hasher.o', :source => 'Utils/Hasher.cpp', :category => :other, :optimize => :very_heavy define_component 'AppTypes.o', :source => 'AppTypes.cpp', :category => :other define_component 'jsoncpp.o', :source => 'vendor-modified/jsoncpp/jsoncpp.cpp', :category => :json, :optimize => true define_component 'vendor-modified/modp_b64.o', :source => 'vendor-modified/modp_b64.cpp', :category => :bas64, :optimize => true define_component 'UnionStationFilterSupport.o', :source => 'UnionStationFilterSupport.cpp', :category => :union_station_filter end # A subset of the objects are linked to the Nginx binary. This defines # what those objects are. NGINX_LIBS_SELECTOR = [:base, 'WatchdogLauncher.o', 'AppTypes.o', 'Utils/CachedFileStat.o', 'UnionStationFilterSupport.o'] passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/000755 000765 000024 00000000000 12233035540 025242 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/console_text_template.rb000644 000765 000024 00000004163 12233035540 030727 0ustar00honglistaff000000 000000 # encoding: utf-8 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'erb' PhusionPassenger.require_passenger_lib 'utils/ansi_colors' module PhusionPassenger class ConsoleTextTemplate def initialize(input, options = {}) @buffer = '' if input[:file] filename = "#{PhusionPassenger.resources_dir}/templates/#{input[:file]}.txt.erb" data = File.read(filename) else data = input[:text] end @colors = options[:colors] || AnsiColors.new @template = ERB.new(@colors.ansi_colorize(data), nil, '-', '@buffer') @template.filename = filename if filename options.each_pair do |name, value| self[name] = value end end def []=(name, value) instance_variable_set("@#{name}".to_sym, value) return self end def result return @template.result(binding) end end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/constants.rb000644 000765 000024 00000013056 12233035540 026343 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module PhusionPassenger PASSENGER_TXN_ID = "PASSENGER_TXN_ID".freeze PASSENGER_APP_GROUP_NAME = "PASSENGER_APP_GROUP_NAME".freeze PASSENGER_DELTA_MONOTONIC = "PASSENGER_DELTA_MONOTONIC".freeze RACK_HIJACK_IO = "rack.hijack_io".freeze LVL_CRIT = 0 LVL_ERROR = 1 LVL_WARN = 2 LVL_NOTICE = 3 LVL_INFO = 4 LVL_DEBUG = 5 LVL_DEBUG2 = 6 LVL_DEBUG3 = 7 # Constants shared between the C++ and Ruby codebase. The C++ Constants.h # is automatically generated by the build system from the following # definitions. module SharedConstants # Default config values DEFAULT_LOG_LEVEL = 3 DEFAULT_INTEGRATION_MODE = "standalone" DEFAULT_SOCKET_BACKLOG = 2048 DEFAULT_RUBY = "ruby" DEFAULT_PYTHON = "python" DEFAULT_NODEJS = "node" DEFAULT_MAX_POOL_SIZE = 6 DEFAULT_POOL_IDLE_TIME = 300 DEFAULT_MAX_PRELOADER_IDLE_TIME = 5 * 60 DEFAULT_START_TIMEOUT = 90_000 DEFAULT_WEB_APP_USER = "nobody" DEFAULT_APP_ENV = "production" DEFAULT_SPAWN_METHOD = "smart" # Apache's unixd.h also defines DEFAULT_USER, so we avoid naming clash here. PASSENGER_DEFAULT_USER = "nobody" DEFAULT_CONCURRENCY_MODEL = "process" DEFAULT_STICKY_SESSIONS_COOKIE_NAME = "_passenger_route" DEFAULT_APP_THREAD_COUNT = 1 DEFAULT_RESPONSE_BUFFER_HIGH_WATERMARK = 1024 * 1024 * 128 DEFAULT_MAX_REQUEST_QUEUE_SIZE = 100 DEFAULT_STAT_THROTTLE_RATE = 10 DEFAULT_ANALYTICS_LOG_USER = DEFAULT_WEB_APP_USER DEFAULT_ANALYTICS_LOG_GROUP = "" DEFAULT_ANALYTICS_LOG_PERMISSIONS = "u=rwx,g=rx,o=rx" DEFAULT_UNION_STATION_GATEWAY_ADDRESS = "gateway.unionstationapp.com" DEFAULT_UNION_STATION_GATEWAY_PORT = 443 DEFAULT_HTTP_SERVER_LISTEN_ADDRESS = "tcp://127.0.0.1:3000" DEFAULT_UST_ROUTER_LISTEN_ADDRESS = "tcp://127.0.0.1:9344" DEFAULT_LVE_MIN_UID = 500 # Size limits MESSAGE_SERVER_MAX_USERNAME_SIZE = 100 MESSAGE_SERVER_MAX_PASSWORD_SIZE = 100 POOL_HELPER_THREAD_STACK_SIZE = 1024 * 256 # Small mbuf sizes avoid memory overhead (up to 1 blocksize per request), but # also introduce context switching and smaller transfer writes. The size is picked # to balance this out. DEFAULT_MBUF_CHUNK_SIZE = 1024 * 4 # Affects input and output buffering (between app and client). Threshold is picked # such that it fits most output (i.e. html page size, not assets), and allows for # high concurrency with low mem overhead. On the upload side there is a penalty # but there's no real average upload size anyway so we choose mem safety instead. DEFAULT_FILE_BUFFERED_CHANNEL_THRESHOLD = 1024 * 128 SERVER_KIT_MAX_SERVER_ENDPOINTS = 4 # Time limits PROCESS_SHUTDOWN_TIMEOUT = 60 # In seconds PROCESS_SHUTDOWN_TIMEOUT_DISPLAY = "1 minute" # Versions PASSENGER_VERSION = PhusionPassenger::VERSION_STRING PASSENGER_API_VERSION_MAJOR = 0 PASSENGER_API_VERSION_MINOR = 3 PASSENGER_API_VERSION = "#{PASSENGER_API_VERSION_MAJOR}.#{PASSENGER_API_VERSION_MINOR}" SERVER_INSTANCE_DIR_STRUCTURE_MAJOR_VERSION = 3 SERVER_INSTANCE_DIR_STRUCTURE_MINOR_VERSION = 0 SERVER_INSTANCE_DIR_STRUCTURE_MIN_SUPPORTED_MINOR_VERSION = 0 # Misc FEEDBACK_FD = 3 PROGRAM_NAME = "Phusion Passenger" SHORT_PROGRAM_NAME = "Passenger" SERVER_TOKEN_NAME = "Phusion_Passenger" FLYING_PASSENGER_NAME = "Flying Passenger" SUPPORT_URL = "https://www.phusionpassenger.com/documentation_and_support" ENTERPRISE_URL = "https://www.phusionpassenger.com/enterprise" GLOBAL_NAMESPACE_DIRNAME = PhusionPassenger::GLOBAL_NAMESPACE_DIRNAME_ # Subdirectory under $HOME to use for storing stuff. USER_NAMESPACE_DIRNAME = PhusionPassenger::USER_NAMESPACE_DIRNAME_ AGENT_EXE = "PassengerAgent" DEB_MAIN_PACKAGE = "passenger" DEB_DEV_PACKAGE = "passenger-dev" DEB_APACHE_MODULE_PACKAGE = "libapache2-mod-passenger" DEB_NGINX_PACKAGE = "nginx-extras" RPM_MAIN_PACKAGE = "passenger" RPM_DEV_PACKAGE = "passenger-devel" RPM_APACHE_MODULE_PACKAGE = "mod_passenger" RPM_NGINX_PACKAGE = "nginx" end SharedConstants.constants.each do |name| const_set(name, SharedConstants.const_get(name)) unless const_defined? name end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/debug_logging.rb000644 000765 000024 00000007542 12233035540 027126 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2013 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'constants' module PhusionPassenger module DebugLogging # We don't refer to STDERR directly because STDERR's reference might # change during runtime. @@log_level = DEFAULT_LOG_LEVEL @@log_device = nil @@log_filename = nil @@stderr_evaluator = lambda { STDERR } def self.included(klass) klass.class_eval do private :debug private :trace end end def self.log_level return @@log_level end def self.log_level=(level) @@log_level = level end def self.log_file=(filename) if filename && filename.empty? @@log_filename = nil else @@log_filename = filename end @@log_device.close if @@log_device && !@@log_device.closed? @@log_device = nil end def self._log_device return @@log_device end def self.stderr_evaluator=(block) if block @@stderr_evaluator = block else @@stderr_evaluator = lambda { STDERR } end end def error(message) _output(1, message, 1) end module_function :error def warn(message) _output(2, message, 1) end module_function :warn def notice(message) _output(3, message, 1) end module_function :notice def info(message) _output(4, message, 1) end module_function :info def debug(message) _output(5, message, 1) end module_function :debug def trace(level, message) _output(4 + level, message, 1) end module_function :trace def _output(level, message, nesting_level = 0) if @@log_level >= level if @@log_filename if !@@log_device || @@log_device.closed? @@log_device = File.open(@@log_filename, "a") end output = @@log_device else output = @@stderr_evaluator.call end location = caller[nesting_level].sub(/.*phusion_passenger\//, '') location.sub!(/(.*):.*/, '\1') now = Time.now time_str = now.strftime("%Y-%m-%d %H:%M:%S.") time_str << sprintf("%04d", now.usec / 100) current_thread = Thread.current if !(thread_id = current_thread[:id]) current_thread.to_s =~ /:(0x[0-9a-f]+)/i thread_id = current_thread[:id] = $1 || '?' end if thread_name = current_thread[:name] thread_name = "(#{thread_name})" end output.write("[ #{time_str} #{$$}/#{thread_id}#{thread_name} #{location} ]: #{message}\n") output.flush end end module_function :_output end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/loader_shared_helpers.rb000644 000765 000024 00000053030 12233035540 030641 0ustar00honglistaff000000 000000 # encoding: binary # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2011-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'public_api' PhusionPassenger.require_passenger_lib 'ruby_core_enhancements' PhusionPassenger.require_passenger_lib 'debug_logging' PhusionPassenger.require_passenger_lib 'utils/shellwords' module PhusionPassenger # Provides shared functions for loader and preloader apps. module LoaderSharedHelpers extend self # To be called by the (pre)loader as soon as possible. def init(options) Thread.main[:name] = "Main thread" # We don't dump PATH info because at this point it's # unlikely to be changed. dump_ruby_environment check_rvm_using_wrapper_script(options) return sanitize_spawn_options(options) end def check_rvm_using_wrapper_script(options) ruby = options["ruby"] if ruby =~ %r(/\.?rvm/) && ruby =~ %r(/bin/ruby$) case options["integration_mode"] || DEFAULT_INTEGRATION_MODE when "nginx" passenger_ruby = "passenger_ruby" passenger_ruby_doc = "https://www.phusionpassenger.com/library/config/nginx/reference/#setting_correct_passenger_ruby_value" when "apache" passenger_ruby = "PassengerRuby" passenger_ruby_doc = "https://www.phusionpassenger.com/library/config/apache/reference/#setting_correct_passenger_ruby_value" when "standalone" passenger_ruby = "--ruby" passenger_ruby_doc = "https://www.phusionpassenger.com/library/config/standalone/reference/#setting_correct_passenger_ruby_value" end raise "You've set the `#{passenger_ruby}` option to '#{ruby}'. " + "However, because you are using RVM, this is not allowed: the option must point to " + "an RVM wrapper script, not a raw Ruby binary. This is because RVM is implemented " + "through various environment variables, which are set through the wrapper script.\n" + "\n" + "To find out the correct value for `#{passenger_ruby}`, please read:\n\n" + " #{passenger_ruby_doc}\n" + "\n-------------------------\n" end end # To be called whenever the (pre)loader is about to abort with an error. def about_to_abort(options, exception = nil) dump_all_information(options) # https://code.google.com/p/phusion-passenger/issues/detail?id=1039 puts end def to_boolean(value) return !(value.nil? || value == false || value == "false") end def sanitize_spawn_options(options) defaults = { "app_type" => "rack", "environment" => "production", "print_exceptions" => true } options = defaults.merge(options) options["app_group_name"] = options["app_root"] if !options["app_group_name"] options["print_exceptions"] = to_boolean(options["print_exceptions"]) options["analytics"] = to_boolean(options["analytics"]) options["show_version_in_header"] = to_boolean(options["show_version_in_header"]) options["log_level"] = options["log_level"].to_i if options["log_level"] # TODO: smart spawning is not supported when using ruby-debug. We should raise an error # in this case. options["debugger"] = to_boolean(options["debugger"]) options["spawn_method"] = "direct" if options["debugger"] return options end def dump_all_information(options) dump_ruby_environment dump_envvars dump_system_metrics(options) end def dump_ruby_environment if dir = ENV['PASSENGER_DEBUG_DIR'] File.open("#{dir}/ruby_info", "w") do |f| f.puts "RUBY_VERSION = #{RUBY_VERSION}" f.puts "RUBY_PLATFORM = #{RUBY_PLATFORM}" f.puts "RUBY_ENGINE = #{defined?(RUBY_ENGINE) ? RUBY_ENGINE : 'nil'}" end File.open("#{dir}/load_path", "wb") do |f| $LOAD_PATH.each do |path| f.puts path end end File.open("#{dir}/loaded_libs", "wb") do |f| $LOADED_FEATURES.each do |filename| f.puts filename end end # We write to these files last because the 'require' calls can fail. require 'rbconfig' if !defined?(RbConfig::CONFIG) File.open("#{dir}/rbconfig", "wb") do |f| RbConfig::CONFIG.each_pair do |key, value| f.puts "#{key} = #{value}" end end begin require 'rubygems' if !defined?(Gem) rescue LoadError end if defined?(Gem) File.open("#{dir}/ruby_info", "a") do |f| f.puts "RubyGems version = #{Gem::VERSION}" if Gem.respond_to?(:path) f.puts "RubyGems paths = #{Gem.path.inspect}" else f.puts "RubyGems paths = unknown; incompatible RubyGems API" end end File.open("#{dir}/activated_gems", "wb") do |f| if Gem.respond_to?(:loaded_specs) Gem.loaded_specs.each_pair do |name, spec| f.puts "#{name} => #{spec.version}" end else f.puts "Unable to query this information; incompatible RubyGems API." end end end end rescue SystemCallError # Don't care. end def dump_envvars if dir = ENV['PASSENGER_DEBUG_DIR'] File.open("#{dir}/envvars", "wb") do |f| ENV.each_pair do |key, value| f.puts "#{key} = #{value}" end end end rescue SystemCallError # Don't care. end def dump_system_metrics(options) if dir = ENV['PASSENGER_DEBUG_DIR'] # When invoked through Passenger Standalone, we want passenger-config # to use the PassengerAgent in the Passsenger Standalone buildout directory, # because the one in the source root may not exist. passenger_config = "#{PhusionPassenger.bin_dir}/passenger-config" if is_ruby_program?(passenger_config) ruby = options["ruby"] else ruby = nil end command = [ "env", "PASSENGER_LOCATION_CONFIGURATION_FILE=#{PhusionPassenger.install_spec}", ruby, passenger_config, "system-metrics" ].compact contents = `#{Shellwords.join(command)}` if $? && $?.exitstatus == 0 File.open("#{dir}/system_metrics", "wb") do |f| f.write(contents) end end end rescue SystemCallError # Don't care. end def is_ruby_program?(path) File.open(path, "rb") do |f| f.readline =~ /ruby/ end rescue EOFError false end # Prepare an application process using rules for the given spawn options. # This method is to be called before loading the application code. # # +startup_file+ is the application type's startup file, e.g. # "config/environment.rb" for Rails apps and "config.ru" for Rack apps. # +options+ are the spawn options that were given. # # This function may modify +options+. The modified options are to be # passed to the request handler. def before_loading_app_code_step1(startup_file, options) DebugLogging.log_level = options["log_level"] if options["log_level"] # We always load the union_station_hooks_* gems and do not check for # `options["analytics"]` here. The gems don't actually initialize (and # load the bulk of their code) unless they have determined that # `options["analytics"]` is true. Regardless of whether Union Station # support is enabled in Passenger, the UnionStationHooks namespace must # be available so that applications can call it, even though the actual # calls don't do anything when Union Station support is disabled. PhusionPassenger.require_passenger_lib 'vendor/union_station_hooks_core/lib/union_station_hooks_core' UnionStationHooks.vendored = true PhusionPassenger.require_passenger_lib 'vendor/union_station_hooks_rails/lib/union_station_hooks_rails' UnionStationHooksRails.vendored = true end def run_load_path_setup_code(options) # rack-preloader.rb depends on the 'rack' library, but the app # might want us to use a bundled version instead of a # gem/apt-get/yum/whatever-installed version. Therefore we must setup # the correct load paths before requiring 'rack'. # # The most popular tool for bundling dependencies is Bundler. Bundler # works as follows: # - If the bundle is locked then a file .bundle/environment.rb exists # which will setup the load paths. # - If the bundle is not locked then the load paths must be set up by # calling Bundler.setup. # - Rails 3's boot.rb automatically loads .bundle/environment.rb or # calls Bundler.setup if that's not available. # - Other Rack apps might not have a boot.rb but we still want to setup # Bundler. # - Some Rails 2 apps might have explicitly added Bundler support. # These apps call Bundler.setup in their preinitializer.rb. # # So the strategy is as follows: # Our strategy might be completely unsuitable for the app or the # developer is using something other than Bundler, so we let the user # manually specify a load path setup file. if options["load_path_setup_file"] require File.expand_path(options["load_path_setup_file"]) # The app developer may also override our strategy with this magic file. elsif File.exist?('config/setup_load_paths.rb') require File.expand_path('config/setup_load_paths') # Older versions of Bundler use .bundle/environment.rb as the Bundler # environment lock file. This has been replaced by Gemfile.lock in later # versions, but we still support the older mechanism. # If the Bundler environment lock file exists then load that. If it # exists then there's a 99.9% chance that loading it is the correct # thing to do. elsif File.exist?('.bundle/environment.rb') running_bundler(options) do require File.expand_path('.bundle/environment') end # If the legacy Bundler environment file doesn't exist then there are two # possibilities: # 1. Bundler is not used, in which case we don't have to do anything. # 2. Bundler *is* used, but either the user is using a newer Bundler versions, # or the gems are not locked. In either case, we're supposed to call # Bundler.setup. # # The existence of Gemfile indicates whether (2) is true: elsif File.exist?('Gemfile') # In case of Rails 3, config/boot.rb already calls Bundler.setup. # However older versions of Rails may not so loading boot.rb might # not be the correct thing to do. To be on the safe side we # call Bundler.setup ourselves; calling Bundler.setup twice is # harmless. If this isn't the correct thing to do after all then # there's always the load_path_setup_file option and # setup_load_paths.rb. running_bundler(options) do activate_gem 'bundler', 'bundler/setup' end end # !!! NOTE !!! # If the app is using Bundler then any dependencies required past this # point must be specified in the Gemfile. Like ruby-debug if debugging is on... end # This method is to be called after the load path has been set up # (e.g. Bundler.setup is called), but before loading the app code. def before_loading_app_code_step2(options) # Do nothing end # This method is to be called after loading the application code but # before forking a worker process. def after_loading_app_code(options) UnionStationHooks.check_initialized end # If the current working directory equals `app_root`, and `abs_path` is a # file inside `app_root`, then returns its basename. Otherwise, returns # `abs_path`. # # The main use case for this method is to ensure that we load config.ru # with a relative path (only its base name) in most circumstances, # instead of with an absolute path. This is necessary in order to retain # compatibility with apps that expect config.ru's __FILE__ to be relative. # See https://github.com/phusion/passenger/issues/1596 def maybe_make_path_relative_to_app_root(app_root, abs_path) if Dir.logical_pwd == app_root && File.dirname(abs_path) == app_root File.basename(abs_path) else abs_path end end def create_socket_address(protocol, address) if protocol == 'unix' return "unix:#{address}" elsif protocol == 'tcp' return "tcp://#{address}" else raise ArgumentError, "Unknown protocol '#{protocol}'" end end def advertise_readiness # https://code.google.com/p/phusion-passenger/issues/detail?id=1039 puts puts "!> Ready" end def advertise_sockets(output, request_handler) request_handler.server_sockets.each_pair do |name, options| concurrency = PhusionPassenger.advertised_concurrency_level || options[:concurrency] output.puts "!> socket: #{name};#{options[:address]};#{options[:protocol]};#{concurrency}" end end # To be called before the request handler main loop is entered, but after the app # startup file has been loaded. This function will fire off necessary events # and perform necessary preparation tasks. # # +forked+ indicates whether the current worker process is forked off from # an ApplicationSpawner that has preloaded the app code. # +options+ are the spawn options that were passed. def before_handling_requests(forked, options) if forked # Reseed pseudo-random number generator for security reasons. srand end if options["process_title"] && !options["process_title"].empty? $0 = options["process_title"] + ": " + options["app_group_name"] end # If we were forked from a preloader process then clear or # re-establish ActiveRecord database connections. This prevents # child processes from concurrently accessing the same # database connection handles. if forked && defined?(ActiveRecord::Base) if ActiveRecord::Base.respond_to?(:clear_all_connections!) ActiveRecord::Base.clear_all_connections! elsif ActiveRecord::Base.respond_to?(:clear_active_connections!) ActiveRecord::Base.clear_active_connections! elsif ActiveRecord::Base.respond_to?(:connected?) && ActiveRecord::Base.connected? ActiveRecord::Base.establish_connection end end # Fire off events. PhusionPassenger.call_event(:starting_worker_process, forked) if options["pool_account_username"] && options["pool_account_password_base64"] password = options["pool_account_password_base64"].unpack('m').first PhusionPassenger.call_event(:credentials, options["pool_account_username"], password) else PhusionPassenger.call_event(:credentials, nil, nil) end end # To be called after the request handler main loop is exited. This function # will fire off necessary events perform necessary cleanup tasks. def after_handling_requests PhusionPassenger.call_event(:stopping_worker_process) end # Activate a gem and require it. This method exists in order to load # a library from RubyGems instead of from vendor_ruby. For example, # on Debian systems, Rack may be installed from APT, but that is usually # a very old version which we don't want. This method ensures that the # RubyGems-installed version is loaded, not the the version in vendor_ruby. # See the following threads for discussion: # https://github.com/phusion/passenger/issues/1478 # https://github.com/phusion/passenger/issues/1480 def activate_gem(gem_name, library_name = nil) if !defined?(::Gem) begin require 'rubygems' rescue LoadError end end if Kernel.respond_to?(:gem, true) begin gem(gem_name) rescue Gem::LoadError end end require(library_name || gem_name) end private def running_bundler(options) yield rescue Exception => e if (defined?(Bundler::GemNotFound) && e.is_a?(Bundler::GemNotFound)) || (defined?(Bundler::GitError) && e.is_a?(Bundler::GitError)) PhusionPassenger.require_passenger_lib 'platform_info/ruby' comment = "

It looks like Bundler could not find a gem. Maybe you didn't install all the " + "gems that this application needs. To install your gems, please run:

\n\n" + "
bundle install
\n\n" ruby = options["ruby"] if ruby =~ %r(^/usr/local/rvm/) comment << "

If that didn't work, then maybe the problem is that your gems are installed " + "to #{h home_dir}/.rvm/gems, while at the same time you set " + "PassengerRuby (Apache) or passenger_ruby (Nginx) to " + "#{h ruby}. Because of the latter, RVM does not load gems from the " + "home directory.

\n\n" + "

To make RVM load gems from the home directory, you need to set " + "PassengerRuby/passenger_ruby to an RVM wrapper script " + "inside the home directory:

\n\n" + "
    \n" + "
  1. Login as #{h whoami}.
  2. \n" if PlatformInfo.rvm_installation_mode == :multi comment << "
  3. Enable RVM mixed mode by running:\n" + "
    rvm user gemsets
  4. \n" end comment << "
  5. Run this to find out what to set PassengerRuby/passenger_ruby to:\n" + "
    #{PlatformInfo.ruby_command} \\\n" +
                "#{PhusionPassenger.bin_dir}/passenger-config --detect-ruby
  6. \n" + "
\n\n" + "

If that didn't help either, then maybe your application is being run under a " + "different environment than it's supposed to. Please check the following:

\n\n" else comment << "

If that didn't work, then the problem is probably caused by your " + "application being run under a different environment than it's supposed to. " + "Please check the following:

\n\n" end comment << "
    \n" comment << "
  1. Is this app supposed to be run as the #{h whoami} user?
  2. \n" + "
  3. Is this app being run on the correct Ruby interpreter? Below you will\n" + " see which Ruby interpreter Phusion Passenger attempted to use.
  4. \n" if PlatformInfo.in_rvm? comment << "
  5. Please check whether the correct RVM gemset is being used.
  6. \n" + "
  7. Sometimes, RVM gemsets may be broken.\n" + " Try resetting them.
  8. \n" end comment << "
\n" prepend_exception_html_comment(e, comment) end raise e end def prepend_exception_html_comment(e, comment) # Since Exception doesn't allow changing the message, we monkeypatch # the #message and #to_s methods. separator = "\n

-------- The exception is as follows: -------

\n" new_message = comment + separator + h(e.message) new_s = comment + separator + h(e.to_s) metaclass = class << e; self; end metaclass.send(:define_method, :message) do new_message end metaclass.send(:define_method, :to_s) do new_s end metaclass.send(:define_method, :html?) do true end end def h(text) require 'erb' if !defined?(ERB) return ERB::Util.h(text) end def whoami require 'etc' if !defined?(Etc) begin user = Etc.getpwuid(Process.uid) rescue ArgumentError user = nil end if user return user.name else return "##{Process.uid}" end end def home_dir return PhusionPassenger.home_dir end end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/message_channel.rb000644 000765 000024 00000025150 12233035540 027441 0ustar00honglistaff000000 000000 # encoding: binary # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module PhusionPassenger # This class allows reading and writing structured messages over # I/O channels. This is the Ruby implementation of src/cxx_supportlib/Utils/MessageIO.h; # see that file for more information. class MessageChannel HEADER_SIZE = 2 # :nodoc: DELIMITER = "\0" # :nodoc: DELIMITER_NAME = "null byte" # :nodoc: UINT16_PACK_FORMAT = "n" # :nodoc: UINT32_PACK_FORMAT = "N" # :nodoc: class InvalidHashError < StandardError end # The wrapped IO object. attr_accessor :io # Create a new MessageChannel by wrapping the given IO object. def initialize(io = nil) @io = io # Make it binary just in case. @io.binmode if @io end # Read an array message from the underlying file descriptor. # Returns the array message as an array, or nil when end-of-stream has # been reached. # # Might raise SystemCallError, IOError or SocketError when something # goes wrong. def read buffer = new_buffer if !@io.read(HEADER_SIZE, buffer) return nil end while buffer.size < HEADER_SIZE tmp = @io.read(HEADER_SIZE - buffer.size) if tmp.empty? return nil else buffer << tmp end end chunk_size = buffer.unpack(UINT16_PACK_FORMAT)[0] if !@io.read(chunk_size, buffer) return nil end while buffer.size < chunk_size tmp = @io.read(chunk_size - buffer.size) if tmp.empty? return nil else buffer << tmp end end message = [] offset = 0 delimiter_pos = buffer.index(DELIMITER, offset) while !delimiter_pos.nil? if delimiter_pos == 0 message << "" else message << buffer[offset .. delimiter_pos - 1] end offset = delimiter_pos + 1 delimiter_pos = buffer.index(DELIMITER, offset) end return message rescue Errno::ECONNRESET return nil end # Read an array message from the underlying file descriptor and return the # result as a hash instead of an array. This assumes that the array message # has an even number of elements. # Returns nil when end-of-stream has been reached. # # Might raise SystemCallError, IOError or SocketError when something # goes wrong. def read_hash buffer = new_buffer if !@io.read(HEADER_SIZE, buffer) return nil end while buffer.size < HEADER_SIZE tmp = @io.read(HEADER_SIZE - buffer.size) if tmp.empty? return nil else buffer << tmp end end chunk_size = buffer.unpack(UINT16_PACK_FORMAT)[0] if !@io.read(chunk_size, buffer) return nil end while buffer.size < chunk_size tmp = @io.read(chunk_size - buffer.size) if tmp.empty? return nil else buffer << tmp end end result = {} offset = 0 delimiter_pos = buffer.index(DELIMITER, offset) while !delimiter_pos.nil? if delimiter_pos == 0 name = "" else name = buffer[offset .. delimiter_pos - 1] end offset = delimiter_pos + 1 delimiter_pos = buffer.index(DELIMITER, offset) if delimiter_pos.nil? raise InvalidHashError elsif delimiter_pos == 0 value = "" else value = buffer[offset .. delimiter_pos - 1] end result[name] = value offset = delimiter_pos + 1 delimiter_pos = buffer.index(DELIMITER, offset) end return result rescue Errno::ECONNRESET return nil end # Read a scalar message from the underlying IO object. Returns the # read message, or nil on end-of-stream. # # Might raise SystemCallError, IOError or SocketError when something # goes wrong. # # The +buffer+ argument specifies a buffer in which #read_scalar # stores the read data. It is good practice to reuse existing buffers # in order to minimize stress on the garbage collector. # # The +max_size+ argument allows one to specify the maximum allowed # size for the scalar message. If the received scalar message's size # is larger than +max_size+, then a SecurityError will be raised. def read_scalar(buffer = new_buffer, max_size = nil) if !@io.read(4, buffer) return nil end while buffer.size < 4 tmp = @io.read(4 - buffer.size) if tmp.empty? return nil else buffer << tmp end end size = buffer.unpack(UINT32_PACK_FORMAT)[0] if size == 0 buffer.replace('') return buffer else if !max_size.nil? && size > max_size raise SecurityError, "Scalar message size (#{size}) " << "exceeds maximum allowed size (#{max_size})." end if !@io.read(size, buffer) return nil end if buffer.size < size tmp = '' while buffer.size < size if !@io.read(size - buffer.size, tmp) return nil else buffer << tmp end end end return buffer end rescue Errno::ECONNRESET return nil end # Send an array message, which consists of the given elements, over the underlying # file descriptor. _name_ is the first element in the message, and _args_ are the # other elements. These arguments will internally be converted to strings by calling # to_s(). # # Might raise SystemCallError, IOError or SocketError when something # goes wrong. def write(name, *args) check_argument(name) args.each do |arg| check_argument(arg) end message = "#{name}#{DELIMITER}" args.each do |arg| message << arg.to_s << DELIMITER end if message.size > 2 ** 16 - 1 raise ArgumentError, 'Message size too large' end @io.write([message.size].pack('n') << message) @io.flush end # Send a scalar message over the underlying IO object. # # Might raise SystemCallError, IOError or SocketError when something # goes wrong. def write_scalar(data) @io.write([data.size].pack('N') << data) @io.flush end # Receive an IO object (a file descriptor) from the channel. The other # side must have sent an IO object by calling send_io(). Note that # this only works on Unix sockets. # # Might raise SystemCallError, IOError or SocketError when something # goes wrong. def recv_io(klass = IO, negotiate = true) write("pass IO") if negotiate io = @io.recv_io(klass) write("got IO") if negotiate return io end # Send an IO object (a file descriptor) over the channel. The other # side must receive the IO object by calling recv_io(). Note that # this only works on Unix sockets. # # Might raise SystemCallError, IOError or SocketError when something # goes wrong. def send_io(io) # We read a message before actually calling #send_io # in order to prevent the other side from accidentally # read()ing past the normal data and reading our file # descriptor too. # # For example suppose that side A looks like this: # # read(fd, buf, 1024) # read_io(fd) # # and side B: # # write(fd, buf, 100) # send_io(fd_to_pass) # # If B completes both write() and send_io(), then A's read() call # reads past the 100 bytes that B sent. On some platforms, like # Linux, this will cause read_io() to fail. And it just so happens # that Ruby's IO#read method slurps more than just the given amount # of bytes. result = read if !result raise EOFError, "End of stream" elsif result != ["pass IO"] raise IOError, "IO passing pre-negotiation header expected" else @io.send_io(io) # Once you've sent the IO you expect to be able to close it on the # sender's side, even if the other side hasn't read the IO yet. # Not so: on some operating systems (I'm looking at you OS X) this # can cause the receiving side to receive a bad file descriptor. # The post negotiation protocol ensures that we block until the # other side has really received the IO. result = read if !result raise EOFError, "End of stream" elsif result != ["got IO"] raise IOError, "IO passing post-negotiation header expected" end end end # Return the file descriptor of the underlying IO object. def fileno return @io.fileno end # Close the underlying IO stream. Might raise SystemCallError or # IOError when something goes wrong. def close @io.close end # Checks whether the underlying IO stream is closed. def closed? return @io.closed? end private def check_argument(arg) if arg.to_s.index(DELIMITER) raise ArgumentError, "Message name and arguments may not contain #{DELIMITER_NAME}." end end if defined?(ByteString) def new_buffer return ByteString.new end else def new_buffer return "" end end end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/message_client.rb000644 000765 000024 00000007571 12233035540 027316 0ustar00honglistaff000000 000000 # encoding: binary # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'socket' PhusionPassenger.require_passenger_lib 'message_channel' PhusionPassenger.require_passenger_lib 'utils' module PhusionPassenger # A convenience class for communicating with MessageServer servers, # for example the ApplicationPool server. class MessageClient include Utils class ProtocolError < StandardError end # Connect to the given server. By default it connects to the current # generation's core. def initialize(username, password, address) @socket = connect_to_server(address) begin @channel = MessageChannel.new(@socket) result = @channel.read if result.nil? raise EOFError elsif result.size != 2 || result[0] != "version" raise IOError, "The message server didn't sent a valid version identifier" elsif result[1] != "1" raise IOError, "Unsupported message server protocol version #{result[1]}" end @channel.write_scalar(username) @channel.write_scalar(password) result = @channel.read if result.nil? raise EOFError elsif result[0] != "status" raise ProtocolError, "Invalid authentication response: expected \"status\", got #{result[0].inspect}" elsif result[1] == "ok" # Do nothing elsif result[1] == "error" if result[2] raise SecurityError, "Authentication error: #{result[2]}" else raise SecurityError, "Authentication error (no server message given)" end else raise ProtocolError, "Invalid authentication response: #{result.inspect}" end rescue Exception @socket.close raise end end def close @socket.close if @socket @channel = @socket = nil end def connected? return !!@channel end ### Low level I/O methods ### def read return @channel.read rescue auto_disconnect raise end def write(*args) @channel.write(*args) rescue auto_disconnect raise end def write_scalar(*args) @channel.write_scalar(*args) rescue auto_disconnect raise end def read_scalar return @channel.read_scalar rescue auto_disconnect raise end def recv_io(klass = IO, negotiate = true) return @channel.recv_io(klass, negotiate) rescue auto_disconnect raise end private def auto_disconnect if @channel @socket.close rescue nil @socket = @channel = nil end end end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/native_support.rb000644 000765 000024 00000037255 12233035540 027420 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'constants' module PhusionPassenger class NativeSupportLoader def self.supported? return !defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby" || RUBY_ENGINE == "rbx" end def try_load if defined?(NativeSupport) return true else load_from_native_support_output_dir || load_from_buildout_dir || load_from_load_path || load_from_home_dir end end def start if ENV['PASSENGER_USE_RUBY_NATIVE_SUPPORT'] == '0' STDERR.puts " [#{library_name}] will not be used (PASSENGER_USE_RUBY_NATIVE_SUPPORT=0)" STDERR.puts " --> Passenger will still operate normally." return false elsif try_load return true elsif compile_and_load || download_binary_and_load STDERR.puts " [#{library_name}] successfully loaded." return true else STDERR.puts " [#{library_name}] will not be used (can't compile or download) " STDERR.puts " --> Passenger will still operate normally." return false end end private def archdir @archdir ||= begin PhusionPassenger.require_passenger_lib 'platform_info/binary_compatibility' PlatformInfo.ruby_extension_binary_compatibility_id end end def libext @libext ||= begin PhusionPassenger.require_passenger_lib 'platform_info/operating_system' PlatformInfo.library_extension end end def library_name return "passenger_native_support.#{libext}" end def extconf_rb File.join(PhusionPassenger.ruby_extension_source_dir, "extconf.rb") end def load_from_native_support_output_dir # Quick workaround for people suffering from # https://code.google.com/p/phusion-passenger/issues/detail?id=865 output_dir = ENV['PASSENGER_NATIVE_SUPPORT_OUTPUT_DIR'] if output_dir && !output_dir.empty? begin return load_native_extension("#{output_dir}/#{VERSION_STRING}/#{archdir}/#{library_name}") rescue LoadError return false end else return false end end def load_from_buildout_dir if PhusionPassenger.build_system_dir begin return load_native_extension("#{PhusionPassenger.build_system_dir}/buildout/ruby/#{archdir}/#{library_name}") rescue LoadError return false end else return false end end def load_from_load_path return load_native_extension('passenger_native_support') rescue LoadError return false end def load_from_home_dir begin return load_native_extension("#{PhusionPassenger.home_dir}/#{USER_NAMESPACE_DIRNAME}/native_support/#{VERSION_STRING}/#{archdir}/#{library_name}") rescue LoadError return false end end def download_binary_and_load if !PhusionPassenger.installed_from_release_package? STDERR.puts " [#{library_name}] not downloading because passenger wasn't installed from a release package" return end if ENV['PASSENGER_DOWNLOAD_NATIVE_SUPPORT_BINARY'] == '0' STDERR.puts " [#{library_name}] not downloading because PASSENGER_DOWNLOAD_NATIVE_SUPPORT_BINARY=0" return end STDERR.puts " [#{library_name}] finding downloads for the current Ruby interpreter..." STDERR.puts " (set PASSENGER_DOWNLOAD_NATIVE_SUPPORT_BINARY=0 to disable)" require 'logger' PhusionPassenger.require_passenger_lib 'platform_info/ruby' PhusionPassenger.require_passenger_lib 'utils/tmpio' PhusionPassenger.require_passenger_lib 'utils/download' PhusionPassenger.require_passenger_lib 'utils/shellwords' PhusionPassenger::Utils.mktmpdir("passenger-native-support-") do |dir| Dir.chdir(dir) do basename = "rubyext-#{archdir}.tar.gz" if !download(basename, dir, :total_timeout => 30) return false end s_basename = Shellwords.escape(basename) sh "tar xzf #{s_basename}" sh "rm -f #{s_basename}" STDERR.puts " Checking whether downloaded binary is usable..." File.open("test.rb", "w") do |f| f.puts(%Q{ require File.expand_path('passenger_native_support') f = File.open("test.txt", "w") PhusionPassenger::NativeSupport.writev(f.fileno, ["hello", "\n"]) }) end if sh_nonfatal("#{PlatformInfo.ruby_command} -I. test.rb") && File.exist?("test.txt") && File.read("test.txt") == "hello\n" STDERR.puts " Binary is usable." File.unlink("test.rb") File.unlink("test.txt") result = try_directories(installation_target_dirs) do |target_dir| files = Dir["#{dir}/*"] STDERR.puts " Installing " + files.map{ |n| File.basename(n) }.join(' ') FileUtils.cp(files, target_dir) load_result = load_native_extension("#{target_dir}/#{library_name}") [load_result, false] end return result else STDERR.puts " Binary is not usable." return false end end end end def compile_and_load if ENV['PASSENGER_COMPILE_NATIVE_SUPPORT_BINARY'] == '0' STDERR.puts " [#{library_name}] not compiling because PASSENGER_COMPILE_NATIVE_SUPPORT_BINARY=0" return false end if PhusionPassenger.custom_packaged? && !File.exist?(PhusionPassenger.ruby_extension_source_dir) PhusionPassenger.require_passenger_lib 'constants' STDERR.puts " [#{library_name}] not found for current Ruby interpreter." case PhusionPassenger.packaging_method when 'deb' STDERR.puts " This library provides various optimized routines that make" STDERR.puts " #{PhusionPassenger::PROGRAM_NAME} faster. Please run 'sudo apt-get install #{PhusionPassenger::DEB_DEV_PACKAGE}'" STDERR.puts " so that #{PhusionPassenger::PROGRAM_NAME} can compile one on the next run." when 'rpm' STDERR.puts " This library provides various optimized routines that make" STDERR.puts " #{PhusionPassenger::PROGRAM_NAME} faster. Please run 'sudo yum install #{PhusionPassenger::RPM_DEV_PACKAGE}-#{PhusionPassenger::VERSION_STRING}'" STDERR.puts " so that #{PhusionPassenger::PROGRAM_NAME} can compile one on the next run." else STDERR.puts " #{PhusionPassenger::PROGRAM_NAME} can compile one, but an extra package must be installed" STDERR.puts " first. Please ask your packager or operating system vendor for instructions." end return false end STDERR.puts " [#{library_name}] trying to compile for the current user (#{current_user_name_or_id}) and Ruby interpreter..." STDERR.puts " (set PASSENGER_COMPILE_NATIVE_SUPPORT_BINARY=0 to disable)" require 'fileutils' PhusionPassenger.require_passenger_lib 'utils/shellwords' PhusionPassenger.require_passenger_lib 'platform_info/ruby' target_dir = compile(installation_target_dirs) if target_dir return load_native_extension("#{target_dir}/#{library_name}") else return false end end # Name of the user under which we are executing, or the id as fallback # N.B. loader_shared_helpers.rb has the same method def current_user_name_or_id require 'etc' if !defined?(Etc) begin user = Etc.getpwuid(Process.uid) rescue ArgumentError user = nil end if user return user.name else return "##{Process.uid}" end end def installation_target_dirs target_dirs = [] if (output_dir = ENV['PASSENGER_NATIVE_SUPPORT_OUTPUT_DIR']) && !output_dir.empty? target_dirs << "#{output_dir}/#{VERSION_STRING}/#{archdir}" end if PhusionPassenger.build_system_dir target_dirs << "#{PhusionPassenger.build_system_dir}/buildout/ruby/#{archdir}" end target_dirs << "#{PhusionPassenger.home_dir}/#{USER_NAMESPACE_DIRNAME}/native_support/#{VERSION_STRING}/#{archdir}" return target_dirs end def download(name, output_dir, options = {}) logger = Logger.new(STDERR) logger.level = Logger::WARN logger.formatter = proc do |severity, datetime, progname, msg| msg.gsub(/^/, " ") + "\n" end sites = PhusionPassenger.binaries_sites sites.each_with_index do |site, i| if real_download(site, name, output_dir, logger, options) logger.warn "Download OK!" if i > 0 return true elsif i != sites.size - 1 logger.warn "Trying next mirror..." end end return false end def real_download(site, name, output_dir, logger, options) url = "#{site[:url]}/#{VERSION_STRING}/#{name}" filename = "#{output_dir}/#{name}" real_options = options.merge( :cacert => site[:cacert], :use_cache => true, :logger => logger ) return PhusionPassenger::Utils::Download.download(url, filename, real_options) end def log(message, options = {}) if logger = options[:logger] logger.puts(message) else STDERR.puts " #{message}" end end def mkdir(dir, options = {}) begin log("# mkdir -p #{dir}", options) FileUtils.mkdir_p(dir) rescue Errno::EEXIST end end def sh(command_string) if !sh_nonfatal(command_string) raise "Could not compile #{library_name} (\"#{command_string}\" failed)" end end def sh_nonfatal(command_string, options = {}) log("# #{command_string}", options) if logger = options[:logger] s_logpath = Shellwords.escape(logger.path) return system("(#{command_string}) >>#{s_logpath} 2>&1") else Utils.mktmpdir("passenger-native-support-") do |tmpdir| s_tmpdir = Shellwords.escape(tmpdir) result = system("(#{command_string}) >#{s_tmpdir}/log 2>&1") system("cat #{s_tmpdir}/log | sed 's/^/ /' >&2") return result end end end def compile(target_dirs) logger = Utils::TmpIO.new('passenger_native_support', :mode => File::WRONLY | File::APPEND, :binary => false, :suffix => ".log", :unlink_immediately => false) options = { :logger => logger } begin result = try_directories(target_dirs, options) do |target_dir| make_result = nil # Perform the actual compilation in a temporary directory to avoid # problems with multiple processes trying to concurrently compile. # https://github.com/phusion/passenger/issues/1570 PhusionPassenger::Utils.mktmpdir("passenger-native-support-") do |tmpdir| Dir.chdir(tmpdir) do make_result = sh_nonfatal("#{PlatformInfo.ruby_command} #{Shellwords.escape extconf_rb}", options) && sh_nonfatal("make", options) if make_result begin FileUtils.cp_r(".", target_dir) rescue SystemCallError => e log("Error: #{e}") make_result = false end end end end if make_result log "Compilation successful. The logs are here:" log logger.path [target_dir, false] else [nil, false] end end if !result log "Warning: compilation didn't succeed. To learn why, read this file:" log logger.path end return result end ensure logger.close if logger end def try_directories(dirs, options = {}) result = nil log("# current user is: #{current_user_name_or_id}", options) dirs.each_with_index do |dir, i| begin mkdir(dir, options) File.open("#{dir}/.permission_test", "w").close File.unlink("#{dir}/.permission_test") log("# cd #{dir}", options) Dir.chdir(dir) do result, should_retry = yield(dir) return result if !should_retry end rescue Errno::EACCES # If we encountered a permission error, then try # the next target directory. If we get a permission # error on the last one too then propagate the # exception. if i == dirs.size - 1 log("Encountered permission error, " + "but no more directories to try. Giving up.", options) log("-------------------------------", options) return nil else log("Encountered permission error, " + "trying a different directory...", options) log("-------------------------------", options) end rescue Errno::ENOTDIR # This can occur when locations.ini set build_system_dir # to an invalid path. Just ignore this error. if i == dirs.size - 1 log("Not a valid directory, " + "but no more directories to try. Giving up.", options) log("-------------------------------", options) return nil else log("Not a valid directory. Trying a different one...", options) log("-------------------------------", options) end end end end def load_native_extension(name_or_filename) # If passenger_native_support.so exited because it detected that it was compiled # for a different Ruby version, then subsequent require("passenger_native_support") # calls will do nothing. So we remove passenger_native_support from $LOADED_FEATURES # to force it to be loaded. $LOADED_FEATURES.reject! { |fn| File.basename(fn) == library_name } begin require(name_or_filename) return defined?(PhusionPassenger::NativeSupport) rescue LoadError => e if e.to_s =~ /dlopen/ # Print dlopen failures. We're not interested in any other # kinds of failures, such as file-not-found. puts e.to_s.gsub(/^/, " ") end return false end end end end # module PhusionPassenger if PhusionPassenger::NativeSupportLoader.supported? PhusionPassenger::NativeSupportLoader.new.start end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/nginx/000755 000765 000024 00000000000 12233035540 025120 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/packaging.rb000644 000765 000024 00000010301 12233035540 026241 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module PhusionPassenger module Packaging # A list of HTML files that are generated with Asciidoc. ASCII_DOCS = [ 'doc/Users guide.html', 'doc/Users guide Apache.html', 'doc/Users guide Nginx.html', 'doc/Users guide Standalone.html', 'doc/Security of user switching support.html', 'doc/Design and Architecture.html' ] # Files that must be generated before packaging. PREGENERATED_FILES = [ 'src/cxx_supportlib/Constants.h', 'doc/Packaging.html', 'doc/CloudLicensingConfiguration.html', 'doc/ServerOptimizationGuide.html' ] + ASCII_DOCS USER_EXECUTABLES = [ 'passenger', 'passenger-install-apache2-module', 'passenger-install-nginx-module', 'passenger-config' ] SUPER_USER_EXECUTABLES = [ 'passenger-status', 'passenger-memory-stats' ] # Used during native packaging. Specifies executables for # which the shebang should NOT be set to #!/usr/bin/ruby, # so that these executables can be run with any Ruby interpreter # the user desires. EXECUTABLES_WITH_FREE_RUBY = [ 'passenger', 'passenger-config', 'passenger-install-apache2-module', 'passenger-install-nginx-module' ] # A list of globs which match all files that should be packaged # in the Phusion Passenger gem or tarball. GLOB = [ '.editorconfig', 'configure', 'Rakefile', 'README.md', 'CONTRIBUTORS', 'CONTRIBUTING.md', 'LICENSE', 'CHANGELOG', 'INSTALL.md', 'NEWS', 'package.json', 'npm-shrinkwrap.json', 'passenger.gemspec', 'build/**/*', 'bin/*', 'doc/**/*', 'man/*', 'dev/**/*', 'src/**/*', 'resources/**/*' ] # Files that should be excluded from the gem or tarball. Overrides GLOB. EXCLUDE_GLOB = [ '**/.DS_Store', '.gitignore', '.gitattributes', '.gitmodules', '.github/*', '.travis.yml', '.settings/*', '.externalToolBuilders/*', '.cproject', '.project', 'Gemfile', 'Gemfile.lock', 'Vagrantfile', 'Passenger.sublime-project', 'Passenger.xcodeproj/**/*', 'build/support/vendor/*/.*', 'build/support/vendor/*/spec/**/*', 'src/ruby_supportlib/phusion_passenger/vendor/*/.*', 'src/ruby_supportlib/phusion_passenger/vendor/*/hacking/**/*', 'src/ruby_supportlib/phusion_passenger/vendor/*/spec/**/*', 'src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/rails_test_apps/**/*', 'packaging/**/*', 'test/**/*' ] # Files and directories that should be excluded from the Homebrew installation. HOMEBREW_EXCLUDE = [ "package.json", "npm-shrinkwrap.json" ] def self.files result = Dir[*GLOB] - Dir[*EXCLUDE_GLOB] result.reject! { |path| path =~ %r{/\.\.?$} } result end end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/000755 000765 000024 00000000000 12233035540 026634 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info.rb000644 000765 000024 00000040300 12233035540 027156 0ustar00honglistaff000000 000000 # encoding: binary # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'utils/tmpio' module PhusionPassenger # This module autodetects various platform-specific information, and # provides that information through constants. module PlatformInfo private @@cache_dir = nil @@verbose = ['1', 'true', 'on', 'yes'].include?(ENV['VERBOSE']) @@log_implementation = lambda do |message| message = reindent(message, 3) message.sub!(/^ /, '') STDERR.puts " * #{message}" end def self.private_class_method(name) metaclass = class << self; self; end metaclass.send(:private, name) end private_class_method :private_class_method # Turn the specified class method into a memoized one. If the given # class method is called without arguments, then its result will be # memoized, frozen, and returned upon subsequent calls without arguments. # Calls with arguments are never memoized. # # If +cache_to_disk+ is true and a cache directory has been set with # PlatformInfo.cache_dir= then result is cached to a file on disk, # so that memoized results persist over multiple process runs. This # cache file expires in +cache_time+ seconds (1 hour by default) after # it has been written. # # def self.foo(max = 10) # rand(max) # end # memoize :foo # # foo # => 3 # foo # => 3 # foo(100) # => 49 # foo(100) # => 26 # foo # => 3 def self.memoize(method, cache_to_disk = false, cache_time = 3600) # We use class_eval here because Ruby 1.8.5 doesn't support class_variable_get/set. metaclass = class << self; self; end metaclass.send(:alias_method, "_unmemoized_#{method}", method) variable_name = "@@memoized_#{method}".sub(/\?/, '') check_variable_name = "@@has_memoized_#{method}".sub(/\?/, '') eval(%Q{ #{variable_name} = nil #{check_variable_name} = false }) line = __LINE__ + 1 source = %Q{ def self.#{method}(*args) # def self.httpd(*args) if args.empty? # if args.empty? if !#{check_variable_name} # if !@@has_memoized_httpd if @@cache_dir # if @@cache_dir cache_file = File.join(@@cache_dir, "#{method}") # cache_file = File.join(@@cache_dir, "httpd") end # end read_from_cache_file = false # read_from_cache_file = false if #{cache_to_disk} && cache_file && File.exist?(cache_file) # if #{cache_to_disk} && File.exist?(cache_file) cache_file_stat = File.stat(cache_file) # cache_file_stat = File.stat(cache_file) read_from_cache_file = # read_from_cache_file = Time.now - cache_file_stat.mtime < #{cache_time} # Time.now - cache_file_stat.mtime < #{cache_time} end # end if read_from_cache_file # if read_from_cache_file data = File.read(cache_file) # data = File.read(cache_file) #{variable_name} = Marshal.load(data).freeze # @@memoized_httpd = Marshal.load(data).freeze #{check_variable_name} = true # @@has_memoized_httpd = true else # else #{variable_name} = _unmemoized_#{method}.freeze # @@memoized_httpd = _unmemoized_httpd.freeze #{check_variable_name} = true # @@has_memoized_httpd = true if cache_file && #{cache_to_disk} # if cache_file && #{cache_to_disk} begin # begin if !File.directory?(@@cache_dir) # if !File.directory?(@@cache_dir) Dir.mkdir(@@cache_dir) # Dir.mkdir(@@cache_dir) end # end File.open(cache_file, "wb") do |f| # File.open(cache_file, "wb") do |f| f.write(Marshal.dump(#{variable_name})) # f.write(Marshal.dump(@@memoized_httpd)) end # end rescue Errno::EACCES # rescue Errno::EACCES # Ignore permission error. # # Ignore permission error. end # end end # end end # end end # end #{variable_name} # @@memoized_httpd else # else _unmemoized_#{method}(*args) # _unmemoized_httpd(*args) end # end end # end } class_eval(source, __FILE__, line) end private_class_method :memoize # Look in the directory +dir+ and check whether there's an executable # whose base name is equal to one of the elements in +possible_names+. # If so, returns the full filename. If not, returns nil. def self.select_executable(dir, *possible_names) possible_names.each do |name| filename = "#{dir}/#{name}" if File.file?(filename) && File.executable?(filename) return filename end end return nil end private_class_method :select_executable def self.unindent(str) str = str.dup str.gsub!(/\A([\s\t]*\n)+/, '') str.gsub!(/[\s\t\n]+\Z/, '') indent = str.split("\n").select{ |line| !line.strip.empty? }.map{ |line| line.index(/[^\s]/) }.compact.min || 0 str.gsub!(/^[[:blank:]]{#{indent}}/, '') return str end private_class_method :unindent def self.reindent(str, level) str = unindent(str) str.gsub!(/^/, ' ' * level) return str end private_class_method :reindent def self.create_temp_file(name, dir = tmpdir) # This function is mostly used for compiling C programs to autodetect # system properties. We create a secure temp subdirectory to prevent # TOCTU attacks, especially because we don't know how the compiler # handles this. PhusionPassenger::Utils.mktmpdir("passenger.", dir) do |subdir| filename = "#{subdir}/#{name}" f = File.open(filename, "w") begin yield(filename, f) ensure f.close if !f.closed? end end end private_class_method :create_temp_file def self.log(message) if verbose? @@log_implementation.call(message) end end private_class_method :log public class RuntimeError < ::RuntimeError end def self.cache_dir=(value) @@cache_dir = value end def self.cache_dir return @@cache_dir end def self.verbose=(val) @@verbose = val end def self.verbose? return @@verbose end def self.log_implementation=(impl) @@log_implementation = impl end def self.log_implementation return @@log_implementation end def self.env_defined?(name) return !ENV[name].nil? && !ENV[name].empty? end def self.string_env(name, default_value = nil) value = ENV[name] if value.nil? || value.empty? return default_value else return value end end def self.read_file(filename) return File.open(filename, "rb") do |f| f.read end rescue return "" end # Clears all memoized values. However, the disk cache (if any is configured) # is still used. def self.clear_memoizations class_variables.each do |name| if name.to_s =~ /^@@has_memoized_/ class_variable_set(name, false) end end end def self.tmpdir result = ENV['TMPDIR'] if result && !result.empty? return result.sub(/\/+\Z/, '') else return '/tmp' end end memoize :tmpdir # Returns the directory in which test executables should be placed. The # returned directory is guaranteed to be writable and guaranteed to # not be mounted with the 'noexec' option. # If no such directory can be found then it will raise a PlatformInfo::RuntimeError # with an appropriate error message. def self.tmpexedir basename = "test-exe.#{Process.pid}.#{Thread.current.object_id}" attempts = [] dir = tmpdir filename = "#{dir}/#{basename}" begin File.open(filename, 'w') do |f| f.puts("#!/bin/sh") end File.chmod(0700, filename) if system(filename) return dir else attempts << { :dir => dir, :error => "This directory's filesystem is mounted with the 'noexec' option." } end rescue Errno::ENOENT attempts << { :dir => dir, :error => "This directory doesn't exist." } rescue Errno::EACCES attempts << { :dir => dir, :error => "This program doesn't have permission to write to this directory." } rescue SystemCallError => e attempts << { :dir => dir, :error => e.message } ensure File.unlink(filename) rescue nil end dir = Dir.pwd filename = "#{dir}/#{basename}" begin File.open(filename, 'w') do |f| f.puts("#!/bin/sh") end File.chmod(0700, filename) if system(filename) return dir else attempts << { :dir => dir, :error => "This directory's filesystem is mounted with the 'noexec' option." } end rescue Errno::ENOENT attempts << { :dir => dir, :error => "This directory doesn't exist." } rescue Errno::EACCES attempts << { :dir => dir, :error => "This program doesn't have permission to write to this directory." } rescue SystemCallError => e attempts << { :dir => dir, :error => e.message } ensure File.unlink(filename) rescue nil end message = "ERROR: Cannot find suitable temporary directory\n" + "In order to run certain tests, this program " + "must be able to write temporary\n" + "executable files to some directory. However no such " + "directory can be found. \n" + "The following directories have been tried:\n\n" attempts.each do |attempt| message << " * #{attempt[:dir]}\n" message << " #{attempt[:error]}\n" end message << "\nYou can solve this problem by telling this program what directory to write\n" << "temporary executable files to, as follows:\n" << "\n" << " Set the $TMPDIR environment variable to the desired directory's filename and\n" << " re-run this program.\n" << "\n" << "Notes:\n" << "\n" << " * If you're using 'sudo'/'rvmsudo', remember that 'sudo'/'rvmsudo' unsets all\n" << " environment variables, so you must set the environment variable *after*\n" << " having gained root privileges.\n" << " * The directory you choose must writeable and must not be mounted with the\n" << " 'noexec' option." raise RuntimeError, message end memoize :tmpexedir def self.rb_config if defined?(::RbConfig) return ::RbConfig::CONFIG else return ::Config::CONFIG end end # Check whether the specified command is in $PATH, and return its # absolute filename. Returns nil if the command is not found. # # This function exists because system('which') doesn't always behave # correctly, for some weird reason. # # When `is_executable` is true, this function checks whether # there is an executable named `name` in $PATH. When false, it # assumes that `name` is not an executable name but a command string # (e.g. "ccache gcc"). It then infers the executable name ("ccache") # from the command string, and checks for that instead. def self.find_command(name, is_executable = true) name = name.to_s if !is_executable && name =~ / / name = name.sub(/ .*/, '') end if name =~ /^\// if File.executable?(name) return name else return nil end else ENV['PATH'].to_s.split(File::PATH_SEPARATOR).each do |directory| next if directory.empty? path = File.join(directory, name) if File.file?(path) && File.executable?(path) return path end end return nil end end def self.find_all_commands(name) search_dirs = ENV['PATH'].to_s.split(File::PATH_SEPARATOR) search_dirs.concat(%w(/bin /sbin /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin)) ["/opt/*/bin", "/opt/*/sbin", "/usr/local/*/bin", "/usr/local/*/sbin"].each do |glob| search_dirs.concat(Dir[glob]) end # Solaris systems may have Apache installations in # /usr/apache2/2.2/bin/sparcv9/ Dir["/usr/apache2/*/bin"].each do |bindir| search_dirs << bindir Dir["#{bindir}/*"].each do |binsubdir| if File.directory?(binsubdir) search_dirs << binsubdir end end end search_dirs.delete("") search_dirs.uniq! result = [] search_dirs.each do |directory| path = File.join(directory, name) if !File.exist?(path) log "Looking for #{path}: not found" elsif !File.file?(path) log "Looking for #{path}: found, but is not a file" elsif !File.executable?(path) log "Looking for #{path}: found, but is not executable" else log "Looking for #{path}: found" result << path end end return result end end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/plugin.rb000644 000765 000024 00000005662 12233035540 025631 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'etc' module PhusionPassenger class Plugin @@hooks = {} @@classes = {} def self.load(name, load_once = true) PLUGIN_DIRS.each do |plugin_dir| if plugin_dir =~ /\A~/ # File.expand_path uses ENV['HOME'] which we don't want. home = Etc.getpwuid(Process.uid).dir plugin_dir = plugin_dir.sub(/\A~/, home) end plugin_dir = File.expand_path(plugin_dir) Dir["#{plugin_dir}/*/#{name}.rb"].each do |filename| if load_once require(filename) else load(filename) end end end end def self.register_hook(name, &block) hooks_list = (@@hooks[name] ||= []) hooks_list << block end def self.call_hook(name, *args, &block) last_result = nil if (hooks_list = @@hooks[name]) hooks_list.each do |callback| last_result = callback.call(*args, &block) end end return last_result end def self.register(name, klass) classes = (@@classes[name] ||= []) classes << klass end def initialize(name, *args, &block) Plugin.load(name) classes = @@classes[name] if classes @instances = classes.map do |klass| klass.new(*args, &block) end else return nil end end def call_hook(name, *args, &block) last_result = nil if @instances @instances.each do |instance| if instance.respond_to?(name.to_sym) last_result = instance.__send__(name.to_sym, *args, &block) end end end return last_result end end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/preloader_shared_helpers.rb000644 000765 000024 00000012707 12233035540 031356 0ustar00honglistaff000000 000000 # encoding: binary # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2011-2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'socket' require 'tmpdir' PhusionPassenger.require_passenger_lib 'utils' PhusionPassenger.require_passenger_lib 'native_support' module PhusionPassenger # Provides shared functions for preloader apps. module PreloaderSharedHelpers extend self def init(options) if !Kernel.respond_to?(:fork) message = "Smart spawning is not available on this Ruby " + "implementation because it does not support `Kernel.fork`. " if ENV['SERVER_SOFTWARE'].to_s =~ /nginx/i message << "Please set `passenger_spawn_method` to `direct`." else message << "Please set `PassengerSpawnMethod` to `direct`." end raise(message) end return options end def accept_and_process_next_client(server_socket) original_pid = Process.pid client = server_socket.accept client.binmode begin command = client.readline rescue EOFError return nil end if command !~ /\n\Z/ STDERR.puts "Command must end with a newline" elsif command == "spawn\n" while client.readline != "\n" # Do nothing. end # Improve copy-on-write friendliness. GC.start pid = fork if pid.nil? $0 = "#{$0} (forking...)" client.puts "OK" client.puts Process.pid client.flush client.sync = true return [:forked, client] elsif defined?(NativeSupport) NativeSupport.detach_process(pid) else Process.detach(pid) end else STDERR.puts "Unknown command '#{command.inspect}'" end return nil ensure if client && Process.pid == original_pid begin client.close rescue Errno::EINVAL # Work around OS X bug. # https://code.google.com/p/phusion-passenger/issues/detail?id=854 end end end def run_main_loop(options) $0 = "Passenger AppPreloader: #{options['app_root']}" client = nil original_pid = Process.pid if defined?(NativeSupport) unix_path_max = NativeSupport::UNIX_PATH_MAX else unix_path_max = options.fetch('UNIX_PATH_MAX', 100).to_i end if options['socket_dir'] socket_dir = options['socket_dir'] socket_prefix = "preloader" else socket_dir = Dir.tmpdir socket_prefix = "PsgPreloader" end socket_filename = nil server = nil Utils.retry_at_most(128, Errno::EADDRINUSE) do socket_filename = "#{socket_dir}/#{socket_prefix}.#{rand(0xFFFFFFFF).to_s(36)}" socket_filename = socket_filename.slice(0, unix_path_max - 10) server = UNIXServer.new(socket_filename) end server.close_on_exec! File.chmod(0600, socket_filename) # Update the dump information just before telling the preloader that we're # ready because the Passenger core will read and memorize this information. LoaderSharedHelpers.dump_all_information(options) puts "!> Ready" puts "!> socket: unix:#{socket_filename}" puts "!> " while true # We call ::select just in case someone overwrites the global select() # function by including ActionView::Helpers in the wrong place. # https://code.google.com/p/phusion-passenger/issues/detail?id=915 ios = Kernel.select([server, STDIN])[0] if ios.include?(server) result, client = accept_and_process_next_client(server) if result == :forked STDIN.reopen(client) STDOUT.reopen(client) STDOUT.sync = true client.close return :forked end end if ios.include?(STDIN) if STDIN.tty? begin # Prevent bash from exiting when we press Ctrl-D. STDIN.read_nonblock(1) rescue Errno::EAGAIN # Do nothing. end end break end end return nil ensure server.close if server if original_pid == Process.pid File.unlink(socket_filename) rescue nil end end end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/public_api.rb000644 000765 000024 00000006235 12233035540 026437 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module PhusionPassenger class << self @@event_starting_worker_process = [] @@event_stopping_worker_process = [] @@event_starting_request_handler_thread = [] @@event_credentials = [] @@event_after_installing_signal_handlers = [] @@event_oob_work = [] @@advertised_concurrency_level = nil @@union_station_key = nil def on_event(name, &block) callback_list_for_event(name) << block end def call_event(name, *args) callback_list_for_event(name).each do |callback| callback.call(*args) end end # For backward compatibility def install_framework_extensions!(user_options = {}) if PhusionPassenger::App.options["analytics"] config = PhusionPassenger::App.options.merge(UnionStationHooks.config) user_options.each_pair do |key, value| config[key.to_s] = value end UnionStationHooks.config.update(config) UnionStationHooks.initialize! end end def advertised_concurrency_level @@advertised_concurrency_level end def advertised_concurrency_level=(value) @@advertised_concurrency_level = value end def union_station_key @@union_station_key end def union_station_key=(value) @@union_station_key = value end private def callback_list_for_event(name) return case name when :starting_worker_process @@event_starting_worker_process when :stopping_worker_process @@event_stopping_worker_process when :starting_request_handler_thread @@event_starting_request_handler_thread when :credentials @@event_credentials when :after_installing_signal_handlers @@event_after_installing_signal_handlers when :oob_work @@event_oob_work else raise ArgumentError, "Unknown event name '#{name}'" end end end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/rack/000755 000765 000024 00000000000 12233035540 024715 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/rack_handler.rb000644 000765 000024 00000006023 12233035540 026740 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. ## Magic comment: begin bootstrap ## libdir = File.expand_path('..', File.dirname(__FILE__)) $LOAD_PATH.unshift(libdir) begin require 'rubygems' rescue LoadError end require 'phusion_passenger' ## Magic comment: end bootstrap ## PhusionPassenger.locate_directories require 'rbconfig' module Rack module Handler class PhusionPassenger class << self def run(app, options = {}) result = system(ruby_executable, '-S', find_passenger_standalone, 'start', *build_args(options)) if !result raise "Error starting Passenger" end end def environment ENV['RAILS_ENV'] || 'development' end def to_s 'Passenger application server' end private def build_args(options) args = ['-e', environment] if options[:Port] args << '-p' args << options[:Port].to_s end if options[:Host] args << '-a' args << options[:Host].to_s end if options[:config] args << '-R' args << options[:config].to_s end args end def rb_config if defined?(::RbConfig) ::RbConfig::CONFIG else ::Config::CONFIG end end def ruby_executable @ruby_executable ||= rb_config['bindir'] + '/' + rb_config['RUBY_INSTALL_NAME'] + rb_config['EXEEXT'] end def find_passenger_standalone ::File.join(::PhusionPassenger.bin_dir, 'passenger') end end end register 'passenger', 'Rack::Handler::PhusionPassenger' def self.default(options = {}) Rack::Handler::PhusionPassenger end end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/request_handler/000755 000765 000024 00000000000 12233035540 027162 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/request_handler.rb000644 000765 000024 00000052205 12233035540 027513 0ustar00honglistaff000000 000000 # encoding: binary # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'socket' require 'fcntl' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'public_api' PhusionPassenger.require_passenger_lib 'message_client' PhusionPassenger.require_passenger_lib 'debug_logging' PhusionPassenger.require_passenger_lib 'native_support' PhusionPassenger.require_passenger_lib 'utils' PhusionPassenger.require_passenger_lib 'ruby_core_enhancements' PhusionPassenger.require_passenger_lib 'ruby_core_io_enhancements' PhusionPassenger.require_passenger_lib 'request_handler/thread_handler' module PhusionPassenger class RequestHandler include DebugLogging include Utils # Signal which will cause the application to exit immediately. HARD_TERMINATION_SIGNAL = "SIGTERM" BACKLOG_SIZE = 500 # String constants which exist to relieve Ruby's garbage collector. IGNORE = 'IGNORE' # :nodoc: DEFAULT = 'DEFAULT' # :nodoc: # A hash containing all server sockets that this request handler listens on. # The hash is in the form of: # # { # name1 => [socket_address1, socket_type1, socket1], # name2 => [socket_address2, socket_type2, socket2], # ... # } # # +name+ is a Symbol. +socket_addressx+ is the address of the socket, # +socket_typex+ is the socket's type (either 'unix' or 'tcp') and # +socketx+ is the actual socket IO objec. # There's guaranteed to be at least one server socket, namely one with the # name +:main+. attr_reader :server_sockets attr_reader :concurrency # A password with which clients must authenticate. Default is unauthenticated. attr_accessor :connect_password # Create a new RequestHandler with the given owner pipe. # +owner_pipe+ must be the readable part of a pipe IO object. # # Additionally, the following options may be given: # - connect_password def initialize(owner_pipe, options = {}) require_option(options, "app_group_name") install_options_as_ivars(self, options, "app", "app_group_name", "connect_password", "union_station_core" ) @keepalive = options.fetch("keepalive", true).to_s == "true" @force_http_session = ENV["_PASSENGER_FORCE_HTTP_SESSION"] == "true" if @force_http_session @connect_password = nil end @thread_handler = options["thread_handler"] || ThreadHandler @concurrency = 1 ############# ############# @server_sockets = {} if should_use_unix_sockets? @main_socket_address, @main_socket = create_unix_socket_on_filesystem(options) else @main_socket_address, @main_socket = create_tcp_socket end @server_sockets[:main] = { :address => @main_socket_address, :socket => @main_socket, :protocol => @force_http_session ? :http_session : :session, :concurrency => @concurrency } @http_socket_address, @http_socket = create_tcp_socket @server_sockets[:http] = { :address => @http_socket_address, :socket => @http_socket, :protocol => :http, :concurrency => 1 } @owner_pipe = owner_pipe @options = options @previous_signal_handlers = {} @main_loop_generation = 0 @main_loop_thread_lock = Mutex.new @main_loop_thread_cond = ConditionVariable.new @threads = [] @threads_mutex = Mutex.new @main_loop_running = false ############# end # Clean up temporary stuff created by the request handler. # # If the main loop was started by #main_loop, then this method may only # be called after the main loop has exited. # # If the main loop was started by #start_main_loop_thread, then this method # may be called at any time, and it will stop the main loop thread. def cleanup if @main_loop_thread @main_loop_thread_lock.synchronize do @graceful_termination_pipe[1].close rescue nil end @main_loop_thread.join end @server_sockets.each_value do |info| socket = info[:socket] type = get_socket_address_type(info[:address]) begin socket.close if !socket.closed? rescue Exception => e # Ignore "stream closed" error, which occurs in some unit tests. # We catch Exception here instead of IOError because of a Ruby 1.8.7 bug. if e.to_s !~ /stream closed/ && e.message.to_s !~ /stream closed/ raise e end end if type == :unix filename = info[:address].sub(/^unix:/, '') File.unlink(filename) rescue nil end end @owner_pipe.close rescue nil end # Check whether the main loop's currently running. def main_loop_running? @main_loop_thread_lock.synchronize do return @main_loop_running end end # Enter the request handler's main loop. def main_loop debug("Entering request handler main loop") reset_signal_handlers begin @graceful_termination_pipe = IO.pipe @graceful_termination_pipe[0].close_on_exec! @graceful_termination_pipe[1].close_on_exec! @main_loop_thread_lock.synchronize do @main_loop_generation += 1 @main_loop_running = true @main_loop_thread_cond.broadcast @select_timeout = nil @selectable_sockets = [] @server_sockets.each_value do |value| socket = value[2] @selectable_sockets << socket if socket end @selectable_sockets << @owner_pipe @selectable_sockets << @graceful_termination_pipe[0] end install_useful_signal_handlers start_threads wait_until_termination_requested wait_until_all_threads_are_idle terminate_threads debug("Request handler main loop exited normally") rescue EOFError # Exit main loop. trace(2, "Request handler main loop interrupted by EOFError exception") rescue Interrupt # Exit main loop. trace(2, "Request handler main loop interrupted by Interrupt exception") rescue SignalException => signal trace(2, "Request handler main loop interrupted by SignalException") if signal.message != HARD_TERMINATION_SIGNAL raise end rescue Exception => e trace(2, "Request handler main loop interrupted by #{e.class} exception") raise ensure debug("Exiting request handler main loop") revert_signal_handlers @main_loop_thread_lock.synchronize do @graceful_termination_pipe[1].close rescue nil @graceful_termination_pipe[0].close rescue nil @selectable_sockets = [] @main_loop_generation += 1 @main_loop_running = false @main_loop_thread_cond.broadcast end end end # Start the main loop in a new thread. This thread will be stopped by #cleanup. def start_main_loop_thread current_generation = @main_loop_generation @main_loop_thread = create_thread_and_abort_on_exception do main_loop end @main_loop_thread_lock.synchronize do while @main_loop_generation == current_generation @main_loop_thread_cond.wait(@main_loop_thread_lock) end end end private def should_use_unix_sockets? # Historical note: # There seems to be a bug in MacOS X Leopard w.r.t. Unix server # sockets file descriptors that are passed to another process. # Usually Unix server sockets work fine, but when they're passed # to another process, then clients that connect to the socket # can incorrectly determine that the client socket is closed, # even though that's not actually the case. More specifically: # recv()/read() calls on these client sockets can return 0 even # when we know EOF is not reached. # # The ApplicationPool infrastructure used to connect to a backend # process's Unix socket in the Passenger core process, and then # pass the connection file descriptor to the web server, which # triggers this kernel bug. We used to work around this by using # TCP sockets instead of Unix sockets; TCP sockets can still fail # with this fake-EOF bug once in a while, but not nearly as often # as with Unix sockets. # # This problem no longer applies today. The web server now passes # all I/O through the Passenger core, and the bug is no longer # triggered. Nevertheless, we keep this function intact so that # if something like this ever happens again, we know why, and we # can easily reactivate the workaround. Or maybe if we just need # TCP sockets for some other reason. #return RUBY_PLATFORM !~ /darwin/ ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby" # Unix domain socket implementation on JRuby # is still bugged as of version 1.7.0. They can # cause unexplicable freezes when used in combination # with threading. return !@force_http_session && ruby_engine != "jruby" end def create_unix_socket_on_filesystem(options) if defined?(NativeSupport) unix_path_max = NativeSupport::UNIX_PATH_MAX else unix_path_max = options.fetch('UNIX_PATH_MAX', 100).to_i end if options['socket_dir'] socket_dir = options['socket_dir'] socket_prefix = "ruby" else socket_dir = Dir.tmpdir socket_prefix = "PsgRubyApp" end retry_at_most(128, Errno::EADDRINUSE) do socket_address = "#{socket_dir}/#{socket_prefix}.#{generate_random_id(:base64)}" socket_address = socket_address.slice(0, unix_path_max - 10) socket = UNIXServer.new(socket_address) socket.listen(BACKLOG_SIZE) socket.binmode socket.sync = true socket.close_on_exec! File.chmod(0600, socket_address) ["unix:#{socket_address}", socket] end end def create_tcp_socket # We use "127.0.0.1" as address in order to force # TCPv4 instead of TCPv6. socket = TCPServer.new('127.0.0.1', 0) socket.listen(BACKLOG_SIZE) socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) socket.binmode socket.sync = true socket.close_on_exec! socket_address = "tcp://127.0.0.1:#{socket.addr[1]}" return [socket_address, socket] end # Reset signal handlers to their default handler, and install some # special handlers for a few signals. The previous signal handlers # will be put back by calling revert_signal_handlers. def reset_signal_handlers Signal.list_trappable.each_key do |signal| begin prev_handler = trap(signal, DEFAULT) if prev_handler != DEFAULT @previous_signal_handlers[signal] = prev_handler end rescue ArgumentError # Signal cannot be trapped; ignore it. end end trap('HUP', IGNORE) PhusionPassenger.call_event(:after_installing_signal_handlers) end def install_useful_signal_handlers trappable_signals = Signal.list_trappable trap('ABRT') do print_status_report abort end if trappable_signals.has_key?('ABRT') trap('QUIT') do print_status_report end if trappable_signals.has_key?('QUIT') end def revert_signal_handlers @previous_signal_handlers.each_pair do |signal, handler| trap(signal, handler) end end def print_status_report warn(Utils.global_backtrace_report) warn("Threads: #{@threads.inspect}") end def start_threads common_options = { :app => @app, :app_group_name => @app_group_name, :connect_password => @connect_password, :union_station_core => @union_station_core, :keepalive_enabled => @keepalive } main_socket_options = common_options.merge( :server_socket => @main_socket, :socket_name => "main socket", :protocol => @server_sockets[:main][:protocol] == :session ? :session : :http ) http_socket_options = common_options.merge( :server_socket => @http_socket, :socket_name => "HTTP socket", :protocol => :http ) # Used for marking threads that have finished initializing, # or failed during initialization. Threads that are not yet done # are not in `initialization_state`. Threads that have succeeded # set their own state to true. Threads that have failed set their # own state to false. initialization_state_mutex = Mutex.new initialization_state_cond = ConditionVariable.new initialization_state = {} set_initialization_state = lambda do |value| initialization_state_mutex.synchronize do initialization_state[Thread.current] = value initialization_state_cond.signal end end set_initialization_state_to_true = lambda do set_initialization_state.call(true) end # Actually start all the threads. thread_handler = @thread_handler expected_nthreads = 0 @threads_mutex.synchronize do @concurrency.times do |i| thread = create_thread_and_abort_on_exception(i) do |number| begin Thread.current[:name] = "Worker #{number + 1}" handler = thread_handler.new(self, main_socket_options) handler.install handler.main_loop(set_initialization_state_to_true) ensure set_initialization_state.call(false) unregister_current_thread end end @threads << thread expected_nthreads += 1 end thread = create_thread_and_abort_on_exception do begin Thread.current[:name] = "HTTP helper worker" handler = thread_handler.new(self, http_socket_options) handler.install handler.main_loop(set_initialization_state_to_true) ensure set_initialization_state.call(false) unregister_current_thread end end @threads << thread expected_nthreads += 1 end # Wait until all threads have finished starting. initialization_state_mutex.synchronize do while initialization_state.size != expected_nthreads initialization_state_cond.wait(initialization_state_mutex) end end end def unregister_current_thread @threads_mutex.synchronize do @threads.delete(Thread.current) end end def wait_until_termination_requested ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby" if ruby_engine == "jruby" # On JRuby, selecting on an input TTY always returns, so # we use threads to do the job. owner_pipe_watcher = IO.pipe owner_pipe_watcher_thread = create_thread_and_abort_on_exception do Thread.current[:name] = "Owner pipe waiter" begin @owner_pipe.read(1) ensure owner_pipe_watcher[1].write('x') end end begin ios = select([owner_pipe_watcher[0], @graceful_termination_pipe[0]])[0] if ios.include?(owner_pipe_watcher[0]) trace(2, "Owner pipe closed") else trace(2, "Graceful termination pipe closed") end ensure owner_pipe_watcher_thread.kill owner_pipe_watcher_thread.join owner_pipe_watcher[0].close if !owner_pipe_watcher[0].closed? owner_pipe_watcher[1].close if !owner_pipe_watcher[1].closed? end else ios = select([@owner_pipe, @graceful_termination_pipe[0]])[0] if ios.include?(@owner_pipe) trace(2, "Owner pipe closed") else trace(2, "Graceful termination pipe closed") end end end def wakeup_all_threads threads = [] if get_socket_address_type(@server_sockets[:main][:address]) == :unix && !File.exist?(@server_sockets[:main][:address].sub(/^unix:/, '')) # It looks like someone deleted the Unix domain socket we listen on. # This makes it impossible to wake up the worker threads gracefully, # so we hard kill them. warn("Unix domain socket gone; force aborting all threads") @threads_mutex.synchronize do @threads.each do |thread| thread.raise(RuntimeError.new("Force abort")) end end else @concurrency.times do threads << create_thread_and_abort_on_exception(@server_sockets[:main][:address]) do |address| begin debug("Shutting down worker thread by connecting to #{address}") connect_to_server(address).close rescue Errno::ECONNREFUSED debug("Worker thread listening on #{address} already exited") rescue SystemCallError, IOError => e debug("Error shutting down worker thread (#{address}): #{e} (#{e.class})") end end end end threads << create_thread_and_abort_on_exception(@server_sockets[:http][:address]) do |address| begin debug("Shutting down HTTP thread by connecting to #{address}") connect_to_server(address).close rescue Errno::ECONNREFUSED debug("Worker thread listening on #{address} already exited") rescue SystemCallError, IOError => e debug("Error shutting down HTTP thread (#{address}): #{e} (#{e.class})") end end return threads end def terminate_threads debug("Stopping all threads") threads = @threads_mutex.synchronize do @threads.dup end threads.each do |thr| thr.raise(ThreadHandler::Interrupted.new) end threads.each do |thr| thr.join end debug("All threads stopped") end def wait_until_all_threads_are_idle debug("Waiting until all threads have become idle...") # We wait until 100 ms have passed since all handlers have become # interruptable and remained in the same iterations. done = false while !done handlers = @threads_mutex.synchronize do @threads.map do |thr| thr[:passenger_thread_handler] end end debug("There are currently #{handlers.size} threads") if handlers.empty? # There are no threads, so we're done. done = true break end # Record initial state. handlers.each { |h| h.stats_mutex.lock } iterations = handlers.map { |h| h.iteration } handlers.each { |h| h.stats_mutex.unlock } start_time = Time.now sleep 0.01 while true if handlers.size != @threads_mutex.synchronize { @threads.size } debug("The number of threads changed. Restarting waiting algorithm") break end # Record current state. handlers.each { |h| h.stats_mutex.lock } all_interruptable = handlers.all? { |h| h.interruptable } new_iterations = handlers.map { |h| h.iteration } # Are all threads interruptable and has there been no activity # since last time we checked? if all_interruptable && new_iterations == iterations # Yes. If enough time has passed then we're done. handlers.each { |h| h.stats_mutex.unlock } if Time.now >= start_time + 0.1 done = true break end else # No. We reset the timer and check again later. handlers.each { |h| h.stats_mutex.unlock } iterations = new_iterations start_time = Time.now sleep 0.01 end end end debug("All threads are now idle") end end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/ruby_core_enhancements.rb000644 000765 000024 00000012402 12233035540 031042 0ustar00honglistaff000000 000000 # encoding: binary # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. begin require 'rubygems' rescue LoadError end require 'socket' require 'thread' if (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby") && RUBY_VERSION < "1.8.7" begin require 'fastthread' rescue LoadError abort "You are using a very old Ruby version. You must install " + "the 'fastthread' gem to fix some bugs in the Ruby threading system: " + "gem install fastthread" end end require 'pathname' class Exception def backtrace_string(current_location = nil) if current_location.nil? location = nil else location = "in #{current_location} " end current_thread = Thread.current if !(thread_id = current_thread[:id]) current_thread.to_s =~ /:(0x[0-9a-f]+)/i thread_id = $1 || '?' end if thread_name = current_thread[:name] thread_name = "(#{thread_name})" end return "*** Exception #{self.class} #{location}" << "(#{self}) (process #{$$}, thread #{thread_id}#{thread_name}):\n" << "\tfrom " << backtrace.join("\n\tfrom ") end end class Dir # The current working directory may contain one or more symlinks # in its path. Both Dir.pwd and the C getcwd() call resolve symlinks # in the path. # # It turns out that there is no such thing as a path without # unresolved symlinks. The shell presents a working directory with # unresolved symlinks (which it calls the "logical working directory"), # but that is an illusion provided by the shell. The shell reports # the logical working directory though the PWD environment variable. # # This method tries to use the PWD environment variable if it # matches the actual working directory. # # See also: # https://github.com/phusion/passenger/issues/1596#issuecomment-138154045 # http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/pwd.c # http://www.opensource.apple.com/source/shell_cmds/shell_cmds-170/pwd/pwd.c def self.logical_pwd physical_pwd = Dir.pwd logical_pwd = ENV['PWD'] if logical_pwd.nil? || logical_pwd.empty? return physical_pwd end # Check whether $PWD matches the actual working directory. # This algorithm similar to the one used by GNU coreutils. begin logical_stat = File.stat(logical_pwd) physical_stat = File.stat(physical_pwd) if logical_stat.ino == physical_stat.ino && logical_stat.dev == physical_stat.dev logical_pwd else physical_pwd end rescue SystemCallError physical_pwd end end end class File # Dir.pwd resolves symlinks. So in turn, File.expand_path/File.absolute_path # do that too. This method fixes that by using Dir.logical_pwd. if File.respond_to?(:absolute_path) def self.absolute_logical_path(path, base = Dir.logical_pwd) return File.absolute_path(path, base) end else def self.absolute_logical_path(path, base = Dir.logical_pwd) return File.expand_path(path, base) end end end module Signal # Like Signal.list, but only returns signals that we can actually trap. def self.list_trappable ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby" case ruby_engine when /jruby/ result = Signal.list.dup result.delete("QUIT") result.delete("ILL") result.delete("FPE") result.delete("SEGV") result.delete("USR1") result.delete("IOT") else result = Signal.list.dup result.delete("ALRM") result.delete("VTALRM") end # Don't touch SIGCHLD no matter what! On OS X waitpid() will # malfunction if SIGCHLD doesn't have a correct handler. result.delete("CLD") result.delete("CHLD") # Other stuff that we don't want to trap no matter which # Ruby engine. result.delete("STOP") result.delete("KILL") result.delete("EXIT") return result end end module GC if !respond_to?(:copy_on_write_friendly?) # Checks whether the current Ruby interpreter's garbage # collector is copy-on-write friendly. def self.copy_on_write_friendly? return false end end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/ruby_core_io_enhancements.rb000644 000765 000024 00000007044 12233035540 031537 0ustar00honglistaff000000 000000 # encoding: binary # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'native_support' class IO if defined?(PhusionPassenger::NativeSupport) # Writes all of the strings in the +components+ array into the given file # descriptor using the +writev()+ system call. Unlike IO#write, this method # does not require one to concatenate all those strings into a single buffer # in order to send the data in a single system call. Thus, #writev is a great # way to perform zero-copy I/O. # # Unlike the raw writev() system call, this method ensures that all given # data is written before returning, by performing multiple writev() calls # and whatever else is necessary. # # io.writev(["hello ", "world", "\n"]) def writev(components) return PhusionPassenger::NativeSupport.writev(fileno, components) end # Like #writev, but accepts two arrays. The data is written in the given order. # # io.writev2(["hello ", "world", "\n"], ["another ", "message\n"]) def writev2(components, components2) return PhusionPassenger::NativeSupport.writev2(fileno, components, components2) end # Like #writev, but accepts three arrays. The data is written in the given order. # # io.writev3(["hello ", "world", "\n"], # ["another ", "message\n"], # ["yet ", "another ", "one", "\n"]) def writev3(components, components2, components3) return PhusionPassenger::NativeSupport.writev3(fileno, components, components2, components3) end else def writev(components) return write(components.pack('a*' * components.size)) end def writev2(components, components2) joined = components + components2 return write(joined.pack('a*' * joined.size)) end def writev3(components, components2, components3) joined = components + components2 + components3 return write(joined.pack('a*' * joined.size)) end end if IO.method_defined?(:close_on_exec=) def close_on_exec! begin self.close_on_exec = true rescue NotImplementedError end end else require 'fcntl' if defined?(Fcntl::F_SETFD) def close_on_exec! fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) end else def close_on_exec! end end end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/simple_benchmarking.rb000644 000765 000024 00000003632 12233035540 030327 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. class Object # :nodoc: @@benchmark_results = {} def b!(name) time1 = Time.now begin yield ensure time2 = Time.now @@benchmark_results[name] = 0 unless @@benchmark_results.has_key?(name) @@benchmark_results[name] += time2 - time1 end end def benchmark_report(main = nil) total = 0 if main.nil? @@benchmark_results.each_value do |time| total += time end else total = @@benchmark_results[main] end @@benchmark_results.each_pair do |name, time| printf "%-12s: %.4f (%.2f%%)\n", name, time, time / total * 100 end printf "-- Total: %.4f\n", total end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/standalone/000755 000765 000024 00000000000 12233035540 026125 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/utils/000755 000765 000024 00000000000 12233035540 025135 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/utils.rb000644 000765 000024 00000017376 12233035540 025500 0ustar00honglistaff000000 000000 # encoding: binary # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'base64' module PhusionPassenger # Utility functions. module Utils extend self # Make methods available as class methods. def self.included(klass) # When included into another class, make sure that Utils # methods are made private. public_instance_methods(false).each do |method_name| klass.send(:private, method_name) end end # Generate a long, cryptographically secure random ID string, which # is also a valid filename. def generate_random_id(method) data = File.open("/dev/urandom", "rb") do |f| f.read(64) end case method when :base64 data = base64(data) data.gsub!("+", '') data.gsub!("/", '') data.gsub!(/==$/, '') return data when :hex return data.unpack('H*')[0] else raise ArgumentError, "Invalid method #{method.inspect}" end end def retry_at_most(n, *exceptions) n.times do |i| begin return yield rescue *exceptions if i == n - 1 raise end end end end # Print the given exception, including the stack trace, to STDERR. # # +current_location+ is a string which describes where the code is # currently at. Usually the current class name will be enough. # It may be nil. # # This method requires 'ruby_core_enhancements'. If 'debug_logging' # is loaded and included in the current module, it will use that for # logging. def print_exception(current_location, exception, destination = nil) if !exception.is_a?(SystemExit) data = exception.backtrace_string(current_location) if defined?(DebugLogging) && self.is_a?(DebugLogging) error(data) else destination ||= STDERR destination.puts(data) destination.flush if destination.respond_to?(:flush) end end end # A wrapper around Thread.new that installs a default exception handler. # If an uncaught exception is encountered, it will immediately log the # exception and abort the entire program. # # Thread#abort_on_exception is also supposed to do that, but the problem # is that it is implemented by forwarding the uncaught exception # to the main thread, which may not expect that particular exception # and may not handle it properly. The exception could be forwarded to # the main thread during any point of the main thread's execution. # # This method requires 'thread' and 'ruby_core_enhancements'. # If 'debug_logging' is loaded and included in the current module, # it will use that for logging. def create_thread_and_abort_on_exception(*args) Thread.new do Thread.current.abort_on_exception = true begin yield(*args) rescue SystemExit raise rescue Exception => e print_exception(nil, e) exit(1) end end end def get_socket_address_type(address) if address =~ %r{^unix:.} return :unix elsif address =~ %r{^tcp://.} return :tcp else return :unknown end end def connect_to_server(address) case get_socket_address_type(address) when :unix return UNIXSocket.new(address.sub(/^unix:/, '')) when :tcp host, port = address.sub(%r{^tcp://}, '').split(':', 2) port = port.to_i return TCPSocket.new(host, port) else raise ArgumentError, "Unknown socket address type for '#{address}'." end end def local_socket_address?(address) case get_socket_address_type(address) when :unix return true when :tcp host, port = address.sub(%r{^tcp://}, '').split(':', 2) return host == "127.0.0.1" || host == "::1" || host == "localhost" else raise ArgumentError, "Unknown socket address type for '#{address}'." end end # Checks whether the given process exists. def process_is_alive?(pid) begin Process.kill(0, pid) return true rescue Errno::ESRCH return false rescue SystemCallError => e return true end end def require_option(hash, key) if hash.has_key?(key) return hash[key] else raise ArgumentError, "Option #{key.inspect} required" end end def install_options_as_ivars(object, options, *keys) keys.each do |key| object.instance_variable_set("@#{key}", options[key]) end end if Base64.respond_to?(:strict_encode64) def base64(data) Base64.strict_encode64(data) end else # Base64-encodes the given data. Newlines are removed. # This is like `Base64.strict_encode64`, but also works # on Ruby 1.8 which doesn't have that method. def base64(data) result = Base64.encode64(data) result.delete!("\n") result end end # Returns a string which reports the backtraces for all threads, # or if that's not supported the backtrace for the current thread. def global_backtrace_report if Kernel.respond_to?(:caller_for_all_threads) all_thread_stacks = caller_for_all_threads elsif Thread.respond_to?(:list) && Thread.public_method_defined?(:backtrace) all_thread_stacks = {} Thread.list.each do |thread| all_thread_stacks[thread] = thread.backtrace end end output = "========== Process #{Process.pid}: backtrace dump ==========\n" if all_thread_stacks all_thread_stacks.each_pair do |thread, stack| if thread_name = thread[:name] thread_name = "(#{thread_name})" end stack ||= ["(empty)"] output << ("-" * 60) << "\n" output << "# Thread: #{thread.inspect}#{thread_name}, " if thread == Thread.main output << "[main thread], " end if thread == Thread.current output << "[current thread], " end output << "alive = #{thread.alive?}\n" output << ("-" * 60) << "\n" output << " " << stack.join("\n ") output << "\n\n" end else output << ("-" * 60) << "\n" output << "# Current thread: #{Thread.current.inspect}\n" output << ("-" * 60) << "\n" output << " " << caller.join("\n ") end return output end #################################### end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/000755 000765 000024 00000000000 12233035540 025272 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/crash_watch/000755 000765 000024 00000000000 12233035540 027560 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/daemon_controller/000755 000765 000024 00000000000 12233035540 031000 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/daemon_controller.rb000644 000765 000024 00000073311 12233035540 031332 0ustar00honglistaff000000 000000 # daemon_controller, library for robust daemon management # Copyright (c) 2010-2016 Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'tempfile' require 'fcntl' require 'socket' require 'pathname' require 'timeout' if Process.respond_to?(:spawn) require 'rbconfig' end PhusionPassenger.require_passenger_lib 'vendor/daemon_controller/lock_file' module PhusionPassenger # Main daemon controller object. See the README for an introduction and tutorial. class DaemonController ALLOWED_CONNECT_EXCEPTIONS = [Errno::ECONNREFUSED, Errno::ENETUNREACH, Errno::ETIMEDOUT, Errno::ECONNRESET, Errno::EINVAL, Errno::EADDRNOTAVAIL] SPAWNER_FILE = File.expand_path(File.join(File.dirname(__FILE__), "daemon_controller", "spawn.rb")) class Error < StandardError end class TimeoutError < Error end class AlreadyStarted < Error end class StartError < Error end class StartTimeout < TimeoutError end class StopError < Error end class StopTimeout < TimeoutError end class ConnectError < Error end class DaemonizationTimeout < TimeoutError end # Create a new DaemonController object. # # === Mandatory options # # [:identifier] # A human-readable, unique name for this daemon, e.g. "Sphinx search server". # This identifier will be used in some error messages. On some platforms, it will # be used for concurrency control: on such platforms, no two DaemonController # objects will operate on the same identifier on the same time. # # [:start_command] # The command to start the daemon. This must be a a String, e.g. # "mongrel_rails start -e production", or a Proc which returns a String. # # If the value is a Proc, and the +before_start+ option is given too, then # the +start_command+ Proc is guaranteed to be called after the +before_start+ # Proc is called. # # [:ping_command] # The ping command is used to check whether the daemon can be connected to. # It is also used to ensure that #start only returns when the daemon can be # connected to. # # The value may be a command string. This command must exit with an exit code of # 0 if the daemon can be successfully connected to, or exit with a non-0 exit # code on failure. # # The value may also be an Array which specifies the socket address of the daemon. # It must be in one of the following forms: # - [:tcp, host_name, port] # - [:unix, filename] # # The value may also be a Proc, which returns an expression that evaluates to # true (indicating that the daemon can be connected to) or false (failure). # If the Proc raises Errno::ECONNREFUSED, Errno::ENETUNREACH, Errno::ETIMEDOUT # Errno::ECONNRESET, Errno::EINVAL or Errno::EADDRNOTAVAIL then that also # means that the daemon cannot be connected to. # NOTE: if the ping command returns an object which responds to # #close, then that method will be called on it. # This makes it possible to specify a ping command such as # lambda { TCPSocket.new('localhost', 1234) }, without having to worry # about closing it afterwards. # Any exceptions raised by #close are ignored. # # [:pid_file] # The PID file that the daemon will write to. Used to check whether the daemon # is running. # # [:lock_file] # The lock file to use for serializing concurrent daemon management operations. # Defaults to "(filename of PID file).lock". # # [:log_file] # The log file that the daemon will write to. It will be consulted to see # whether the daemon has printed any error messages during startup. # # === Optional options # [:stop_command] # A command to stop the daemon with, e.g. "/etc/rc.d/nginx stop". If no stop # command is given (i.e. +nil+), then DaemonController will stop the daemon # by killing the PID written in the PID file. # # The default value is +nil+. # # [:restart_command] # A command to restart the daemon with, e.g. "/etc/rc.d/nginx restart". If # no restart command is given (i.e. +nil+), then DaemonController will # restart the daemon by calling #stop and #start. # # The default value is +nil+. # # [:before_start] # This may be a Proc. It will be called just before running the start command. # The before_start proc is not subject to the start timeout. # # [:start_timeout] # The maximum amount of time, in seconds, that #start may take to start # the daemon. Since #start also waits until the daemon can be connected to, # that wait time is counted as well. If the daemon does not start in time, # then #start will raise an exception. # # The default value is 15. # # [:stop_timeout] # The maximum amount of time, in seconds, that #stop may take to stop # the daemon. Since #stop also waits until the daemon is no longer running, # that wait time is counted as well. If the daemon does not stop in time, # then #stop will raise an exception. # # The default value is 15. # # [:log_file_activity_timeout] # Once a daemon has gone into the background, it will become difficult to # know for certain whether it is still initializing or whether it has # failed and exited, until it has written its PID file. Suppose that it # failed with an error after daemonizing but before it has written its PID file; # not many system administrators want to wait 15 seconds (the default start # timeout) to be notified of whether the daemon has terminated with an error. # # An alternative way to check whether the daemon has terminated with an error, # is by checking whether its log file has been recently updated. If, after the # daemon has started, the log file hasn't been updated for the amount of seconds # given by the :log_file_activity_timeout option, then the daemon is assumed to # have terminated with an error. # # The default value is 7. # # [:daemonize_for_me] # Normally daemon_controller will wait until the daemon has daemonized into the # background, in order to capture any errors that it may print on stdout or # stderr before daemonizing. However, if the daemon doesn't support daemonization # for some reason, then setting this option to true will cause daemon_controller # to do the daemonization for the daemon. # # The default is false. # # [:keep_ios] # Upon spawning the daemon, daemon_controller will normally close all file # descriptors except stdin, stdout and stderr. However if there are any file # descriptors you want to keep open, specify the IO objects here. This must be # an array of IO objects. # # [:env] # This must be a Hash. The hash will contain the environment variables available # to be made available to the daemon. Hash keys must be strings, not symbols. def initialize(options) [:identifier, :start_command, :ping_command, :pid_file, :log_file].each do |option| if !options.has_key?(option) raise ArgumentError, "The ':#{option}' option is mandatory." end end @identifier = options[:identifier] @start_command = options[:start_command] @stop_command = options[:stop_command] @ping_command = options[:ping_command] @restart_command = options[:restart_command] @ping_interval = options[:ping_interval] || 0.1 @pid_file = options[:pid_file] @log_file = options[:log_file] @before_start = options[:before_start] @start_timeout = options[:start_timeout] || 15 @stop_timeout = options[:stop_timeout] || 15 @log_file_activity_timeout = options[:log_file_activity_timeout] || 7 @daemonize_for_me = options[:daemonize_for_me] @keep_ios = options[:keep_ios] || [] @lock_file = determine_lock_file(options, @identifier, @pid_file) @env = options[:env] || {} end # Start the daemon and wait until it can be pinged. # # Raises: # - AlreadyStarted - the daemon is already running. # - StartError - the start command failed. # - StartTimeout - the daemon did not start in time. This could also # mean that the daemon failed after it has gone into the background. def start @lock_file.exclusive_lock do start_without_locking end end # Connect to the daemon by running the given block, which contains the # connection logic. If the daemon isn't already running, then it will be # started. # # The block must return nil or raise Errno::ECONNREFUSED, Errno::ENETUNREACH, # Errno::ETIMEDOUT, Errno::ECONNRESET, Errno::EINVAL and Errno::EADDRNOTAVAIL # to indicate that the daemon cannot be # connected to. It must return non-nil if the daemon can be connected to. # Upon successful connection, the return value of the block will # be returned by #connect. # # Note that the block may be called multiple times. # # Raises: # - StartError - an attempt to start the daemon was made, but the start # command failed with an error. # - StartTimeout - an attempt to start the daemon was made, but the daemon # did not start in time, or it failed after it has gone into the background. # - ConnectError - the daemon wasn't already running, but we couldn't connect # to the daemon even after starting it. def connect connection = nil @lock_file.shared_lock do begin connection = yield rescue *ALLOWED_CONNECT_EXCEPTIONS connection = nil end end if connection.nil? @lock_file.exclusive_lock do if !daemon_is_running? start_without_locking end connect_exception = nil begin connection = yield rescue *ALLOWED_CONNECT_EXCEPTIONS => e connection = nil connect_exception = e end if connection.nil? # Daemon is running but we couldn't connect to it. Possible # reasons: # - The daemon froze. # - Bizarre security restrictions. # - There's a bug in the yielded code. if connect_exception raise ConnectError, "Cannot connect to the daemon: #{connect_exception} (#{connect_exception.class})" else raise ConnectError, "Cannot connect to the daemon" end else connection end end else connection end end # Stop the daemon and wait until it has exited. # # Raises: # - StopError - the stop command failed. # - StopTimeout - the daemon didn't stop in time. def stop @lock_file.exclusive_lock do begin Timeout.timeout(@stop_timeout, Timeout::Error) do kill_daemon wait_until do !daemon_is_running? end end rescue Timeout::Error raise StopTimeout, "Daemon '#{@identifier}' did not exit in time" end end end # Restarts the daemon. Uses the restart_command if provided, otherwise # calls #stop and #start. def restart if @restart_command run_command(@restart_command) else stop start end end # Returns the daemon's PID, as reported by its PID file. Returns the PID # as an integer, or nil there is no valid PID in the PID file. # # This method doesn't check whether the daemon's actually running. # Use #running? if you want to check whether it's actually running. # # Raises SystemCallError or IOError if something went wrong during # reading of the PID file. def pid @lock_file.shared_lock do read_pid_file end end # Checks whether the daemon is still running. This is done by reading # the PID file and then checking whether there is a process with that # PID. # # Raises SystemCallError or IOError if something went wrong during # reading of the PID file. def running? @lock_file.shared_lock do daemon_is_running? end end # Checks whether ping Unix domain sockets is supported. Currently # this is supported on all Ruby implementations, except JRuby. def self.can_ping_unix_sockets? RUBY_PLATFORM != "java" end private def start_without_locking if daemon_is_running? raise AlreadyStarted, "Daemon '#{@identifier}' is already started" end save_log_file_information delete_pid_file begin started = false before_start Timeout.timeout(@start_timeout, Timeout::Error) do done = false spawn_daemon record_activity # We wait until the PID file is available and until # the daemon responds to pings, but we wait no longer # than @start_timeout seconds in total (including daemon # spawn time). # Furthermore, if the log file hasn't changed for # @log_file_activity_timeout seconds, and the PID file # still isn't available or the daemon still doesn't # respond to pings, then assume that the daemon has # terminated with an error. wait_until do if log_file_has_changed? record_activity elsif no_activity?(@log_file_activity_timeout) raise Timeout::Error, "Daemon seems to have exited" end pid_file_available? end wait_until(@ping_interval) do if log_file_has_changed? record_activity elsif no_activity?(@log_file_activity_timeout) raise Timeout::Error, "Daemon seems to have exited" end run_ping_command || !daemon_is_running? end started = run_ping_command end result = started rescue DaemonizationTimeout, Timeout::Error => e start_timed_out if pid_file_available? kill_daemon_with_signal(true) end if e.is_a?(DaemonizationTimeout) result = :daemonization_timeout else result = :start_timeout end end if !result raise(StartError, differences_in_log_file || "Daemon '#{@identifier}' failed to start.") elsif result == :daemonization_timeout raise(StartTimeout, differences_in_log_file || "Daemon '#{@identifier}' didn't daemonize in time.") elsif result == :start_timeout raise(StartTimeout, differences_in_log_file || "Daemon '#{@identifier}' failed to start in time.") else true end end def before_start if @before_start @before_start.call end end def spawn_daemon if @start_command.respond_to?(:call) run_command(@start_command.call) else run_command(@start_command) end end def kill_daemon if @stop_command begin run_command(@stop_command) rescue StartError => e raise StopError, e.message end else kill_daemon_with_signal end end def kill_daemon_with_signal(force = false) pid = read_pid_file if pid if force Process.kill('SIGKILL', pid) else Process.kill('SIGTERM', pid) end end rescue Errno::ESRCH, Errno::ENOENT end def daemon_is_running? begin pid = read_pid_file rescue Errno::ENOENT # The PID file may not exist, or another thread/process # executing #running? may have just deleted the PID file. # So we catch this error. pid = nil end if pid.nil? false elsif check_pid(pid) true else delete_pid_file false end end def read_pid_file pid = File.read(@pid_file).strip if pid =~ /\A\d+\Z/ pid.to_i else nil end end def delete_pid_file File.unlink(@pid_file) rescue Errno::EPERM, Errno::EACCES, Errno::ENOENT # ignore end def check_pid(pid) Process.kill(0, pid) true rescue Errno::ESRCH false rescue Errno::EPERM # We didn't have permission to kill the process. Either the process # is owned by someone else, or the system has draconian security # settings and we aren't allowed to kill *any* process. Assume that # the process is running. true end def wait_until(sleep_interval = 0.1) while !yield sleep(sleep_interval) end end def wait_until_pid_file_is_available_or_log_file_has_changed while !(pid_file_available? || log_file_has_changed?) sleep 0.1 end pid_file_is_available? end def wait_until_daemon_responds_to_ping_or_has_exited_or_log_file_has_changed while !(run_ping_command || !daemon_is_running? || log_file_has_changed?) sleep(@ping_interval) end run_ping_command end def record_activity @last_activity_time = Time.now end # Check whether there has been no recorded activity in the past +seconds+ seconds. def no_activity?(seconds) Time.now - @last_activity_time > seconds end def pid_file_available? File.exist?(@pid_file) && File.stat(@pid_file).size != 0 end # This method does nothing and only serves as a hook for the unit test. def start_timed_out end # This method does nothing and only serves as a hook for the unit test. def daemonization_timed_out end def save_log_file_information @original_log_file_stat = File.stat(@log_file) rescue nil @current_log_file_stat = @original_log_file_stat end def log_file_has_changed? if @current_log_file_stat stat = File.stat(@log_file) rescue nil if stat result = @current_log_file_stat.mtime != stat.mtime || @current_log_file_stat.size != stat.size @current_log_file_stat = stat result else true end else false end end def differences_in_log_file if @original_log_file_stat && @original_log_file_stat.file? File.open(@log_file, 'r') do |f| f.seek(@original_log_file_stat.size, IO::SEEK_SET) diff = f.read.strip if diff.empty? nil else diff end end else nil end rescue Errno::ENOENT, Errno::ESPIPE # ESPIPE means the log file is a pipe. nil end def determine_lock_file(options, identifier, pid_file) if options[:lock_file] LockFile.new(File.expand_path(options[:lock_file])) else LockFile.new(File.expand_path(pid_file + ".lock")) end end def self.fork_supported? RUBY_PLATFORM != "java" && RUBY_PLATFORM !~ /win32/ end def self.spawn_supported? # Process.spawn doesn't work very well in JRuby. Process.respond_to?(:spawn) && RUBY_PLATFORM != "java" end def run_command(command) if should_capture_output_while_running_command? run_command_while_capturing_output(command) else run_command_without_capturing_output(command) end end def should_capture_output_while_running_command? if is_std_channel_chardev?(@log_file) false else begin real_log_file = Pathname.new(@log_file).realpath.to_s rescue SystemCallError real_log_file = nil end if real_log_file !is_std_channel_chardev?(real_log_file) else true end end end def is_std_channel_chardev?(path) path == "/dev/stdout" || path == "/dev/stderr" || path == "/dev/fd/1" || path == "/dev/fd/2" || path =~ %r(\A/proc/([0-9]+|self)/fd/[12]\Z) end def run_command_while_capturing_output(command) # Create tempfile for storing the command's output. tempfile = Tempfile.new('daemon-output') tempfile_path = tempfile.path File.chmod(0666, tempfile_path) tempfile.close if self.class.fork_supported? || self.class.spawn_supported? if Process.respond_to?(:spawn) options = { :in => "/dev/null", :out => tempfile_path, :err => tempfile_path, :close_others => true } @keep_ios.each do |io| options[io] = io end if @daemonize_for_me pid = Process.spawn(@env, ruby_interpreter, SPAWNER_FILE, command, options) else pid = Process.spawn(@env, command, options) end else pid = safe_fork(@daemonize_for_me) do ObjectSpace.each_object(IO) do |obj| if !@keep_ios.include?(obj) obj.close rescue nil end end STDIN.reopen("/dev/null", "r") STDOUT.reopen(tempfile_path, "w") STDERR.reopen(tempfile_path, "w") ENV.update(@env) exec(command) end end # run_command might be running in a timeout block (like # in #start_without_locking). begin interruptable_waitpid(pid) rescue Errno::ECHILD # Maybe a background thread or whatever waitpid()'ed # this child process before we had the chance. There's # no way to obtain the exit status now. Assume that # it started successfully; if it didn't we'll know # that later by checking the PID file and by pinging # it. return rescue Timeout::Error daemonization_timed_out # If the daemon doesn't fork into the background # in time, then kill it. begin Process.kill('SIGTERM', pid) rescue SystemCallError end begin Timeout.timeout(5, Timeout::Error) do begin interruptable_waitpid(pid) rescue SystemCallError end end rescue Timeout::Error begin Process.kill('SIGKILL', pid) interruptable_waitpid(pid) rescue SystemCallError end end raise DaemonizationTimeout end if $?.exitstatus != 0 raise StartError, File.read(tempfile_path).strip end else if @env && !@env.empty? raise "Setting the :env option is not supported on this Ruby implementation." elsif @daemonize_for_me raise "Setting the :daemonize_for_me option is not supported on this Ruby implementation." end cmd = "#{command} >\"#{tempfile_path}\"" cmd << " 2>\"#{tempfile_path}\"" unless PLATFORM =~ /mswin/ if !system(cmd) raise StartError, File.read(tempfile_path).strip end end ensure File.unlink(tempfile_path) rescue nil end def run_command_without_capturing_output(command) if self.class.fork_supported? || self.class.spawn_supported? if Process.respond_to?(:spawn) options = { :in => "/dev/null", :out => :out, :err => :err, :close_others => true } @keep_ios.each do |io| options[io] = io end if @daemonize_for_me pid = Process.spawn(@env, ruby_interpreter, SPAWNER_FILE, command, options) else pid = Process.spawn(@env, command, options) end else pid = safe_fork(@daemonize_for_me) do ObjectSpace.each_object(IO) do |obj| if !@keep_ios.include?(obj) obj.close rescue nil end end STDIN.reopen("/dev/null", "r") ENV.update(@env) exec(command) end end # run_command might be running in a timeout block (like # in #start_without_locking). begin interruptable_waitpid(pid) rescue Errno::ECHILD # Maybe a background thread or whatever waitpid()'ed # this child process before we had the chance. There's # no way to obtain the exit status now. Assume that # it started successfully; if it didn't we'll know # that later by checking the PID file and by pinging # it. return rescue Timeout::Error daemonization_timed_out # If the daemon doesn't fork into the background # in time, then kill it. begin Process.kill('SIGTERM', pid) rescue SystemCallError end begin Timeout.timeout(5, Timeout::Error) do begin interruptable_waitpid(pid) rescue SystemCallError end end rescue Timeout::Error begin Process.kill('SIGKILL', pid) interruptable_waitpid(pid) rescue SystemCallError end end raise DaemonizationTimeout end if $?.exitstatus != 0 raise StartError, "Daemon '#{@identifier}' failed to start." end else if @env && !@env.empty? raise "Setting the :env option is not supported on this Ruby implementation." elsif @daemonize_for_me raise "Setting the :daemonize_for_me option is not supported on this Ruby implementation." end if !system(command) raise StartError, "Daemon '#{@identifier}' failed to start." end end end def run_ping_command if @ping_command.respond_to?(:call) begin value = @ping_command.call if value.respond_to?(:close) value.close rescue nil end value rescue *ALLOWED_CONNECT_EXCEPTIONS false end elsif @ping_command.is_a?(Array) type, *args = @ping_command if self.class.can_ping_unix_sockets? case type when :tcp hostname, port = args sockaddr = Socket.pack_sockaddr_in(port, hostname) ping_tcp_socket(sockaddr) when :unix socket_domain = Socket::Constants::AF_LOCAL sockaddr = Socket.pack_sockaddr_un(args[0]) ping_socket(socket_domain, sockaddr) else raise ArgumentError, "Unknown ping command type #{type.inspect}" end else case type when :tcp hostname, port = args ping_socket(hostname, port) when :unix raise "Pinging Unix domain sockets is not supported on this Ruby implementation" else raise ArgumentError, "Unknown ping command type #{type.inspect}" end end else system(@ping_command) end end if !can_ping_unix_sockets? require 'java' def ping_socket(host_name, port) channel = java.nio.channels.SocketChannel.open begin address = java.net.InetSocketAddress.new(host_name, port) channel.configure_blocking(false) if channel.connect(address) return true end deadline = Time.now.to_f + 0.1 done = false while true begin if channel.finish_connect return true end rescue java.net.ConnectException => e if e.message =~ /Connection refused/i return false else throw e end end # Not done connecting and no error. sleep 0.01 if Time.now.to_f >= deadline return false end end ensure channel.close end end else def ping_socket(socket_domain, sockaddr) begin socket = Socket.new(socket_domain, Socket::Constants::SOCK_STREAM, 0) begin socket.connect_nonblock(sockaddr) rescue Errno::ENOENT, Errno::EINPROGRESS, Errno::EAGAIN, Errno::EWOULDBLOCK if select(nil, [socket], nil, 0.1) begin socket.connect_nonblock(sockaddr) rescue Errno::EISCONN rescue Errno::EINVAL if RUBY_PLATFORM =~ /freebsd/i raise Errno::ECONNREFUSED else raise end end else raise Errno::ECONNREFUSED end end true rescue Errno::ECONNREFUSED, Errno::ENOENT false ensure socket.close if socket end end def ping_tcp_socket(sockaddr) begin ping_socket(Socket::Constants::AF_INET, sockaddr) rescue Errno::EAFNOSUPPORT ping_socket(Socket::Constants::AF_INET6, sockaddr) end end end def ruby_interpreter if defined?(RbConfig) rb_config = RbConfig::CONFIG else rb_config = Config::CONFIG end File.join( rb_config['bindir'], rb_config['RUBY_INSTALL_NAME'] ) + rb_config['EXEEXT'] end def safe_fork(double_fork) pid = fork if pid.nil? begin if double_fork pid2 = fork if pid2.nil? Process.setsid yield end else yield end rescue Exception => e message = "*** Exception #{e.class} " << "(#{e}) (process #{$$}):\n" << "\tfrom " << e.backtrace.join("\n\tfrom ") STDERR.write(e) STDERR.flush exit! ensure exit!(0) end else if double_fork Process.waitpid(pid) rescue nil pid else pid end end end if RUBY_VERSION < "1.9" def interruptable_waitpid(pid) Process.waitpid(pid) end else # On Ruby 1.9, Thread#kill (which is called by timeout.rb) may # not be able to interrupt Process.waitpid. So here we use a # special version that's a bit less efficient but is at least # interruptable. def interruptable_waitpid(pid) result = nil while !result result = Process.waitpid(pid, Process::WNOHANG) sleep 0.01 if !result end result end end end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/000755 000765 000024 00000000000 12233035540 032376 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/000755 000765 000024 00000000000 12233035540 032560 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/Gemfile000644 000765 000024 00000000520 12233035540 034050 0ustar00honglistaff000000 000000 source 'https://rubygems.org' gem 'rake' gem 'rspec' # If you update the dependency version here, also update # the version in union_station_hooks_rails.gemspec # and union_station_hooks_rails.rb method # `require_and_check_union_station_hooks_core`. gem 'union_station_hooks_core', '~> 2.0.4' group :notravis do gem 'rubocop' end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/Gemfile.lock000644 000765 000024 00000001701 12233035540 035001 0ustar00honglistaff000000 000000 GEM remote: https://rubygems.org/ specs: ast (2.1.0) astrolabe (1.3.1) parser (~> 2.2) diff-lcs (1.2.5) parser (2.2.2.6) ast (>= 1.1, < 3.0) powerpack (0.1.1) rainbow (2.0.0) rake (10.4.2) rspec (3.3.0) rspec-core (~> 3.3.0) rspec-expectations (~> 3.3.0) rspec-mocks (~> 3.3.0) rspec-core (3.3.2) rspec-support (~> 3.3.0) rspec-expectations (3.3.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.3.0) rspec-mocks (3.3.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.3.0) rspec-support (3.3.0) rubocop (0.33.0) astrolabe (~> 1.3) parser (>= 2.2.2.5, < 3.0) powerpack (~> 0.1) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.4) ruby-progressbar (1.7.5) union_station_hooks_core (2.0.4) PLATFORMS ruby DEPENDENCIES rake rspec rubocop union_station_hooks_core (~> 2.0.4) BUNDLED WITH 1.10.6 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/000755 000765 000024 00000000000 12233035540 033326 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/LICENSE.md000644 000765 000024 00000002055 12233035540 034166 0ustar00honglistaff000000 000000 Copyright (c) 2010-2015 Phusion Holding B.V. 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, sublicense, 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 NONINFRINGEMENT. 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. passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/Rakefile000644 000765 000024 00000011775 12233035540 034240 0ustar00honglistaff000000 000000 # Union Station - https://www.unionstationapp.com/ # Copyright (c) 2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. TRAVIS_PASSENGER_BRANCH = 'master' if defined?(Bundler) # Undo Bundler environment so that calls to 'bundle install' won't try to # access the .bundle directory in the gem's toplevel directory. clean_env = nil Bundler.with_clean_env do clean_env = ENV.to_hash end ENV.replace(clean_env) ARGV.each do |arg| if arg =~ /^(\w+)=(.*)$/m ENV[$1] = $2 end end end ush_core_path = ENV['USH_CORE_PATH'] if ush_core_path require "#{ush_core_path}/lib/union_station_hooks_core" else require 'union_station_hooks_core' end require File.expand_path(File.dirname(__FILE__) + '/lib/union_station_hooks_rails') require 'shellwords' desc 'Install the gem bundles of test apps' task :install_test_app_bundles do bundle_args = ENV['BUNDLE_ARGS'] Dir['rails_test_apps/*'].each do |dir| next if !should_run_rails_test?(dir) puts "Installing gem bundle for Rails #{File.basename(dir)}" sh "mkdir -p tmp.bundler" begin sh "cp #{dir}/Gemfile #{dir}/Gemfile.lock tmp.bundler/" puts "Editing tmp.bundler/Gemfile.lock" content = File.open("tmp.bundler/Gemfile.lock", "r") do |f| f.read end content.gsub!(/union_station_hooks_core \(.+\)/, "union_station_hooks_core (#{UnionStationHooks::VERSION_STRING})") content.gsub!(/union_station_hooks_rails \(.+\)/, "union_station_hooks_rails (#{UnionStationHooksRails::VERSION_STRING})") File.open("tmp.bundler/Gemfile.lock", "w") do |f| f.write(content) end sh "cd tmp.bundler && " \ "ln -s #{Shellwords.escape UnionStationHooks::ROOT} ush_core && " \ "ln -s #{Shellwords.escape UnionStationHooksRails::ROOT} ush_rails && " \ "bundle install --without development doc #{bundle_args}" ensure sh "rm -rf tmp.bundler" end end end desc 'Run tests' task :spec do if ENV['E'] arg = "-e #{Shellwords.escape ENV['E']}" end sh "bundle exec rspec -c -f d #{arg}".strip end task :test => :spec desc 'Run tests in Travis' task "spec:travis" do if !ENV['PASSENGER_CONFIG'] Rake::Task['travis:install_passenger'].invoke end Rake::Task['spec'].invoke end desc 'Build gem' task :gem do sh 'gem build union_station_hooks_rails.gemspec' end namespace :travis do task :install_passenger do if File.exist?('../../../../../bin/passenger-config') # We are vendored into Passenger Rake::Task['travis:install_passenger_vendor'].invoke else Rake::Task['travis:install_passenger_git'].invoke end end task :install_passenger_vendor do passenger_config = File.expand_path('../../../../../bin/passenger-config') ENV['PASSENGER_CONFIG'] = passenger_config sh "#{passenger_config} install-standalone-runtime --auto" end task :install_passenger_git do if !File.exist?('passenger/.git') sh "git clone --recursive --branch #{TRAVIS_PASSENGER_BRANCH} git://github.com/phusion/passenger.git" else puts 'cd passenger' Dir.chdir('passenger') do sh 'git fetch' sh 'rake clean' sh "git reset --hard origin/#{TRAVIS_PASSENGER_BRANCH}" sh 'git submodule update --init --recursive' end puts 'cd ..' end passenger_config = "#{Dir.pwd}/passenger/bin/passenger-config" envs = { 'PASSENGER_CONFIG' => passenger_config, 'CC' => 'ccache cc', 'CXX' => 'ccache c++', 'CCACHE_COMPRESS' => '1', 'CCACHE_COMPRESS_LEVEL' => '3', 'CCACHE_DIR' => "#{Dir.pwd}/passenger/.ccache" } envs.each_pair do |key, val| ENV[key] = val puts "$ export #{key}='#{val}'" end sh 'mkdir -p passenger/.ccache' sh "#{passenger_config} install-standalone-runtime --auto" end end def should_run_rails_test?(dir) # Rails >= 4.0 requires Ruby >= 1.9 RUBY_VERSION >= '1.9' || File.basename(dir) < '4.0' end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/README.md000644 000765 000024 00000011053 12233035540 034037 0ustar00honglistaff000000 000000 # Union Station Ruby on Rails hooks [![Build Status](https://travis-ci.org/phusion/union_station_hooks_rails.svg?branch=master)](https://travis-ci.org/phusion/union_station_hooks_rails) [Union Station](https://www.unionstationapp.com) is a web application monitoring and performance analytics platform for Ruby. In order for Union Station to analyze your application, your application must send data to Union Station. This gem allows you to do that. `union_station_hooks_rails` is a gem that automatically hooks into various parts of Rails, so that the right information is sent to Union Station. Under the hood, it makes use of the APIs provided by [union_station_hooks_core](https://github.com/phusion/union_station_hooks_core). **Resources:** [About Union Station](https://www.unionstationapp.com) | [Github](https://github.com/phusion/union_station_hooks_rails) **Table of contents** * [Installation](#installation) - [Using with Passenger](#using-with-passenger) - [Overriding Passenger's version](#overriding-passengers-version) - [Using without Passenger](#using-without-passenger) * [Legacy code](#legacy-code) * [Contributing](contributing) --- ## Installation ### Using with Passenger **Note: This documentation section only applies to Passenger 5.0.20 or later!** If you use [Passenger](https://www.phusionpassenger.com/), then you do not need to install the `union_station_hooks_rails` gem. `union_station_hooks_rails` is bundled with Passenger. The only thing you need to do is to create a file `config/initializers/union_station.rb` in which you call this code: if defined?(UnionStationHooks) UnionStationHooks.initialize! end (Are you already calling `PhusionPassenger.install_framework_extensions!` from somewhere in your codebase? Please read [Legacy code](#legacy-code) for important information.) When you have this call in place, enable Union Station support in Passenger. Here are some examples: * _Passenger with Nginx integration mode_
Insert the following config in your virtual host configuration, then restart Nginx: union_station_support on; union_station_key ; * _Passenger with Apache integration mode_
Insert the following config in your virtual host configuration, then restart Apache: UnionStationSupport on UnionStationKey * _Passenger Standalone_
Start Passenger with the `--union-station-key` parameter: $ passenger start --union-station-key Or set the `union_station_key` configuration option in Passengerfile.json: { "union_station_key": "" } ### Overriding Passenger's version Each version of Passenger bundles its own version of the `union_station_hooks_rails` gem (and the `union_station_hooks_core` gem, which is a dependency). The Passenger maintainers regularly update their bundled versions with the latest version. Sometimes, you may wish to use a specific version of `union_station_hooks_rails` and `union_station_hooks_core`, overriding the versions that came bundled with Passenger. For example, we have may published a new version of `union_station_hooks_rails` with some bug fixes, even though Passenger hasn't been updated yet. You can override Passenger's bundled versions as follows. 1. Add the gems you want to override to your Gemfile, like this: # Uncomment the following line if you want to override Passenger's # bundled version #gem 'union_station_hooks_core' gem 'union_station_hooks_rails' 2. Install your gem bundle: bundle install ### Using without Passenger It is currently not possible to use Union Station without Passenger. If you would like to have this feature, please let us know. ## Legacy code Before Passenger 5.0.20, the Union Station setup instructions used to tell you to create a `config/initializers/passenger.rb` in which you call the following code: PhusionPassenger.install_framework_extensions! if defined?(PhusionPassenger) Since Passenger 5.0.20, `PhusionPassenger.install_framework_extensions!` has become an alias for `UnionStationHooks.initialize!`, but the former is considered deprecated. Please replace the above code with: if defined?(UnionStationHooks) UnionStationHooks.initialize! end And please also rename `config/initializers/passenger.rb` to `config/initializers/union_station.rb`. ## Contributing Looking to contribute to this gem? Please read the documentation in the [hacking/](https://github.com/phusion/union_station_hooks_rails/blob/master/hacking) directory. ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/union_station_hooks_rails.gemspec000644 000765 000024 00000002275 12233035540 041342 0ustar00honglistaff000000 000000 passenger-5.0.30/srcversion_file = File.expand_path('lib/union_station_hooks_rails/version_data.rb', File.dirname(__FILE__)) version_data = eval(File.read(version_file)) Gem::Specification.new do |s| s.name = "union_station_hooks_rails" s.version = version_data[:string] s.authors = ["Hongli Lai"] s.description = "Union Station Rails hooks." s.summary = "Union Station Rails hooks" s.email = "info@phusion.nl" s.license = "MIT" s.files = Dir[ "README.md", "LICENSE.md", "*.gemspec", "lib/**/*" ] s.homepage = "https://github.com/phusion/union_station_hooks_rails" s.require_paths = ["lib"] # If you update the dependency version here, also update # the version in Gemfile and union_station_hooks_rails.rb method # `require_and_check_union_station_hooks_core` s.add_dependency("union_station_hooks_core", "~> 2.0.4") s.add_dependency("activesupport", ">= 3.0") s.add_dependency("activemodel", ">= 3.0") s.add_dependency("actionpack", ">= 3.0") s.add_dependency("railties", ">= 3.0") # DO NOT ADD ANY FURTHER DEPENDENCIES! See # https://github.com/phusion/union_station_hooks_core/blob/master/hacking/Vendoring.md, # section "No dependencies", for more information. end ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/000755 000765 000024 00000000000 12233035540 040535 5ustar00honglistaff000000 000000 passenger-5.0.30/srcruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails.rb000644 000765 000024 00000017001 12233035540 041061 0ustar00honglistaff000000 000000 passenger-5.0.30/src# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. # Important notes: # # - We can't define a Railtie in this gem because union_station_hooks_rails # may be loaded from Passenger, before Rails is loaded. # - Make sure that you do not install any actual hooks until `initialize!` # is called. Otherwise, union_station_hooks_core won't be able to properly # upgrade the Union Station hook code vendored in Passenger. # See https://github.com/phusion/union_station_hooks/hacking/Vendoring.md # for more information. if defined?(UnionStationHooksRails::VERSION_STRING) if UnionStationHooksRails.initialized? raise 'Attempted to load union_station_hooks_core even though an ' \ 'alternative version was already loaded and initialized' end if UnionStationHooksRails.vendored? # Passenger loaded its vendored Union Station hooks code, but the # application has also included 'union_station_hooks_*' in its Gemfile. We # want the version in the Gemfile to take precedence, so we unload the old # version. At this point, the Union Station hooks aren't installed yet, so # removing the module is enough to unload the old version. # # See also: # https://github.com/phusion/union_station_hooks/blob/master/hacking/Vendoring.md if defined?(UnionStationHooks) UnionStationHooks.initializers.delete(UnionStationHooksRails) end Object.send(:remove_const, :UnionStationHooksRails) end end module UnionStationHooksRails # The path to the `union_station_hooks_rails` Ruby library directory. # # @private LIBROOT = File.expand_path(File.dirname(__FILE__)) # The path to the `union_station_hooks_rails` gem root directory. # # @private ROOT = File.dirname(LIBROOT) class << self # @private @@initialized = false # @private @@vendored = false # Initializes `union_station_hooks_rails`. This method is automatically # called by `UnionStationHooks.initialized!` because we registered # ourselves in `UnionStationHooks.initializers`. The application does # not need to call this. # # If this method successfully initializes, then it returns true. # # Calling this method may or may not actually initialize # `union_station_hooks_rails`. If this gem determines that initialization # is not desired, then this method won't do anything and will return # `false`. See {UnionStationHooksRails#should_initialize?}. # # Initializing twice is a no-op. It only causes this method to return true. # # @private # @return [Boolean] Whether initialization was successful. def initialize! return false if !should_initialize? return true if initialized? begin require_lib('initialize') require_lib('active_record_subscriber') require_lib('exception_logger') if defined?(ActionView) require_lib('action_view_subscriber') end if defined?(ActiveSupport::Cache::Store) require_lib('active_support_cache_subscriber') end if defined?(ActionController::Base) require_lib('action_controller_extension') end if defined?(ActiveSupport::Benchmarkable) require_lib('active_support_benchmarkable_extension') end rescue => e if UnionStationHooks.config[:initialize_from_check] # The union_station_hooks_core gem already reported the error # to Union Station. STDERR.puts(' *** WARNING: an error occurred while initializing ' \ 'the Union Station Rails hooks. This is because you did not ' \ 'initialize the Union Station hooks from an initializer file. ' \ 'Please create an initializer file ' \ '`config/initializers/union_station.rb` in which you call ' \ "this:\n\n" \ " if defined?(UnionStationHooks)\n" \ " UnionStationHooks.initialize!\n" \ " end\n\n" \ "The error is as follows:\n" \ "#{e} (#{e.class})\n " + e.backtrace.join("\n ")) else raise e end end @@initialized = true true end # Returns whether the Union Station hooks are initialized. def initialized? @@initialized end # Returns whether the Union Station hooks should be initialized. If this # method returns false, then {UnionStationHooksRails.initialize!} doesn't # do anything. # # This method only returns true if ActiveSupport >= 3 is currently loaded. def should_initialize? if defined?(::ActiveSupport) && !defined?(::ActiveSupport::VERSION) require 'active_support/version' end defined?(::ActiveSupport) && ::ActiveSupport::VERSION::MAJOR >= 3 end # Returns whether this `union_station_hooks_rails` gem is bundled with # Passenger (as opposed to a standalone gem added to the Gemfile). # See the README and the file `hacking/Vendoring.md` in the # `union_station_hooks_core` gem for information about how Passenger # bundles `union_station_hooks_*` gems. # # @private def vendored? @@vendored end # @private def vendored=(val) @@vendored = val end # @private def require_lib(name) require("#{LIBROOT}/union_station_hooks_rails/#{name}") end # @private def require_and_check_union_station_hooks_core if !defined?(UnionStationHooks::VERSION_STRING) require 'union_station_hooks_core' end # If you update the dependency version here, also update # the version in the gemspec and in the Gemfile. compatible = UnionStationHooks::MAJOR_VERSION == 2 && ush_core_minor_and_tiny_version_compatible? if !compatible raise "This version of the union_station_hooks_rails gem " \ "(#{VERSION_STRING}) is only compatible with the " \ "union_station_hooks_core gem 2.x.x, starting from v2.0.3. " \ "However, you have loaded union_station_hooks_core #{UnionStationHooks::VERSION_STRING}" end end # @private def ush_core_minor_and_tiny_version_compatible? UnionStationHooks::MINOR_VERSION >= 1 || UnionStationHooks::TINY_VERSION >= 4 end end end UnionStationHooksRails.require_lib('version') UnionStationHooksRails.require_and_check_union_station_hooks_core UnionStationHooks.initializers << UnionStationHooksRails vendor/union_station_hooks_rails/lib/union_station_hooks_rails/action_controller_extension.rb000644 000765 000024 00000003373 12233035540 046704 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module UnionStationHooksRails module ActionControllerExtension def process_action(action, *args) reporter = request.env['union_station_hooks'] return super if !reporter options = { :controller_name => self.class.name, :action_name => action_name, :method => request.request_method } reporter.log_controller_action_block(options) do super end end end end ActionController::Base.class_eval do include UnionStationHooksRails::ActionControllerExtension end vendor/union_station_hooks_rails/lib/union_station_hooks_rails/action_view_subscriber.rb000644 000765 000024 00000003757 12233035540 045630 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module UnionStationHooksRails class ActionViewSubscriber < ActiveSupport::LogSubscriber def render_collection(event) log_view_rendering(event) end def render_template(event) log_view_rendering(event) end def render_partial(event) log_view_rendering(event) end private def log_view_rendering(event) reporter = Thread.current[:union_station_hooks] return if !reporter UnionStationHooks.call_event_pre_hook(event) reporter.log_view_rendering( :begin_time => event.time, :end_time => UnionStationHooks.now, :name => event.payload[:identifier], :has_error => event.payload[:exception] ) end end end UnionStationHooksRails::ActionViewSubscriber.attach_to(:action_view) vendor/union_station_hooks_rails/lib/union_station_hooks_rails/active_record_subscriber.rb000644 000765 000024 00000003321 12233035540 046115 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module UnionStationHooksRails class ActiveRecordSubscriber < ActiveSupport::LogSubscriber def sql(event) reporter = Thread.current[:union_station_hooks] return if !reporter UnionStationHooks.call_event_pre_hook(event) reporter.log_database_query( :begin_time => event.time, :end_time => UnionStationHooks.now, :query => event.payload[:sql] ) end end end UnionStationHooksRails::ActiveRecordSubscriber.attach_to(:active_record) union_station_hooks_rails/lib/union_station_hooks_rails/active_support_benchmarkable_extension.rb000644 000765 000024 00000003571 12233035540 051071 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module UnionStationHooksRails module ActiveSupportBenchmarkableExtension def benchmark_with_union_station(message = 'Benchmarking', *args) reporter = Thread.current[:union_station_hooks] if reporter reporter.log_benchmark_block(message) do benchmark_without_union_station(message, *args) do yield end end else benchmark_without_union_station(message, *args) do yield end end end end end ActiveSupport::Benchmarkable.class_eval do include UnionStationHooksRails::ActiveSupportBenchmarkableExtension alias_method_chain :benchmark, :union_station end vendor/union_station_hooks_rails/lib/union_station_hooks_rails/active_support_cache_subscriber.rb000644 000765 000024 00000005552 12233035540 047506 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module UnionStationHooksRails class ActiveSupportCacheSubscriber < ActiveSupport::LogSubscriber def cache_read(event) reporter = Thread.current[:union_station_hooks] return if !reporter UnionStationHooks.call_event_pre_hook(event) if event.payload[:hit] reporter.log_cache_hit(event.payload[:key]) else reporter.log_cache_miss(event.payload[:key]) end end def cache_fetch_hit(event) reporter = Thread.current[:union_station_hooks] return if !reporter UnionStationHooks.call_event_pre_hook(event) reporter.log_cache_hit(event.payload[:key]) end def cache_generate(event) reporter = Thread.current[:union_station_hooks] return if !reporter UnionStationHooks.call_event_pre_hook(event) reporter.log_cache_miss(event.payload[:key], (event.duration * 1_000_000).to_i) end end end # Instrumentation is always on since 4.2. This method will be removed in Rails 5. if defined?(ActiveSupport::Cache::Store) && ActiveSupport::Cache::Store.respond_to?(:instrument=) ActiveSupport::Deprecation.silence do ActiveSupport::Cache::Store.instrument = true end if defined?(PhusionPassenger) PhusionPassenger.on_event(:starting_request_handler_thread) do if defined?(ActiveSupport::Cache::Store) ActiveSupport::Deprecation.silence do # This flag is thread-local, so re-initialize it for every # request handler thread. ActiveSupport::Cache::Store.instrument = true end end end end end UnionStationHooksRails::ActiveSupportCacheSubscriber.attach_to(:active_support) phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/exception_logger.rb000644 000765 000024 00000003761 12233035540 044426 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module UnionStationHooksRails class ExceptionLogger def initialize(app) @app = app end def call(env) @app.call(env) rescue Exception => e report_exception(env, e) raise e end private def report_exception(env, exception) reporter = env['union_station_hooks'] if reporter reporter.log_exception(exception) end end end end if defined?(ActionDispatch::DebugExceptions) exceptions_middleware = ActionDispatch::DebugExceptions elsif defined?(ActionDispatch::ShowExceptions) exceptions_middleware = ActionDispatch::ShowExceptions end if exceptions_middleware && defined?(Rails) Rails.application.middleware.insert_after( exceptions_middleware, UnionStationHooksRails::ExceptionLogger) end phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/initialize.rb000644 000765 000024 00000002633 12233035540 043227 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. # Initialize garbage collector statistics if GC.respond_to?(:enable_stats) GC.enable_stats end if defined?(GC::Profiler) && GC::Profiler.respond_to?(:enable) GC::Profiler.enable end phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/version.rb000644 000765 000024 00000003044 12233035540 042550 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module UnionStationHooksRails version_file = File.expand_path('version_data.rb', File.dirname(__FILE__)) version_data = eval(File.read(version_file)) MAJOR_VERSION = version_data[:major] MINOR_VERSION = version_data[:minor] TINY_VERSION = version_data[:tiny] VERSION_STRING = version_data[:string] end phusion_passenger/vendor/union_station_hooks_rails/lib/union_station_hooks_rails/version_data.rb000644 000765 000024 00000004022 12233035540 043536 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. # This file contains union_station_hook_rails's version number. It is meant # to be read and evaluated by the gemspec. We do not define any functions or # modules here because we need to support the following situation: # # 1. Passenger loads its vendored union_station_hooks_* gems. This defines # various modules. # 2. The app specified union_station_hooks_* gems in its Gemfile, which # indicates that it wants to override Passenger's vendored # union_station_hooks_* gems with its own versions. This will cause Bundler # to load union_station_hooks_*.gemspec. # # To make the gemspecs load properly and without affecting the already-loaded # union_station_hooks_* gems code, we must not define any functions or modules # here. { :major => 2, :minor => 0, :tiny => 1, :string => '2.0.1' } passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/CONFIG.md000644 000765 000024 00000001274 12233035540 033671 0ustar00honglistaff000000 000000 # Configuration keys ## `:union_station_key` Default: automatically set by Passenger ## `:app_group_name` Default: automatically set by Passenger ## `:ust_router_address` Default: automatically set by Passenger ## `:ust_router_password` Default: automatically set by Passenger ## `:node_name` Default: current host name ## `:event_preprocessor` Default: none ## `:debug` Default: false Whether to print debugging messages for `union_station_hooks_*` gems. ## `:check_initialized` Default: true If enabled, and Union Station support in Passenger is enabled too, then Passenger will complain during application startup if the application never called `UnionStationHooks.initialize!`. passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/Gemfile000644 000765 000024 00000000357 12233035540 033676 0ustar00honglistaff000000 000000 source 'https://rubygems.org/' gem 'rake' gem 'rspec' gem 'timecop' gem 'simplecov', :require => false gem 'rubocop', :groups => [:notravis] group :doc, :notravis do gem 'yard' gem 'redcarpet' end group :travis do gem 'rack' end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/Gemfile.lock000644 000765 000024 00000002231 12233035540 034616 0ustar00honglistaff000000 000000 GEM remote: https://rubygems.org/ specs: ast (2.1.0) astrolabe (1.3.1) parser (~> 2.2) diff-lcs (1.2.5) docile (1.1.3) multi_json (1.11.2) parser (2.2.2.6) ast (>= 1.1, < 3.0) powerpack (0.1.1) rack (1.6.4) rainbow (2.0.0) rake (10.4.2) redcarpet (3.2.2) rspec (3.3.0) rspec-core (~> 3.3.0) rspec-expectations (~> 3.3.0) rspec-mocks (~> 3.3.0) rspec-core (3.3.2) rspec-support (~> 3.3.0) rspec-expectations (3.3.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.3.0) rspec-mocks (3.3.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.3.0) rspec-support (3.3.0) rubocop (0.33.0) astrolabe (~> 1.3) parser (>= 2.2.2.5, < 3.0) powerpack (~> 0.1) rainbow (>= 1.99.1, < 3.0) ruby-progressbar (~> 1.4) ruby-progressbar (1.7.5) simplecov (0.8.2) docile (~> 1.1.0) multi_json simplecov-html (~> 0.8.0) simplecov-html (0.8.0) timecop (0.5.3) yard (0.8.7.6) PLATFORMS ruby DEPENDENCIES rack rake redcarpet rspec rubocop simplecov timecop yard BUNDLED WITH 1.10.6 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/000755 000765 000024 00000000000 12233035540 033144 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/LICENSE.md000644 000765 000024 00000002055 12233035540 034004 0ustar00honglistaff000000 000000 Copyright (c) 2010-2016 Phusion Holding B.V. 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, sublicense, 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 NONINFRINGEMENT. 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. passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/Rakefile000644 000765 000024 00000006767 12233035540 034063 0ustar00honglistaff000000 000000 # Union Station - https://www.unionstationapp.com/ # Copyright (c) 2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'shellwords' TRAVIS_PASSENGER_BRANCH = 'master' desc 'Run tests' task :spec do pattern = ENV['E'] if pattern args = "-e #{Shellwords.escape(pattern)}" end sh 'rm -rf coverage' sh "bundle exec rspec -c -f d spec/*_spec.rb #{args}" end task :test => :spec desc 'Run tests in Travis' task "spec:travis" do if !ENV['PASSENGER_CONFIG'] Rake::Task['travis:install_passenger'].invoke end if ENV['TRAVIS_WITH_SUDO'] sh 'cp ruby_versions.yml.travis-with-sudo ruby_versions.yml' else sh 'cp ruby_versions.yml.travis ruby_versions.yml' end Rake::Task['spec'].invoke end desc 'Build gem' task :gem do sh 'gem build union_station_hooks_core.gemspec' end desc 'Check coding style' task :rubocop do sh 'bundle exec rubocop -D lib' end desc 'Generate API documentation' task :doc do sh 'rm -rf doc' sh 'bundle exec yard' end namespace :travis do task :install_passenger do if File.exist?('../../../../../bin/passenger-config') # We are vendored into Passenger Rake::Task['travis:install_passenger_vendor'].invoke else Rake::Task['travis:install_passenger_git'].invoke end end task :install_passenger_vendor do passenger_config = File.expand_path('../../../../../bin/passenger-config') ENV['PASSENGER_CONFIG'] = passenger_config sh "#{passenger_config} install-agent --auto" end task :install_passenger_git do if !File.exist?('passenger/.git') sh "git clone --recursive --branch #{TRAVIS_PASSENGER_BRANCH} git://github.com/phusion/passenger.git" else puts 'cd passenger' Dir.chdir('passenger') do sh 'git fetch' sh 'rake clean' sh "git reset --hard origin/#{TRAVIS_PASSENGER_BRANCH}" sh 'git submodule update --init --recursive' end puts 'cd ..' end passenger_config = "#{Dir.pwd}/passenger/bin/passenger-config" envs = { 'PASSENGER_CONFIG' => passenger_config, 'CC' => 'ccache cc', 'CXX' => 'ccache c++', 'CCACHE_COMPRESS' => '1', 'CCACHE_COMPRESS_LEVEL' => '3', 'CCACHE_DIR' => "#{Dir.pwd}/passenger/.ccache" } envs.each_pair do |key, val| ENV[key] = val puts "$ export #{key}='#{val}'" end sh 'mkdir -p passenger/.ccache' sh "#{passenger_config} install-agent --auto" end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/README-API.md000644 000765 000024 00000000550 12233035540 034264 0ustar00honglistaff000000 000000 # union_station_hooks_core: public API Welcome to the `union_station_hooks_core` API documentation. Please visit the {UnionStationHooks} module description to begin. That module is the entry point to the API. **Not familiar with `union_station_hooks_core`? Please read the [README](https://github.com/phusion/union_station_hooks_core) for an introduction.** passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/README.md000644 000765 000024 00000013435 12233035540 033663 0ustar00honglistaff000000 000000 # Union Station Ruby hooks core [![Build Status](https://travis-ci.org/phusion/union_station_hooks_core.svg?branch=master)](https://travis-ci.org/phusion/union_station_hooks_core) [Union Station](https://www.unionstationapp.com) is a web application monitoring and performance analytics platform for Ruby. In order for Union Station to analyze your application, your application must send data to Union Station. This gem allows you to do that. `union_station_hooks_core` is a web-framework-agnostic gem that **hooks into various Ruby internals** in order to send generic Ruby application analytics information to Union Station. Not all information can be automatically inferred by hooking into the Ruby internals, so this gem also provides an **API** that you can call at key places in the codebase to supply the right information. ### Using Ruby on Rails? If your application is a Rails application, then you should use the [union_station_hooks_rails](https://github.com/phusion/union_station_hooks_rails) gem instead of this gem. `union_station_hooks_rails` automatically hooks into Rails to send the right information to Union Station, so that you don't have to call any APIs. Under the hood, `union_station_hooks_rails` makes use of the `union_station_hooks_core` API. **Resources:** [About Union Station](https://www.unionstationapp.com) | [Github](https://github.com/phusion/union_station_hooks_core) | [API docs](http://www.rubydoc.info/github/phusion/union_station_hooks_core/UnionStationHooks) **Table of contents** * [Installation](#installation) - [Using with Passenger](#using-with-passenger) - [Overriding Passenger's version](#overriding-passengers-version) - [Using without Passenger](#using-without-passenger) * [API](#api) * [Legacy code](#legacy-code) * [Contributing](contributing) --- ## Installation ### Using with Passenger **Note: This documentation section only applies to Passenger 5.0.20 or later!** If you use [Passenger](https://www.phusionpassenger.com/), then you do not need to install the `union_station_hooks_core` gem. `union_station_hooks_core` is bundled with Passenger. The only thing you need to do is to ensure that the code is called during application startup: if defined?(UnionStationHooks) UnionStationHooks.initialize! end (Are you already calling `PhusionPassenger.install_framework_extensions!` from somewhere in your codebase? Please read [Legacy code](#legacy-code) for important information.) A good place to call this is in your Rackup file, `config.ru`. Or if you are using Rails, you should create a file `config/initializers/union_station.rb` in which you call this. When you have this call in place, enable Union Station support in Passenger. Here are some examples: * _Passenger with Nginx integration mode_
Insert the following config in your virtual host configuration, then restart Nginx: union_station_support on; union_station_key ; * _Passenger with Apache integration mode_
Insert the following config in your virtual host configuration, then restart Apache: UnionStationSupport on UnionStationKey * _Passenger Standalone_
Start Passenger with the `--union-station-key` parameter: $ passenger start --union-station-key Or set the `union_station_key` configuration option in Passengerfile.json: { "union_station_key": "" } ### Overriding Passenger's version **_Note: Are you using `union_station_hooks_rails`? Read [these instructions](https://github.com/phusion/union_station_hooks_rails#overriding-passengers-version) instead._** Each version of Passenger bundles its own version of the `union_station_hooks_core` gem. The Passenger maintainers regularly update their bundled version with the latest version. Sometimes, you may wish to use a specific version of `union_station_hooks_core`, overriding the version that came bundled with Passenger. For example, we have may published a new version of `union_station_hooks_core` with some bug fixes, even though Passenger hasn't been updated yet. You can override Passenger's bundled version as follows. 1. Add `union_station_hooks_core` to your Gemfile, like this: gem 'union_station_hooks_core' 2. Install your gem bundle: bundle install 3. Ensure that your application calls `require 'union_station_hooks_core'` during startup, before you access anything in the `UnionStationHooks` module (so before the `UnionStationHooks.initialize!` call). If you are using Rails, then Rails takes care of that automatically by calling `Bundler.require` in `config/application.rb`, so in that case you don't need to follow this step. ### Using without Passenger It is currently not possible to use Union Station without Passenger. If you would like to have this feature, please let us know. ## API Please refer to [the API documentation website](http://www.rubydoc.info/github/phusion/union_station_hooks_core/UnionStationHooks). ## Legacy code Before Passenger 5.0.20, the Union Station setup instructions used to tell you to create a `config/initializers/passenger.rb` in which you call the following code: PhusionPassenger.install_framework_extensions! if defined?(PhusionPassenger) Since Passenger 5.0.20, `PhusionPassenger.install_framework_extensions!` has become an alias for `UnionStationHooks.initialize!`, but the former is considered deprecated. Please replace the above code with: if defined?(UnionStationHooks) UnionStationHooks.initialize! end And please also rename `config/initializers/passenger.rb` to `config/initializers/union_station.rb`. ## Contributing Looking to contribute to this gem? Please read the documentation in the [hacking/](https://github.com/phusion/union_station_hooks_core/blob/master/hacking) directory. src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/ruby_versions.yml000644 000765 000024 00000000200 12233035540 035743 0ustar00honglistaff000000 000000 passenger-5.0.30#- name: Ruby 1.9.3 # command: rvm-exec ruby-1.9.3-p547 bundle exec ruby - name: Ruby 2.2.3 command: rvm-exec ruby-2.2.3 rubysrc/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/ruby_versions.yml.example000644 000765 000024 00000001223 12233035540 037403 0ustar00honglistaff000000 000000 passenger-5.0.30# This configuration file tells the test suite which Ruby interpreters to # test against. Below is an example utilizing RVM. # # If you want test coverage reporting via simplecov to work, then this list # must contain the main Ruby interpreter that you are using, and the entry # for that Ruby interpreter must execute Ruby through 'bundle exec'. In the # example below, this is the case for Ruby 1.9.3. - name: Ruby 1.8.7 command: rvm-exec ruby-1.8.7-p352 ruby - name: Ruby 1.9.3 command: rvm-exec ruby-1.9.3-p547 bundle exec ruby - name: Ruby 2.2.1 command: rvm-exec ruby-2.2.1 ruby - name: JRuby 9.0.4.0 command: rvm-exec jruby-9.0.4.0 ruby --dev src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/ruby_versions.yml.travis000644 000765 000024 00000001471 12233035540 037265 0ustar00honglistaff000000 000000 passenger-5.0.30# This configuration file tells the test suite which Ruby interpreters to # test against. Below is an example utilizing RVM. # # If you want test coverage reporting via simplecov to work, then this list # must contain the main Ruby interpreter that you are using, and the entry # for that Ruby interpreter must execute Ruby through 'bundle exec'. In the # example below, this is the case for Ruby 1.9.3. - name: Ruby 1.8.7 command: rvm-exec ruby-1.8.7-p374 ruby - name: Ruby 1.9.3 command: rvm-exec ruby-1.9.3-p551 bundle exec ruby - name: Ruby 2.1.5 command: rvm-exec ruby-2.1.5 ruby - name: Ruby 2.2.0 command: rvm-exec ruby-2.2.0 ruby - name: JRuby 9.0.0.0 command: env JRUBY_OPTS= rvm-exec jruby-9.0.0.0.pre1 ruby --dev -J-XX:+TieredCompilation -J-XX:TieredStopAtLevel=1 -J-Xss2m -Xcompile.invokedynamic=false ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/ruby_versions.yml.travis-with-sudo000644 000765 000024 00000001207 12233035540 041203 0ustar00honglistaff000000 000000 passenger-5.0.30/src# This configuration file tells the test suite which Ruby interpreters to # test against. Below is an example utilizing RVM. # # If you want test coverage reporting via simplecov to work, then this list # must contain the main Ruby interpreter that you are using, and the entry # for that Ruby interpreter must execute Ruby through 'bundle exec'. In the # example below, this is the case for Ruby 1.9.3. - name: Ruby 1.8.7 command: rvm-exec ruby-1.8.7-p374 ruby - name: Ruby 1.9.3 command: rvm-exec ruby-1.9.3-p551 bundle exec ruby - name: Ruby 2.1.5 command: rvm-exec ruby-2.1.5 ruby - name: Ruby 2.2.0 command: rvm-exec ruby-2.2.0 ruby ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/union_station_hooks_core.gemspec000644 000765 000024 00000001356 12233035540 040775 0ustar00honglistaff000000 000000 passenger-5.0.30/srcversion_file = File.expand_path('lib/union_station_hooks_core/version_data.rb', File.dirname(__FILE__)) version_data = eval(File.read(version_file)) Gem::Specification.new do |s| s.name = "union_station_hooks_core" s.version = version_data[:string] s.authors = ["Hongli Lai"] s.description = "Union Station Ruby hooks core code." s.summary = "Union Station Ruby hooks core code" s.email = "info@phusion.nl" s.license = "MIT" s.files = Dir[ "README.md", "LICENSE.md", "*.gemspec", "lib/**/*" ] s.homepage = "https://github.com/phusion/union_station_hooks_core" s.require_paths = ["lib"] # DO NOT ADD ANY FURTHER DEPENDENCIES! See hacking/Vendoring.md, # section "No dependencies", for more information. end src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/000755 000765 000024 00000000000 12233035540 040171 5ustar00honglistaff000000 000000 passenger-5.0.30ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core.rb000644 000765 000024 00000033612 12233035540 040523 0ustar00honglistaff000000 000000 passenger-5.0.30/src# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. if defined?(UnionStationHooks::VERSION_STRING) if UnionStationHooks.initialized? raise 'Attempted to load union_station_hooks_core even though an ' \ 'alternative version was already loaded and initialized' end if UnionStationHooks.vendored? # Passenger loaded its vendored Union Station hooks code, but the # application has also included 'union_station_hooks_*' in its Gemfile. We # want the version in the Gemfile to take precedence, so we unload the old # version. At this point, the Union Station hooks aren't installed yet, so # removing the module is enough to unload the old version. # # See also hacking/Vendoring.md config_from_vendored_ush = UnionStationHooks.config initializers_from_vendored_ush = UnionStationHooks.initializers Object.send(:remove_const, :UnionStationHooks) end end # The UnionStationHooks module is the entry point to the # `union_station_hooks_core` gem's public API. Note that this API is only # available since Passenger 5.0.20! # # **_Not familiar with `union_station_hooks_core`? Please read the # [README](https://github.com/phusion/union_station_hooks_core) # for an introduction._** # # ## Places of interest # # You will probably be most interested in these: # # * {UnionStationHooks.initialize!} # * {UnionStationHooks.begin_rack_request} and # {UnionStationHooks.end_rack_request} # * {UnionStationHooks::RequestReporter} # * {UnionStationHooks.log_exception} # # ## Rack example # # Here is a small example showing to use `union_station_hooks_core` with a # bare Rack application. There are three main things you see in this example: # # 1. The `union_station_hooks_*` gems are initialized. # 2. Obtaining a RequestReporter object, which is the main object that you # will be using as an application developer to log information to Union # Station. # 3. Using the RequestReporter object by calling methods on it. # # Example code follows: # # # (1) Initialize all `union_station_hooks_*` gems. # UnionStationHooks.initialize! # # # Define application object. # app = lambda do |env| # body, rendering_time = process_this_request(env) # # # (2) You can obtain a RequestReporter object as follows. With that # # object, you can log to Union Station information about the current # # request. # reporter = env['union_station_hooks'] # # -OR- (if you don't have access to the Rack env): # reporter = Thread.current[:union_station_hooks] # # # The reporter object may be nil because of various error conditions, # # so you must check for it. # if reporter # # (3) For example you can log the amount of time it took to render # # the view. # reporter.log_total_view_rendering_time(rendering_time) # end # # [200, { "Content-Type" => "text/plain" }, body] # end # # # Tell the application server to run this application object. # run app module UnionStationHooks # The path to the `union_station_hooks_core` Ruby library directory. # # @private LIBROOT = File.expand_path(File.dirname(__FILE__)) # The path to the `union_station_hooks_core` gem root directory. # # @private ROOT = File.dirname(LIBROOT) class << self # This error is raised by {UnionStationHooks.initialize!} if a required # {UnionStationHooks.config configuration option} is missing. class ConfigurationError < StandardError end # @private @@config = {} # @private @@context = nil # @private @@initializers = [] # @private @@initialized = false # @private @@app_group_name = nil # @private @@key = nil # @private @@vendored = false # Initializes the Union Station hooks. If there are any other # `union_station_hooks_*` gems loaded, then they are initialized too. # # Applications must call this during startup. Hooks aren't actually # installed until this method is called, so until you call this you cannot # use the public APIs of any `union_station_hooks_*` gems (besides trivial # things such as {UnionStationHooks.initialized?}). # # A good place to call this is in the Rackup file `config.ru`. Or, if your # application is a Rails app, then you should create an initializer file # `config/initializers/union_station.rb` in which you call this. # # If this method successfully initializes, then it returns true. # # Calling this method may or may not actually initialize the hooks. If # this gem determines that initialization is not desired, then this method # won't do anything and will return `false`. See # {UnionStationHooks#should_initialize?}. # # Initialization takes place according to parameters set in the # {UnionStationHooks.config configuration hash}. If a required # configuration option is missing, then this method will raise a # {ConfigurationError}. # # Initializing twice is a no-op. It only causes this method to return true. # # @raise [ConfigurationError] A required configuration option is missing. # @return [Boolean] Whether initialization was successful. def initialize! return false if !should_initialize? return true if initialized? finalize_and_validate_config require_lib('api') create_context install_postfork_hook install_event_pre_hook initialize_other_union_station_hooks_gems finalize_install true end # Returns whether the Union Station hooks are initialized. def initialized? @@initialized end # Returns whether the Union Station hooks should be initialized. If this # method returns false, then {UnionStationHooks.initialize!} doesn't do # anything. # # At present, this method only returns true when the app is running inside # Passenger. This may change if and when in the future Union Station # supports application servers besides Passenger. def should_initialize? if defined?(PhusionPassenger) PhusionPassenger::App.options['analytics'] else true end end # Returns whether this `union_station_hooks_core` gem is bundled with # Passenger (as opposed to a standalone gem added to the Gemfile). # See the README and the file `hacking/Vendoring.md` for information # about how Passenger bundles `union_station_hooks_*` gems. # # @private def vendored? @@vendored end # @private def vendored=(val) @@vendored = val end # @private def require_lib(name) require("#{LIBROOT}/union_station_hooks_core/#{name}") end # Returns the configuration hash. This configuration is used by all # `union_station_hooks_*` gems. You are supposed to set this hash # before calling {UnionStationHooks.initialize!}. # # At present, none of the `union_station_hooks_*` gems require # additional configuration. All necessary configuration is pulled from # Passenger. This may change if and when Union Station in the future # supports application servers besides Passenger. # # This hash is supposed to only contain symbol keys, not string keys. # When {UnionStationHooks.initialize!} is called, that method will # convert all string keys to symbol keys before doing anything else # with the config hash, so assigning string keys works even though # we don't recommend it. Furthermore, the config hash is frozen after # initialization. # # @return [Hash] def config @@config end # The singleton {Context} object, created during initialization. # All the `union_station_hooks_*` gem internals make use of this context # object. # # @private def context @@context end # An array of objects on which `#initialize!` will be called when # {UnionStationHooks.initialize!} is called. Other `union_station_hooks_*` # gems register themselves in this list when they are loaded, so that # a call to {UnionStationHooks.initialize!} will initialize them too. # # @private def initializers @@initializers end # @private def app_group_name @@app_group_name end # The currently active Union Station key. This is pulled from the # {UnionStationHooks.config configuration}. # # @private def key @@key end # @private def call_event_pre_hook(_event) raise 'This method may only be called after ' \ 'UnionStationHooks.initialize! is called' end # Called by Passenger after loading the application, to check whether or # not the application developer forgot to call # {UnionStationHooks.initialize!}. If so, it logs the problem and # initializes now. # # @private def check_initialized return if !should_initialize? || initialized? return if !config.fetch(:check_initialized, true) if defined?(::Rails) message = 'The Union Station hooks are not initialized. Please ensure ' \ 'that you have an initializer file ' \ '`config/initializers/union_station.rb` in which you call ' \ "this:\n\n" \ " if defined?(UnionStationHooks)\n" \ " UnionStationHooks.initialize!\n" \ " end" else message = 'The Union Station hooks are not initialized. Please ensure ' \ 'that the following code is called during application ' \ "startup:\n\n" \ " if defined?(UnionStationHooks)\n" \ " UnionStationHooks.initialize!\n" \ " end" end STDERR.puts(" *** WARNING: #{message}") @@config[:initialize_from_check] = true initialize! report_internal_information('HOOKS_NOT_INITIALIZED', message) end def now # When `initialize!` is called, the definition in # `api.rb` will override this implementation. nil end def begin_rack_request(_rack_env) # When `initialize!` is called, the definition in # `api.rb` will override this implementation. nil end def end_rack_request(_rack_env, _uncaught_exception_raised_during_request = false) # When `initialize!` is called, the definition in # `api.rb` will override this implementation. nil end def log_exception(_exception) # When `initialize!` is called, the definition in # `api.rb` will override this implementation. nil end def get_delta_monotonic # When `initialize!` is called, the definition in # `api.rb` will override this implementation. nil end private def finalize_and_validate_config final_config = {} if defined?(PhusionPassenger) import_into_final_config(final_config, PhusionPassenger::App.options) end import_into_final_config(final_config, config) validate_final_config(final_config) @@config = final_config end def import_into_final_config(dest, source) source.each_pair do |key, val| dest[key.to_sym] = val end end def validate_final_config(config) require_non_empty_config_key(config, :union_station_key) require_non_empty_config_key(config, :app_group_name) require_non_empty_config_key(config, :ust_router_address) require_non_empty_config_key(config, :ust_router_password) end def require_non_empty_config_key(config, key) if config[key].nil? || config[key].empty? raise ArgumentError, "Union Station hooks configuration option required: #{key}" end end def require_simple_json if defined?(PhusionPassenger) begin PhusionPassenger.require_passenger_lib('utils/json') UnionStationHooks.const_set(:SimpleJSON, PhusionPassenger::Utils) rescue LoadError end end if !defined?(UnionStationHooks::SimpleJSON) require_lib('simple_json') end end def report_internal_information(type, message, data = nil) data ||= {} data[:app_type] ||= :ruby if defined?(::Rails) data[:framework_type] = :rails end if defined?(PhusionPassenger) data[:app_server] = { :id => :passenger, :version => PhusionPassenger::VERSION_STRING } end body = SimpleJSON::JSON.generate( :type => type, :message => message, :data => data ) transaction = context.new_transaction(app_group_name, :internal_information, key) begin transaction.message(body) ensure transaction.close end end end end UnionStationHooks.require_lib('version') if config_from_vendored_ush UnionStationHooks.config.replace(config_from_vendored_ush) UnionStationHooks.initializers.replace(initializers_from_vendored_ush) end phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/api.rb000644 000765 000024 00000022442 12233035540 041273 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. UnionStationHooks.require_lib 'utils' UnionStationHooks.require_lib 'time_point' UnionStationHooks.require_lib 'request_reporter' module UnionStationHooks class << self # @note You do not have to call this! Passenger automatically calls # this for you! Just obtain the RequestReporter object that has been made # available for you. # # Indicates that a Rack request has begun. Given a Rack environment hash, # this method returns {RequestReporter} object, which you can use for # logging Union Station information about this request. This method should # be called as early as possible during a request, before any processing # has begun. Only after calling this method will it be possible to log # request-specific information to Union Station. # # The {RequestReporter} object that this method creates is also made # available through the `union_station_hooks` key in the Rack environment # hash, as well as the `:union_station_hooks` key in the current thread's # object: # # env['union_station_hooks'] # # => RequestReporter object or nil # # Thread.current[:union_station_hooks] # # => RequestReporter object or nil # # If this method was already called on this Rack request, then this method # does nothing and merely returns the previously created {RequestReporter}. # # See {RequestReporter} to learn what kind of information you can log to # Union Station about Rack requests. # # @return [RequestReporter, nil] A {RequestReporter} object, or nil or # because of certain error conditions. See the RequestReporter class # description for when this may be nil. def begin_rack_request(rack_env) reporter = rack_env['union_station_hooks'] return reporter if reporter # PASSENGER_TXN_ID may be nil here even if Union Station support is # enabled. For example, if the user tried to access the application # process directly through its private HTTP socket. txn_id = rack_env['PASSENGER_TXN_ID'] return nil if !txn_id # Workaround for Ruby < 2.1 support where there is no function for querying # the monotonic time. delta_monotonic_env = rack_env['PASSENGER_DELTA_MONOTONIC'] set_delta_monotonic(delta_monotonic_env.to_i) if delta_monotonic_env reporter = RequestReporter.new(context, txn_id, app_group_name, key) return if reporter.null? rack_env['union_station_hooks'] = reporter Thread.current[:union_station_hooks] = reporter reporter.log_request_begin reporter.log_gc_stats_on_request_begin reporter end # @note You do not have to call this! Passenger automatically calls # this for you! # # Indicates that a Rack request, on which {begin_rack_request} was called, # has ended. You should call this method as late as possible during a # request, after all processing have ended. Preferably after the Rack # response body has closed. # # The {RequestReporter} object associated with this Rack request and with # the current, will be closed (by calling {RequestReporter#close}), which # finalizes the Union Station logs for this request. # # This method MUST be called in the same thread that called # {begin_rack_request}. # # It is undefined what will happen if you call this method a Rack request # on which {begin_rack_request} was not called, so don't do that. # # This method does nothing if it was already called on this Rack request. def end_rack_request(rack_env, uncaught_exception_raised_during_request = false) reporter = rack_env.delete('union_station_hooks') Thread.current[:union_station_hooks] = nil if reporter begin reporter.log_gc_stats_on_request_end reporter.log_request_end(uncaught_exception_raised_during_request) ensure reporter.close end end end # Logs an exception that did NOT occur during a request. # # This method should be used for logging exceptions outside the # request-response cycle, e.g. exceptions in threads. If you want to # log an exception that occurred during a request, use # {RequestReporter#log_exception} instead. That method will also log # any related request-specific information, while this method does not. # # @param [Exception] exception # @since 2.1.0 def log_exception(exception) transaction = context.new_transaction(app_group_name, :exceptions, key) begin return do_nothing_on_null(:log_exception) if transaction.null? base64_message = exception.message base64_message = exception.to_s if base64_message.empty? base64_message = Utils.base64(base64_message) base64_backtrace = Utils.base64(exception.backtrace.join("\n")) transaction.message("Message: #{base64_message}") transaction.message("Class: #{exception.class.name}") transaction.message("Backtrace: #{base64_backtrace}") ensure transaction.close end end # Returns an opaque object (a {TimePoint}) that represents a collection # of metrics about the current time. # # This TimePoint samples monotonic time (with a fallback to wall clock # time) as well as CPU time, time spent in userspace and kernel space, # time spent context switching, etc. The exact information contained # in the object is operating system specific, hence the object is opaque. # # You should use it for the various API methods that require timing # information. Those methods also accept standard Ruby `Time` objects, # but we strongly recommended against doing this, because wall clock # time can jump forwards and backwards, which may create issues. # # See {RequestReporter#log_controller_action} for an example of # an API method which expects timing information. # `RequestReporter#log_controller_action` expects you to # provide timing information about a controller action. That timing # information is supposed to be obtained by calling # `UnionStationHooks.now`. # # @return [TimePoint] def now monotime_usec = Utils.monotime_usec_now pt = Utils.process_times TimePoint.new(monotime_usec, pt.utime, pt.stime) end # Returns a best-estimate delta (usec) between the wallclock and # the monotonic clock (updated every request), such that: # time_monotic_usec = time_wallclock_usec - delta def get_delta_monotonic @mono_mutex.synchronize { @delta_monotonic } end private # Although the de-facto state seems to be that reading/writing instance variables # is MT-safe, it's not guaranteed so better safe than sorry here. def set_delta_monotonic(val) @mono_mutex.synchronize { @delta_monotonic = val } end def create_context require_lib('context') @@context = Context.new(config[:ust_router_address], config[:ust_router_username] || 'logging', config[:ust_router_password], config[:node_name]) end def install_postfork_hook if defined?(PhusionPassenger) PhusionPassenger.on_event(:starting_worker_process) do |forked| if forked UnionStationHooks.context.clear_connection end end end end def install_event_pre_hook preprocessor = @@config[:event_preprocessor] if preprocessor define_singleton_method(:call_event_pre_hook, &preprocessor) else def call_event_pre_hook(_event) # Do nothing end end end def initialize_other_union_station_hooks_gems @@initializers.each do |initializer| initializer.initialize! end end def finalize_install @@config.freeze @@initializers.freeze @@app_group_name = @@config[:app_group_name] @@key = @@config[:union_station_key] if @@config[:debug] UnionStationHooks::Log.debugging = true end require_simple_json @mono_mutex = Mutex.new @delta_monotonic = 0 @@initialized = true end end end phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/connection.rb000644 000765 000024 00000003653 12233035540 042664 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'thread' UnionStationHooks.require_lib 'message_channel' module UnionStationHooks # Represents a connection to the UstRouter process. # # @private class Connection attr_reader :mutex attr_accessor :channel def initialize(io) @mutex = Mutex.new @refcount = 1 @channel = MessageChannel.new(io) if io end def connected? !!@channel end def disconnect @channel.io.close if @channel @channel = nil end def ref @refcount += 1 end def unref @refcount -= 1 if @refcount == 0 disconnect end end def synchronize @mutex.synchronize do yield end end end end phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/context.rb000644 000765 000024 00000022256 12233035540 042211 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'thread' require 'socket' UnionStationHooks.require_lib 'connection' UnionStationHooks.require_lib 'transaction' UnionStationHooks.require_lib 'log' UnionStationHooks.require_lib 'lock' UnionStationHooks.require_lib 'utils' module UnionStationHooks # A Context object is the "heart" of all `union_station_hooks_*` gems. It # contains a connection to the UstRouter (through a Connection object) # and allows you to create Transaction objects. # # Context is a singleton. During initialization # (`UnionStationHooks.initialize!`), an instance is created and stored in # `UnionStationHooks.context`. All the public API methods make use of this # singleton context. # # See hacking/Architecture.md for an overview. # # @private class Context RETRY_SLEEP = 0.2 NETWORK_ERRORS = [ Errno::EPIPE, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EHOSTUNREACH, Errno::ENETDOWN, Errno::ENETUNREACH, Errno::ETIMEDOUT ] include Utils attr_accessor :max_connect_tries attr_accessor :reconnect_timeout def initialize(ust_router_address, username, password, node_name) @server_address = ust_router_address @username = username @password = password if node_name && node_name.empty? @node_name = nil end # This mutex protects the following instance variables, but # not the contents of @connection. @mutex = Mutex.new @connection = Connection.new(nil) if @server_address && local_socket_address?(@server_address) @max_connect_tries = 10 else @max_connect_tries = 1 end @reconnect_timeout = 1 @next_reconnect_time = Time.utc(1980, 1, 1) end def connection @mutex.synchronize do @connection end end def clear_connection @mutex.synchronize do @connection.synchronize do @connection.unref @connection = Connection.new(nil) end end end def close @mutex.synchronize do @connection.synchronize do @connection.unref @connection = nil end end end def new_transaction(group_name, category, key) if !@server_address return Transaction.new(nil, nil) elsif !group_name || group_name.empty? raise ArgumentError, 'Group name may not be empty' end Lock.new(@mutex).synchronize do |_lock| if Time.now < @next_reconnect_time return Transaction.new(nil, nil) end Lock.new(@connection.mutex).synchronize do |connection_lock| if !@connection.connected? begin connect connection_lock.reset(@connection.mutex) rescue SystemCallError, IOError @connection.disconnect UnionStationHooks::Log.warn( "Cannot connect to the UstRouter at #{@server_address}; " \ "retrying in #{@reconnect_timeout} second(s).") @next_reconnect_time = Time.now + @reconnect_timeout return Transaction.new(nil, nil) rescue Exception => e @connection.disconnect raise e end end begin @connection.channel.write('openTransaction', '', group_name, '', category, Utils.encoded_timestamp, key, true, true) result = @connection.channel.read if result[0] != 'status' raise "Expected UstRouter to respond with 'status', " \ "but got #{result.inspect} instead" elsif result[1] == 'error' if result[2] raise "Unable to close transaction: #{result[2]}" else raise 'Unable to close transaction (no server message given)' end elsif result[1] != 'ok' raise "Expected UstRouter to respond with 'ok' or 'error', " \ "but got #{result.inspect} instead" elsif result.size < 3 raise 'Expected UstRouter to respond with an autogenerated ' \ 'transaction ID, but got none' end return Transaction.new(@connection, result[2]) rescue SystemCallError, IOError @connection.disconnect UnionStationHooks::Log.warn( "The UstRouter at #{@server_address}" \ ' closed the connection; will reconnect in ' \ "#{@reconnect_timeout} second(s).") @next_reconnect_time = Time.now + @reconnect_timeout return Transaction.new(nil, nil) rescue Exception => e @connection.disconnect raise e end end end end def continue_transaction(txn_id, group_name, category, key) if !@server_address return Transaction.new(nil, nil) elsif !txn_id || txn_id.empty? raise ArgumentError, 'Transaction ID may not be empty' end Lock.new(@mutex).synchronize do |_lock| if Time.now < @next_reconnect_time return Transaction.new(nil, nil) end Lock.new(@connection.mutex).synchronize do |connection_lock| if !@connection.connected? begin connect connection_lock.reset(@connection.mutex) rescue SystemCallError, IOError @connection.disconnect UnionStationHooks::Log.warn( "Cannot connect to the UstRouter at #{@server_address}; " \ "retrying in #{@reconnect_timeout} second(s).") @next_reconnect_time = Time.now + @reconnect_timeout return Transaction.new(nil, nil) rescue Exception => e @connection.disconnect raise e end end begin @connection.channel.write('openTransaction', txn_id, group_name, '', category, Utils.encoded_timestamp, key, true) return Transaction.new(@connection, txn_id) rescue SystemCallError, IOError @connection.disconnect UnionStationHooks::Log.warn( "The UstRouter at #{@server_address}" \ ' closed the connection; will reconnect in ' \ "#{@reconnect_timeout} second(s).") @next_reconnect_time = Time.now + @reconnect_timeout return Transaction.new(nil, nil) rescue Exception => e @connection.disconnect raise e end end end end private RANDOM_CHARS = %w( a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 0 1 2 3 4 5 6 7 8 9 ) def connect socket = connect_to_server(@server_address) channel = MessageChannel.new(socket) handshake_version(channel) handshake_authentication(channel) handshake_initialization(channel) @connection.unref @connection = Connection.new(socket) rescue Exception => e socket.close if socket && !socket.closed? raise e end def handshake_version(channel) result = channel.read if result.nil? raise EOFError elsif result.size != 2 || result[0] != 'version' raise IOError, "The UstRouter didn't sent a valid version identifier" elsif result[1] != '1' raise IOError, "Unsupported UstRouter protocol version #{result[1]}" end end def handshake_authentication(channel) channel.write_scalar(@username) channel.write_scalar(@password) process_ust_router_reply(channel, 'UstRouter client authentication error', SecurityError) end def handshake_initialization(channel) if @node_name channel.write('init', @node_name) else channel.write('init') end process_ust_router_reply(channel, 'UstRouter client initialization error') end end end phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/lock.rb000644 000765 000024 00000003517 12233035540 041454 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module UnionStationHooks # A wrapper around a mutex, for use within Connection. # # @private class Lock def initialize(mutex) @mutex = mutex @locked = false end def reset(mutex, lock_now = true) unlock if @locked @mutex = mutex lock if lock_now end def synchronize lock if !@locked begin yield(self) ensure unlock if @locked end end def lock raise if @locked @mutex.lock @locked = true end def unlock raise if !@locked @mutex.unlock @locked = false end end end phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/log.rb000644 000765 000024 00000004240 12233035540 041277 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module UnionStationHooks # Provides methods for `union_station_*` gems to log internal warnings and # debugging messages. This module is *not* to be used by application # developers for the purpose of logging information to Union Station. # # @private module Log @@debugging = false @@warn_callback = nil @@debug_callback = nil def self.debugging=(value) @@debugging = value end def self.warn(message) if @@warn_callback @@warn_callback.call(message) else STDERR.puts("[UnionStationHooks] #{message}") end end def self.debug(message) if @@debugging if @@debug_callback @@debug_callback.call(message) else STDERR.puts("[UnionStationHooks] #{message}") end end end def self.warn_callback=(cb) @@warn_callback = cb end def self.debug_callback=(cb) @@debug_callback = cb end end end phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/message_channel.rb000644 000765 000024 00000011352 12233035540 043634 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib# encoding: binary # Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module UnionStationHooks # This class allows reading and writing structured messages over # I/O channels. This is the Ruby implementation of Passenger's # src/cxx_supportlib/Utils/MessageIO.h; see that file for more information. # # @private class MessageChannel HEADER_SIZE = 2 DELIMITER = "\0" DELIMITER_NAME = 'null byte' UINT16_PACK_FORMAT = 'n' UINT32_PACK_FORMAT = 'N' class InvalidHashError < StandardError end # The wrapped IO object. attr_accessor :io # Create a new MessageChannel by wrapping the given IO object. def initialize(io = nil) @io = io # Make it binary just in case. @io.binmode if @io end # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity # rubocop:disable Metrics/PerceivedComplexity, Metrics/AbcSize # Read an array message from the underlying file descriptor. # Returns the array message as an array, or nil when end-of-stream has # been reached. # # Might raise SystemCallError, IOError or SocketError when something # goes wrong. def read buffer = new_buffer if !@io.read(HEADER_SIZE, buffer) return nil end while buffer.size < HEADER_SIZE tmp = @io.read(HEADER_SIZE - buffer.size) if tmp.empty? return nil else buffer << tmp end end chunk_size = buffer.unpack(UINT16_PACK_FORMAT)[0] if !@io.read(chunk_size, buffer) return nil end while buffer.size < chunk_size tmp = @io.read(chunk_size - buffer.size) if tmp.empty? return nil else buffer << tmp end end message = [] offset = 0 delimiter_pos = buffer.index(DELIMITER, offset) while !delimiter_pos.nil? if delimiter_pos == 0 message << '' else message << buffer[offset..delimiter_pos - 1] end offset = delimiter_pos + 1 delimiter_pos = buffer.index(DELIMITER, offset) end message rescue Errno::ECONNRESET nil end # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity # rubocop:enable Metrics/PerceivedComplexity, Metrics/AbcSize # Send an array message, which consists of the given elements, over the # underlying file descriptor. _name_ is the first element in the message, # and _args_ are the other elements. These arguments will internally be # converted to strings by calling to_s(). # # Might raise SystemCallError, IOError or SocketError when something # goes wrong. def write(name, *args) check_argument(name) args.each do |arg| check_argument(arg) end message = "#{name}#{DELIMITER}" args.each do |arg| message << arg.to_s << DELIMITER end @io.write([message.size].pack('n') << message) @io.flush end # Send a scalar message over the underlying IO object. # # Might raise SystemCallError, IOError or SocketError when something # goes wrong. def write_scalar(data) @io.write([data.size].pack('N') << data) @io.flush end private def check_argument(arg) if arg.to_s.index(DELIMITER) raise ArgumentError, "Message name and arguments may not contain #{DELIMITER_NAME}" end end if defined?(ByteString) def new_buffer ByteString.new end else def new_buffer '' end end end end # module UnionStationHooks phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/request_reporter/000755 000765 000024 00000000000 12233035540 043603 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlibphusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/request_reporter.rb000644 000765 000024 00000014474 12233035540 044142 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. UnionStationHooks.require_lib 'request_reporter/basics' UnionStationHooks.require_lib 'request_reporter/controllers' UnionStationHooks.require_lib 'request_reporter/view_rendering' UnionStationHooks.require_lib 'request_reporter/misc' module UnionStationHooks # A RequestReporter object is used for logging request-specific information # to Union Station. "Information" may include (and are not limited to): # # * Web framework controller and action name. # * Exceptions raised during the request. # * Cache hits and misses. # * Database actions. # # A unique RequestReporter is created by Passenger at the beginning of every # request (by calling {UnionStationHooks.begin_rack_request}). This object is # closed at the end of the same request (after the Rack body object is # closed). # # As an application developer, the RequestReporter is the main class # that you will be interfacing with. See the {UnionStationHooks} module # description for an example of how you can use RequestReporter. # # ## Obtaining a RequestReporter # # You are not supposed to create a RequestReporter object directly. # You are supposed to obtain the RequestReporter object that Passenger creates # for you. This is done through the `union_station_hooks` key in the Rack # environment hash, as well as through the `:union_station_hooks` key in # the current thread's object: # # env['union_station_hooks'] # # => RequestReporter object or nil # # Thread.current[:union_station_hooks] # # => RequestReporter object or nil # # Note that Passenger may not have created such an object because of an # error. At present, there are two error conditions that would cause a # RequestReporter object not to be created. However, your code should take # into account that in the future more error conditions may trigger this. # # 1. There is no transaction ID associated with the current request. # When Union Station support is enabled in Passenger, Passenger always # assigns a transaction ID. However, administrators can also # {https://www.phusionpassenger.com/library/admin/nginx/request_individual_processes.html # access Ruby processes directly} through process-private HTTP sockets, # bypassing Passenger's load balancing mechanism. In that case, no # transaction ID will be assigned. # 2. An error occurred recently while sending data to the UstRouter, either # because the UstRouter crashed or because of some other kind of # communication error occurred. This error condition isn't cleared until # certain a timeout has passed. # # The UstRouter is a Passenger process which runs locally and is # responsible for aggregating Union Station log data from multiple # processes, with the goal of sending the aggregate data over the network # to the Union Station service. # # This kind of error is automatically recovered from after a certain # period of time. # # ## Null mode # # The error condition 2 described above may also cause an existing # RequestReporter object to enter the "null mode". When this mode is entered, # any further actions on the RequestReporter object will become no-ops. # You can check whether the null mode is active by calling {#null?}. # # Closing a RequestReporter also causes it to enter the null mode. # # ## Thread-safety # # RequestReporter is *not* thread-safe. If you access it concurrently, be sure # to wrap its operations in a mutex. class RequestReporter # Returns a new RequestReporter object. You should not call # `RequestReporter.new` directly. See "Obtaining a RequestReporter" # in the {RequestReporter class description}. # # @api private def initialize(context, txn_id, app_group_name, key) raise ArgumentError, 'Transaction ID must be given' if txn_id.nil? raise ArgumentError, 'App group name must be given' if app_group_name.nil? raise ArgumentError, 'Union Station key must be given' if key.nil? @context = context @txn_id = txn_id @app_group_name = app_group_name @key = key @transaction = continue_transaction @next_view_rendering_number = 1 @next_user_activity_number = 1 @next_benchmark_number = 1 @next_database_query_number = 1 end # Indicates that no further information will be logged for this # request. # # @api private def close @transaction.close end # Returns whether is this RequestReporter object is in null mode. # See the {RequestReporter class description} for more information. def null? @transaction.null? end # Other methods are implemented in the files in the # 'request_reporter/' subdirectory. private def continue_transaction @context.continue_transaction(@txn_id, @app_group_name, :requests, @key) end # Called when one of the methods return early upon detecting null # mode. Used by tests to verify that methods return early. def do_nothing_on_null(_source) # Do nothing by default. Tests will stub this. end end end phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/simple_json.rb000644 000765 000024 00000025150 12233035540 043043 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib# encoding: utf-8 # ## Stupid small pure Ruby JSON parser & generator. # # Copyright © 2013 Mislav Marohnić # # 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, sublicense, 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 NONINFRINGEMENT. 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. # We use this in Phusion Passenger at places where we cannot depend on the JSON # gem being available, for example in 'passenger start' before the RuntimeInstaller # has run. require 'strscan' require 'forwardable' module UnionStationHooks # @private module SimpleJSON # Usage: # # JSON.parse(json_string) => Array/Hash # JSON.generate(object) => json string # # Run tests by executing this file directly. Pipe standard input to the script to have it # parsed as JSON and to display the result in Ruby. class JSON def self.parse(data) new(data).parse end WSP = /(\s|\/\/.*?\n|\/\*.*?\*\/)+/m OBJ = /[{\[]/; HEN = /\}/; AEN = /\]/ COL = /\s*:\s*/; KEY = /\s*,\s*/ NUM = /-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/ BOL = /true|false/; NUL = /null/ extend Forwardable attr_reader :scanner alias_method :s, :scanner def_delegators :scanner, :scan, :matched private :s, :scan, :matched def initialize data @scanner = StringScanner.new data.to_s end def parse space object end private def space() scan WSP end def endkey() scan(KEY) or space end def object matched == '{' ? hash : array if scan(OBJ) end def value object or string or scan(NUL) ? nil : scan(BOL) ? matched.size == 4: scan(NUM) ? eval(matched) : error end def hash obj = {} space repeat_until(HEN) do space k = string scan(COL) obj[k] = value endkey end obj end def array ary = [] space repeat_until(AEN) do space ary << value endkey end ary end SPEC = {'b' => "\b", 'f' => "\f", 'n' => "\n", 'r' => "\r", 't' => "\t"} UNI = 'u'; CODE = /[a-fA-F0-9]{4}/ STR = /"/; STE = '"' ESC = '\\' def string if scan(STR) str, esc = '', false while c = s.getch if esc str << (c == UNI ? (s.scan(CODE) || error).to_i(16).chr : SPEC[c] || c) esc = false else case c when ESC then esc = true when STE then break else str << c end end end str end end def error raise "parse error at: #{scan(/.{1,20}/m).inspect}" end def repeat_until reg until scan(reg) pos = s.pos yield error unless s.pos > pos end end module Generator def generate(obj) raise ArgumentError unless obj.is_a? Array or obj.is_a? Hash generate_type(obj) end alias dump generate private def generate_type(obj) type = obj.is_a?(Numeric) ? :Numeric : obj.class.name begin send(:"generate_#{type}", obj) rescue NoMethodError; raise ArgumentError, "can't serialize #{type}" end end ESC_MAP = Hash.new {|h,k| k }.update \ "\r" => 'r', "\n" => 'n', "\f" => 'f', "\t" => 't', "\b" => 'b' def quote(str) %("#{str}") end def generate_String(str) quote str.gsub(/[\r\n\f\t\b"\\]/) { "\\#{ESC_MAP[$&]}"} end def generate_simple(obj) obj.inspect end alias generate_Numeric generate_simple alias generate_TrueClass generate_simple alias generate_FalseClass generate_simple def generate_Symbol(sym) generate_String(sym.to_s) end def generate_Time(time) quote time.strftime(time.utc? ? "%F %T UTC" : "%F %T %z") end def generate_Date(date) quote date.to_s end def generate_NilClass(*) 'null' end def generate_Array(ary) '[%s]' % ary.map {|o| generate_type(o) }.join(', ') end def generate_Hash(hash) '{%s}' % hash.map { |key, value| "#{generate_String(key.to_s)}: #{generate_type(value)}" }.join(', ') end end extend Generator end if __FILE__ == $0 if !$stdin.tty? data = JSON.parse $stdin.read require 'pp' pp data else require 'test/unit' require 'date' class ParserTest < Test::Unit::TestCase PARSED = JSON.parse DATA.read def parsed() PARSED end def parse_string(str) JSON.parse(%(["#{str}"]).gsub('\\\\', '\\')).first end def test_string assert_equal "Pagination library for \"Rails 3\", Sinatra, Merb, DataMapper, and more", parsed['head']['repository']['description'] end def test_string_specials assert_equal "\r\n\t\f\b", parse_string('\r\n\t\f\b') assert_equal "aA", parse_string('\u0061\u0041') assert_equal "\e", parse_string('\u001B') assert_equal "xyz", parse_string('\x\y\z') assert_equal '"\\/', parse_string('\"\\\\\\/') assert_equal 'no #{interpolation}', parse_string('no #{interpolation}') end def test_hash assert_equal %w[label ref repository sha user], parsed['head'].keys.sort end def test_number assert_equal 124.3e2, parsed['head']['repository']['size'] end def test_bool assert_equal true, parsed['head']['repository']['fork'] assert_equal false, parsed['head']['repository']['private'] end def test_nil assert_nil parsed['head']['user']['company'] end def test_array assert_equal ["4438f", {"a" => "b"}], parsed['head']['sha'] end def test_invalid assert_raises(RuntimeError) { JSON.parse %({) } assert_raises(RuntimeError) { JSON.parse %({ "foo": }) } assert_raises(RuntimeError) { JSON.parse %([ "foo": "bar" ]) } assert_raises(RuntimeError) { JSON.parse %([ ~"foo" ]) } assert_raises(RuntimeError) { JSON.parse %([ "foo ]) } assert_raises(RuntimeError) { JSON.parse %([ "foo\\" ]) } assert_raises(RuntimeError) { JSON.parse %([ "foo\\uabGd" ]) } end def test_single_line_comments source = %Q{ // comment before document { // comment "foo": "1", "bar": "2", // another comment "baz": "3", "array": [ // comment inside array 1, 2, 3 // comment at end of array ] // comment at end of hash } // comment after document } doc = { "foo" => "1", "bar" => "2", "baz" => "3", "array" => [1, 2, 3] } assert_equal(doc, JSON.parse(source)) end def test_multi_line_comments source = %Q{ /* comment before * document */ { /* comment */ "foo": "1", "bar": "2", /* another comment */ "baz": "3", "array": [ /* comment inside array */ 1, 2, 3, 4, /* comment inside an array */ 5, /* // "nested" comments { "faux json": "inside comment" } */ 6, 7 /** * comment at end of array */ ] /************************** comment at end of hash **************************/ } /* comment after document */ } doc = { "foo" => "1", "bar" => "2", "baz" => "3", "array" => [1, 2, 3, 4, 5, 6, 7] } assert_equal(doc, JSON.parse(source)) end end class GeneratorTest < Test::Unit::TestCase def generate(obj) JSON.generate(obj) end def test_array assert_equal %([1, 2, 3]), generate([1, 2, 3]) end def test_bool assert_equal %([true, false]), generate([true, false]) end def test_null assert_equal %([null]), generate([nil]) end def test_string assert_equal %(["abc\\n123"]), generate(["abc\n123"]) end def test_string_unicode assert_equal %(["ć\\"č\\nž\\tš\\\\đ"]), generate(["ć\"č\nž\tš\\đ"]) end def test_time time = Time.utc(2012, 04, 19, 1, 2, 3) assert_equal %(["2012-04-19 01:02:03 UTC"]), generate([time]) end def test_date time = Date.new(2012, 04, 19) assert_equal %(["2012-04-19"]), generate([time]) end def test_symbol assert_equal %(["abc"]), generate([:abc]) end def test_hash json = generate(:abc => 123, 123 => 'abc') assert_match /^\{/, json assert_match /\}$/, json assert_equal [%("123": "abc"), %("abc": 123)], json[1...-1].split(', ').sort end def test_nested_structure json = generate(:hash => {1=>2}, :array => [1,2]) assert json.include?(%("hash": {"1": 2})) assert json.include?(%("array": [1, 2])) end def test_invalid_json assert_raises(ArgumentError) { generate("abc") } end def test_invalid_object err = assert_raises(ArgumentError) { generate("a" => Object.new) } assert_equal "can't serialize Object", err.message end end end end end # module SimpleJSON end # module UnionStationHooks __END__ { "head": { "ref": "master", "repository": { "forks": 0, "integrate_branch": "rails3", "watchers": 1, "language": "Ruby", "description": "Pagination library for \"Rails 3\", Sinatra, Merb, DataMapper, and more", "has_downloads": true, "fork": true, "created_at": "2011/10/24 03:20:48 -0700", "homepage": "http://github.com/mislav/will_paginate/wikis", "size": 124.3e2, "private": false, "has_wiki": true, "name": "will_paginate", "owner": "dbackeus", "url": "https://github.com/dbackeus/will_paginate", "has_issues": false, "open_issues": 0, "pushed_at": "2011/10/25 05:44:05 -0700" }, "label": "dbackeus:master", "sha": ["4438f", { "a" : "b" }], "user": { "name": "David Backeus", "company": null, "gravatar_id": "ebe96524f5db9e92188f0542dc9d1d1a", "location": "Stockholm (Sweden)", "type": "User", "login": "dbackeus" } } }phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/spec_helper.rb000644 000765 000024 00000023275 12233035540 043020 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'fileutils' require 'net/http' require 'uri' module UnionStationHooks # Contains helper methods for use in unit tests across all the # `union_station_hooks_*` gems. # # @private module SpecHelper extend self # Make methods available as class methods. def self.included(klass) # When included into another class, make sure that Utils # methods are made private. public_instance_methods(false).each do |method_name| klass.send(:private, method_name) end end # To be called during initialization of the test suite. def initialize! load_passenger initialize_ush_api initialize_debugging undo_bundler end # Lookup the `passenger-config` command, either by respecting the # `PASSENGER_CONFIG` environment variable, or by looking it up in `PATH`. # If the command cannot be found, the current process aborts with an # error message. def find_passenger_config passenger_config = ENV['PASSENGER_CONFIG'] if passenger_config.nil? || passenger_config.empty? passenger_config = find_passenger_config_vendor || find_passenger_config_in_path end if passenger_config.nil? || passenger_config.empty? abort 'ERROR: The unit tests are to be run against a specific ' \ 'Passenger version. However, the \'passenger-config\' command is ' \ 'not found. Please install Passenger, or (if you are sure ' \ 'Passenger is installed) set the PASSENGER_CONFIG environment ' \ 'variable to the \'passenger-config\' command.' end passenger_config end # Looks for the passenger-config command in PATH, returning nil # if not found. def find_passenger_config_in_path ENV['PATH'].split(':').each do |path| if File.exist?("#{path}/passenger-config") return "#{path}/passenger-config" end end end # Checks whether this union_station_hooks installation is a copy that # is vendored into Passenger, and if so, returns the full path to the # containing Passenger's passenger-config command. def find_passenger_config_vendor path = "#{UnionStationHooks::ROOT}/../../../../../bin/passenger-config" if File.exist?(path) File.expand_path(path) else nil end end # Uses `find_passenger_config` to lookup a Passenger installation, and # loads the Passenger Ruby support library associated with that # installation. All the constants defined in the Passenger Ruby support # library are loaded. In addition, checks whether the Passenger agent # executable is installed. If not, the current process aborts with an # error message. def load_passenger passenger_config = find_passenger_config puts "Using Passenger installation at: #{passenger_config}" passenger_ruby_libdir = `#{passenger_config} about ruby-libdir`.strip require("#{passenger_ruby_libdir}/phusion_passenger") PhusionPassenger.locate_directories PhusionPassenger.require_passenger_lib 'constants' puts "Loaded Passenger version #{PhusionPassenger::VERSION_STRING}" agent = PhusionPassenger.find_support_binary(PhusionPassenger::AGENT_EXE) if agent.nil? abort "ERROR: The Passenger agent isn't installed. Please ensure " \ "that it is installed, e.g. using:\n\n" \ " #{passenger_config} install-agent\n\n" end end def initialize_ush_api UnionStationHooks.require_lib('api') UnionStationHooks.instance_variable_set(:@mono_mutex, Mutex.new) UnionStationHooks.instance_variable_set(:@delta_monotonic, 0) end def initialize_debugging @@debug = !ENV['DEBUG'].to_s.empty? if @@debug UnionStationHooks.require_lib('log') UnionStationHooks::Log.debugging = true end end # Unit tests must undo the Bundler environment so that the gem's # own Gemfile doesn't affect subprocesses that may have their # own Gemfile. def undo_bundler clean_env = nil Bundler.with_clean_env do clean_env = ENV.to_hash end ENV.replace(clean_env) end # Checks whether `initialize_debugging` enabled debugging mode. def debug? @@debug end # Writes the given content to the file at the given path. If or or more # parent directories don't exist, then they are created. def write_file(path, content) dir = File.dirname(path) if !File.exist?(dir) FileUtils.mkdir_p(dir) end File.open(path, 'wb') do |f| f.write(content) end end def get_response(path) uri = URI.parse("#{root_url}#{path}") Net::HTTP.get_response(uri) end def get(path) response = get_response(path) return_200_response_body(path, response) end def return_200_response_body(path, response) if response.code == '200' response.body else raise "HTTP request to #{path} failed.\n" \ "Code: #{response.code}\n" \ "Body:\n" \ "#{response.body}" end end # Opens a debug shell. By default, the debug shell is opened in the current # working directory. If the current module has the `prepare_debug_shell` # method, that method is called before opening the debug shell. The method # could, for example, change the working directory. # # This method does *not* raise an exception if the debug shell exits with # an error. def debug_shell puts '------ Opening debug shell -----' @orig_dir = Dir.pwd begin if respond_to?(:prepare_debug_shell) prepare_debug_shell end system('bash') ensure Dir.chdir(@orig_dir) end puts '------ Exiting debug shell -----' end # Returns the path of a specific UstRouter dump file. # Requires that `@dump_dir` is set. def dump_file_path(category = 'requests') raise '@dump_dir variable required' if !@dump_dir "#{@dump_dir}/#{category}" end # Reads the contents of a specific UstRouter dump file. # Requires that `@dump_dir` is set. # # @raise SystemError Something went wrong during reading. def read_dump_file(category = 'requests') File.read(dump_file_path(category)) end # Waits until the dump file exists. Raises an error if # this doesn't become true within the default {#eventually} # timeout. def wait_for_dump_file_existance(category = 'requests') eventually do File.exist?(dump_file_path(category)) end end # Assert that the dump file eventually exists and that its contents # eventually match the given regex. def eventually_expect_dump_file_to_match(regex, category = 'requests') wait_for_dump_file_existance(category) eventually do read_dump_file(category) =~ regex end end # Assert that the dump file (if it ever exists) its contents will never match # the given regex. def never_expect_dump_file_to_match(regex, category = 'requests') should_never_happen do File.exist?(dump_file_path(category)) && read_dump_file(category) =~ regex end end # Makes `UnionStationHooks::Log.warn` not print anything. def silence_warnings UnionStationHooks::Log.warn_callback = lambda { |_message| } end # Asserts that something should eventually happen. This is done by checking # that the given block eventually returns true. The block is called # once every `check_interval` msec. If the block does not return true # within `deadline_duration` secs, then an exception is raised. def eventually(deadline_duration = 3, check_interval = 0.05) deadline = Time.now + deadline_duration while Time.now < deadline if yield return else sleep(check_interval) end end raise 'Time limit exceeded' end # Asserts that something should never happen. This is done by checking that # the given block never returns true. The block is called once every # `check_interval` msec, until `deadline_duration` seconds have passed. # If the block ever returns true, then an exception is raised. def should_never_happen(deadline_duration = 0.5, check_interval = 0.05) deadline = Time.now + deadline_duration while Time.now < deadline if yield raise "That which shouldn't happen happened anyway" else sleep(check_interval) end end end end end phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/time_point.rb000644 000765 000024 00000003027 12233035540 042667 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module UnionStationHooks # See {UnionStationHooks.now} for more information. class TimePoint # @api private attr_reader :monotime, :utime, :stime # @api private def initialize(monotime, utime, stime) @monotime = monotime @utime = utime @stime = stime end end end phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/transaction.rb000644 000765 000024 00000012551 12233035540 043047 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. UnionStationHooks.require_lib 'log' UnionStationHooks.require_lib 'context' UnionStationHooks.require_lib 'time_point' UnionStationHooks.require_lib 'utils' module UnionStationHooks # @private class Transaction attr_reader :txn_id def initialize(connection, txn_id) @connection = connection @txn_id = txn_id if connection raise ArgumentError, 'Transaction ID required' if txn_id.nil? connection.ref end end def null? !@connection || !@connection.connected? end def message(text) if !@connection log_message_to_null(text) return end @connection.synchronize do if !@connection.connected? log_message_to_null(text) return end UnionStationHooks::Log.debug('[Union Station log] ' \ "#{@txn_id} #{Utils.encoded_timestamp} #{text}") io_operation do @connection.channel.write('log', @txn_id, Utils.encoded_timestamp) @connection.channel.write_scalar(text) end end end def log_activity_block(name, extra_info = nil) has_error = false log_activity_begin(name, UnionStationHooks.now, extra_info) begin yield rescue Exception has_error = true is_closed = closed? raise ensure if !is_closed log_activity_end(name, UnionStationHooks.now, has_error) end end end def log_activity_begin(name, time = UnionStationHooks.now, extra_info = nil) if extra_info extra_info_base64 = Utils.base64(extra_info) else extra_info_base64 = nil end if time.is_a?(TimePoint) message "BEGIN: #{name} (#{time.monotime.to_s(36)}," \ "#{time.utime.to_s(36)},#{time.stime.to_s(36)}) " \ "#{extra_info_base64}" else message "BEGIN: #{name} (#{Utils.monotime_usec_from_time(time).to_s(36)})" \ " #{extra_info_base64}" end end def log_activity_end(name, time = UnionStationHooks.now, has_error = false) if time.is_a?(TimePoint) if has_error message "FAIL: #{name} (#{time.monotime.to_s(36)}," \ "#{time.utime.to_s(36)},#{time.stime.to_s(36)})" else message "END: #{name} (#{time.monotime.to_s(36)}," \ "#{time.utime.to_s(36)},#{time.stime.to_s(36)})" end else if has_error message "FAIL: #{name} (#{Utils.monotime_usec_from_time(time).to_s(36)})" else message "END: #{name} (#{Utils.monotime_usec_from_time(time).to_s(36)})" end end end def log_activity(name, begin_time, end_time, extra_info = nil, has_error = false) log_activity_begin(name, begin_time, extra_info) log_activity_end(name, end_time, has_error) end def close return if !@connection @connection.synchronize do return if !@connection.connected? begin io_operation do # We need an ACK here so that we the UstRouter doesn't end up # processing the Core's openTransaction and closeTransaction pair # before it has received this process's openTransaction command. @connection.channel.write('closeTransaction', @txn_id, Utils.encoded_timestamp, true) Utils.process_ust_router_reply(@connection.channel, "Error handling reply for 'closeTransaction' message") end ensure @connection.unref @connection = nil end end end def closed? return nil if !@connection @connection.synchronize do !@connection.connected? end end private def log_message_to_null(text) UnionStationHooks::Log.debug('[Union Station log to null] ' \ "#{@txn_id} #{Utils.encoded_timestamp} #{text}") end def io_operation yield rescue SystemCallError, IOError => e @connection.disconnect UnionStationHooks::Log.warn( "Error communicating with the UstRouter: #{e.message}") rescue Exception => e @connection.disconnect raise e end end end phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/utils.rb000644 000765 000024 00000013175 12233035540 041665 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'base64' module UnionStationHooks # Various utility methods. # # @private module Utils extend self # Make methods available as class methods. def self.included(klass) # When included into another class, make sure that Utils # methods are made private. public_instance_methods(false).each do |method_name| klass.send(:private, method_name) end end def require_key(options, key) if !options.key?(key) raise ArgumentError, "Option #{key.inspect} is required" end end def require_non_empty_key(options, key) value = options[key] if value.nil? || value.empty? raise ArgumentError, "Option #{key.inspect} is required " \ 'and must be non-empty' else value end end def get_socket_address_type(address) if address =~ %r{^unix:.} :unix elsif address =~ %r{^tcp://.} :tcp else :unknown end end def connect_to_server(address) case get_socket_address_type(address) when :unix UNIXSocket.new(address.sub(/^unix:/, '')) when :tcp host, port = address.sub(%r{^tcp://}, '').split(':', 2) port = port.to_i TCPSocket.new(host, port) else raise ArgumentError, "Unknown socket address type for '#{address}'." end end def local_socket_address?(address) case get_socket_address_type(address) when :unix return true when :tcp host, _port = address.sub(%r{^tcp://}, '').split(':', 2) host == '127.0.0.1' || host == '::1' || host == 'localhost' else raise ArgumentError, "Unknown socket address type for '#{address}'." end end if Process.const_defined?(:CLOCK_MONOTONIC) def monotime_usec_now Process.clock_gettime(Process::CLOCK_MONOTONIC, :microsecond) end else # Workaround for approximating the monotonic clock def monotime_usec_now monotime_usec_from_time end end def monotime_usec_from_time(time = Time.now) timestamp = time.to_i * 1_000_000 + time.usec - UnionStationHooks.get_delta_monotonic end def encoded_timestamp time = Time.now timestamp = time.to_i * 1_000_000 + time.usec timestamp.to_s(36) end if Base64.respond_to?(:strict_encode64) def base64(data) Base64.strict_encode64(data) end else # Base64-encodes the given data. Newlines are removed. # This is like `Base64.strict_encode64`, but also works # on Ruby 1.8 which doesn't have that method. def base64(data) result = Base64.encode64(data) result.delete!("\n") result end end def process_ust_router_reply(channel, error_description, error_class = RuntimeError, unexpected_error_class = RuntimeError) result = channel.read if result.nil? raise unexpected_error_class, "#{error_description}: UstRouter did not send a reply" end process_ust_router_reply_message(result, error_description, error_class, unexpected_error_class) result end def process_ust_router_reply_message(message, error_description, error_class = RuntimeError, unexpected_error_class = RuntimeError) if message[0] != 'status' raise unexpected_error_class, "#{error_description}: expected UstRouter to respond with " \ "'status', but got #{message.inspect} instead" end if message[1] == 'error' if message[2] raise error_class, "#{error_description}: #{message[2]}" else raise error_class, "#{error_description} (no server message given)" end elsif message[1] != 'ok' raise unexpected_error_class, "#{error_description}: expected UstRouter to respond with " \ "'ok' or 'error', but got #{message.inspect} instead" end end if defined?(PhusionPassenger::NativeSupport) def process_times PhusionPassenger::NativeSupport.process_times end else ProcessTimes = Struct.new(:utime, :stime) def process_times times = Process.times ProcessTimes.new((times.utime * 1_000_000).to_i, (times.stime * 1_000_000).to_i) end end end end phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/version.rb000644 000765 000024 00000003037 12233035540 042206 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module UnionStationHooks version_file = File.expand_path('version_data.rb', File.dirname(__FILE__)) version_data = eval(File.read(version_file)) MAJOR_VERSION = version_data[:major] MINOR_VERSION = version_data[:minor] TINY_VERSION = version_data[:tiny] VERSION_STRING = version_data[:string] end phusion_passenger/vendor/union_station_hooks_core/lib/union_station_hooks_core/version_data.rb000644 000765 000024 00000004021 12233035540 043171 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2016 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. # This file contains union_station_hook_core's version number. It is meant # to be read and evaluated by the gemspec. We do not define any functions or # modules here because we need to support the following situation: # # 1. Passenger loads its vendored union_station_hooks_* gems. This defines # various modules. # 2. The app specified union_station_hooks_* gems in its Gemfile, which # indicates that it wants to override Passenger's vendored # union_station_hooks_* gems with its own versions. This will cause Bundler # to load union_station_hooks_*.gemspec. # # To make the gemspecs load properly and without affecting the already-loaded # union_station_hooks_* gems code, we must not define any functions or modules # here. { :major => 2, :minor => 1, :tiny => 1, :string => '2.1.1' } vendor/union_station_hooks_core/lib/union_station_hooks_core/request_reporter/basics.rb000644 000765 000024 00000016654 12233035540 045410 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'thread' module UnionStationHooks class RequestReporter ###### Logging basic request information ###### # A mutex for synchronizing GC stats reporting. We do this because in # multithreaded situations we don't want to interleave GC stats access with # calls to `GC.clear_stats`. Not that GC stats are very helpful in # multithreaded situations, but this is better than nothing. # # @private GC_MUTEX = Mutex.new # @private OBJECT_SPACE_SUPPORTS_LIVE_OBJECTS = ObjectSpace.respond_to?(:live_objects) # @private OBJECT_SPACE_SUPPORTS_ALLOCATED_OBJECTS = ObjectSpace.respond_to?(:allocated_objects) # @private OBJECT_SPACE_SUPPORTS_COUNT_OBJECTS = ObjectSpace.respond_to?(:count_objects) # @private GC_SUPPORTS_TIME = GC.respond_to?(:time) # @private GC_SUPPORTS_CLEAR_STATS = GC.respond_to?(:clear_stats) # Logs the beginning of a Rack request. This is automatically called # from {UnionStationHooks.begin_rack_request} (and thus automatically # from Passenger). # # @private def log_request_begin return do_nothing_on_null(:log_request_begin) if null? @transaction.log_activity_begin('app request handler processing') end # Logs the end of a Rack request. This means that the Rack response body # has been written out, and that the `#close` has been called on the Rack # response body. # # This does not necessarily indicate that any buffering mechanism in # between the app and the client (e.g. Nginx, or the Passenger core) is # done flushing the response to the client. # # This is automatically called from {UnionStationHooks.begin_rack_request} # (and thus automatically from Passenger). # # @private def log_request_end(uncaught_exception_raised_during_request = false) return do_nothing_on_null(:log_request_end) if null? @transaction.log_activity_end('app request handler processing', UnionStationHooks.now, uncaught_exception_raised_during_request) end # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity # @private def log_gc_stats_on_request_begin return do_nothing_on_null(:log_gc_stats_on_request_begin) if null? # See the docs for MUTEX on why we synchronize this. GC_MUTEX.synchronize do if OBJECT_SPACE_SUPPORTS_LIVE_OBJECTS @transaction.message('Initial objects on heap: ' \ "#{ObjectSpace.live_objects}") end if OBJECT_SPACE_SUPPORTS_ALLOCATED_OBJECTS @transaction.message('Initial objects allocated so far: ' \ "#{ObjectSpace.allocated_objects}") elsif OBJECT_SPACE_SUPPORTS_COUNT_OBJECTS count = ObjectSpace.count_objects @transaction.message('Initial objects allocated so far: ' \ "#{count[:TOTAL] - count[:FREE]}") end if GC_SUPPORTS_TIME @transaction.message("Initial GC time: #{GC.time}") end end end # @private def log_gc_stats_on_request_end return do_nothing_on_null(:log_gc_stats_on_request_end) if null? # See the docs for MUTEX on why we synchronize this. GC_MUTEX.synchronize do if OBJECT_SPACE_SUPPORTS_LIVE_OBJECTS @transaction.message('Final objects on heap: ' \ "#{ObjectSpace.live_objects}") end if OBJECT_SPACE_SUPPORTS_ALLOCATED_OBJECTS @transaction.message('Final objects allocated so far: ' \ "#{ObjectSpace.allocated_objects}") elsif OBJECT_SPACE_SUPPORTS_COUNT_OBJECTS count = ObjectSpace.count_objects @transaction.message('Final objects allocated so far: ' \ "#{count[:TOTAL] - count[:FREE]}") end if GC_SUPPORTS_TIME @transaction.message("Final GC time: #{GC.time}") end if GC_SUPPORTS_CLEAR_STATS # Clear statistics to void integer wraps. GC.clear_stats end end end # rubocop:enable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity # Logs that the application server is about to write out the Rack # response body. # # This call *must* be followed by a call to {#log_writing_rack_body_end} # some time later. # # This is automatically called by Passenger's Rack handler. # # @private # @return An ID to be passed later to {#log_writing_rack_body_end}, or nil # on error. def log_writing_rack_body_begin return do_nothing_on_null(:log_writing_rack_body_begin) if null? @transaction.log_activity_begin('app writing out response body') :writing_rack_body # Random non-nil ID end # Logs that the application server is done writing out the Rack # response body. This does not necessarily indicate that any buffering # mechanism in between the app and the client (e.g. Nginx, or the Passenger # core) is done flushing the response to the client. # # This is automatically called by Passenger's Rack handler. # # @private def log_writing_rack_body_end(id) return do_nothing_on_null(:log_writing_rack_body_end) if null? return if id.nil? @transaction.log_activity_end('app writing out response body') end # Logs that the application server is about to call `#close` on the # Rack body object. # # This call *must* be followed by a call to {#log_closing_rack_body_end} # some time later. # # This is automatically called by Passenger's Rack handler. # # @private # @return An ID to be passed later to {#log_closing_rack_body_end}, or nil # on error. def log_closing_rack_body_begin return do_nothing_on_null(:log_closing_rack_body_begin) if null? @transaction.log_activity_begin('closing rack body object') :closing_rack_body # Random non-nil ID end # Logs that the application server is done calling `#close` on the # Rack body object. # # This is automatically called by Passenger's Rack handler. # # @private def log_closing_rack_body_end(id) return do_nothing_on_null(:log_closing_rack_body_end) if null? return if id.nil? @transaction.log_activity_end('closing rack body object') end end end vendor/union_station_hooks_core/lib/union_station_hooks_core/request_reporter/controllers.rb000644 000765 000024 00000017525 12233035540 046510 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. UnionStationHooks.require_lib 'utils' module UnionStationHooks class RequestReporter ###### Logging controller-related information ###### # Logs that you are calling a web framework controller action. Of course, # you should only call this if your web framework has the concept of # controller actions. For example Rails does, but Sinatra and Grape # don't. # # This form takes an options hash as well as a block. You can pass # additional information about the web framework controller action, which # will be logged. The block is expected to perform the actual request # handling. When the block returns, timing information about the block is # automatically logged. # # See also {#log_controller_action} for a form that doesn't # expect a block. # # The `union_station_hooks_rails` gem automatically calls this for you # if your application is a Rails app. # # @yield The given block is expected to perform request handling. # @param [Hash] options Information about the controller action. # All options are optional. # @option options [String] :controller_name # The controller's name, e.g. `PostsController`. # @option options [String] :action_name # The controller action's name, e.g. `create`. # @option options [String] :method # The HTTP method that the web framework thinks this request should have, # e.g. `GET` and `PUT`. The main use case for this option is to support # Rails's HTTP verb emulation. Rails uses a parameter named # [`_method`](http://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-patch-put-or-delete-methods-work-questionmark) # to emulate HTTP verbs besides GET and POST. Other web frameworks may # have a similar mechanism. # @return The return value of the block. # # @example Rails example # # This example shows what to put inside a Rails controller action # # method. Note that all of this is automatically done for you if you # # use the union_station_hooks_rails gem. # options = { # :controller_name => self.class.name, # :action_name => action_name, # :method => request.request_method # } # reporter.log_controller_action_block(options) do # do_some_request_processing_here # end def log_controller_action_block(options = {}) if null? do_nothing_on_null(:log_controller_action_block) yield else build_full_controller_action_string(options) has_error = true begin_time = UnionStationHooks.now begin result = yield has_error = false result ensure log_controller_action( options.merge( :begin_time => begin_time, :end_time => UnionStationHooks.now, :has_error => has_error ) ) end end end # Logs that you are calling a web framework controller action. Of course, # you should only call this if your web framework has the concept of # controller actions. For example Rails does, but Sinatra and Grape # don't. # # You can pass additional information about the web framework controller # action, which will be logged. # # Unlike {#log_controller_action_block}, this form does not expect a block. # However, you are expected to pass timing information to the options # hash. # # The `union_station_hooks_rails` gem automatically calls # {#log_controller_action_block} for you if your application is a Rails # app. # # @param [Hash] options Information about the controller action. # @option options [String] :controller_name (optional) # The controller's name, e.g. `PostsController`. # @option options [String] :action_name (optional if :controller_name # isn't set) The controller action's name, e.g. `create`. # @option options [String] :method (optional) # The HTTP method that the web framework thinks this request should have, # e.g. `GET` and `PUT`. The main use case for this option is to support # Rails's HTTP verb emulation. Rails uses a parameter named # [`_method`](http://guides.rubyonrails.org/form_helpers.html#how-do-forms-with-patch-put-or-delete-methods-work-questionmark) # to emulate HTTP verbs besides GET and POST. Other web frameworks may # have a similar mechanism. # @option options [TimePoint or Time] :begin_time The time at which the # controller action begun. See {UnionStationHooks.now} to learn more. # @option options [TimePoint or Time] :end_time The time at which the # controller action ended. See {UnionStationHooks.now} to learn more. # @option options [Boolean] :has_error (optional) Whether an uncaught # exception occurred during the request. Default: false. # # @example # # This example shows what to put inside a Rails controller action # # method. Note that all of this is automatically done for you if you # # use the union_station_hooks_rails gem. # options = { # :controller_name => self.class.name, # :action_name => action_name, # :method => request.request_method, # :begin_time => UnionStationHooks.now # } # begin # do_some_request_processing_here # rescue Exception # options[:has_error] = true # raise # ensure # options[:end_time] = UnionStationHooks.now # reporter.log_controller_action(options) # end def log_controller_action(options) return do_nothing_on_null(:log_controller_action) if null? Utils.require_key(options, :begin_time) Utils.require_key(options, :end_time) if options[:controller_name] build_full_controller_action_string(options) @transaction.message("Controller action: #{@controller_action}") end if options[:method] @transaction.message("Application request method: #{options[:method]}") end @transaction.log_activity('framework request processing', options[:begin_time], options[:end_time], nil, options[:has_error]) end # Returns whether {#log_controller_action_block} or # {#log_controller_action} has been called during this request. # # @return [Boolean] def controller_action_logged? !!@controller_action end private def build_full_controller_action_string(options) if options[:controller_name] Utils.require_key(options, :action_name) @controller_action = "#{options[:controller_name]}#" \ "#{options[:action_name]}" end end end end vendor/union_station_hooks_core/lib/union_station_hooks_core/request_reporter/misc.rb000644 000765 000024 00000031411 12233035540 045063 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. UnionStationHooks.require_lib 'utils' module UnionStationHooks class RequestReporter ###### Logging miscellaneous other information ###### # Logs a user-defined activity, for display in the activity timeline. # # An activity is a block in the activity timeline in the Union Station # user interace. It has a name, a begin time and an end time. # # This form takes a block. Before and after the block runs, the time # is measured. # # @param name The name that should show up in the activity timeline. # It can be any arbitrary name but may not contain newlines. # @return The return value of the block. # @yield The block is expected to perform the activity. # @example # reporter.log_user_activity_block('Preheat cache') do # calculate_preheat_values.each_pair do |key, value| # Rails.cache.write(key, value) # end # end def log_user_activity_block(name, &block) if null? do_nothing_on_null(:log_user_activity_block) yield else @transaction.log_activity_block(next_user_activity_name, name, &block) end end # Logs the begin of a user-defined activity, for display in the # activity timeline. # # An activity is a block in the activity timeline in the Union Station # user interace. It has a name, a begin time and an end time. # # This form logs only the name and the begin time. You *must* also # call {#log_user_activity_end} later with the same name to log the end # time. # # @param name The name that should show up in the activity timeline. # It can be any arbitrary name but may not contain newlines. # @return id An ID which you must pass to {#log_user_activity_end} later. def log_user_activity_begin(name) return do_nothing_on_null(:log_user_activity_begin) if null? id = next_user_activity_name @transaction.log_activity_begin(id, UnionStationHooks.now, name) id end # Logs the end of a user-defined activity, for display in the # activity timeline. # # An activity is a block in the activity timeline in the Union Station # user interace. It has a name, a begin time and an end time. # # This form logs only the name and the end time. You *must* also # have called {#log_user_activity_begin} earlier with the same name to log # the begin time. # # @param id The ID which you obtained from {#log_user_activity_begin} # earlier. # @param [Boolean] has_error Whether an uncaught # exception occurred during the activity. def log_user_activity_end(id, has_error = false) return do_nothing_on_null(:log_user_activity_end) if null? @transaction.log_activity_end(id, UnionStationHooks.now, has_error) end # Logs a user-defined activity, for display in the activity timeline. # # An activity is a block in the activity timeline in the Union Station # user interace. It has a name, a begin time and an end time. # # Unlike {#log_user_activity_block}, this form does not expect a block. # However, you are expected to pass timing information. # # @param name The name that should show up in the activity timeline. # It can be any arbitrary name but may not contain newlines. # @param [TimePoint or Time] begin_time The time at which this activity # begun. See {UnionStationHooks.now} to learn more. # @param [TimePoint or Time] end_time The time at which this activity # ended. See {UnionStationHooks.now} to learn more. # @param [Boolean] has_error Whether an uncaught # exception occurred during the activity. def log_user_activity(name, begin_time, end_time, has_error = false) return do_nothing_on_null(:log_user_activity) if null? @transaction.log_activity(next_user_activity_name, begin_time, end_time, name, has_error) end # Logs a benchmarking activity, for display in the activity timeline. # # An activity is a block in the activity timeline in the Union Station # user interace. It has a name, a begin time and an end time. # # The primary use case of this method is to integrate with Rails's # benchmarking API (`ActiveSupport::Benchmarkable`). The Rails benchmarking # API allows you to run a block and to log how long that block has taken. # But you can also use it to integrate with the Ruby standard library's # `Benchmark` class. # # You can wrap a benchmark call in a # `UnionStationHooks.log_benchmark_block` call, so that an entry for it is # displayed in the acitivity timeline. This method measures the time before # and after the block runs. # # The difference between this method and {#log_user_activity_block} is that # this method generates timeline blocks of a different color, as to # differentiate user-defined activities from benchmark activities. # # If your app is a Rails app, then the `union_station_hooks_rails` gem # automatically calls this for you every time # `ActiveSupport::Benchmarkable#benchmark` is called. This includes # `benchmark` calls from controllers and from views. # # @param title A title for this benchmark. It can be any arbitrary name but # may not contain newlines. # @return The return value of the block. # @yield The block is expected to perform the benchmarking activity. # @example Rails example # # This example shows what to put inside a Rails controller action # # method. Note that the `log_benchmark_block` call is automatically done # # for you if you use the union_station_hooks_rails gem. # UnionStationHooks.log_benchmark_block('Process data files') do # benchmark('Process data files') do # expensive_files_operation # end # end def log_benchmark_block(title = 'Benchmarking', &block) if null? do_nothing_on_null(:log_benchmark_block) yield else @transaction.log_activity_block(next_benchmark_name, title, &block) end end # Logs an exception that occurred during a request. # # If you want to use an exception that occurred outside the # request/response cycle, e.g. an exception that occurred in a thread, # use {UnionStationHooks.log_exception} instead. # # If {#log_controller_action_block} or {#log_controller_action} # was called during the same request, then the information passed to # those methods will be included in the exception report. # # @param [Exception] exception def log_exception(exception) transaction = @context.new_transaction( @app_group_name, :exceptions, @key) begin return do_nothing_on_null(:log_exception) if transaction.null? base64_message = exception.message base64_message = exception.to_s if base64_message.empty? base64_message = Utils.base64(base64_message) base64_backtrace = Utils.base64(exception.backtrace.join("\n")) if controller_action_logged? transaction.message("Controller action: #{@controller_action}") end transaction.message("Request transaction ID: #{@txn_id}") transaction.message("Message: #{base64_message}") transaction.message("Class: #{exception.class.name}") transaction.message("Backtrace: #{base64_backtrace}") ensure transaction.close end end # Logs a database query that was performed during the request. # # @option options [String] :name (optional) A name for this database # query activity. Default: "SQL" # @option options [TimePoint or Time] :begin_time The time at which this # database query begun. See {UnionStationHooks.now} to learn more. # @option options [TimePoint or Time] :end_time The time at which this # database query ended. See {UnionStationHooks.now} to learn more. # @option options [String] :query The database query string. def log_database_query(options) return do_nothing_on_null(:log_database_query) if null? Utils.require_key(options, :begin_time) Utils.require_key(options, :end_time) Utils.require_non_empty_key(options, :query) name = options[:name] || 'SQL' begin_time = options[:begin_time] end_time = options[:end_time] query = options[:query] @transaction.log_activity(next_database_query_name, begin_time, end_time, "#{name}\n#{query}") end # Logs that something was successfully retrieved from a cache. # This can be any cache, be it an in-memory Hash, Redis, Memcached, a # flat file or whatever. # # There is just one exception. You should not use this method to log cache # hits in the ActiveRecord SQL cache or similar mechanisms. # Database-related timing should be logged with {#log_database_query}. # # If your app is a Rails app, then the `union_station_hooks_rails` gem # automatically calls this for you every time an `ActiveSupport::Cache` # `#fetch` or `#read` call success. This includes calls to # `Rails.cache.fetch` or `Rails.cache.read`, because `Rails.cache` is # an instance of `ActiveSupport::Cache`. # # @param [String] name A unique name for this cache hit event. The cache # key is a good value to use. # @note At present (30 September 2015), logged cache hit/miss information # isn't shown in the Union Station interface. We may implement this # feature in the near future. def log_cache_hit(name) return do_nothing_on_null(:log_cache_hit) if null? @transaction.message("Cache hit: #{name}") end # Logs the failure to retrieve something from a cache. # This can be any cache, be it an in-memory Hash, Redis, Memcached, a # flat file or whatever. # # There is just one exception. You should not use this method to log cache # misses in the ActiveRecord SQL cache or similar mechanisms. # Database-related timing should be logged with {#log_database_query}. # # If your app is a Rails app, then the `union_station_hooks_rails` gem # automatically calls this for you every time an `ActiveSupport::Cache` # `#fetch` or `#read` call success. This includes calls to # `Rails.cache.fetch` or `Rails.cache.read`, because `Rails.cache` is # an instance of `ActiveSupport::Cache`. # # @param [String] name A unique name for this cache miss event. The cache # key is a good value to use. # @param [Numeric] miss_cost_duration The amount of time that was spent in # calculating or processing something, as a result of this cache miss. # This time is in **microseconds**. # @note At present (30 September 2015), logged cache hit/miss information # isn't shown in the Union Station interface. We may implement this # feature in the near future. def log_cache_miss(name, miss_cost_duration = nil) return do_nothing_on_null(:log_cache_miss) if null? if miss_cost_duration @transaction.message("Cache miss (#{miss_cost_duration.to_i} usec): " \ "#{name}") else @transaction.message("Cache miss: #{name}") end end private def next_user_activity_name result = @next_user_activity_number @next_user_activity_number += 1 "user activity #{result}" end def next_benchmark_name result = @next_benchmark_number @next_benchmark_number += 1 "benchmark #{result}" end def next_database_query_name result = @next_database_query_number @next_database_query_number += 1 "database query #{result}" end end end vendor/union_station_hooks_core/lib/union_station_hooks_core/request_reporter/view_rendering.rb000644 000765 000024 00000007507 12233035540 047150 0ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger# Union Station - https://www.unionstationapp.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. UnionStationHooks.require_lib 'utils' module UnionStationHooks class RequestReporter ###### Logging view rendering-related information ###### # Logs timing information about the rendering of a single view, template or # partial. This form expects a block, which is to perform the # view/template/partial rendering. Timing information is collected before # and after the block returns. # # The `union_station_hooks_rails` gem automatically calls this for you # if your application is a Rails app. It will call this on every view # or partial rendering. # # @param [String] name Name of the view, template or partial that is being # rendered. # @yield The given block is expected to perform the actual view rendering. # @return The return value of the block. def log_view_rendering_block(name, &block) if null? do_nothing_on_null(:log_view_rendering_block) yield else @transaction.log_activity_block(next_view_rendering_name, name, &block) end end # Logs timing information about the rendering of a single view, template or # partial. # # Unlike {#log_view_rendering_block}, this form does not expect a block. # However, you are expected to pass timing information to the options # hash. # # The `union_station_hooks_rails` gem automatically calls # {#log_view_rendering_block} for you if your application is a Rails app. # It will call this on every view or partial rendering. # # @option options [String] :name Name of the view, template or partial # that is being rendered. # @option options [TimePoint or Time] :begin_time The time at which this # view rendering begun. See {UnionStationHooks.now} to learn more. # @option options [TimePoint or Time] :end_time The time at which this view # rendering ended. See {UnionStationHooks.now} to learn more. # @option options [Boolean] :has_error (optional) Whether an uncaught # exception occurred during the view rendering. Default: false. def log_view_rendering(options) return do_nothing_on_null(:log_view_rendering) if null? Utils.require_key(options, :name) Utils.require_key(options, :begin_time) Utils.require_key(options, :end_time) @transaction.log_activity(next_view_rendering_name, options[:begin_time], options[:end_time], options[:name], options[:has_error]) end private def next_view_rendering_name result = @next_view_rendering_number @next_view_rendering_number += 1 "view rendering #{result}" end end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/daemon_controller/lock_file.rb000644 000765 000024 00000011750 12233035540 033260 0ustar00honglistaff000000 000000 # daemon_controller, library for robust daemon management # Copyright (c) 2010-2015 Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'fcntl' module PhusionPassenger class DaemonController # A lock file is a synchronization mechanism, like a Mutex, but it also allows # inter-process synchronization (as opposed to only inter-thread synchronization # within a single process). # # Processes can obtain either a shared lock or an exclusive lock. It's possible # for multiple processes to obtain a shared lock on a file as long as no # exclusive lock has been obtained by a process. If a process has obtained an # exclusive lock, then no other processes can lock the file, whether they're # trying to obtain a shared lock or an exclusive lock. # # Note that on JRuby, LockFile can only guarantee synchronization between # threads if the different threads use the same LockFile object. Specifying the # same filename is not enough. class LockFile class AlreadyLocked < StandardError end # Create a LockFile object. The lock file is initially not locked. # # +filename+ may point to a nonexistant file. In that case, the lock # file will not be created until one's trying to obtain a lock. # # Note that LockFile will use this exact filename. So if +filename+ # is a relative filename, then the actual lock file that will be used # depends on the current working directory. def initialize(filename) @filename = filename end # Obtain an exclusive lock on the lock file, yield the given block, # then unlock the lockfile. If the lock file was already locked (whether # shared or exclusively) by another process/thread then this method will # block until the lock file has been unlocked. # # The lock file *must* be writable, otherwise an Errno::EACCESS # exception will be raised. def exclusive_lock File.open(@filename, 'w') do |f| if Fcntl.const_defined? :F_SETFD f.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) end f.flock(File::LOCK_EX) yield end end # Obtain an exclusive lock on the lock file, yield the given block, # then unlock the lockfile. If the lock file was already exclusively # locked by another process/thread then this method will # block until the exclusive lock has been released. This method will not # block if only shared locks have been obtained. # # The lock file *must* be writable, otherwise an Errno::EACCESS # exception will be raised. def shared_lock File.open(@filename, 'w+') do |f| if Fcntl.const_defined? :F_SETFD f.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) end f.flock(File::LOCK_SH) yield end end # Try to obtain a shared lock on the lock file, similar to #shared_lock. # But unlike #shared_lock, this method will raise AlreadyLocked if # no lock can be obtained, instead of blocking. # # If a lock can be obtained, then the given block will be yielded. def try_shared_lock File.open(@filename, 'w+') do |f| if Fcntl.const_defined? :F_SETFD f.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) end if f.flock(File::LOCK_SH | File::LOCK_NB) yield else raise AlreadyLocked end end end # Try to obtain an exclusive lock on the lock file, similar to #exclusive_lock. # But unlike #exclusive_lock, this method will raise AlreadyLocked if # no lock can be obtained, instead of blocking. # # If a lock can be obtained, then the given block will be yielded. def try_exclusive_lock File.open(@filename, 'w') do |f| if Fcntl.const_defined? :F_SETFD f.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) end if f.flock(File::LOCK_EX | File::LOCK_NB) yield else raise AlreadyLocked end end end end # class LockFile end # class DaemonController end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/daemon_controller/spawn.rb000644 000765 000024 00000002551 12233035540 032460 0ustar00honglistaff000000 000000 # daemon_controller, library for robust daemon management # Copyright (c) 2010 Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. # This helper script is used for daemonizing a command by executing it and # then exiting ourselves. Used on Ruby 1.9 and JRuby because forking may not # be safe/supported on all platforms. Process.setsid Process.spawn(ARGV[0])passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/daemon_controller/version.rb000644 000765 000024 00000002510 12233035540 033010 0ustar00honglistaff000000 000000 # daemon_controller, library for robust daemon management # Copyright (c) 2010-2015 Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module PhusionPassenger class DaemonController MAJOR = 1 MINOR = 2 TINY = 1 VERSION_STRING = "#{MAJOR}.#{MINOR}.#{TINY}" end # class DaemonController end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/crash_watch/app.rb000644 000765 000024 00000011056 12233035540 030670 0ustar00honglistaff000000 000000 # encoding: binary # # Copyright (c) 2010-2015 Phusion # # 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, sublicense, 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 # NONINFRINGEMENT. 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. require 'optparse' PhusionPassenger.require_passenger_lib 'vendor/crash_watch/gdb_controller' PhusionPassenger.require_passenger_lib 'vendor/crash_watch/version' module CrashWatch class App def run(argv) options = {} parser = OptionParser.new do |opts| opts.banner = "Usage: crash-watch [options] PID" opts.separator "" opts.separator "Options:" opts.on("-d", "--debug", "Show GDB commands that crash-watch sends.") do options[:debug] = true end opts.on("--dump", "Dump current process backtrace and exit immediately.") do options[:dump] = true end opts.on("-v", "--version", "Show version number.") do options[:version] = true end opts.on("-h", "--help", "Show this help message.") do options[:help] = true end end begin parser.parse!(argv) rescue OptionParser::ParseError => e puts e puts puts "Please see '--help' for valid options." exit 1 end if options[:help] puts parser exit elsif options[:version] puts "crash-watch version #{CrashWatch::VERSION_STRING}" exit elsif argv.size != 1 puts parser exit 1 end begin gdb = CrashWatch::GdbController.new rescue CrashWatch::Error => e abort e.message end begin gdb.debug = options[:debug] # Ruby sometimes uses SIGVTARLM for thread scheduling. gdb.execute("handle SIGVTALRM noprint pass") if gdb.attach(argv[0]) if options[:dump] puts "*******************************************************" puts "*" puts "* Current thread (#{gdb.current_thread}) backtrace" puts "*" puts "*******************************************************" puts puts " " << gdb.current_thread_backtrace.gsub(/\n/, "\n ") puts puts puts "*******************************************************" puts "*" puts "* All thread backtraces" puts "*" puts "*******************************************************" puts output = gdb.all_threads_backtraces output.gsub!(/\n/, "\n ") output.insert(0, " ") output.gsub!(/^ (Thread .*):$/, "########### \\1 ###########\n") puts output else puts "Monitoring PID #{argv[0]}..." exit_info = gdb.wait_until_exit puts "Process exited at #{Time.now}." puts "Exit code: #{exit_info.exit_code}" if exit_info.exit_code puts "Signal: #{exit_info.signal}" if exit_info.signaled? if exit_info.backtrace puts "Backtrace:" puts " " << exit_info.backtrace.gsub(/\n/, "\n ") end end else puts "ERROR: Cannot attach to process." if File.exist?("/proc/sys/kernel/yama/ptrace_scope") puts puts "This may be the result of kernel ptrace() hardening. Try disabling it with:" puts " sudo sh -c 'echo 0 > /proc/sys/kernel/yama/ptrace_scope'" puts "See http://askubuntu.com/questions/41629/after-upgrade-gdb-wont-attach-to-process for more information." end exit 2 end ensure gdb.close end end end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/crash_watch/gdb_controller.rb000644 000765 000024 00000024617 12233035540 033116 0ustar00honglistaff000000 000000 # encoding: binary # # Copyright (c) 2010-2015 Phusion # # 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, sublicense, 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 # NONINFRINGEMENT. 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. require 'rbconfig' module CrashWatch class Error < StandardError end class GdbNotFound < Error end class GdbBroken < Error end class GdbController class ExitInfo attr_reader :exit_code, :signal, :backtrace, :snapshot def initialize(exit_code, signal, backtrace, snapshot) @exit_code = exit_code @signal = signal @backtrace = backtrace @snapshot = snapshot end def signaled? !!@signal end end END_OF_RESPONSE_MARKER = '--------END_OF_RESPONSE--------' attr_accessor :debug def initialize @pid, @in, @out = popen_command(find_gdb, "-n", "-q") execute("set prompt ") end def execute(command_string, timeout = nil) raise "GDB session is already closed" if !@pid puts "gdb write #{command_string.inspect}" if @debug @in.puts(command_string) @in.puts("echo \\n#{END_OF_RESPONSE_MARKER}\\n") done = false result = "" while !done begin if select([@out], nil, nil, timeout) line = @out.readline puts "gdb read #{line.inspect}" if @debug if line == "#{END_OF_RESPONSE_MARKER}\n" done = true else result << line end else close! done = true result = nil end rescue EOFError done = true end end result end def closed? !@pid end def close if !closed? begin execute("detach", 5) execute("quit", 5) if !closed? rescue Errno::EPIPE end if !closed? @in.close @out.close Process.waitpid(@pid) @pid = nil end end end def close! if !closed? @in.close @out.close Process.kill('KILL', @pid) Process.waitpid(@pid) @pid = nil end end def attach(pid) pid = pid.to_s.strip raise ArgumentError if pid.empty? result = execute("attach #{pid}") result !~ /(No such process|Unable to access task|Operation not permitted)/ end def call(code) result = execute("call #{code}") result =~ /= (.*)$/ $1 end def program_counter execute("p/x $pc").gsub(/.* = /, '') end def current_thread execute("thread") =~ /Current thread is (.+?) / $1 end def current_thread_backtrace execute("bt full").strip end def all_threads_backtraces execute("thread apply all bt full").strip end def ruby_backtrace filename = "/tmp/gdb-capture.#{@pid}.txt" orig_stdout_fd_copy = call("(int) dup(1)") new_stdout = call(%Q{(void *) fopen("#{filename}", "w")}) new_stdout_fd = call("(int) fileno(#{new_stdout})") call("(int) dup2(#{new_stdout_fd}, 1)") # Let's hope stdout is set to line buffered or unbuffered mode... call("(void) rb_backtrace()") call("(int) dup2(#{orig_stdout_fd_copy}, 1)") call("(int) fclose(#{new_stdout})") call("(int) close(#{orig_stdout_fd_copy})") if File.exist?(filename) result = File.read(filename) result.strip! if result.empty? nil else result end else nil end ensure if filename File.unlink(filename) rescue nil end end def wait_until_exit execute("break _exit") signal = nil backtraces = nil snapshot = nil while true result = execute("continue") if result =~ /^Program received signal (.+?),/ signal = $1 backtraces = execute("thread apply all bt full").strip if backtraces.empty? backtraces = execute("bt full").strip end snapshot = yield(self) if block_given? # This signal may or may not be immediately fatal; the # signal might be ignored by the process, or the process # has some clever signal handler that fixes the state, # or maybe the signal handler must run some cleanup code # before killing the process. Let's find out by running # the next machine instruction. old_program_counter = program_counter result = execute("stepi") if result =~ /^Program received signal .+?,/ # Yes, it was fatal. Here we don't care whether the # instruction caused a different signal. The last # one is probably what we're interested in. return ExitInfo.new(nil, signal, backtraces, snapshot) elsif result =~ /^Program (terminated|exited)/ || result =~ /^Breakpoint .*? _exit/ # Running the next instruction causes the program to terminate. # Not sure what's going on but the previous signal and # backtrace is probably what we're interested in. return ExitInfo.new(nil, signal, backtraces, snapshot) elsif old_program_counter == program_counter # The process cannot continue but we're not sure what GDB # is telling us. raise "Unexpected GDB output: #{result}" end # else: # The signal doesn't isn't immediately fatal, so save current # status, continue, and check whether the process exits later. elsif result =~ /^Program terminated with signal (.+?),/ if $1 == signal # Looks like the signal we trapped earlier # caused an exit. return ExitInfo.new(nil, signal, backtraces, snapshot) else return ExitInfo.new(nil, signal, nil, snapshot) end elsif result =~ /^Breakpoint .*? _exit / backtraces = execute("thread apply all bt full").strip if backtraces.empty? backtraces = execute("bt full").strip end snapshot = yield(self) if block_given? # On OS X, gdb may fail to return from the 'continue' command # even though the process exited. Kernel bug? In any case, # we put a timeout here so that we don't wait indefinitely. result = execute("continue", 10) if result =~ /^Program exited with code (\d+)\.$/ return ExitInfo.new($1.to_i, nil, backtraces, snapshot) elsif result =~ /^Program exited normally/ return ExitInfo.new(0, nil, backtraces, snapshot) else return ExitInfo.new(nil, nil, backtraces, snapshot) end elsif result =~ /^Program exited with code (\d+)\.$/ return ExitInfo.new($1.to_i, nil, nil, nil) elsif result =~ /^Program exited normally/ return ExitInfo.new(0, nil, nil, nil) else return ExitInfo.new(nil, nil, nil, nil) end end end private def popen_command(*command) a, b = IO.pipe c, d = IO.pipe if Process.respond_to?(:spawn) args = command.dup args << { STDIN => a, STDOUT => d, STDERR => d, :close_others => true } pid = Process.spawn(*args) else pid = fork do STDIN.reopen(a) STDOUT.reopen(d) STDERR.reopen(d) b.close c.close exec(*command) end end a.close d.close b.binmode c.binmode [pid, b, c] end def find_gdb result = nil if ENV['GDB'] && File.executable?(ENV['GDB']) result = ENV['GDB'] else ENV['PATH'].to_s.split(/:+/).each do |path| filename = "#{path}/gdb" if File.file?(filename) && File.executable?(filename) result = filename break end end end puts "Found gdb at: #{result}" if result config = defined?(RbConfig) ? RbConfig::CONFIG : Config::CONFIG if config['target_os'] =~ /freebsd/ && result == "/usr/bin/gdb" # /usr/bin/gdb on FreeBSD is broken: # https://github.com/FooBarWidget/crash-watch/issues/1 # Look for a newer one that's installed from ports. puts "#{result} is broken on FreeBSD. Looking for an alternative..." result = nil ["/usr/local/bin/gdb76", "/usr/local/bin/gdb66"].each do |candidate| if File.executable?(candidate) result = candidate break end end if result.nil? raise GdbBroken, "*** ERROR ***: '/usr/bin/gdb' is broken on FreeBSD. " + "Please install the one from the devel/gdb port instead. " + "If you want to use another gdb" else puts "Found gdb at: #{result}" if result result end elsif result.nil? raise GdbNotFound, "*** ERROR ***: 'gdb' not found. Please install it (and if using Nginx " + "ensure that PATH isn't filtered out, see also its \"env\" option).\n" + " Debian/Ubuntu: sudo apt-get install gdb\n" + "RedHat/CentOS/Fedora: sudo yum install gdb\n" + " Mac OS X: please install the Developer Tools or XCode\n" + " FreeBSD: use the devel/gdb port\n" else result end end end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/vendor/crash_watch/version.rb000644 000765 000024 00000002170 12233035540 031572 0ustar00honglistaff000000 000000 # Copyright (c) 2010-2015 Phusion # # 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, sublicense, 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 # NONINFRINGEMENT. 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. module CrashWatch VERSION_STRING = '1.1.12' end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/utils/ansi_colors.rb000644 000765 000024 00000011603 12233035540 027776 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module PhusionPassenger module Utils module AnsiColors RESET = "\e[0m".freeze BOLD = "\e[1m".freeze GRAY = "\e[38;5;248m".freeze DGRAY = "\e[90m".freeze RED = "\e[31m".freeze ORANGE = "\e[38;5;214m".freeze GREEN = "\e[32m".freeze YELLOW = "\e[33m".freeze WHITE = "\e[37m".freeze BLACK_BG = "\e[40m".freeze BLUE_BG = "\e[44m".freeze DEFAULT_TERMINAL_COLOR = "#{RESET}#{WHITE}#{BLACK_BG}".freeze extend self # Make methods available as class methods. def self.new(type = :auto) AnsiColorsPrinter.new(type) end def self.included(klass) # When included into another class, make sure that Utils # methods are made private. public_instance_methods(false).each do |method_name| klass.send(:private, method_name) end end def ansi_colorize(text) text = text.gsub(%r{(.*?)}m, "#{BOLD}\\1#{DEFAULT_TERMINAL_COLOR}") text.gsub!(%r{(.*?)}m, "#{BOLD}#{DGRAY}\\1#{DEFAULT_TERMINAL_COLOR}") text.gsub!(%r{(.*?)}m, "#{BOLD}#{GRAY}\\1#{DEFAULT_TERMINAL_COLOR}") text.gsub!(%r{(.*?)}m, "#{BOLD}#{RED}\\1#{DEFAULT_TERMINAL_COLOR}") text.gsub!(%r{(.*?)}m, "#{BOLD}#{ORANGE}\\1#{DEFAULT_TERMINAL_COLOR}") text.gsub!(%r{(.*?)}m, "#{BOLD}#{GREEN}\\1#{DEFAULT_TERMINAL_COLOR}") text.gsub!(%r{(.*?)}m, "#{BOLD}#{YELLOW}\\1#{DEFAULT_TERMINAL_COLOR}") text.gsub!(%r{(.*?)}m, "#{BOLD}#{BLUE_BG}#{YELLOW}\\1#{DEFAULT_TERMINAL_COLOR}") text end def strip_color_tags(text) text = text.gsub(%r{(.*?)}m, "\\1") text = text.gsub(%r{(.*?)}m, "\\1") text = text.gsub(%r{(.*?)}m, "\\1") text.gsub!(%r{(.*?)}m, "\\1") text.gsub!(%r{(.*?)}m, "\\1") text.gsub!(%r{(.*?)}m, "\\1") text.gsub!(%r{(.*?)}m, "\\1") text.gsub!(%r{(.*?)}m, "\\1") text end end class AnsiColorsPrinter def initialize(enabled = :auto) @enabled = enabled end def reset maybe_colorize(AnsiColors::RESET) end def bold maybe_colorize(AnsiColors::BOLD) end def dgray maybe_colorize(AnsiColors::DGRAY) end def gray maybe_colorize(AnsiColors::GRAY) end def red maybe_colorize(AnsiColors::RED) end def orange maybe_colorize(AnsiColors::ORANGE) end def green maybe_colorize(AnsiColors::GREEN) end def yellow maybe_colorize(AnsiColors::YELLOW) end def white maybe_colorize(AnsiColors::WHITE) end def black_bg maybe_colorize(AnsiColors::BLACK_BG) end def blue_bg maybe_colorize(AnsiColors::BLUE_BG) end def default_terminal_color maybe_colorize(AnsiColors::DEFAULT_TERMINAL_COLOR) end def ansi_colorize(text) if should_output_color? AnsiColors.ansi_colorize(text) else AnsiColors.strip_color_tags(text) end end private def maybe_colorize(ansi_color) if should_output_color? ansi_color else "" end end def should_output_color? if @enabled == :auto STDOUT.tty? else @enabled end end end end # module Utils end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/utils/download.rb000644 000765 000024 00000020217 12233035540 027273 0ustar00honglistaff000000 000000 # encoding: utf-8 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2013-2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'utils/shellwords' require 'fileutils' module PhusionPassenger module Utils module Download extend self # Make methods available as class methods. def self.included(klass) # When included into another class, make sure that Utils # methods are made private. public_instance_methods(false).each do |method_name| klass.send(:private, method_name) end end # Downloads a file from the given URL and saves it to the given filename. # Returns whether the download succeeded. # # Options: # # show_progress: whether to show download progress. Default: false. # logger: the logger to use. If not given, this function will log to STDERR. # cacert: a CA certificate file to use for verifying SSL websites. # The default is to use the download tool's down CA database. # use_cache: Whether to copy the file from the download cache, if available. # Default: false. # connect_timeout: The maximum amount of time to spend on DNS lookup # and establishing the TCP connection. Set to nil to # disable this timeout. Default: 4. # idle_timeout: The maximum idle read time. Set to nil to set this timeout # to the default wget value, 900. Set to nil to disable this # timeout. Default: 5. # total_timeout: The maximum amount of time spent on the whole download # operation, including connection time. Only has effect on curl. # Set to nil to disable this timeout. Default: nil. def download(url, output, options = {}) options = { :connect_timeout => 4, :idle_timeout => 5 }.merge(options) logger = options[:logger] || Logger.new(STDERR) if options[:use_cache] && cache_dir = PhusionPassenger.download_cache_dir basename = basename_from_url(url) if File.exist?("#{cache_dir}/#{basename}") logger.info "Copying #{basename} from #{cache_dir}..." FileUtils.cp("#{cache_dir}/#{basename}", output) return true end end if PlatformInfo.find_command("curl") return download_with_curl(logger, url, output, options) elsif PlatformInfo.find_command("wget") return download_with_wget(logger, url, output, options) else logger.error "Could not download #{url}: no download tool found (curl or wget required)" return false end end private def basename_from_url(url) return url.sub(/.*\//, '') end def download_with_curl(logger, url, output, options) command = ["curl", "-f", "-L", "-o", output] if options[:show_progress] command << "-#" else command << "-s" command << "-S" end if options[:cacert] command << "--cacert" command << options[:cacert] end if options[:connect_timeout] command << "--connect-timeout" command << options[:connect_timeout].to_s end if options[:idle_timeout] command << "--speed-time" command << options[:idle_timeout].to_s command << "--speed-limit" command << "1" end if options[:total_timeout] command << "--max-time" command << options[:total_timeout].to_s end command << url command_str = Shellwords.join(command) logger.info("Invoking: #{command_str}") if options[:show_progress] # If curl errors out we don't want it to display 'curl: ' prefixes, # so we parse its output. begin io = IO.popen("#{command_str} 2>&1", "r") rescue SystemCallError => e logger.error("Could not invoke curl: #{e}") return false end begin non_empty_line_encountered = false while !io.eof? # We split on "\r" because progress bar lines do not contain "\n". data = io.gets("\r") data = remove_curl_output_prefix(data) # If an error occurs then the first few lines may be empty. # Skip those. if !non_empty_line_encountered && data =~ /\A\n+/ data.gsub!(/\A\n+/, '') end non_empty_line_encountered = true STDERR.write(data) STDERR.flush end ensure io.close end result = $?.exitstatus == 0 else begin output = `#{command_str} 2>&1` rescue SystemCallError => e logger.error("Could not invoke curl: #{e}") return false end result = $?.exitstatus == 0 if !result output = remove_curl_output_prefix(output) output.chomp! logger.error("Could not download #{url}: #{output}") end end return result end def remove_curl_output_prefix(line) return line.gsub(/^curl: (\([0-9]+\) )?/, '') end def download_with_wget(logger, url, output, options) command = ["wget", "--tries=1", "-O", output] if !options[:show_progress] command << "-nv" end if options[:cacert] command << "--ca-certificate=#{options[:cacert]}" end if options[:connect_timeout] command << "--dns-timeout=#{options[:connect_timeout]}" command << "--connect-timeout=#{options[:connect_timeout]}" end if options[:idle_timeout] command << "--timeout=#{options[:idle_timeout]}" end command << url command_str = Shellwords.join(command) logger.info("Invoking: #{command_str}") if options[:show_progress] begin result = system(*command) rescue SystemCallError => e logger.error("Could not invoke wget: #{e}") return false end if !result logger.error("Could not download #{url}: #{output}") end else begin output = `#{command_str} 2>&1` rescue SystemCallError => e logger.error("Could not invoke wget: #{e}") return false end result = $?.exitstatus == 0 if !result # Error output may begin with ":\n" which is redundant. output.gsub!(/\A#{Regexp.escape url}:\n/, '') output.chomp! logger.error("Could not download #{url}: #{output}") end end return result end end end # module Utils end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/utils/file_system_watcher.rb000644 000765 000024 00000014471 12233035540 031531 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'native_support' module PhusionPassenger module Utils # Watches changes on one or more files or directories. To use this class, # construct an object, passing it file or directory names to watch, then # call #wait_for_change. #wait_for_change waits until one of the following # events has happened since the constructor was called: # # - One of the specified files has been renamed, deleted, or its access # revoked. This will cause +true+ to be returned. # - One of the specified directories has been modified, renamed, deleted, # or its access revoked. This will cause +true+ to be returned. # - +termination_pipe+ (as passed to the constructor) becomes readable. # This will cause +nil+ to be returned. # - The thread is interrupted. This will cause +nil+ to be returned. # # The constructor will attempt to stat and possibly also open all specified # files/directories. If one of them cannot be statted or opened, then # +false+ will be returned by #wait_for_change. # # #wait_for_change may only be called once. After calling it one should # create a new object if one wishes to watch the filesystem again. # # Always call #close when a FileSystemWatcher object is no longer needed # in order to free resources. # # This class tries to use kqueue for efficient filesystem watching on # platforms that support it. On other platforms it'll fallback to stat # polling instead. if defined?(NativeSupport::FileSystemWatcher) FileSystemWatcher = NativeSupport::FileSystemWatcher FileSystemWatcher.class_eval do def self.new(filenames, termination_pipe = nil) # Default parameter values, type conversion and exception # handling in C is too much of a pain. filenames = filenames.map do |filename| filename.to_s end return _new(filenames, termination_pipe) end def self.opens_files? return true end end else class FileSystemWatcher attr_accessor :poll_interval def self.opens_files? return false end def initialize(filenames, termination_pipe = nil) @poll_interval = 3 @termination_pipe = termination_pipe @dirs = [] @files = [] begin filenames.each do |filename| stat = File.stat(filename) if stat.directory? @dirs << DirInfo.new(filename, stat) else @files << FileInfo.new(filename, stat) end end rescue Errno::EACCES, Errno::ENOENT @dirs = @files = nil end end def wait_for_change if !@dirs return false end while true if changed? return true elsif select([@termination_pipe], nil, nil, @poll_interval) return nil end end end def close end private class DirInfo DOT = "." DOTDOT = ".." def initialize(filename, stat) @filename = filename @stat = stat @subfiles = {} Dir.foreach(filename) do |entry| next if entry == DOT || entry == DOTDOT subfilename = "#{filename}/#{entry}" @subfiles[entry] = FileInfo.new(subfilename, File.stat(subfilename)) end end def changed? new_stat = File.stat(@filename) if @stat.ino != new_stat.ino || !new_stat.directory? || @stat.mtime != new_stat.mtime return true end count = 0 Dir.foreach(@filename) do |entry| next if entry == DOT || entry == DOTDOT subfilename = "#{@filename}/#{entry}" file_info = @subfiles[entry] if !file_info || file_info.changed?(false) return true else count += 1 end end return count != @subfiles.size rescue Errno::EACCES, Errno::ENOENT return true end end class FileInfo def initialize(filename, stat) @filename = filename @stat = stat end def changed?(check_mtime = true) new_stat = File.stat(@filename) if check_mtime mtime_changed = @stat.mtime != new_stat.mtime || @stat.size != new_stat.size else mtime_changed = false end return @stat.ino != new_stat.ino || @stat.ftype != new_stat.ftype || mtime_changed rescue Errno::EACCES, Errno::ENOENT return true end end def changed? return @dirs.any? { |dir_info| dir_info.changed? } || @files.any? { |file_info| file_info.changed? } end end end end # module Utils end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/utils/hosts_file_parser.rb000644 000765 000024 00000007716 12233035540 031210 0ustar00honglistaff000000 000000 # encoding: binary # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2013 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'platform_info/operating_system' module PhusionPassenger module Utils # A /etc/hosts parser. Also supports writing groups of data to the file. class HostsFileParser def self.flush_dns_cache! if PlatformInfo.os_name_simple == "macosx" system("dscacheutil -flushcache") end end def initialize(filename_or_io = "/etc/hosts") if filename_or_io.respond_to?(:readline) read_and_parse(filename_or_io) else File.open(filename_or_io, "rb") do |f| read_and_parse(f) end end end def ip_count return @ips.size end def host_count return @host_names.size end def resolve(host_name) if host_name.downcase == "localhost" return "127.0.0.1" else return @host_names[host_name.downcase] end end def resolves_to_localhost?(hostname) ip = resolve(hostname) return ip == "127.0.0.1" || ip == "::1" || ip == "0.0.0.0" end def add_group_data(marker, data) begin_index = find_line(0, "###### BEGIN #{marker} ######") end_index = find_line(begin_index + 1, "###### END #{marker} ######") if begin_index if begin_index && end_index @lines[begin_index + 1 .. end_index - 1] = data.split("\n") else @lines << "###### BEGIN #{marker} ######" @lines.concat(data.split("\n")) @lines << "###### END #{marker} ######" end end def write(io) @lines.each do |line| io.puts(line) end end private def read_and_parse(io) lines = [] ips = {} all_host_names = {} while !io.eof? line = io.readline line.sub!(/\n\Z/m, '') lines << line ip, host_names = parse_line(line) if ip ips[ip] ||= [] ips[ip].concat(host_names) host_names.each do |host_name| all_host_names[host_name.downcase] = ip end end end @lines = lines @ips = ips @host_names = all_host_names end def parse_line(line) return nil if line =~ /^[\s\t]*#/ line = line.strip return nil if line.empty? ip, *host_names = line.split(/[ \t]+/) return [ip, host_names] end def find_line(start_index, content) i = start_index while i < @lines.size if @lines[i] == content return i else i += 1 end end return nil end end end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/utils/json.rb000644 000765 000024 00000025122 12233035540 026435 0ustar00honglistaff000000 000000 # encoding: utf-8 # ## Stupid small pure Ruby JSON parser & generator. # # Copyright © 2013 Mislav Marohnić # # 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, sublicense, 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 NONINFRINGEMENT. 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. # We use this in Phusion Passenger at places where we cannot depend on the JSON # gem being available, for example in 'passenger start' before the RuntimeInstaller # has run. require 'strscan' require 'forwardable' module PhusionPassenger module Utils # Usage: # # JSON.parse(json_string) => Array/Hash # JSON.generate(object) => json string # # Run tests by executing this file directly. Pipe standard input to the script to have it # parsed as JSON and to display the result in Ruby. # class JSON def self.parse(data) new(data).parse end WSP = /(\s|\/\/.*?\n|\/\*.*?\*\/)+/m OBJ = /[{\[]/; HEN = /\}/; AEN = /\]/ COL = /\s*:\s*/; KEY = /\s*,\s*/ NUM = /-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/ BOL = /true|false/; NUL = /null/ extend Forwardable attr_reader :scanner alias_method :s, :scanner def_delegators :scanner, :scan, :matched private :s, :scan, :matched def initialize data @scanner = StringScanner.new data.to_s end def parse space object end private def space() scan WSP end def endkey() scan(KEY) or space end def object matched == '{' ? hash : array if scan(OBJ) end def value object or string or scan(NUL) ? nil : scan(BOL) ? matched.size == 4: scan(NUM) ? eval(matched) : error end def hash obj = {} space repeat_until(HEN) do space k = string scan(COL) obj[k] = value endkey end obj end def array ary = [] space repeat_until(AEN) do space ary << value endkey end ary end SPEC = {'b' => "\b", 'f' => "\f", 'n' => "\n", 'r' => "\r", 't' => "\t"} UNI = 'u'; CODE = /[a-fA-F0-9]{4}/ STR = /"/; STE = '"' ESC = '\\' def string if scan(STR) str, esc = '', false while c = s.getch if esc str << (c == UNI ? (s.scan(CODE) || error).to_i(16).chr : SPEC[c] || c) esc = false else case c when ESC then esc = true when STE then break else str << c end end end str end end def error raise "parse error at: #{scan(/.{1,20}/m).inspect}" end def repeat_until reg until scan(reg) pos = s.pos yield error unless s.pos > pos end end module Generator def generate(obj) raise ArgumentError unless obj.is_a? Array or obj.is_a? Hash generate_type(obj) end alias dump generate private def generate_type(obj) type = obj.is_a?(Numeric) ? :Numeric : obj.class.name begin send(:"generate_#{type}", obj) rescue NoMethodError; raise ArgumentError, "can't serialize #{type}" end end ESC_MAP = Hash.new {|h,k| k }.update \ "\r" => 'r', "\n" => 'n', "\f" => 'f', "\t" => 't', "\b" => 'b' def quote(str) %("#{str}") end def generate_String(str) quote str.gsub(/[\r\n\f\t\b"\\]/) { "\\#{ESC_MAP[$&]}"} end def generate_simple(obj) obj.inspect end alias generate_Numeric generate_simple alias generate_TrueClass generate_simple alias generate_FalseClass generate_simple def generate_Symbol(sym) generate_String(sym.to_s) end def generate_Time(time) quote time.strftime(time.utc? ? "%F %T UTC" : "%F %T %z") end def generate_Date(date) quote date.to_s end def generate_NilClass(*) 'null' end def generate_Array(ary) '[%s]' % ary.map {|o| generate_type(o) }.join(', ') end def generate_Hash(hash) '{%s}' % hash.map { |key, value| "#{generate_String(key.to_s)}: #{generate_type(value)}" }.join(', ') end end extend Generator end if __FILE__ == $0 if !$stdin.tty? data = JSON.parse $stdin.read require 'pp' pp data else require 'test/unit' require 'date' class ParserTest < Test::Unit::TestCase PARSED = JSON.parse DATA.read def parsed() PARSED end def parse_string(str) JSON.parse(%(["#{str}"]).gsub('\\\\', '\\')).first end def test_string assert_equal "Pagination library for \"Rails 3\", Sinatra, Merb, DataMapper, and more", parsed['head']['repository']['description'] end def test_string_specials assert_equal "\r\n\t\f\b", parse_string('\r\n\t\f\b') assert_equal "aA", parse_string('\u0061\u0041') assert_equal "\e", parse_string('\u001B') assert_equal "xyz", parse_string('\x\y\z') assert_equal '"\\/', parse_string('\"\\\\\\/') assert_equal 'no #{interpolation}', parse_string('no #{interpolation}') end def test_hash assert_equal %w[label ref repository sha user], parsed['head'].keys.sort end def test_number assert_equal 124.3e2, parsed['head']['repository']['size'] end def test_bool assert_equal true, parsed['head']['repository']['fork'] assert_equal false, parsed['head']['repository']['private'] end def test_nil assert_nil parsed['head']['user']['company'] end def test_array assert_equal ["4438f", {"a" => "b"}], parsed['head']['sha'] end def test_invalid assert_raises(RuntimeError) { JSON.parse %({) } assert_raises(RuntimeError) { JSON.parse %({ "foo": }) } assert_raises(RuntimeError) { JSON.parse %([ "foo": "bar" ]) } assert_raises(RuntimeError) { JSON.parse %([ ~"foo" ]) } assert_raises(RuntimeError) { JSON.parse %([ "foo ]) } assert_raises(RuntimeError) { JSON.parse %([ "foo\\" ]) } assert_raises(RuntimeError) { JSON.parse %([ "foo\\uabGd" ]) } end def test_single_line_comments source = %Q{ // comment before document { // comment "foo": "1", "bar": "2", // another comment "baz": "3", "array": [ // comment inside array 1, 2, 3 // comment at end of array ] // comment at end of hash } // comment after document } doc = { "foo" => "1", "bar" => "2", "baz" => "3", "array" => [1, 2, 3] } assert_equal(doc, JSON.parse(source)) end def test_multi_line_comments source = %Q{ /* comment before * document */ { /* comment */ "foo": "1", "bar": "2", /* another comment */ "baz": "3", "array": [ /* comment inside array */ 1, 2, 3, 4, /* comment inside an array */ 5, /* // "nested" comments { "faux json": "inside comment" } */ 6, 7 /** * comment at end of array */ ] /************************** comment at end of hash **************************/ } /* comment after document */ } doc = { "foo" => "1", "bar" => "2", "baz" => "3", "array" => [1, 2, 3, 4, 5, 6, 7] } assert_equal(doc, JSON.parse(source)) end end class GeneratorTest < Test::Unit::TestCase def generate(obj) JSON.generate(obj) end def test_array assert_equal %([1, 2, 3]), generate([1, 2, 3]) end def test_bool assert_equal %([true, false]), generate([true, false]) end def test_null assert_equal %([null]), generate([nil]) end def test_string assert_equal %(["abc\\n123"]), generate(["abc\n123"]) end def test_string_unicode assert_equal %(["ć\\"č\\nž\\tš\\\\đ"]), generate(["ć\"č\nž\tš\\đ"]) end def test_time time = Time.utc(2012, 04, 19, 1, 2, 3) assert_equal %(["2012-04-19 01:02:03 UTC"]), generate([time]) end def test_date time = Date.new(2012, 04, 19) assert_equal %(["2012-04-19"]), generate([time]) end def test_symbol assert_equal %(["abc"]), generate([:abc]) end def test_hash json = generate(:abc => 123, 123 => 'abc') assert_match /^\{/, json assert_match /\}$/, json assert_equal [%("123": "abc"), %("abc": 123)], json[1...-1].split(', ').sort end def test_nested_structure json = generate(:hash => {1=>2}, :array => [1,2]) assert json.include?(%("hash": {"1": 2})) assert json.include?(%("array": [1, 2])) end def test_invalid_json assert_raises(ArgumentError) { generate("abc") } end def test_invalid_object err = assert_raises(ArgumentError) { generate("a" => Object.new) } assert_equal "can't serialize Object", err.message end end end end end # module Utils end # module PhusionPassenger __END__ { "head": { "ref": "master", "repository": { "forks": 0, "integrate_branch": "rails3", "watchers": 1, "language": "Ruby", "description": "Pagination library for \"Rails 3\", Sinatra, Merb, DataMapper, and more", "has_downloads": true, "fork": true, "created_at": "2011/10/24 03:20:48 -0700", "homepage": "http://github.com/mislav/will_paginate/wikis", "size": 124.3e2, "private": false, "has_wiki": true, "name": "will_paginate", "owner": "dbackeus", "url": "https://github.com/dbackeus/will_paginate", "has_issues": false, "open_issues": 0, "pushed_at": "2011/10/25 05:44:05 -0700" }, "label": "dbackeus:master", "sha": ["4438f", { "a" : "b" }], "user": { "name": "David Backeus", "company": null, "gravatar_id": "ebe96524f5db9e92188f0542dc9d1d1a", "location": "Stockholm (Sweden)", "type": "User", "login": "dbackeus" } } }passenger-5.0.30/src/ruby_supportlib/phusion_passenger/utils/lock.rb000644 000765 000024 00000003645 12233035540 026422 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module PhusionPassenger module Utils class Lock def initialize(mutex) @mutex = mutex @locked = false end def reset(mutex, lock_now = true) unlock if @locked @mutex = mutex lock if lock_now end def synchronize lock if !@locked begin yield(self) ensure unlock if @locked end end def lock raise if @locked @mutex.lock @locked = true end def unlock raise if !@locked @mutex.unlock @locked = false end end end # module Utils end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/utils/native_support_utils.rb000644 000765 000024 00000004701 12233035540 031766 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'native_support' module PhusionPassenger module Utils # Utility functions that can potentially be accelerated by native_support functions. module NativeSupportUtils extend self if defined?(PhusionPassenger::NativeSupport) # Split the given string into an hash. Keys and values are obtained by splitting the # string using the null character as the delimitor. def split_by_null_into_hash(data) return PhusionPassenger::NativeSupport.split_by_null_into_hash(data) end # Wrapper for getrusage(). def process_times return PhusionPassenger::NativeSupport.process_times end else NULL = "\0".freeze class ProcessTimes < Struct.new(:utime, :stime) end def split_by_null_into_hash(data) args = data.split(NULL, -1) args.pop return Hash[*args] end def process_times times = Process.times return ProcessTimes.new((times.utime * 1_000_000).to_i, (times.stime * 1_000_000).to_i) end end end end # module Utils end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/utils/progress_bar.rb000644 000765 000024 00000003743 12233035540 030161 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module PhusionPassenger class ProgressBar THROBBLER = ["-", "\\", "|", "/", "-"] def initialize(output = STDOUT) @output = output @tty = output.tty? @throbbler_index = 0 end def set(percentage) if @tty width = (percentage * 50).to_i bar = "*" * width space = " " * (50 - width) text = sprintf("[%s%s] %s", bar, space, THROBBLER[@throbbler_index]) @throbbler_index = (@throbbler_index + 1) % THROBBLER.size @output.write("#{text}\r") @output.flush else @output.write(".") @output.flush end end def finish @output.write("\n") @output.flush end end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/utils/shellwords.rb000644 000765 000024 00000001530 12233035540 027647 0ustar00honglistaff000000 000000 # Polyfill for the shellwords library on Ruby 1.8.5. require 'shellwords' if !Shellwords.respond_to?(:escape) Shellwords.class_eval do def self.escape(str) # An empty argument will be skipped, so return empty quotes. return "''" if str.empty? str = str.dup # Treat multibyte characters as is. It is caller's responsibility # to encode the string in the right encoding for the shell # environment. str.gsub!(/([^A-Za-z0-9_\-.,:\/@\n])/, "\\\\\\1") # A LF cannot be escaped with a backslash because a backslash + LF # combo is regarded as line continuation and simply ignored. str.gsub!(/\n/, "'\n'") return str end end end if !Shellwords.respond_to?(:join) Shellwords.class_eval do def self.join(array) array.map { |arg| escape(arg) }.join(' ') end end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/utils/tee_input.rb000644 000765 000024 00000015344 12233035540 027465 0ustar00honglistaff000000 000000 # encoding: binary # # This file is taken from Unicorn. The following license applies to this file # (and this file only, not to the rest of Phusion Passenger): # # 1. You may make and give away verbatim copies of the source form of the # software without restriction, provided that you duplicate all of the # original copyright notices and associated disclaimers. # # 2. You may modify your copy of the software in any way, provided that # you do at least ONE of the following: # # a) place your modifications in the Public Domain or otherwise make them # Freely Available, such as by posting said modifications to Usenet or an # equivalent medium, or by allowing the author to include your # modifications in the software. # # b) use the modified software only within your corporation or # organization. # # c) rename any non-standard executables so the names do not conflict with # standard executables, which must also be provided. # # d) make other distribution arrangements with the author. # # 3. You may distribute the software in object code or executable # form, provided that you do at least ONE of the following: # # a) distribute the executables and library files of the software, # together with instructions (in the manual page or equivalent) on where # to get the original distribution. # # b) accompany the distribution with the machine-readable source of the # software. # # c) give non-standard executables non-standard names, with # instructions on where to get the original software distribution. # # d) make other distribution arrangements with the author. # # 4. You may modify and include the part of the software into any other # software (possibly commercial). But some files in the distribution # are not written by the author, so that they are not under this terms. # # 5. The scripts and library files supplied as input to or produced as # output from the software do not automatically fall under the # copyright of the software, but belong to whomever generated them, # and may be sold commercially, and may be aggregated with this # software. # # 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE. require 'stringio' PhusionPassenger.require_passenger_lib 'utils/tmpio' module PhusionPassenger module Utils # acts like tee(1) on an input input to provide a input-like stream # while providing rewindable semantics through a File/StringIO backing # store. On the first pass, the input is only read on demand so your # Rack application can use input notification (upload progress and # like). This should fully conform to the Rack::Lint::InputWrapper # specification on the public API. This class is intended to be a # strict interpretation of Rack::Lint::InputWrapper functionality and # will not support any deviations from it. # # When processing uploads, Unicorn exposes a TeeInput object under # "rack.input" of the Rack environment. class TeeInput CONTENT_LENGTH = "CONTENT_LENGTH".freeze TRANSFER_ENCODING = "TRANSFER_ENCODING".freeze CHUNKED = "chunked".freeze # The maximum size (in +bytes+) to buffer in memory before # resorting to a temporary file. Default is 112 kilobytes. @@client_body_buffer_size = 112 * 1024 # sets the maximum size of request bodies to buffer in memory, # amounts larger than this are buffered to the filesystem def self.client_body_buffer_size=(bytes) @@client_body_buffer_size = bytes end # returns the maximum size of request bodies to buffer in memory, # amounts larger than this are buffered to the filesystem def self.client_body_buffer_size @@client_body_buffer_size end # Initializes a new TeeInput object. You normally do not have to call # this unless you are writing an HTTP server. def initialize(socket, env) if @len = env[CONTENT_LENGTH] @len = @len.to_i elsif env[TRANSFER_ENCODING] != CHUNKED @len = 0 end @socket = socket @bytes_read = 0 if @len && @len <= @@client_body_buffer_size @tmp = StringIO.new("") else @tmp = TmpIO.new("PassengerTeeInput") end @tmp.binmode end def close @tmp.close end def size if @len @len else pos = @tmp.pos consume! @tmp.pos = pos @len = @tmp.size end end def read(len = nil, buf = "") buf ||= "" if len if len < 0 raise ArgumentError, "negative length #{len} given" elsif len == 0 buf.replace('') buf else if socket_drained? @tmp.read(len, buf) else tee(read_exact(len, buf)) end end else if socket_drained? @tmp.read(nil, buf) else tee(read_all(buf)) end end end def gets if socket_drained? @tmp.gets else if @bytes_read == @len nil elsif line = @socket.gets if @len max_len = @len - @bytes_read line.slice!(max_len, line.size - max_len) end @bytes_read += line.size tee(line) else nil end end end def seek(*args) if !socket_drained? # seek may be forward, or relative to the end, so we need to consume the socket fully into tmp pos = @tmp.pos # save/restore tmp.pos, to not break relative seeks consume! @tmp.pos = pos end @tmp.seek(*args) end def rewind return 0 if 0 == @tmp.size consume! if !socket_drained? @tmp.rewind # Rack does not specify what the return value is here end def each while line = gets yield line end self # Rack does not specify what the return value is here end private def socket_drained? if @socket if @socket.eof? @socket = nil true else false end else true end end # consumes the stream of the socket def consume! junk = "" nil while read(16 * 1024, junk) @socket = nil end def tee(buffer) if buffer && buffer.size > 0 @tmp.write(buffer) end buffer end def read_exact(len, buf) if @len max_len = @len - @bytes_read len = max_len if len > max_len return nil if len == 0 end ret = @socket.read(len, buf) @bytes_read += ret.size if ret ret end def read_all(buf) if @len ret = @socket.read(@len - @bytes_read, buf) if ret @bytes_read += ret.size ret else buf.replace("") buf end else ret = @socket.read(nil, buf) @bytes_read += ret.size ret end end end end # module Utils end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/utils/terminal_choice_menu.rb000644 000765 000024 00000014134 12233035540 031636 0ustar00honglistaff000000 000000 # encoding: utf-8 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2013 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module PhusionPassenger module Utils class TerminalChoiceMenu class Choice attr_reader :name attr_accessor :checked alias checked? checked def self.create(choice) if choice.is_a?(Choice) return choice else return Choice.new(choice) end end def initialize(name, checked = false) @name = name @checked = checked end def toggle! @checked = !@checked end end def initialize(choices, mode = :multiple_choice) @choices = choices.map { |choice| Choice.create(choice) } @mode = mode @pointer = 0 @index = index_choices end def display_choices display(render_to_string) end def query if STDIN.tty? done = false begin raw_no_echo_mode hide_cursor while !done display_choices done = process_input clear_screen if !done end if @mode == :single_choice [@pointer, @choices[@pointer].name] else nil end ensure restore_mode show_cursor puts end else display_choices puts nil end end def [](name) return @index[name] end def selected_choices @choices.find_all{ |c| c.checked? }.map{ |c| c.name } end private JRUBY = defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby' def index_choices index = {} @choices.each do |choice| index[choice.name] = choice end return index end def process_input case getchar(STDIN) when "\x1b" process_cursor_move false when " " if @mode == :multiple_choice process_toggle end false when "!" process_disable_utf8 false when "\r" true else false end end def process_cursor_move if getchar(STDIN) == "[" case getchar(STDIN) when "A" # up @pointer = [@pointer - 1, 0].max when "B" # down @pointer = [@pointer + 1, @choices.size - 1].min end end end def process_toggle @choices[@pointer].toggle! end def process_disable_utf8 ENV['UTF8_MENUS'] = '0' end def render_to_string str = "" @choices.each_with_index do |choice, i| pointer = render_pointer(i) checkbox = render_checkbox(choice.checked) str << " #{pointer} #{checkbox} #{choice.name}\r\n" end str.chomp! str end def render_pointer(index) @pointer == index ? maybe_utf8("‣", ">") : " " end def render_checkbox(checked) if @mode == :multiple_choice if checked maybe_utf8("⬢", "(*)") else maybe_utf8("⬡", "( )") end else nil end end def display(str) STDOUT.write(str) STDOUT.flush end def clear_screen number_of_lines = render_to_string.split("\n").size display("\r") (number_of_lines - 1).times do display(move_up) end end def maybe_utf8(utf8, plain) if ENV['UTF8_MENUS'] == '0' return plain else return utf8 end end def hide_cursor display("\x1b[?25l") end def show_cursor display("\x1b[?25h") end def move_up return "\x1b[0A" end def getchar(io) char = io.getc char = char.chr if char.is_a?(Integer) return char end if JRUBY require 'java' require 'readline' java_import 'jline.console.ConsoleReader' def raw_no_echo_mode input = STDIN.to_inputstream output = STDOUT.to_outputstream @console = ConsoleReader.new(input, output) @console.set_history_enabled(false) @console.set_bell_enabled(true) @console.set_pagination_enabled(false) @terminal_state = @console.getEchoCharacter @console.setEchoCharacter(0) end def restore_mode @console.setEchoCharacter(@terminal_state) @console.get_terminal.restore end else def raw_no_echo_mode @terminal_state = `stty -g` system("stty raw -echo -icanon isig") end def restore_mode system("stty #{@terminal_state}") end end end end # module Utils end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/utils/tmpio.rb000644 000765 000024 00000003567 12233035540 026625 0ustar00honglistaff000000 000000 require 'tmpdir' require 'fileutils' module PhusionPassenger module Utils # some versions of Ruby had a broken Tempfile which didn't work # well with unlinked files. This one is much shorter, easier # to understand, and slightly faster. class TmpIO < File # creates and returns a new File object. The File is unlinked # immediately, switched to binary mode, and userspace output # buffering is disabled def self.new(namespace, options = nil) if options mode = options[:mode] || RDWR binary = options.fetch(:binary, true) suffix = options[:suffix] unlink_immediately = options.fetch(:unlink_immediately, true) else mode = RDWR binary = true suffix = nil unlink_immediately = true end fp = begin super("#{Dir::tmpdir}/#{namespace}-#{rand(0x100000000).to_s(36)}#{suffix}", mode | CREAT | EXCL, 0600) rescue Errno::EEXIST retry end unlink(fp.path) if unlink_immediately fp.binmode if binary fp.sync = true fp end # for easier env["rack.input"] compatibility with Rack <= 1.1 def size stat.size end unless File.method_defined?(:size) end # Like Dir.mktmpdir, but creates shorter filenames. def self.mktmpdir(prefix_suffix=nil, tmpdir=nil) case prefix_suffix when nil prefix = "d" suffix = "" when String prefix = prefix_suffix suffix = "" when Array prefix = prefix_suffix[0] suffix = prefix_suffix[1] else raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}" end tmpdir ||= Dir.tmpdir begin path = "#{tmpdir}/#{prefix}#{rand(0x100000000).to_s(36)}" path << suffix Dir.mkdir(path, 0700) rescue Errno::EEXIST retry end if block_given? begin yield path ensure FileUtils.remove_entry_secure path end else path end end end # module Utils end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/utils/unseekable_socket.rb000644 000765 000024 00000020571 12233035540 031155 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'utils' # So that we can know whether #writev is supported. module PhusionPassenger module Utils # Some frameworks (e.g. Merb) call `seek` and `rewind` on the input stream # if it responds to these methods. In case of Phusion Passenger, the input # stream is a socket, and altough socket objects respond to `seek` and # `rewind`, calling these methods will raise an exception. We don't want # this to happen so in AbstractRequestHandler we wrap the client socket # into an UnseekableSocket wrapper, which doesn't respond to these methods. # # We used to dynamically undef `seek` and `rewind` on sockets, but this # blows the Ruby interpreter's method cache and made things slower. # Wrapping a socket is faster despite extra method calls. # # Furthermore, all exceptions originating from the wrapped socket will # be annotated. One can check whether a certain exception originates # from the wrapped socket by calling #source_of_exception? class UnseekableSocket def self.wrap(socket) return new.wrap(socket) end def wrap(socket) # Some people report that sometimes their Ruby (MRI/REE) # processes get stuck with 100% CPU usage. Upon further # inspection with strace, it turns out that these Ruby # processes are continuously calling lseek() on a socket, # which of course returns ESPIPE as error. gdb reveals # lseek() is called by fwrite(), which in turn is called # by rb_fwrite(). The affected socket is the # AbstractRequestHandler client socket. # # I inspected the MRI source code and didn't find # anything that would explain this behavior. This makes # me think that it's a glibc bug, but that's very # unlikely. # # The rb_fwrite() implementation takes an entirely # different code path if I set 'sync' to true: it will # skip fwrite() and use write() instead. So here we set # 'sync' to true in the hope that this will work around # the problem. socket.sync = true # There's no need to set the encoding for Ruby 1.9 because # abstract_request_handler.rb is tagged with 'encoding: binary'. @socket = socket return self end # Don't allow disabling of sync. def sync=(value) end # Socket is sync'ed so flushing shouldn't do anything. def flush end # Already set to binary mode. def binmode end # This makes select() work. def to_io @socket end def simulate_eof! @simulate_eof = true end def stop_simulating_eof! @simulate_eof = false end def fileno @socket.fileno end def addr @socket.addr rescue => e raise annotate(e) end def write(string) @socket.write(string) rescue => e raise annotate(e) end def write_nonblock(string) @socket.write_nonblock(string) rescue => e raise annotate(e) end def writev(components) @socket.writev(components) rescue => e raise annotate(e) end if IO.method_defined?(:writev) def writev2(components, components2) @socket.writev2(components, components2) rescue => e raise annotate(e) end if IO.method_defined?(:writev2) def writev3(components, components2, components3) @socket.writev3(components, components2, components3) rescue => e raise annotate(e) end if IO.method_defined?(:writev3) def send(*args) @socket.send(*args) rescue => e raise annotate(e) end def sendmsg(*args) @socket.sendmsg(*args) rescue => e raise annotate(e) end def sendmsg_nonblock(*args) @socket.sendmsg_nonblock(*args) rescue => e raise annotate(e) end def puts(*args) @socket.puts(*args) rescue => e raise annotate(e) end def gets return nil if @simulate_eof @socket.gets rescue => e raise annotate(e) end def read(*args) if @simulate_eof length, buffer = args if buffer buffer.replace(binary_string("")) else buffer = binary_string("") end if length return nil else return buffer end end @socket.read(*args) rescue => e raise annotate(e) end def read_nonblock(*args) raise EOFError, "end of file reached" if @simulate_eof @socket.read_nonblock(*args) rescue => e raise annotate(e) end def readpartial(*args) raise EOFError, "end of file reached" if @simulate_eof @socket.readpartial(*args) rescue => e raise annotate(e) end def readline raise EOFError, "end of file reached" if @simulate_eof @socket.readline rescue => e raise annotate(e) end def recv(*args) raise EOFError, "end of file reached" if @simulate_eof @socket.recv(*args) rescue => e raise annotate(e) end def recvfrom(*args) raise EOFError, "end of file reached" if @simulate_eof @socket.recvfrom(*args) rescue => e raise annotate(e) end def recvfrom_nonblock(*args) raise EOFError, "end of file reached" if @simulate_eof @socket.recvfrom_nonblock(*args) rescue => e raise annotate(e) end def each(&block) return if @simulate_eof @socket.each(&block) rescue => e raise annotate(e) end def eof? return true if @simulate_eof @socket.eof? rescue => e raise annotate(e) end def closed? @socket.closed? rescue => e raise annotate(e) end def close @socket.close rescue => e raise annotate(e) end def close_read @socket.close_read rescue => e raise annotate(e) end def close_write @socket.close_write rescue => e raise annotate(e) end def source_of_exception?(exception) return exception.instance_variable_get(:"@from_unseekable_socket") == @socket.object_id end def to_hash { :socket => "Not JSON Encodable", :eof => @simulate_eof } end private def annotate(exception) exception.instance_variable_set(:"@from_unseekable_socket", @socket.object_id) return exception end def raise_error_because_activity_disallowed! raise IOError, "It is not possible to read or write from the client socket because the current." end if ''.respond_to?(:force_encoding) def binary_string(str) return ''.force_encoding('binary') end else def binary_string(str) return '' end end end end # module Utils end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/standalone/app_finder.rb000644 000765 000024 00000012713 12233035540 030565 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'ruby_core_enhancements' PhusionPassenger.require_passenger_lib 'standalone/config_utils' PhusionPassenger.require_passenger_lib 'utils/file_system_watcher' module PhusionPassenger module Standalone class AppFinder STARTUP_FILES = [ "config.ru", "passenger_wsgi.py", "app.js", ".meteor" ] WATCH_ENTRIES = [ "config", "Passengerfile.json", "passenger-standalone.json" ] attr_accessor :dirs attr_reader :apps attr_reader :execution_root def self.looks_like_app_directory?(dir, options = {}) options = options.dup ConfigUtils.load_local_config_file!(dir, options) return options[:app_type] || STARTUP_FILES.any? do |file| File.exist?("#{dir}/#{file}") end end def self.supports_multi? false end def initialize(dirs, options = {}) @dirs = dirs @options = options.dup determine_mode_and_execution_root(options) end def scan apps = [] watchlist = [] if single_mode? app_root = find_app_root apps << { :server_names => ["_"], :root => app_root } watchlist << app_root WATCH_ENTRIES.each do |entry| if File.exist?("#{app_root}/#{entry}") watchlist << "#{app_root}/#{entry}" end end apps.map! do |app| @options.merge(app) end end @apps = apps @watchlist = watchlist return apps end def monitor(termination_pipe) raise "You must call #scan first" if !@apps watcher = PhusionPassenger::Utils::FileSystemWatcher.new(@watchlist, termination_pipe) if wait_on_io(termination_pipe, 3) return end while true changed = watcher.wait_for_change watcher.close if changed old_apps = @apps # The change could be caused by a write to some Passengerfile.json file. # Wait for a short period so that the write has a chance to finish. if wait_on_io(termination_pipe, 0.25) return end new_apps = scan watcher = PhusionPassenger::Utils::FileSystemWatcher.new(@watchlist, termination_pipe) if old_apps != new_apps yield(new_apps) end # Don't process change events again for a short while, # but do detect changes while waiting. if wait_on_io(termination_pipe, 3) return end else return end end ensure watcher.close if watcher end def single_mode? return @mode == :single end def multi_mode? return !single_mode? end ################## private class ConfigLoadError < StandardError end def find_app_root if @dirs.empty? return File.absolute_logical_path(".") else return File.absolute_logical_path(@dirs[0]) end end def looks_like_app_directory?(dir, options = {}) return AppFinder.looks_like_app_directory?(dir, options) end def filename_to_server_names(filename) basename = File.basename(filename) names = [basename] if basename !~ /^www\.$/i names << "www.#{basename}" end return names end # Wait until the given IO becomes readable, or until the timeout has # been reached. Returns true if the IO became readable, false if the # timeout has been reached. def wait_on_io(io, timeout) return !!select([io], nil, nil, timeout) end def determine_mode_and_execution_root(options) @mode = :single if @dirs.empty? @execution_root = Dir.logical_pwd elsif @dirs.size == 1 @execution_root = File.absolute_logical_path(@dirs[0]) else @execution_root = Dir.logical_pwd end end ################## end end # module Standalone end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/standalone/command.rb000644 000765 000024 00000005056 12233035540 030076 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module PhusionPassenger module Standalone class Command def initialize(argv) @argv = argv.dup @options = self.class.create_default_options end private def self.create_default_options return {} end def parse_options load_and_merge_global_options(@options) @parsed_options = self.class.create_default_options @parser = self.class.create_option_parser(@parsed_options) begin @original_argv = @argv.dup @parser.parse!(@argv) @options.merge!(@parsed_options) rescue OptionParser::ParseError => e STDERR.puts "*** ERROR: #{e}" abort @parser.to_s end if @options[:help] puts @parser exit end end def load_and_merge_global_options(options) path = ConfigUtils.global_config_file_path if File.exist?(path) begin @global_options = ConfigUtils.load_config_file(path) rescue ConfigUtils::ConfigLoadError => e STDERR.puts "*** Warning: #{e.message}" return end @options.merge!(@global_options) else @global_options = {} end end end end # module Standalone end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/standalone/config_options_list.rb000644 000765 000024 00000055441 12233035540 032536 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2015-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'platform_info/ruby' # This file contains a specification of all supported Passenger Standalone # configuration options. The specifications are spread over multiple constants, # one for each category. The command line parser for `passenger start` is # automatically generated from these specifications. The configuration file # parser and the environment variable parser also use these specifications. # # A specification is an array of hashes. The following keys are supported: # # - :name # The name of the configuration option. If you omit this option, while # simultaneously setting the `:cli` option, then it means this configuration # option is available as a command line option only. # # - :type # (default: :string) # The value type. Supported types are: :string, :integer, :boolean, :path, # :array, :map, :hostname. # This is used for determining a default parser and for checking the value. # # - :type_desc # (default: automatically inferred) # A description of the value type, to be used in command line option help # messages. For example, the `:address` option should have the type desc # `HOST` so that the help message `--address HOST` is generated. Boolean # options have a `type_desc` of nil. # # - :cli # (default: automatically inferred) # The name of the corresponding command line option. If not specified, then # a default one will be automatically generated form the configuration option # name. For example, `:ssl_certificate_path` becomes `--ssl-certificate-path`. # If set to nil, then it means there is no corresponding command line option. # # - :cli_parser # (default: automatically inferred) # Command line options are parsed based on the configuration option type. # If you want to parse it differently, then you can set this option to a # lambda to specify a custom parser. # # - :short_cli # The name of the corresponding short command line option, if any. For # example, the `:address` option should have `short_cli` set to `-a`. # # - :desc # A description to be displayed in command line options help message. # # - :default # The default value. # # - :min # If :type is :integer, then this specifies the minimum accepted value. # Has no meaing if :type is not :integer. # This value is used by the default CLI parser to check whether the # passed argument is acceptable. module PhusionPassenger module Standalone # Server configuration options SERVER_CONFIG_SPEC = [ { :name => :address, :type => :hostname, :type_desc => 'HOST', :short_cli => '-a', :default => '0.0.0.0', :desc => "Bind to the given address.\n" \ "Default: %DEFAULT%" }, { :name => :port, :type => :integer, :short_cli => '-p', :default => 3000, :desc => 'Use the given port number. Default: %DEFAULT%' }, { :name => :socket_file, :type => :path, :type_desc => 'FILE', :cli => '--socket', :short_cli => '-S', :desc => "Bind to Unix domain socket instead of TCP\n" \ 'socket' }, { :name => :socket_backlog, :type => :integer, :cli => '--socket-backlog', :desc => "Override size of the socket backlog.\n" \ "Default: #{DEFAULT_SOCKET_BACKLOG}" }, { :name => :ssl, :type => :boolean, :desc => "Enable SSL support (Nginx\n" \ 'engine only)' }, { :name => :ssl_certificate, :type => :path, :desc => "Specify the SSL certificate path\n" \ '(Nginx engine only)' }, { :name => :ssl_certificate_key, :type => :path, :desc => "Specify the SSL key path (Nginx\n" \ 'engine only)' }, { :name => :ssl_port, :type => :integer, :type_desc => 'PORT', :desc => "Listen for SSL on this port, while\n" \ "listening for HTTP on the normal port\n" \ '(Nginx engine only)' }, { :name => :daemonize, :type => :boolean, :short_cli => '-d', :desc => 'Daemonize into the background' }, { :name => :user, :type_desc => 'USERNAME', :desc => "User to run as. Ignored unless\n" \ 'running as root' }, { :name => :log_file, :type => :path, :desc => "Where to write log messages. Default:\n" \ 'console, or /dev/null when daemonized' }, { :name => :pid_file, :type => :path, :desc => 'Where to store the PID file' }, { :name => :instance_registry_dir, :type => :path, :desc => 'Use the given instance registry directory' }, { :name => :data_buffer_dir, :type => :path, :desc => 'Use the given data buffer directory' }, { :name => :core_file_descriptor_ulimit, :type => :integer, :desc => "Set custom file descriptor ulimit for the\n" \ "#{SHORT_PROGRAM_NAME} core" } ] # Application loading configuration options APPLICATION_LOADING_CONFIG_SPECS = [ { :name => :environment, :type_desc => 'NAME', :short_cli => '-e', :default => ENV['RAILS_ENV'] || ENV['RACK_ENV'] || ENV['NODE_ENV'] || ENV['PASSENGER_APP_ENV'] || 'development', :desc => "Web framework environment. Default:\n" \ "%DEFAULT%" }, { :name => :ruby, :type_desc => 'FILENAME', :desc => "Executable to use for Ruby apps.\n" \ "Default: #{PlatformInfo.ruby_command}\n" \ "(current context)" }, { :name => :python, :type_desc => 'FILENAME', :desc => 'Executable to use for Python apps' }, { :name => :nodejs, :type_desc => 'FILENAME', :desc => 'Executable to use for Node.js apps' }, { :name => :meteor_app_settings, :type => :path, :type_desc => 'FILENAME', :desc => "Settings file to use for (development mode)\n" \ 'Meteor apps' }, { :type => :path, :type_desc => 'FILENAME', :cli => '--rackup', :short_cli => '-R', :cli_parser => lambda do |options, value| options[:app_type] = 'rack' options[:startup_file] = File.absolute_logical_path(value, Dir.logical_pwd) end, :desc => "Consider application a Ruby app, and use\n" \ 'the given rackup file' }, { :name => :app_type, :type_desc => 'NAME', :desc => 'Force app to be detected as the given type' }, { :name => :startup_file, :type => :path, :type_desc => 'FILENAME', :desc => 'Force given startup file to be used' }, { :name => :spawn_method, :type_desc => 'NAME', :default => PlatformInfo.ruby_supports_fork? ? DEFAULT_SPAWN_METHOD : 'direct', :desc => 'The spawn method to use. Default: %DEFAULT%' }, { :name => :static_files_dir, :type => :path, :desc => "Specify the static files dir (Nginx engine\n" \ 'only)' }, { :name => :restart_dir, :type => :path, :desc => 'Specify the restart dir' }, { :name => :friendly_error_pages, :type => :boolean, :desc => 'Turn on friendly error pages' }, { :type => :boolean, :cli => '--no-friendly-error-pages', :cli_parser => lambda do |options, value| options[:friendly_error_pages] = false end, :desc => 'Turn off friendly error pages' }, { :name => :load_shell_envvars, :type => :boolean, # The Standalone mode is primarily used for serving a single app (except # when in mass deployment mode), so load_shell_envvars id disabled by # default. However, it's enabled by default in the Core, so we need to # explicitly set it to disabled here. :default => false, :desc => "Load shell startup files before loading\n" \ 'application' }, { :name => :app_file_descriptor_ulimit, :type => :integer, :desc => "Set custom file descriptor ulimit for the\n" \ "application" }, { :name => :debugger, :type => :boolean, :desc => 'Enable debugger support' }, { :name => :envvars, :type => :map, :type_desc => 'NAME=VALUE', :default => {}, :cli => '--envvar', :cli_parser => lambda do |options, value| if value !~ /=.+/ abort "*** ERROR: invalid --envvar format: #{value}" end key, real_value = value.split('=', 2) options[:envvars] ||= {} options[:envvars][key] = real_value end, :desc => 'Pass environment variable to application' } ] # Process management configuration options PROCESS_MANAGEMENT_CONFIG_SPECS = [ { :name => :max_pool_size, :type => :integer, :min => 1, :desc => "Maximum number of application processes.\n" \ "Default: #{DEFAULT_MAX_POOL_SIZE}" }, { :name => :min_instances, :type => :integer, :min => 0, :desc => "Minimum number of processes per\n" \ 'application. Default: 1' }, { :name => :pool_idle_time, :type => :integer, :type_desc => 'SECONDS', :desc => "Maximum time that processes may be idle.\n" \ "Default: #{DEFAULT_POOL_IDLE_TIME}" }, { :name => :max_preloader_idle_time, :type => :integer, :type_desc => 'SECONDS', :desc => "Maximum time that preloader processes may\n" \ "be idle. A value of 0 means that preloader\n" \ "processes never timeout. Default: #{DEFAULT_MAX_PRELOADER_IDLE_TIME}" }, { :name => :force_max_concurrent_requests_per_process, :type => :integer, :desc => "Force #{SHORT_PROGRAM_NAME} to believe that an\n" \ "application process can handle the given\n" \ "number of concurrent requests per process" }, { :name => :concurrency_model, :type_desc => 'NAME', :desc => "The concurrency model to use, either\n" \ "'process' or 'thread' (Enterprise only).\n" \ "Default: #{DEFAULT_CONCURRENCY_MODEL}" }, { :name => :thread_count, :type => :integer, :desc => "The number of threads to use when using\n" \ "the 'thread' concurrency model (Enterprise\n" \ "only). Default: #{DEFAULT_APP_THREAD_COUNT}" }, { :name => :memory_limit, :type => :integer, :type_desc => 'MB', :desc => "Restart application processes that go over\n" \ "the given memory limit (Enterprise only)" }, { :name => :rolling_restarts, :type => :boolean, :desc => "Enable rolling restarts (Enterprise only)" }, { :name => :resist_deployment_errors, :type => :boolean, :desc => "Enable deployment error resistance\n" \ '(Enterprise only)' } ] # Request handling configuration options REQUEST_HANDLING_CONFIG_SPECS = [ { :name => :max_request_time, :type => :integer, :type_desc => 'SECONDS', :min => 0, :desc => "Abort requests that take too much time\n" \ '(Enterprise only)' }, { :name => :max_request_queue_size, :type => :integer, :min => 0, :desc => "Specify request queue size. Default: #{DEFAULT_MAX_REQUEST_QUEUE_SIZE}" }, { :name => :sticky_sessions, :type => :boolean, :desc => 'Enable sticky sessions' }, { :name => :sticky_sessions_cookie_name, :type_desc => 'NAME', :desc => "Cookie name to use for sticky sessions.\n" \ "Default: #{DEFAULT_STICKY_SESSIONS_COOKIE_NAME}" }, { :name => :vary_turbocache_by_cookie, :type_desc => 'NAME', :desc => "Vary the turbocache by the cookie of the\n" \ 'given name' }, { :name => :turbocaching, :type => :boolean, :cli => nil }, { :type => :boolean, :cli => '--disable-turbocaching', :desc => 'Disable turbocaching', :cli_parser => lambda do |options, value| options[:turbocaching] = false end }, { :name => :unlimited_concurrency_paths, :type => :array, :type_desc => 'URI-PATH', :cli => '--unlimited-concurrency-path', :desc => "Specify URI path which supports unlimited\n" \ "concurrency. Specify multiple times for\n" \ "multiple paths", :default => [], :cli_parser => lambda do |options, value| options[:unlimited_concurrency_paths] ||= [] options[:unlimited_concurrency_paths] << value end }, { :name => :abort_websockets_on_process_shutdown, :type => :boolean, :cli => nil }, { :type => :boolean, :cli => '--no-abort-websockets-on-process-shutdown', :desc => "Do not abort WebSocket connections on\n" \ 'process restart', :cli_parser => lambda do |options, value| options[:abort_websockets_on_process_shutdown] = false end } ] # Union Station configuration options UNION_STATION_CONFIG_SPECS = [ { :name => :union_station_gateway_address, :cli => nil }, { :name => :union_station_gateway_port, :type => :integer, :cli => nil }, { :type_desc => 'HOST:PORT', :cli => '--union-station-gateway', :cli_parser => lambda do |options, value| host, port = value.split(":", 2) port = port.to_i port = 443 if port == 0 options[:union_station_gateway_address] = host options[:union_station_gateway_port] = port.to_i end, :desc => 'Specify Union Station Gateway host and port' }, { :name => :union_station_key, :type_desc => 'KEY', :desc => 'Specify Union Station key' }, { :name => :union_station_gateway_cert, :type => :path, :desc => "The certificate to use for contacting the\n" \ "Union Station gateway, or '-' to disable\n" \ "certificate checking", :config_value_parser => lambda do |value, base_dir| if value == '-' '-' else File.absolute_logical_path(value.to_s, base_dir) end end, :cli_parser => lambda do |options, value| if value == '-' options[:union_station_gateway_cert] = '-' else options[:union_station_gateway_cert] = File.absolute_logical_path(value, Dir.logical_pwd) end end } ] # Nginx engine configuration options NGINX_ENGINE_CONFIG_SPECS = [ { :name => :nginx_bin, :type => :path, :type_desc => 'FILENAME', :desc => 'Nginx binary to use as core' }, { :name => :nginx_version, :type_desc => 'VERSION', :default => PREFERRED_NGINX_VERSION, :desc => "Nginx version to use as core.\n" \ "Default: #{PREFERRED_NGINX_VERSION}" }, { :name => :nginx_tarball, :type => :path, :type_desc => 'FILENAME', :desc => "If Nginx needs to be installed, then the\n" \ "given tarball will be used instead of\n" \ "downloading from the Internet" }, { :name => :nginx_config_template, :type => :path, :type_desc => 'FILENAME', :desc => "The template to use for generating the\n" \ 'Nginx config file' }, { :name => :debug_nginx_config, :type => :boolean, :desc => 'Print Nginx config template and exit' } ] # Advanced configuration options ADVANCED_CONFIG_SPECS = [ { :name => :engine, :type_desc => 'NAME', :default => 'nginx', :desc => "Underlying HTTP engine to use. Available\n" \ "options: nginx (default), builtin" }, { :name => :log_level, :type => :integer, :default => DEFAULT_LOG_LEVEL, :desc => "Log level to use. Default: #{DEFAULT_LOG_LEVEL}" }, { :name => :auto, :type => :boolean, :default => !STDIN.tty? || !STDOUT.tty?, :desc => "Run in non-interactive mode. Default when\n" \ 'stdin or stdout is not a TTY' }, { :name => :ctls, :type => :array, :type_desc => 'NAME=VALUE', :default => [], :cli_parser => lambda do |options, value| if value !~ /=.+/ abort "*** ERROR: invalid --ctl format: #{value}" end options[:ctls] ||= [] options[:ctls] << value end }, { :name => :binaries_url_root, :type_desc => 'URL', :desc => "If Nginx needs to be installed, then the\n" \ "specified URL will be checked for binaries\n" \ 'prior to a local build' }, { :name => :runtime_check_only, :type => :boolean, :desc => "Quit after checking whether the\n" \ "#{PROGRAM_NAME} Standalone runtime files\n" \ 'are installed' }, { :name => :dont_install_runtime, :type => :boolean, :cli => '--no-install-runtime', :desc => 'Abort if runtime must be installed' }, { :name => :dont_compile_runtime, :type => :boolean, :cli => '--no-compile-runtime', :desc => 'Abort if runtime must be compiled' } ] CONFIG_SPECS = [ SERVER_CONFIG_SPEC, APPLICATION_LOADING_CONFIG_SPECS, PROCESS_MANAGEMENT_CONFIG_SPECS, REQUEST_HANDLING_CONFIG_SPECS, UNION_STATION_CONFIG_SPECS, NGINX_ENGINE_CONFIG_SPECS, ADVANCED_CONFIG_SPECS ] # Maps configuration options to their default value. Automatically # set by code later in this file. # # To inspect the value of this array, run: # # ./dev/runner -r standalone/config_options_list -r pp \ # 'pp Standalone::CONFIG_DEFAULTS; nil' CONFIG_DEFAULTS = {} # Indexes all configuration specification items by name. # # To inspect the value of this array, run: # # ./dev/runner -r standalone/config_options_list -r pp \ # 'pp Standalone::CONFIG_NAME_INDEX; nil' CONFIG_NAME_INDEX = {} ############ # Apply transformations on the specification constants, e.g. set default values. make_default_type_desc_value = lambda do |spec_item| case spec_item[:type] when :string 'STRING' when :integer 'NUMBER' when :path 'PATH' when :array 'ARRAY' when :map 'MAP' when :hostname 'HOSTNAME' else nil end end make_default_cli_value = lambda do |spec_item| '--' + spec_item[:name].to_s.gsub('_', '-') end CONFIG_SPECS.each do |spec| spec.each do |spec_item| spec_item[:type] ||= :string spec_item[:type_desc] ||= make_default_type_desc_value.call(spec_item) if spec_item[:name] if !spec_item.key?(:cli) spec_item[:cli] = make_default_cli_value.call(spec_item) end if spec_item.key?(:default) CONFIG_DEFAULTS[spec_item[:name]] = spec_item[:default] end CONFIG_NAME_INDEX[spec_item[:name]] = spec_item end end end end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/standalone/config_utils.rb000644 000765 000024 00000030306 12233035540 031141 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'ruby_core_enhancements' PhusionPassenger.require_passenger_lib 'standalone/config_options_list' module PhusionPassenger module Standalone module ConfigUtils extend self # Make methods available as class methods. def self.included(klass) # When included into another class, make sure that Utils # methods are made private. public_instance_methods(false).each do |method_name| klass.send(:private, method_name) end end class ConfigLoadError < StandardError end def global_config_file_path @global_config_file_path ||= File.join(PhusionPassenger.home_dir, USER_NAMESPACE_DIRNAME, "standalone", "config.json") end def load_local_config_file_from_app_dir_param!(argv) if argv.empty? app_dir = Dir.logical_pwd elsif argv.size == 1 app_dir = argv[0] end local_options = {} if app_dir begin ConfigUtils.load_local_config_file!(app_dir, local_options) rescue ConfigUtils::ConfigLoadError => e abort "*** ERROR: #{e.message}" end end local_options end def load_local_config_file!(app_dir, options) config_file = File.join(app_dir, "Passengerfile.json") if !File.exist?(config_file) config_file = File.join(app_dir, "passenger-standalone.json") end if File.exist?(config_file) local_options = load_config_file(config_file) options.merge!(local_options) end end def load_config_file(filename) if !defined?(PhusionPassenger::Utils::JSON) PhusionPassenger.require_passenger_lib 'utils/json' end begin data = File.open(filename, "r:utf-8") do |f| f.read end rescue SystemCallError => e raise ConfigLoadError, "cannot load config file #{filename} (#{e})" end begin config = PhusionPassenger::Utils::JSON.parse(data) rescue => e raise ConfigLoadError, "cannot parse config file #{filename} (#{e})" end if !config.is_a?(Hash) raise ConfigLoadError, "cannot parse config file #{filename} (it does not contain an object)" end result = {} config_file_dir = File.dirname(File.absolute_logical_path(filename)) config.each_pair do |key, val| key = key.to_sym spec_item = CONFIG_NAME_INDEX[key] if spec_item begin result[key] = parse_config_value(spec_item, val, config_file_dir) rescue ConfigLoadError => e raise ConfigLoadError, "cannot parse config file #{filename} " \ "(error in config option '#{key}': #{e.message})" end else STDERR.puts "*** WARNING: #{filename}: configuration key '#{key}' is not supported" result[key] = val end end result end def load_env_config! begin load_env_config rescue ConfigUtils::ConfigLoadError => e abort "*** ERROR: #{e.message}" end end def load_env_config config = {} pwd = Dir.logical_pwd ENV.each_pair do |name, value| next if name !~ /^PASSENGER_(.+)/ key = $1.downcase.to_sym spec_item = CONFIG_NAME_INDEX[key] next if !spec_item next if !config_type_supported_in_envvar?(spec_item[:type]) next if value.empty? begin config[key] = parse_config_value(spec_item, value, pwd) rescue ConfigLoadError => e raise ConfigLoadError, "cannot parse environment variable '#{name}' " \ "(#{e.message})" end end config end def add_option_parser_options_from_config_spec(parser, spec, options) spec.each do |spec_item| next if spec_item[:cli].nil? args = [] if spec_item[:short_cli] args << spec_item[:short_cli] end args << make_long_cli_switch(spec_item) if type = determine_cli_switch_type(spec_item) args << type end args << format_cli_switch_description(spec_item) cli_parser = make_cli_switch_parser(parser, spec_item, options) parser.on(*args, &cli_parser) end end # We want the command line options to override the options in the local # config file, but the local config file could only be parsed when the # command line options have been parsed. This method remerges all the # config options from different sources so that options are overriden # according to the following order: # # - CONFIG_DEFAULTS # - global config file # - local config file # - environment variables # - command line options def remerge_all_config(global_options, local_options, env_options, parsed_options) CONFIG_DEFAULTS.merge(remerge_all_config_without_defaults( global_options, local_options, env_options, parsed_options)) end def remerge_all_config_without_defaults(global_options, local_options, env_options, parsed_options) global_options. merge(local_options). merge(env_options). merge(parsed_options) end def find_pid_and_log_file(execution_root, options) if options[:socket_file] pid_basename = 'passenger.pid' log_basename = 'passenger.log' else pid_basename = "passenger.#{options[:port]}.pid" log_basename = "passenger.#{options[:port]}.log" end if File.directory?("#{execution_root}/tmp/pids") options[:pid_file] ||= "#{execution_root}/tmp/pids/#{pid_basename}" else options[:pid_file] ||= "#{execution_root}/#{pid_basename}" end if File.directory?("#{execution_root}/log") options[:log_file] ||= "#{execution_root}/log/#{log_basename}" else options[:log_file] ||= "#{execution_root}/#{log_basename}" end end private def config_type_supported_in_envvar?(type) type == :string || type == :integer || type == :boolean || type == :path || type == :hostname end def parse_config_value(spec_item, value, base_dir) if parser = spec_item[:config_value_parser] return parser.call(value, base_dir) end case spec_item[:type] when :string value.to_s when :integer value = value.to_i if spec_item[:min] && value < spec_item[:min] raise ConfigLoadError, "value must be greater than or equal to #{spec_item[:min]}" else value end when :boolean value = value.to_s.downcase value == 'true' || value == 'yes' || value == 'on' || value == '1' when :path File.absolute_logical_path(value.to_s, base_dir) when :array if value.is_a?(Array) value else raise ConfigLoadError, "array expected" end when :map if value.is_a?(Hash) value else raise ConfigLoadError, "map expected" end when :hostname begin resolve_hostname(value) rescue SocketError => e raise ConfigLoadError, "hostname #{value} cannot be resolved: #{e}" end else raise ArgumentError, "Unsupported type #{spec_item[:type]}" end end def make_long_cli_switch(spec_item) case spec_item[:type] when :string, :integer, :path, :array, :map, :hostname "#{spec_item[:cli]} #{spec_item[:type_desc]}" when :boolean spec_item[:cli] else raise ArgumentError, "Cannot create long CLI switch for type #{spec_item[:type]}" end end def determine_cli_switch_type(spec_item) case spec_item[:type] when :string, :path, :array, :map, :hostname String when :integer Integer when :boolean nil else raise ArgumentError, "Cannot determine CLI switch type for #{spec_item[:type]}" end end def format_cli_switch_description(spec_item) desc = spec_item[:desc] return '(no description)' if desc.nil? result = desc.gsub("\n", "\n" + ' ' * 37) result.gsub!('%DEFAULT%', (spec_item[:default] || 'N/A').to_s) result end def make_cli_switch_parser(parser, spec_item, options) if cli_parser = spec_item[:cli_parser] lambda do |value| cli_parser.call(options, value) end elsif spec_item[:type] == :integer lambda do |value| if spec_item[:min] && value < spec_item[:min] abort "*** ERROR: you may only specify for #{spec_item[:cli]} " \ "a number greater than or equal to #{spec_item[:min]}" end options[spec_item[:name]] = value end elsif spec_item[:type] == :path lambda do |value| options[spec_item[:name]] = File.absolute_logical_path(value, Dir.logical_pwd) end elsif spec_item[:type] == :boolean lambda do |value| options[spec_item[:name]] = true end elsif spec_item[:type] == :hostname lambda do |value| begin options[spec_item[:name]] = resolve_hostname(value) rescue SocketError => e abort "*** ERROR: the hostname passed to #{spec_item[:cli]}, #{value}, cannot be resolved: #{e}" end end else lambda do |value| options[spec_item[:name]] = value end end end def resolve_hostname(hostname) # We resolve the hostname into an IP address during configuration loading # because different components in the system (Nginx, Passenger core) may # resolve hostnames differently. If a hostname resolves to multiple addresses # (for example, to an IPv6 and an IPv4 address) then different components may # pick a different address as 'winner'. By resolving the hostname here, we # guarantee consistent behavior. # # Furthermore, `rails server` defaults to setting the hostname to 'localhost'. # But the user almost certainly doesn't explicitly want it to resolve to an IPv6 # address because most tools work better with IPv4 and because `http://[::1]:3000` # just looks weird. So we special case 'localhost' and resolve it to 127.0.0.1. if hostname.downcase == 'localhost' '127.0.0.1' else Socket.getaddrinfo(hostname, nil).first[3] end end end end # module Standalone end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/standalone/control_utils.rb000644 000765 000024 00000006127 12233035540 031360 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module PhusionPassenger module Standalone module ControlUtils extend self # Make methods available as class methods. def self.included(klass) # When included into another class, make sure that Utils # methods are made private. public_instance_methods(false).each do |method_name| klass.send(:private, method_name) end end def require_daemon_controller return if defined?(PhusionPassenger::DaemonController) PhusionPassenger.require_passenger_lib 'vendor/daemon_controller' end def warn_pid_file_not_found(options) if options[:pid_file] STDERR.puts "According to the PID file '#{options[:pid_file]}'," STDERR.puts "#{PROGRAM_NAME} Standalone doesn't seem to be running." else STDERR.puts "#{PROGRAM_NAME} Standalone doesn't seem to be running, " + "because its PID file" STDERR.puts "could not be found." end STDERR.puts STDERR.puts "If you know that #{PROGRAM_NAME} Standalone *is* running then one of these" STDERR.puts "might be the cause of this error:" STDERR.puts STDERR.puts " * The #{PROGRAM_NAME} Standalone instance that you want to stop isn't running" STDERR.puts " on port #{options[:port]}, but on another port. If this is the case then you" STDERR.puts " should specify the right port with --port." STDERR.puts " If the instance is listening on a Unix socket file instead of a TCP port," STDERR.puts " then please specify the PID file's filename with --pid-file." STDERR.puts " * The instance that you want to stop has stored its PID file in a non-standard" STDERR.puts " location. In this case please specify the right PID file with --pid-file." end end end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/standalone/main.rb000644 000765 000024 00000007432 12233035540 027404 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2013-2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'constants' module PhusionPassenger # Core of the `passenger` command (Passenger Standalone). Dispatches a subcommand to a specific class. module Standalone KNOWN_COMMANDS = [ ["start", "StartCommand"], ["stop", "StopCommand"], ["status", "StatusCommand"], ["version", "VersionCommand"] ] def self.run!(argv) if argv.empty? help exit end command_class, new_argv = lookup_command_class_by_argv(argv) if help_requested?(argv) help elsif version_requested?(argv) show_version elsif command_class command = command_class.new(new_argv) command.run else help abort end end def self.help puts "#{PROGRAM_NAME} Standalone, the easiest way to run web apps." puts puts "Available commands:" puts puts " passenger start Start #{PROGRAM_NAME} Standalone." puts " passenger stop Stop a #{PROGRAM_NAME} instance." puts " passenger status Show the status of a running #{PROGRAM_NAME} instance." puts puts "Run 'passenger --help' for more information about each command." end private def self.help_requested?(argv) return argv.size == 1 && (argv[0] == "--help" || argv[0] == "-h" || argv[0] == "help") end def self.version_requested?(argv) return argv.size == 1 && (argv[0] == "--version" || argv[0] == "-v") end def self.show_version command_class, new_argv = lookup_command_class_by_argv(["version"]) command_class.new(new_argv).run end def self.lookup_command_class_by_argv(argv) return nil if argv.empty? # Convert "passenger help " to "passenger --help". if argv.size == 2 && argv[0] == "help" argv = [argv[1], "--help"] end KNOWN_COMMANDS.each do |props| if argv[0] == props[0] command_class = lookup_command_class_by_class_name(props[1]) new_argv = argv[1 .. -1] return [command_class, new_argv] end end return nil end def self.lookup_command_class_by_class_name(class_name) base_name = class_name.gsub(/[A-Z]/) do |match| "_" + match[0..0].downcase end base_name.sub!(/^_/, '') base_name << ".rb" PhusionPassenger.require_passenger_lib("standalone/#{base_name}") return PhusionPassenger::Standalone.const_get(class_name) end end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/standalone/start_command/000755 000765 000024 00000000000 12233035540 030760 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/standalone/start_command.rb000644 000765 000024 00000045766 12233035540 031327 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'optparse' require 'thread' require 'socket' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'ruby_core_enhancements' PhusionPassenger.require_passenger_lib 'standalone/command' PhusionPassenger.require_passenger_lib 'standalone/config_utils' PhusionPassenger.require_passenger_lib 'utils' PhusionPassenger.require_passenger_lib 'utils/tmpio' PhusionPassenger.require_passenger_lib 'platform_info/ruby' # ## Coding notes # # ### Lazy library loading # # We lazy load as many libraries as possible not only to improve startup performance, # but also to ensure that we don't require libraries before we've passed the dependency # checking stage of the runtime installer. module PhusionPassenger module Standalone class StartCommand < Command def run parse_options load_local_config_file load_env_config remerge_all_options sanity_check_options_and_set_defaults lookup_runtime_and_ensure_installed set_stdout_stderr_binmode exit if @options[:runtime_check_only] find_apps find_pid_and_log_file(@app_finder, @options) create_working_dir initialize_vars start_engine begin show_intro_message maybe_daemonize touch_temp_dir_in_background ######################## ######################## watch_log_files_in_background if should_watch_one_or_more_log_files? wait_until_engine_has_exited if should_wait_until_engine_has_exited? rescue Interrupt trapsafe_shutdown_and_cleanup(true) exit 2 rescue SignalException => signal trapsafe_shutdown_and_cleanup(true) if signal.message == 'SIGINT' || signal.message == 'SIGTERM' exit 2 else raise end rescue Exception trapsafe_shutdown_and_cleanup(true) raise else trapsafe_shutdown_and_cleanup(false) ensure reset_traps_intterm end end private ################# Configuration loading, option parsing and initialization ################### def self.create_option_parser(options) logical_pwd = Dir.logical_pwd # If you add or change an option, make sure to update the following places too: # - src/ruby_supportlib/phusion_passenger/standalone/start_command/builtin_engine.rb, # function #build_daemon_controller_options # - resources/templates/config/standalone.erb OptionParser.new do |opts| defaults = CONFIG_DEFAULTS nl = "\n" + ' ' * 37 opts.banner = "Usage: passenger start [DIRECTORY] [OPTIONS]\n" opts.separator "Starts #{PROGRAM_NAME} Standalone and serve one or more web applications." opts.separator "" opts.separator "Server options:" ConfigUtils.add_option_parser_options_from_config_spec(opts, SERVER_CONFIG_SPEC, options) opts.separator "" opts.separator "Application loading options:" ConfigUtils.add_option_parser_options_from_config_spec(opts, APPLICATION_LOADING_CONFIG_SPECS, options) opts.separator "" opts.separator "Process management options:" ConfigUtils.add_option_parser_options_from_config_spec(opts, PROCESS_MANAGEMENT_CONFIG_SPECS, options) opts.separator "" opts.separator "Request handling options:" ConfigUtils.add_option_parser_options_from_config_spec(opts, REQUEST_HANDLING_CONFIG_SPECS, options) opts.separator "" opts.separator "Union Station options:" ConfigUtils.add_option_parser_options_from_config_spec(opts, UNION_STATION_CONFIG_SPECS, options) opts.separator "" opts.separator "Nginx engine options:" ConfigUtils.add_option_parser_options_from_config_spec(opts, NGINX_ENGINE_CONFIG_SPECS, options) opts.separator "" opts.separator "Advanced options:" ConfigUtils.add_option_parser_options_from_config_spec(opts, ADVANCED_CONFIG_SPECS, options) end end def load_local_config_file @local_options = ConfigUtils. load_local_config_file_from_app_dir_param!(@argv) end def load_env_config @env_options = ConfigUtils.load_env_config! end def remerge_all_options @options = ConfigUtils.remerge_all_config(@global_options, @local_options, @env_options, @parsed_options) @options_without_defaults = ConfigUtils. remerge_all_config_without_defaults(@global_options, @local_options, @env_options, @parsed_options) end def sanity_check_options_and_set_defaults if @argv.size > 1 PhusionPassenger.require_passenger_lib 'standalone/app_finder' if !AppFinder.supports_multi? abort "You can only specify a single application directory as argument." end end if (@options_without_defaults[:address] || @options_without_defaults[:port]) && @options_without_defaults[:socket_file] abort "You cannot specify both --address/--port and --socket. Please choose either one." end if @options[:ssl] && !@options[:ssl_certificate] abort "You specified --ssl. Please specify --ssl-certificate as well." end if @options[:ssl] && !@options[:ssl_certificate_key] abort "You specified --ssl. Please specify --ssl-certificate-key as well." end if @options[:engine] != "builtin" && @options[:engine] != "nginx" abort "You've specified an invalid value for --engine. The only values allowed are: builtin, nginx." end if @options[:engine] == "builtin" # We explicitly check that some options are set and warn the user about this, # in case they are using the builtin engine. We don't warn about options # that begin with --nginx- because that should be obvious. check_nginx_option_used_with_builtin_engine(:ssl, "--ssl") check_nginx_option_used_with_builtin_engine(:ssl_certificate, "--ssl-certificate") check_nginx_option_used_with_builtin_engine(:ssl_certificate_key, "--ssl-certificate-key") check_nginx_option_used_with_builtin_engine(:ssl_port, "--ssl-port") check_nginx_option_used_with_builtin_engine(:static_files_dir, "--static-files-dir") end ############# end def check_nginx_option_used_with_builtin_engine(option, argument) if @options.has_key?(option) STDERR.puts "*** Warning: the #{argument} option is only allowed if you use " + "the 'nginx' engine. You are currently using the 'builtin' engine, so " + "this option has been ignored. To switch to the Nginx engine, please " + "pass --engine=nginx." end end def lookup_runtime_and_ensure_installed @agent_exe = PhusionPassenger.find_support_binary(AGENT_EXE) if @options[:nginx_bin] @nginx_binary = @options[:nginx_bin] if !@nginx_binary abort "*** ERROR: Nginx binary #{@options[:nginx_bin]} does not exist" end if !@agent_exe install_runtime @agent_exe = PhusionPassenger.find_support_binary(AGENT_EXE) end else nginx_name = "nginx-#{@options[:nginx_version]}" @nginx_binary = PhusionPassenger.find_support_binary(nginx_name) if !@agent_exe || !@nginx_binary install_runtime @agent_exe = PhusionPassenger.find_support_binary(AGENT_EXE) @nginx_binary = PhusionPassenger.find_support_binary(nginx_name) end end end def install_runtime if @options[:dont_install_runtime] abort "*** ERROR: Refusing to install the #{PROGRAM_NAME} Standalone runtime " + "because --no-install-runtime is given." end args = [ "--brief", "--no-force-tip", # Use default Utils::Download timeouts, which are short. We want short # timeouts so that if the primary server is down and is not responding # (as opposed to responding quickly with an error), then the system # quickly switches to a mirror. "--connect-timeout", "0", "--idle-timeout", "0" ] if @options[:auto] args << "--auto" end if @options[:binaries_url_root] args << "--url-root" args << @options[:binaries_url_root] end if @options[:nginx_version] args << "--nginx-version" args << @options[:nginx_version] end if @options[:nginx_tarball] args << "--nginx-tarball" args << @options[:nginx_tarball] end if @options[:dont_compile_runtime] args << "--no-compile" end PhusionPassenger.require_passenger_lib 'config/install_standalone_runtime_command' PhusionPassenger::Config::InstallStandaloneRuntimeCommand.new(args).run puts puts "--------------------------" puts end def set_stdout_stderr_binmode # We already set STDOUT and STDERR to binmode in bin/passenger, which # fixes https://github.com/phusion/passenger-ruby-heroku-demo/issues/11. # However RuntimeInstaller sets them to UTF-8, so here we set them back. STDOUT.binmode STDERR.binmode end ################## Core logic ################## def find_apps PhusionPassenger.require_passenger_lib 'standalone/app_finder' @app_finder = AppFinder.new(@argv, @options) @apps = @app_finder.scan if @app_finder.multi_mode? && @options[:engine] != 'nginx' puts "Mass deployment enabled, so forcing engine to 'nginx'." @options[:engine] = 'nginx' end end def find_pid_and_log_file(app_finder, options) ConfigUtils.find_pid_and_log_file(app_finder.execution_root, options) end def create_working_dir # We don't remove the working dir in 'passenger start'. In daemon # mode 'passenger start' just quits and lets background processes # running. A specific background process, temp-dir-toucher, is # responsible for cleaning up the working dir. @working_dir = PhusionPassenger::Utils.mktmpdir("passenger-standalone.") File.chmod(0755, @working_dir) Dir.mkdir("#{@working_dir}/logs") @can_remove_working_dir = true end def initialize_vars @console_mutex = Mutex.new @termination_pipe = IO.pipe @threads = [] @interruptable_threads = [] end def start_engine metaclass = class << self; self; end if @options[:engine] == 'nginx' PhusionPassenger.require_passenger_lib 'standalone/start_command/nginx_engine' metaclass.send(:include, PhusionPassenger::Standalone::StartCommand::NginxEngine) else PhusionPassenger.require_passenger_lib 'standalone/start_command/builtin_engine' metaclass.send(:include, PhusionPassenger::Standalone::StartCommand::BuiltinEngine) end start_engine_real end # Returns the URL that the server will be listening on # for the given app. def listen_url(app) if app[:socket_file] "unix:#{app[:socket_file]}" else if @options[:engine] == 'nginx' && app[:ssl] && !app[:ssl_port] scheme = "https" else scheme = "http" end result = "#{scheme}://" if app[:port] == 80 result << app[:address] else result << compose_ip_and_port(app[:address], app[:port]) end result << "/" result end end def compose_ip_and_port(ip, port) if ip =~ /:/ # IPv6 "[#{ip}]:#{port}" else "#{ip}:#{port}" end end def show_intro_message puts "=============== Phusion Passenger Standalone web server started ===============" puts "PID file: #{@options[:pid_file]}" puts "Log file: #{@options[:log_file]}" puts "Environment: #{@options[:environment]}" puts "Accessible via: #{listen_url(@apps[0])}" puts if @options[:daemonize] puts "Serving in the background as a daemon." else puts "You can stop #{PROGRAM_NAME} Standalone by pressing Ctrl-C." end puts "Problems? Check https://www.phusionpassenger.com/library/admin/standalone/troubleshooting/" puts "===============================================================================" end def maybe_daemonize if @options[:daemonize] PhusionPassenger.require_passenger_lib 'platform_info/ruby' if PlatformInfo.ruby_supports_fork? daemonize else abort "Unable to daemonize using the current Ruby interpreter " + "(#{PlatformInfo.ruby_command}) because it does not support forking." end end end def daemonize pid = fork if pid # Parent exit!(0) else # Child trap "HUP", "IGNORE" STDIN.reopen("/dev/null", "r") STDOUT.reopen(@options[:log_file], "a") STDERR.reopen(@options[:log_file], "a") STDOUT.sync = true STDERR.sync = true Process.setsid @threads.clear end end def touch_temp_dir_in_background result = system(@agent_exe, "temp-dir-toucher", @working_dir, "--cleanup", "--daemonize", "--pid-file", "#{@working_dir}/temp_dir_toucher.pid", "--log-file", @options[:log_file]) if !result abort "Cannot start #{@agent_exe} temp-dir-toucher" end @can_remove_working_dir = false end def watch_log_file(log_file) if File.exist?(log_file) backward = 0 else # tail bails out if the file doesn't exist, so wait until it exists. while !File.exist?(log_file) sleep 1 end backward = 10 end if PlatformInfo.os_name_simple != 'solaris' backward_arg = "-n #{backward}" end IO.popen("tail -f #{backward_arg} \"#{log_file}\"", "rb") do |f| begin while true begin line = f.readline @console_mutex.synchronize do STDOUT.write(line) STDOUT.flush end rescue EOFError break end end ensure Process.kill('TERM', f.pid) rescue nil end end end def watch_log_files_in_background @apps.each do |app| thread = Utils.create_thread_and_abort_on_exception do watch_log_file("#{app[:root]}/log/#{@options[:environment]}.log") end @threads << thread @interruptable_threads << thread end if should_watch_main_log_file? thread = Utils.create_thread_and_abort_on_exception do watch_log_file(@options[:log_file]) end @threads << thread @interruptable_threads << thread end end def should_watch_one_or_more_log_files? !@options[:daemonize] end def should_watch_main_log_file? if @options[:daemonize] false else begin stat = File.stat(@options[:log_file]) rescue Errno::ENOENT stat = nil end if stat stat.file? else true end end end def should_wait_until_engine_has_exited? return !@options[:daemonize] || @app_finder.multi_mode? end ################## Shut down and cleanup ################## def capture_traps_intterm return if @traps_captured @traps_captured = 1 trap("INT", &method(:trapped_intterm)) trap("TERM", &method(:trapped_intterm)) end def reset_traps_intterm @traps_captured = nil trap("INT", "DEFAULT") trap("TERM", "DEFAULT") end def trapped_intterm(signal) if @traps_captured == 1 @traps_captured += 1 puts "Ignoring signal #{signal} during shutdown. Send it again to force exit." else exit!(1) end end def trapsafe_shutdown_and_cleanup(error_occurred) # Ignore INT and TERM once, to allow clean shutdown in e.g. Foreman capture_traps_intterm # Stop engine if @engine && (error_occurred || should_wait_until_engine_has_exited?) @console_mutex.synchronize do STDOUT.write("Stopping web server...") STDOUT.flush @engine.stop STDOUT.puts " done" STDOUT.flush end @engine = nil end # Stop threads if @threads if !@termination_pipe[1].closed? @termination_pipe[1].write("x") @termination_pipe[1].close end @interruptable_threads.each do |thread| thread.terminate end @interruptable_threads = [] @threads.each do |thread| thread.join end @threads = nil end if @can_remove_working_dir FileUtils.remove_entry_secure(@working_dir) @can_remove_working_dir = false end end ################# end end # module Standalone end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/standalone/status_command.rb000644 000765 000024 00000010462 12233035540 031476 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'optparse' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'standalone/command' PhusionPassenger.require_passenger_lib 'standalone/config_utils' PhusionPassenger.require_passenger_lib 'standalone/control_utils' PhusionPassenger.require_passenger_lib 'ruby_core_enhancements' module PhusionPassenger module Standalone class StatusCommand < Command def run parse_options load_local_config_file load_env_config remerge_all_options find_pid_file create_controller begin running = @controller.running? pid = @controller.pid rescue SystemCallError, IOError running = false end if running puts "#{PROGRAM_NAME} Standalone is running on PID #{pid}, according to PID file #{@options[:pid_file]}" else puts "#{PROGRAM_NAME} Standalone is not running, according to PID file #{@options[:pid_file]}" end end private def self.create_option_parser(options) OptionParser.new do |opts| defaults = CONFIG_DEFAULTS nl = "\n" + ' ' * 37 opts.banner = "Usage: passenger status [OPTIONS] [APP DIR]\n" opts.separator "Shows the status of a running #{PROGRAM_NAME} Standalone instance." opts.separator "" opts.separator "Options:" opts.on("-p", "--port NUMBER", Integer, "The port number of the #{PROGRAM_NAME}#{nl}" + "instance. Default: #{defaults[:port]}") do |value| options[:port] = value end opts.on("--pid-file FILE", String, "PID file of the running #{PROGRAM_NAME}#{nl}" + "Standalone instance") do |value| options[:pid_file] = value end end end def load_local_config_file @local_options = ConfigUtils. load_local_config_file_from_app_dir_param!(@argv) end def load_env_config @env_options = ConfigUtils.load_env_config! end def remerge_all_options @options = ConfigUtils.remerge_all_config(@global_options, @local_options, @env_options, @parsed_options) end def execution_root @argv[0] || Dir.logical_pwd end def find_pid_file ConfigUtils.find_pid_and_log_file(execution_root, @options) if !@options[:pid_file] if @options[:ignore_pid_not_found] exit else Standalone::ControlUtils.warn_pid_file_not_found(@options) exit 1 end end end def create_controller Standalone::ControlUtils.require_daemon_controller @controller = DaemonController.new( :identifier => "#{PROGRAM_NAME} Standalone engine", :start_command => "true", # Doesn't matter :ping_command => "true", # Doesn't matter :pid_file => @options[:pid_file], :log_file => "/dev/null", :timeout => 25 ) end end end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/standalone/stop_command.rb000644 000765 000024 00000010517 12233035540 031141 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'optparse' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'standalone/command' PhusionPassenger.require_passenger_lib 'standalone/config_utils' PhusionPassenger.require_passenger_lib 'standalone/control_utils' PhusionPassenger.require_passenger_lib 'ruby_core_enhancements' module PhusionPassenger module Standalone class StopCommand < Command def run parse_options load_local_config_file load_env_config remerge_all_options find_pid_file create_controller begin running = @controller.running? rescue SystemCallError, IOError running = false end if running @controller.stop else Standalone::ControlUtils.warn_pid_file_not_found(@options) exit 1 end end private def self.create_option_parser(options) OptionParser.new do |opts| defaults = CONFIG_DEFAULTS nl = "\n" + ' ' * 37 opts.banner = "Usage: passenger stop [OPTIONS] [APP DIR]\n" opts.separator "Stops a running #{PROGRAM_NAME} Standalone instance." opts.separator "" opts.separator "Options:" opts.on("-p", "--port NUMBER", Integer, "The port number of the #{PROGRAM_NAME}#{nl}" + "instance. Default: #{defaults[:port]}") do |value| options[:port] = value end opts.on("--pid-file FILE", String, "PID file of the running #{PROGRAM_NAME}#{nl}" + "Standalone instance") do |value| options[:pid_file] = value end opts.on("--ignore-pid-not-found", "-i", "Don't abort with an error if PID file cannot be found") do options[:ignore_pid_not_found] = true end end end def load_local_config_file @local_options = ConfigUtils. load_local_config_file_from_app_dir_param!(@argv) end def load_env_config @env_options = ConfigUtils.load_env_config! end def remerge_all_options @options = ConfigUtils.remerge_all_config(@global_options, @local_options, @env_options, @parsed_options) end def execution_root @argv[0] || Dir.logical_pwd end def find_pid_file ConfigUtils.find_pid_and_log_file(execution_root, @options) if !@options[:pid_file] if @options[:ignore_pid_not_found] exit else Standalone::ControlUtils.warn_pid_file_not_found(@options) exit 1 end end end def create_controller Standalone::ControlUtils.require_daemon_controller @controller = DaemonController.new( :identifier => "#{PROGRAM_NAME} Standalone engine", :start_command => "true", # Doesn't matter :ping_command => "true", # Doesn't matter :pid_file => @options[:pid_file], :log_file => "/dev/null", :timeout => 25 ) end end end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/standalone/version_command.rb000644 000765 000024 00000003135 12233035540 031637 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'standalone/command' PhusionPassenger.require_passenger_lib 'config/about_command' module PhusionPassenger module Standalone class VersionCommand < Command def run command = PhusionPassenger::Config::AboutCommand.new(['version']) command.run end end end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/standalone/start_command/builtin_engine.rb000644 000765 000024 00000025766 12233035540 034320 0ustar00honglistaff000000 000000 # encoding: utf-8 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'etc' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'standalone/control_utils' PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/operating_system' PhusionPassenger.require_passenger_lib 'utils/shellwords' PhusionPassenger.require_passenger_lib 'utils/json' module PhusionPassenger module Standalone class StartCommand module BuiltinEngine private def start_engine_real Standalone::ControlUtils.require_daemon_controller @engine = DaemonController.new(build_daemon_controller_options) start_engine_no_create end def wait_until_engine_has_exited read_and_delete_report_file! if PlatformInfo.supports_flock? lock = DaemonController::LockFile.new(@watchdog_lock_file_path) lock.shared_lock do # Do nothing end else pid = @engine.pid while process_is_alive?(pid) sleep 1 end end end def start_engine_no_create begin @engine.start rescue DaemonController::AlreadyStarted begin pid = @engine.pid rescue SystemCallError, IOError pid = nil end if pid abort "#{PROGRAM_NAME} Standalone is already running on PID #{pid}." else abort "#{PROGRAM_NAME} Standalone is already running." end rescue DaemonController::StartError => e abort "Could not start the server engine:\n#{e}" end end def build_daemon_controller_options ping_spec = lambda do File.exist?(report_file_path) && File.stat(report_file_path).size > 0 end command = "" if !@options[:envvars].empty? command = "env " @options[:envvars].each_pair do |name, value| command << "#{Shellwords.escape name}=#{Shellwords.escape value} " end end command << "#{@agent_exe} watchdog"; command << " --passenger-root #{Shellwords.escape PhusionPassenger.install_spec}" command << " --daemonize" command << " --no-user-switching" command << " --no-delete-pid-file" command << " --cleanup-pidfile #{Shellwords.escape @working_dir}/temp_dir_toucher.pid" command << " --report-file #{Shellwords.escape @working_dir}/report.json" command << " --ctl prestart_urls=#{Shellwords.escape prestart_urls_base64}" add_param(command, :user, "--user") add_param(command, :log_file, "--log-file") add_param(command, :pid_file, "--pid-file") add_param(command, :instance_registry_dir, "--instance-registry-dir") add_param(command, :data_buffer_dir, "--data-buffer-dir") add_param(command, :log_level, "--log-level") @options[:ctls].each do |ctl| command << " --ctl #{Shellwords.escape ctl}" end if @options[:user] command << " --default-user #{Shellwords.escape @options[:user]}" else user = Etc.getpwuid(Process.uid).name begin group = Etc.getgrgid(Process.gid) rescue ArgumentError # Do nothing. On Heroku, it's normal that the group # database is broken. else command << " --default-group #{Shellwords.escape group.name}" end command << " --default-user #{Shellwords.escape user}" end command << " --BC" # The builtin engine cannot be used in combination with Mass Deployment, # so we know @apps always has 1 app. command << " --listen #{listen_address(@apps[0])}" command << " --no-graceful-exit" add_param(command, :socket_backlog, "--socket-backlog") add_param(command, :environment, "--environment") add_param(command, :app_type, "--app-type") add_param(command, :startup_file, "--startup-file") add_param(command, :spawn_method, "--spawn-method") add_param(command, :restart_dir, "--restart-dir") if @options.has_key?(:friendly_error_pages) if @options[:friendly_error_pages] command << " --force-friendly-error-pages" else command << " --disable-friendly-error-pages" end end if @options[:turbocaching] == false command << " --disable-turbocaching" end if @options[:abort_websockets_on_process_shutdown] == false command << " --no-abort-websockets-on-process-shutdown" end add_param(command, :force_max_concurrent_requests_per_process, "--force-max-concurrent-requests-per-process") add_flag_param(command, :load_shell_envvars, "--load-shell-envvars") add_param(command, :max_pool_size, "--max-pool-size") add_param(command, :min_instances, "--min-instances") add_param(command, :pool_idle_time, "--pool-idle-time") add_param(command, :max_preloader_idle_time, "--max-preloader-idle-time") add_param(command, :max_request_queue_size, "--max-request-queue-size") add_enterprise_param(command, :concurrency_model, "--concurrency-model") add_enterprise_param(command, :thread_count, "--app-thread-count") add_enterprise_param(command, :max_request_time, "--max-request-time") add_enterprise_param(command, :memory_limit, "--memory-limit") add_enterprise_flag_param(command, :rolling_restarts, "--rolling-restarts") add_enterprise_flag_param(command, :resist_deployment_errors, "--resist-deployment-errors") add_enterprise_flag_param(command, :debugger, "--debugger") add_flag_param(command, :sticky_sessions, "--sticky-sessions") add_param(command, :vary_turbocache_by_cookie, "--vary-turbocache-by-cookie") add_param(command, :sticky_sessions_cookie_name, "--sticky-sessions-cookie-name") add_param(command, :union_station_gateway_address, "--union-station-gateway-address") add_param(command, :union_station_gateway_port, "--union-station-gateway-port") add_param(command, :union_station_key, "--union-station-key") add_param(command, :ruby, "--ruby") add_param(command, :python, "--python") add_param(command, :nodejs, "--nodejs") add_param(command, :meteor_app_settings, "--meteor-app-settings") add_param(command, :core_file_descriptor_ulimit, "--core-file-descriptor-ulimit") add_param(command, :app_file_descriptor_ulimit, "--app-file-descriptor-ulimit") command << " #{Shellwords.escape(@apps[0][:root])}" command << " --EC --BU" add_param(command, :core_file_descriptor_ulimit, "--core-file-descriptor-ulimit") return { :identifier => "#{AGENT_EXE} watchdog", :start_command => command, :ping_command => ping_spec, :pid_file => @options[:pid_file], :log_file => @options[:log_file], :timeout => 25 } end def listen_address(options = @options, for_ping_port = false) if options[:socket_file] return "unix:#{options[:socket_file]}" else return "tcp://" + compose_ip_and_port(options[:address], options[:port]) end end def prestart_urls_base64 Utils.base64(listen_url(@apps[0])) end def add_param(command, option_name, param_name) if value = @options[option_name] command << " #{param_name} #{Shellwords.escape value.to_s}" end end def add_flag_param(command, option_name, param_name) if value = @options[option_name] command << " #{param_name}" end end def add_enterprise_param(command, option_name, param_name) if value = @options[option_name] abort "The '#{option_name}' feature is only available in #{PROGRAM_NAME} " + "Enterprise. You are currently running the open source #{PROGRAM_NAME}. " + "Please learn more about and/or buy #{PROGRAM_NAME} Enterprise at " + "https://www.phusionpassenger.com/enterprise" end end def add_enterprise_flag_param(command, option_name, param_name) if value = @options[option_name] abort "The '#{option_name}' feature is only available in #{PROGRAM_NAME} " + "Enterprise. You are currently running the open source #{PROGRAM_NAME}. " + "Please learn more about and/or buy #{PROGRAM_NAME} Enterprise at " + "https://www.phusionpassenger.com/enterprise" end end def report_file_path @report_file_path ||= "#{@working_dir}/report.json" end def read_and_delete_report_file! report = File.open(report_file_path, "r:utf-8") do |f| Utils::JSON.parse(f.read) end # The report file may contain sensitive information, so delete it. File.unlink(report_file_path) @watchdog_lock_file_path = report["instance_dir"] + "/lock" end def process_is_alive?(pid) begin Process.kill(0, pid) true rescue Errno::ESRCH false rescue SystemCallError => e true end end ##################### end # module BuiltinEngine end # module StartCommand end # module Standalone end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/standalone/start_command/nginx_engine.rb000644 000765 000024 00000025666 12233035540 033774 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'erb' require 'etc' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/ruby' PhusionPassenger.require_passenger_lib 'standalone/control_utils' PhusionPassenger.require_passenger_lib 'utils/tmpio' PhusionPassenger.require_passenger_lib 'utils/shellwords' module PhusionPassenger module Standalone class StartCommand module NginxEngine private def start_engine_real write_nginx_config_file(nginx_config_path) maybe_debug_nginx_config test_nginx_config Standalone::ControlUtils.require_daemon_controller @engine = DaemonController.new(build_daemon_controller_options) begin @engine.start rescue DaemonController::AlreadyStarted begin pid = @engine.pid rescue SystemCallError, IOError pid = nil end if pid abort "#{PROGRAM_NAME} Standalone is already running on PID #{pid}." else abort "#{PROGRAM_NAME} Standalone is already running." end rescue DaemonController::StartError => e abort "Could not start the Nginx engine:\n#{e}" end end def wait_until_engine_has_exited # Since the engine is not our child process (it daemonizes) # we cannot use Process.waitpid to wait for it. A busy-sleep-loop with # Process.kill(0, pid) isn't very efficient. Instead we do this: # # Connect to the engine's server and wait until it disconnects the socket # because of timeout. Keep doing this until we can no longer connect. while true if @options[:socket_file] socket = UNIXSocket.new(@options[:socket_file]) else socket = TCPSocket.new(@options[:address], @options[:port]) end begin begin socket.read rescue SystemCallError, IOError, SocketError end ensure begin socket.close rescue SystemCallError, IOError, SocketError end end end rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ENOENT end def maybe_debug_nginx_config if @options[:debug_nginx_config] File.open(nginx_config_path, 'rb') do |f| puts(f.read) end exit end end def test_nginx_config command = "#{Shellwords.escape @nginx_binary}" \ " -c #{Shellwords.escape nginx_config_path}" \ " -p #{Shellwords.escape @working_dir}" \ " -t" output = `#{command} 2>&1` if $? && $?.exitstatus != 0 output.gsub!(nginx_config_path, 'nginx.conf') output = PlatformInfo.send(:reindent, output, 4) message = "*** ERROR: the Nginx configuration that #{PROGRAM_NAME}" \ " Standalone generated internally contains problems. The error " \ "message returned by the Nginx engine is:\n\n" \ "#{output}\n\n" if @options[:nginx_config_template] message << "This probably means that you have a problem in your " \ "Nginx configuration template. Please fix your template.\n\n" \ "Tip: to debug your template, re-run #{SHORT_PROGRAM_NAME} " \ "Standalone with the `--debug-nginx-config` option. This " \ "allows you to see how the final Nginx config file looks like." else debug_log_file = Utils::TmpIO.new('passenger-standalone', :suffix => '.log', :binary => true, :unlink_immediately => false) begin File.open(nginx_config_path, 'rb') do |f| debug_log_file.write(f.read) end ensure debug_log_file.close end message << "This probably means that you have found a bug in " \ "#{PROGRAM_NAME} Standalone. Please report this bug to our " \ "Github issue tracker: https://github.com/phusion/passenger/issues\n\n" \ "In the bug report, please include this error message, as " \ "well as the contents of the file #{debug_log_file.path}" end abort(message) end end def build_daemon_controller_options if @options[:socket_file] ping_spec = [:unix, @options[:socket_file]] else ping_spec = [:tcp, @options[:address], @options[:port]] end return { :identifier => 'Nginx', :start_command => "#{Shellwords.escape @nginx_binary} " + "-c #{Shellwords.escape nginx_config_path} " + "-p #{Shellwords.escape @working_dir}", :ping_command => ping_spec, :pid_file => @options[:pid_file], :log_file => @options[:log_file], :start_timeout => 25, :stop_timeout => 60, :log_file_activity_timeout => 12 } end def nginx_config_path return "#{@working_dir}/nginx.conf" end def write_nginx_config_file(path) File.open(path, 'w') do |f| f.chmod(0644) erb = ERB.new(File.read(nginx_config_template_filename), nil, "-", next_eoutvar) erb.filename = nginx_config_template_filename # The template requires some helper methods which are defined in start_command.rb. output = erb.result(get_binding) f.write(output) if debugging? && !@options[:debug_nginx_config] puts output end end end def nginx_config_template_filename if @options[:nginx_config_template] return @options[:nginx_config_template] else return File.join(PhusionPassenger.resources_dir, "templates", "standalone", "config.erb") end end def debugging? return ENV['PASSENGER_DEBUG'] && !ENV['PASSENGER_DEBUG'].empty? end def next_eoutvar @next_eoutvar_index ||= 0 @next_eoutvar_index += 1 "_erbout#{@next_eoutvar_index}" end #### Config file template helpers #### def nginx_listen_address(options = @options) if options[:socket_file] "unix:#{options[:socket_file]}" else compose_ip_and_port(options[:address], options[:port]) end end def nginx_listen_address_with_ssl_port(options = @options) if options[:socket_file] "unix:#{options[:socket_file]}" else compose_ip_and_port(options[:address], options[:ssl_port]) end end def default_group_for(username) user = Etc.getpwnam(username) group = Etc.getgrgid(user.gid) return group.name end def nginx_http_option(option_name) nginx_option(@options, option_name) end def nginx_option(options, option_name, nginx_config_name = nil) if options.is_a?(Symbol) # Support old syntax for backward compatibility: # nginx_option(nginx_config_name, option_name) nginx_config_name = options options = @options end if options.key?(option_name) nginx_config_name ||= begin if option_name.to_s =~ /^union_station_/ option_name else "passenger_#{option_name}" end end value = options[option_name] if value.is_a?(String) value = "'#{value}'" elsif value == true value = "on" elsif value == false value = "off" end "#{nginx_config_name} #{value};" end end # Method exists for backward compatibility with old Nginx config templates def boolean_config_value(val) val ? "on" : "off" end def include_passenger_internal_template(name, indent = 0, fix_existing_indenting = true, the_binding = get_binding) path = "#{PhusionPassenger.resources_dir}/templates/standalone/#{name}" erb = ERB.new(File.read(path), nil, "-", next_eoutvar) erb.filename = path result = erb.result(the_binding) if fix_existing_indenting # Remove extraneous indenting by 'if' blocks # and collapse multiple empty newlines result.gsub!(/;[\n ]+/, ";\n") end # Set indenting result.gsub!(/^/, " " * indent) result.gsub!(/\A +/, '') result end def current_user Etc.getpwuid(Process.uid).name end def get_binding binding end def default_group_for(username) user = Etc.getpwnam(username) group = Etc.getgrgid(user.gid) return group.name end def serialize_strset(*items) if "".respond_to?(:force_encoding) items = items.map { |x| x.force_encoding('binary') } null = "\0".force_encoding('binary') else null = "\0" end return [items.join(null)].pack('m*').gsub("\n", "").strip end ##################### end # module NginxEngine end # module StartCommand end # module Standalone end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/request_handler/thread_handler.rb000644 000765 000024 00000032102 12233035540 032451 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'debug_logging' PhusionPassenger.require_passenger_lib 'message_channel' PhusionPassenger.require_passenger_lib 'utils' PhusionPassenger.require_passenger_lib 'utils/native_support_utils' PhusionPassenger.require_passenger_lib 'utils/unseekable_socket' module PhusionPassenger class RequestHandler # This class encapsulates the logic of a single RequestHandler thread. class ThreadHandler include DebugLogging include Utils class Interrupted < StandardError end REQUEST_METHOD = 'REQUEST_METHOD'.freeze GET = 'GET'.freeze PING = 'PING'.freeze OOBW = 'OOBW'.freeze PASSENGER_CONNECT_PASSWORD = 'PASSENGER_CONNECT_PASSWORD'.freeze CONTENT_LENGTH = 'CONTENT_LENGTH'.freeze TRANSFER_ENCODING = 'TRANSFER_ENCODING'.freeze MAX_HEADER_SIZE = 128 * 1024 OBJECT_SPACE_SUPPORTS_LIVE_OBJECTS = ObjectSpace.respond_to?(:live_objects) OBJECT_SPACE_SUPPORTS_ALLOCATED_OBJECTS = ObjectSpace.respond_to?(:allocated_objects) OBJECT_SPACE_SUPPORTS_COUNT_OBJECTS = ObjectSpace.respond_to?(:count_objects) GC_SUPPORTS_TIME = GC.respond_to?(:time) GC_SUPPORTS_CLEAR_STATS = GC.respond_to?(:clear_stats) attr_reader :thread attr_reader :stats_mutex attr_reader :interruptable attr_reader :iteration def initialize(request_handler, options = {}) @request_handler = request_handler @server_socket = Utils.require_option(options, :server_socket) @socket_name = Utils.require_option(options, :socket_name) @protocol = Utils.require_option(options, :protocol) @app_group_name = Utils.require_option(options, :app_group_name) Utils.install_options_as_ivars(self, options, :app, :union_station_core, :connect_password, :keepalive_enabled ) @stats_mutex = Mutex.new @interruptable = false @iteration = 0 if @protocol == :session metaclass = class << self; self; end metaclass.class_eval do alias parse_request parse_session_request end elsif @protocol == :http metaclass = class << self; self; end metaclass.class_eval do alias parse_request parse_http_request end else raise ArgumentError, "Unknown protocol specified" end end def install @thread = Thread.current Thread.current[:passenger_thread_handler] = self PhusionPassenger.call_event(:starting_request_handler_thread) end def main_loop(finish_callback) socket_wrapper = Utils::UnseekableSocket.new channel = MessageChannel.new buffer = '' buffer.force_encoding('binary') if buffer.respond_to?(:force_encoding) @union_station_hooks_defined = defined?(UnionStationHooks) begin finish_callback.call while true hijacked = accept_and_process_next_request(socket_wrapper, channel, buffer) socket_wrapper = Utils::UnseekableSocket.new if hijacked end rescue Interrupted # Do nothing. end debug("Thread handler main loop exited normally") ensure @stats_mutex.synchronize { @interruptable = true } end private # Returns true if the socket has been hijacked, false otherwise. def accept_and_process_next_request(socket_wrapper, channel, buffer) @stats_mutex.synchronize do @interruptable = true end if @last_connection connection = @last_connection channel.io = connection @last_connection = nil headers = parse_request(connection, channel, buffer) else connection = socket_wrapper.wrap(@server_socket.accept) end @stats_mutex.synchronize do @interruptable = false @iteration += 1 end trace(3, "Accepted new request on socket #{@socket_name}") if !headers # New socket accepted, instead of keeping-alive an old one channel.io = connection headers = parse_request(connection, channel, buffer) end if headers prepare_request(connection, headers) begin if headers[REQUEST_METHOD] == GET process_request(headers, connection, socket_wrapper, @protocol == :http) elsif headers[REQUEST_METHOD] == PING process_ping(headers, connection) false elsif headers[REQUEST_METHOD] == OOBW process_oobw(headers, connection) false else process_request(headers, connection, socket_wrapper, @protocol == :http) end rescue Exception has_error = true raise ensure if headers[RACK_HIJACK_IO] socket_wrapper = nil connection = nil channel = nil end finalize_request(connection, headers, has_error) trace(3, "Request done.") end else trace(2, "No headers parsed; disconnecting client.") false end rescue Interrupted raise rescue => e if socket_wrapper && socket_wrapper.source_of_exception?(e) # EPIPE is harmless, it just means that the client closed the connection. if !e.is_a?(Errno::EPIPE) print_exception("Passenger RequestHandler's client socket", e) end else if headers log_exception_to_union_station(headers, e) end # should_reraise_error? returns true except in unit tests, # so we normally stop the request handler upon encountering # non-EPIPE errors. We do this because process_request is already # supposed to catch application-level exceptions. If an # exception happened outside of process_request, then it's # probably serious enough to warrant stopping the request handler. raise e if should_reraise_error?(e) end # Here we do not know whether the connection was hijacked, but # to be on the safe side (and have a new socket_wrapper created) # let's say that it is. true ensure # Close connection if keep-alive not possible if connection && !connection.closed? && !@last_connection # The 'close_write' here prevents forked child # processes from unintentionally keeping the # connection open. begin connection.close_write rescue SystemCallError, IOError end begin connection.close rescue SystemCallError end end end def parse_session_request(connection, channel, buffer) headers_data = channel.read_scalar(buffer, MAX_HEADER_SIZE) if headers_data.nil? return end headers = Utils::NativeSupportUtils.split_by_null_into_hash(headers_data) if @connect_password && headers[PASSENGER_CONNECT_PASSWORD] != @connect_password warn "*** Passenger RequestHandler warning: " << "someone tried to connect with an invalid connect password." return else return headers end rescue SecurityError => e warn("*** Passenger RequestHandler warning: " << "HTTP header size exceeded maximum.") return end # Like parse_session_request, but parses an HTTP request. This is a very minimalistic # HTTP parser and is not intended to be complete, fast or secure, since the HTTP server # socket is intended to be used for debugging purposes only. def parse_http_request(connection, channel, buffer) headers = {} data = "" while data !~ /\r\n\r\n/ && data.size < MAX_HEADER_SIZE data << connection.readpartial(16 * 1024) end if data.size >= MAX_HEADER_SIZE warn("*** Passenger RequestHandler warning: " << "HTTP header size exceeded maximum.") return end data.gsub!(/\r\n\r\n.*/, '') data.split("\r\n").each_with_index do |line, i| if i == 0 # GET / HTTP/1.1 line =~ /^([A-Za-z]+) (.+?) (HTTP\/\d\.\d)$/ request_method = $1 request_uri = $2 protocol = $3 if request_method.nil? warn("*** Passenger RequestHandler warning: " << "Invalid HTTP request.") return end path_info, query_string = request_uri.split("?", 2) headers[REQUEST_METHOD] = request_method headers["REQUEST_URI"] = request_uri headers["QUERY_STRING"] = query_string || "" headers["SCRIPT_NAME"] = "" headers["PATH_INFO"] = path_info headers["SERVER_NAME"] = "127.0.0.1" headers["SERVER_PORT"] = connection.addr[1].to_s headers["SERVER_PROTOCOL"] = protocol else header, value = line.split(/\s*:\s*/, 2) header.upcase! # "Foo-Bar" => "FOO-BAR" header.gsub!("-", "_") # => "FOO_BAR" if header == CONTENT_LENGTH || header == "CONTENT_TYPE" headers[header] = value else headers["HTTP_#{header}"] = value end end end if @connect_password && headers["HTTP_X_PASSENGER_CONNECT_PASSWORD"] != @connect_password warn "*** Passenger RequestHandler warning: " << "someone tried to connect with an invalid connect password." return else return headers end rescue EOFError return end def process_ping(env, connection) connection.write("pong") end def process_oobw(env, connection) PhusionPassenger.call_event(:oob_work) connection.write("oobw done") end # def process_request(env, connection, socket_wrapper, full_http_response) # raise NotImplementedError, "Override with your own implementation!" # end def prepare_request(connection, headers) transfer_encoding = headers[TRANSFER_ENCODING] content_length = headers[CONTENT_LENGTH] @can_keepalive = @keepalive_enabled && !transfer_encoding && !content_length @keepalive_performed = false if !transfer_encoding && !content_length connection.simulate_eof! end if @union_station_hooks_defined @ush_reporter = UnionStationHooks.begin_rack_request(headers) end ################# end def finalize_request(connection, headers, has_error) if connection connection.stop_simulating_eof! end if @union_station_hooks_defined UnionStationHooks.end_rack_request(headers, has_error) @ush_reporter = nil end if !has_error && @keepalive_performed && connection trace(3, "Keep-aliving connection.") @last_connection = connection end ################# end def log_exception_to_union_station(env, exception) reporter = env['union_station_hooks'] if reporter reporter.log_exception(exception) end end def should_reraise_error?(e) # Stubable by unit tests. return true end def should_swallow_app_error?(e, socket_wrapper) return socket_wrapper && socket_wrapper.source_of_exception?(e) && e.is_a?(Errno::EPIPE) end end end # class RequestHandler end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/rack/out_of_band_gc.rb000644 000765 000024 00000011344 12233035540 030175 0ustar00honglistaff000000 000000 # encoding: binary # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2012-2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'thread' module PhusionPassenger module Rack class OutOfBandGc # Usage: # # OutOfBandGc.new(app, frequency, logger = nil) # OutOfBandGc.new(app, options = {}) def initialize(app, *args) @app = app if args.size == 0 || (args.size == 1 && args[0].is_a?(Hash)) # OutOfBandGc.new(app, options = {}) initialize_with_options(args[0] || {}) else # OutOfBandGc.new(app, frequency, logger = nil) initialize_legacy(*args) end end def call(env) status, headers, body = @app.call(env) case @strategy when :counting @mutex.synchronize do @request_count += 1 if @request_count == @frequency @request_count = 0 headers['!~Request-OOB-Work'] = 'true' end end when :gctools_oobgc if GC::OOB.dry_run headers['!~Request-OOB-Work'] = 'true' end else raise "Unrecognized Out-Of-Band GC strategy #{@strategy.inspect}" end [status, headers, body] end private def initialize_with_options(options) @strategy = options[:strategy] @logger = options[:logger] case @strategy when :counting @frequency = options[:frequency] @request_count = 0 @mutex = Mutex.new if !@frequency || @frequency < 1 raise ArgumentError, "The :frequency option must be a number that is at least 1." end ::PhusionPassenger.on_event(:oob_work) do t0 = Time.now disabled = GC.enable GC.start GC.disable if disabled @logger.info "Out Of Band GC finished in #{Time.now - t0} sec" if @logger end when :gctools_oobgc if !defined?(::GC::OOB) raise "To use the :gctools_oobgc strategy, " + "first add 'gem \"gctools\"' to your Gemfile, " + "then call 'require \"gctools/oobgc\"' and 'GC::OOB.setup' " + "before using the #{self.class.name} middleware." elsif !::GC::OOB.respond_to?(:dry_run) raise "To use the :gctools_oobgc strategy, you must use a sufficiently " + "recent version of the gctools gem. Please see this pull request: " + "https://github.com/tmm1/gctools/pull/5" elsif PhusionPassenger::App.options["spawn_method"] =~ /smart/ # Using GC::OOB with 'smart' currently results in a segfault. raise "The :gctools_oobgc strategy cannot be used with the '" + PhusionPassenger::App.options["spawn_method"] + "' spawning method. " + "Please use 'direct'." end ::PhusionPassenger.on_event(:oob_work) do t0 = Time.now GC::OOB.run @logger.info "Out Of Band GC finished in #{Time.now - t0} sec" if @logger end when nil raise ArgumentError, "You must specify an Out-Of-Band GC strategy with the :strategy option." else raise ArgumentError, "Invalid Out-Of-Band GC strategy #{@strategy.inspect}" end end def initialize_legacy(frequency, logger = nil) initialize_with_options( :strategy => :counting, :frequency => frequency, :logger => logger) end end end # module Rack end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/rack/thread_handler_extension.rb000644 000765 000024 00000037663 12233035540 032321 0ustar00honglistaff000000 000000 # encoding: binary # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'utils/tee_input' module PhusionPassenger module Rack module ThreadHandlerExtension # Constants which exist to relieve Ruby's garbage collector. RACK_VERSION = "rack.version" # :nodoc: RACK_VERSION_VALUE = [1, 2] # :nodoc: RACK_INPUT = "rack.input" # :nodoc: RACK_ERRORS = "rack.errors" # :nodoc: RACK_MULTITHREAD = "rack.multithread" # :nodoc: RACK_MULTIPROCESS = "rack.multiprocess" # :nodoc: RACK_RUN_ONCE = "rack.run_once" # :nodoc: RACK_URL_SCHEME = "rack.url_scheme" # :nodoc: RACK_HIJACK_P = "rack.hijack?" # :nodoc: RACK_HIJACK = "rack.hijack" # :nodoc: HTTP_VERSION = "HTTP_VERSION" # :nodoc: HTTP_1_1 = "HTTP/1.1" # :nodoc: SCRIPT_NAME = "SCRIPT_NAME" # :nodoc: REQUEST_METHOD = "REQUEST_METHOD" # :nodoc: TRANSFER_ENCODING_HEADER = "Transfer-Encoding" # :nodoc: TRANSFER_ENCODING_HEADERS = ["Transfer-Encoding", "Transfer-encoding", "transfer-encoding"] # :nodoc: CONTENT_LENGTH_HEADER = "Content-Length" # :nodoc: CONTENT_LENGTH_HEADERS = ["Content-Length", "Content-length", "content-length"] # :nodoc: X_SENDFILE_HEADER = "X-Sendfile" # :nodoc: X_ACCEL_REDIRECT_HEADER = "X-Accel-Redirect" # :nodoc: CONTENT_LENGTH_HEADER_AND_SEPARATOR = "Content-Length: " # :nodoc TRANSFER_ENCODING_HEADER_AND_VALUE_CRLF = "Transfer-Encoding: chunked\r\n" # :nodoc: CONNECTION_CLOSE_CRLF2 = "Connection: close\r\n\r\n" # :nodoc: HEAD = "HEAD" # :nodoc: HTTPS = "HTTPS" # :nodoc: HTTPS_DOWNCASE = "https" # :nodoc: HTTP = "http" # :nodoc: YES = "yes" # :nodoc: ON = "on" # :nodoc: ONE = "1" # :nodoc: CRLF = "\r\n" # :nodoc: NEWLINE = "\n" # :nodoc: STATUS = "Status: " # :nodoc: NAME_VALUE_SEPARATOR = ": " # :nodoc: TERMINATION_CHUNK = "0\r\n\r\n" # :nodoc: def process_request(env, connection, socket_wrapper, full_http_response) rewindable_input = PhusionPassenger::Utils::TeeInput.new(connection, env) begin env[RACK_VERSION] = RACK_VERSION_VALUE env[RACK_INPUT] = rewindable_input env[RACK_ERRORS] = STDERR env[RACK_MULTITHREAD] = @request_handler.concurrency > 1 env[RACK_MULTIPROCESS] = true env[RACK_RUN_ONCE] = false if env[HTTPS] == YES || env[HTTPS] == ON || env[HTTPS] == ONE env[RACK_URL_SCHEME] = HTTPS_DOWNCASE else env[RACK_URL_SCHEME] = HTTP end env[RACK_HIJACK_P] = true env[RACK_HIJACK] = lambda do env[RACK_HIJACK_IO] ||= begin connection.stop_simulating_eof! connection end end env[HTTP_VERSION] = HTTP_1_1 # Rails somehow modifies env['REQUEST_METHOD'], so we perform the comparison # before the Rack application object is called. is_head_request = env[REQUEST_METHOD] == HEAD begin status, headers, body = @app.call(env) rescue => e if !should_swallow_app_error?(e, socket_wrapper) # It's a good idea to catch application exceptions here because # otherwise maliciously crafted responses can crash the app, # forcing it to be respawned, and thereby effectively DoSing it. print_exception("Rack application object", e) log_exception_to_union_station(env, e) end return false end # Application requested a full socket hijack. if env[RACK_HIJACK_IO] # Since the app hijacked the socket, we don't know what state we're # in and we don't know whether we can recover from it, so we don't # catch any exception here. # # The Rack specification doesn't specify whether the body should # be closed when the socket is hijacked. As of February 2 2015, # Puma and Thin close the body, while Unicorn does not. # However, Rack::Lock and possibly many other middlewares count # on the body being closed, as described here: # https://github.com/ngauthier/tubesock/issues/10#issuecomment-72539461 # So we have chosen to close the body. body.close if body && body.respond_to?(:close) return true end # Application requested a partial socket hijack. if hijack_callback = headers[RACK_HIJACK] # We don't catch exceptions here. EPIPE is already handled # by ThreadHandler's #accept_and_process_next_request. # On any other exception, we don't know what state we're # in and we don't know whether we can recover from it. begin headers_output = generate_headers_array(status, headers) headers_output << CONNECTION_CLOSE_CRLF2 connection.writev(headers_output) connection.flush hijacked_socket = env[RACK_HIJACK].call hijack_callback.call(hijacked_socket) return true ensure body.close if body && body.respond_to?(:close) end end begin process_body(env, connection, socket_wrapper, status.to_i, is_head_request, headers, body) rescue => e if !should_swallow_app_error?(e, socket_wrapper) print_exception("Rack response body object", e) log_exception_to_union_station(env, e) end ensure close_body(body, env, socket_wrapper) end false ensure rewindable_input.close end end private def process_body(env, connection, socket_wrapper, status, is_head_request, headers, body) if @ush_reporter ush_log_id = @ush_reporter.log_writing_rack_body_begin end # Fix up incompliant body objects. Ensure that the body object # can respond to #each. output_body = should_output_body?(status, is_head_request) if body.is_a?(String) body = [body] elsif body.nil? body = [] elsif output_body && body.is_a?(Array) # The body may be an ActionController::StringCoercion::UglyBody # object instead of a real Array, even when #is_a? claims so. # Call #to_a just to be sure that connection.writev() can # accept the body object. body = body.to_a end # Generate preliminary headers and determine whether we need to output a body. headers_output = generate_headers_array(status, headers) # Determine how big the body is, determine whether we should try to keep-alive # the connection, and fix up the headers according to the situation. # # It may not be possible to determine the body's size (e.g. because it's streamed # through #each). In that case we'll want to output the body with a chunked transfer # encoding. But it matters whether the app has already chunked the body or not. # # Note that if the Rack response header contains "Transfer-Encoding: chunked", # then we assume that the Rack body is already in chunked form. This is the way # Rails streaming and Rack::Chunked::Body behave. # The only gem that doesn't behave this way is JRuby-Rack (see # https://blog.engineyard.com/2011/taking-stock-jruby-web-servers), but I'm not # aware of anybody using JRuby-Rack these days. # # We only try to keep-alive the connection if we are able to determine ahead of # time that the body we write out is guaranteed to match what the headers say. # Otherwise we disable keep-alive to prevent the app from being able to mess # up the keep-alive connection. if header = lookup_header(headers, CONTENT_LENGTH_HEADERS) # Easiest case: app has a Content-Length header. The headers # need no fixing. message_length_type = :content_length content_length = header.to_i if lookup_header(headers, TRANSFER_ENCODING_HEADERS) # Disallowed by the HTTP spec raise "Response object may not contain both Content-Length and Transfer-Encoding" end if output_body if !body.is_a?(Array) || headers.has_key?(X_SENDFILE_HEADER) || headers.has_key?(X_ACCEL_REDIRECT_HEADER) # If X-Sendfile or X-Accel-Redirect is set, don't check the # body size. Passenger's Core Controller will ignore the # body anyway. See # ServerKit::HttpHeaderParser::processParseResult(const HttpParseResponse &) @can_keepalive = false else body_size = 0 body.each { |part| body_size += bytesize(part) } if body_size != content_length raise "Response body size doesn't match Content-Length header: #{body_size} vs #{content_length}" end end end elsif lookup_header(headers, TRANSFER_ENCODING_HEADERS) # App has a Transfer-Encoding header. We assume that the app # has already chunked the body. The headers need no fixing. message_length_type = :chunked_by_app if output_body # We have no way to determine whether the body was correct (save for # parsing the chunking headers), so we don't keep-alive the connection # just to be safe. @can_keepalive = false end if lookup_header(headers, CONTENT_LENGTH_HEADERS) # Disallowed by the HTTP spec raise "Response object may not contain both Content-Length and Transfer-Encoding" end elsif status_code_allows_body?(status) # This is a response for which a body is allowed, although the request # may be one which does not expect a body (HEAD requests). # # The app has set neither the Content-Length nor the Transfer-Encoding # header. This means we'll have to add one of those headers. We know exactly how # big our body will be, so we can keep-alive the connection. if body.is_a?(Array) message_length_type = :content_length content_length = 0 body.each { |part| content_length += bytesize(part.to_s) } headers_output << CONTENT_LENGTH_HEADER_AND_SEPARATOR headers_output << content_length.to_s headers_output << CRLF else message_length_type = :needs_chunking headers_output << TRANSFER_ENCODING_HEADER_AND_VALUE_CRLF end end # Finalize headers data. if @can_keepalive headers_output << CRLF else headers_output << CONNECTION_CLOSE_CRLF2 end # If this is a request without body, write out headers without body. if !output_body connection.writev(headers_output) else # Otherwise, write out headers and body. case message_length_type when :content_length if body.is_a?(Array) connection.writev2(headers_output, body) else connection.writev(headers_output) body.each do |part| connection.write(part.to_s) end end when :chunked_by_app connection.writev(headers_output) body.each do |part| connection.write(part.to_s) end when :needs_chunking connection.writev(headers_output) body.each do |part| size = bytesize(part.to_s) if size != 0 connection.writev(chunk_data(part.to_s, size)) end end connection.write(TERMINATION_CHUNK) end end signal_keep_alive_allowed! ensure if @ush_reporter && ush_log_id @ush_reporter.log_writing_rack_body_end(ush_log_id) end end def close_body(body, env, socket_wrapper) if @ush_reporter ush_log_id = @ush_reporter.log_closing_rack_body_begin end begin body.close if body && body.respond_to?(:close) rescue => e if !should_swallow_app_error?(e, socket_wrapper) print_exception("Rack response body object's #close method", e) log_exception_to_union_station(env, e) end ensure if @ush_reporter && ush_log_id @ush_reporter.log_closing_rack_body_end(ush_log_id) end end end def generate_headers_array(status, headers) status_str = status.to_s result = ["HTTP/1.1 #{status_str} Whatever\r\n"] headers.each do |key, values| if values.is_a?(String) values = values.split(NEWLINE) elsif key == RACK_HIJACK # We do not check for this key name in every loop # iteration as an optimization. next else values = values.to_s.split(NEWLINE) end values.each do |value| result << key result << NAME_VALUE_SEPARATOR result << value result << CRLF end end return result end def lookup_header(haystack, needles) needles.each do |needle| if result = haystack[needle] return result end end nil end def status_code_allows_body?(status) status < 100 || (status >= 200 && status != 204 && status != 304) end def should_output_body?(status, is_head_request) status_code_allows_body?(status) && !is_head_request end def chunk_data(data, size) [size.to_s(16), CRLF, data, CRLF] end # Called when body is written out successfully. Indicates that we should # keep-alive the connection if we can. def signal_keep_alive_allowed! @keepalive_performed = @can_keepalive end if "".respond_to?(:bytesize) def bytesize(str) str.bytesize end else def bytesize(str) str.size end end end end # module Rack end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/apache.rb000644 000765 000024 00000073600 12233035540 030410 0ustar00honglistaff000000 000000 # encoding: binary # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/compiler' PhusionPassenger.require_passenger_lib 'platform_info/operating_system' PhusionPassenger.require_passenger_lib 'platform_info/linux' PhusionPassenger.require_passenger_lib 'utils/shellwords' module PhusionPassenger # Wow, I can't believe in how many ways one can build Apache in OS # X! We have to resort to all sorts of tricks to make Passenger build # out of the box on OS X. :-( # # In the name of usability and the "end user is the king" line of thought, # I shall suffer the horrible faith of writing tons of autodetection code! # Users can change the detection behavior by setting the environment variable # APXS2 to the correct 'apxs' (or 'apxs2') binary, as provided by # Apache. module PlatformInfo ################ Programs ################ # The absolute path to the 'apxs' or 'apxs2' executable, or nil if not found. def self.apxs2 if env_defined?("APXS2") return ENV["APXS2"] end ['apxs2', 'apxs'].each do |name| command = find_command(name) if !command.nil? return command end end return nil end memoize :apxs2 # The absolute path to the 'apachectl' or 'apache2ctl' binary, or nil if # not found. def self.apache2ctl(options = {}) return find_apache2_executable('apache2ctl', 'apachectl2', 'apachectl', options) end memoize :apache2ctl # The absolute path to the Apache binary (that is, 'httpd', 'httpd2', 'apache' # or 'apache2'), or nil if not found. def self.httpd(options = {}) apxs2 = options[:apxs2] || self.apxs2 if env_defined?('HTTPD') return ENV['HTTPD'] elsif apxs2.nil? ["apache2", "httpd2", "apache", "httpd"].each do |name| command = find_command(name) if !command.nil? return command end end return nil else return find_apache2_executable(`#{apxs2} -q TARGET`.strip, options) end end memoize :httpd # The Apache version, or nil if Apache is not found. def self.httpd_version(options = nil) if options httpd = options[:httpd] || self.httpd(options) else httpd = self.httpd end if httpd `#{httpd} -v` =~ %r{Apache/([\d\.]+)} return $1 else return nil end end memoize :httpd_version # Run `apache2ctl -V` and return its output, or nil on failure. # # We used to run `httpd -V`, but on systems like Ubuntu it depends on various # environment variables or directories, wich apache2ctl loads or initializes. # # Because on some systems (mostly Ubuntu) `apache2ctl -V` attempts to parse # and load the config file, this command can fail because of configuration file # errors. def self.apache2ctl_V(options = nil) if options apache2ctl = options[:apache2ctl] || self.apache2ctl(options) else apache2ctl = self.apache2ctl end if os_name_simple == "linux" && linux_distro_tags.include?(:gentoo) && apache2ctl == "/usr/sbin/apache2ctl" # On Gentoo, `apache2ctl -V` doesn't forward the command to `apache2 -V`, # but instead prints the OpenRC init system's version. # https://github.com/phusion/passenger/issues/1510 if options httpd = options[:httpd] || self.httpd(options) else httpd = self.httpd end version_command = httpd else version_command = apache2ctl end if version_command create_temp_file("apache2ctl_V") do |filename, f| e_filename = Shellwords.escape(filename) output = `#{version_command} -V 2>#{e_filename}` if $? && $?.exitstatus == 0 stderr_text = File.open(filename, "rb") do |f2| f2.read end # This stderr message shows up on Ubuntu. We ignore it. stderr_text.sub!(/.*Could not reliably determine the server's fully qualified domain name.*\r?\n?/, "") # But we print the rest of stderr. STDERR.write(stderr_text) STDERR.flush output else nil end end else nil end end memoize :apache2ctl_V # The Apache executable's architectural bits. Returns 32 or 64, # or nil if unable to detect. def self.httpd_architecture_bits(options = nil) if options info = apache2ctl_V(options) else info = apache2ctl_V end if info info =~ %r{Architecture:(.*)} text = $1 if text =~ /32/ 32 elsif text =~ /64/ 64 else nil end else nil end end memoize :httpd_architecture_bits # The default Apache root directory, as specified by its compilation parameters. # This may be different from the value of the ServerRoot directive. def self.httpd_default_root(options = nil) if options info = apache2ctl_V(options) else info = apache2ctl_V end if info info =~ / -D HTTPD_ROOT="(.+)"$/ return $1 else return nil end end memoize :httpd_default_root # The default Apache configuration file, or nil if Apache is not found. def self.httpd_default_config_file(options = nil) if options info = apache2ctl_V(options) else info = apache2ctl_V end if info info =~ /-D SERVER_CONFIG_FILE="(.+)"$/ filename = $1 if filename =~ /\A\// return filename else # Not an absolute path. Infer from default root. if root = httpd_default_root(options) return "#{root}/#{filename}" else return nil end end else return nil end end memoize :httpd_default_config_file # Given an Apache config file, returns the a hash with the following elements: # # * `:files` - An array containing `config_file`, as well as all config files # included from that config file, including recursively included # ones. Only filenames that actually exist are put here. # * `:unreadable_files` - All config files that this function was unable # to read. def self.httpd_included_config_files(config_file, options = nil) state = { :files => { config_file => true }, :unreadable_files => [], :root => httpd_default_root(options) } scan_for_included_apache2_config_files(config_file, state, options) return { :files => state[:files].keys, :unreadable_files => state[:unreadable_files] } end # The default Apache error log's filename, as it is compiled into the Apache # main executable. This may not be the actual error log that is used. The actual # error log depends on the configuration file. # # Returns nil if Apache is not detected, or if the default error log filename # cannot be detected. def self.httpd_default_error_log(options = nil) if info = apache2ctl_V(options) info =~ /-D DEFAULT_ERRORLOG="(.+)"$/ filename = $1 if filename =~ /\A\// return filename else # Not an absolute path. Infer from default root. if root = httpd_default_root(options) return "#{root}/#{filename}" else return nil end end else return nil end end memoize :httpd_default_error_log def self.httpd_actual_error_log(options = nil) if config_file = httpd_default_config_file(options) begin contents = File.open(config_file, "rb") { |f| f.read } rescue Errno::ENOENT log "#{config_file} does not exist" return nil rescue Errno::EACCES log "Unable to open #{config_file} for reading" return nil end # We don't want to match comments contents.gsub!(/^[ \t]*#.*/, '') if contents =~ /^[ \t]*ErrorLog[ \t]+(.+)[ \t]*$/i filename = unescape_apache_config_value($1, options) if filename && filename !~ /\A\// # Not an absolute path. Infer from root. if root = httpd_default_root(options) return "#{root}/#{filename}" else return nil end else return filename end elsif contents =~ /ErrorLog/i # The user apparently has ErrorLog set somewhere but # we can't parse it. The default error log location, # as reported by `apache2ctl -V`, may be wrong (it is on OS X). # So to be safe, let's assume that we don't know. log "Unable to parse ErrorLog directive in Apache configuration file" return nil else log "No ErrorLog directive in Apache configuration file" return httpd_default_error_log(options) end else return nil end end memoize :httpd_actual_error_log # The location of the Apache envvars file, which exists on some systems such as Ubuntu. # Returns nil if Apache is not found or if the envvars file is not found. def self.httpd_envvars_file(options = nil) if options httpd = options[:httpd] || self.httpd(options) else httpd = self.httpd end httpd_dir = File.dirname(httpd) if httpd_dir == "/usr/bin" || httpd_dir == "/usr/sbin" if File.exist?("/etc/apache2/envvars") return "/etc/apache2/envvars" elsif File.exist?("/etc/httpd/envvars") return "/etc/httpd/envvars" end end conf_dir = File.expand_path(File.dirname(httpd) + "/../conf") if File.exist?("#{conf_dir}/envvars") return "#{conf_dir}/envvars" end return nil end def self.httpd_infer_envvar(varname, options = nil) if envfile = httpd_envvars_file(options) result = `. '#{envfile}' && echo $#{varname}`.strip if $? && $?.exitstatus == 0 return result else return nil end else return nil end end # Returns the path to the Apache `mods-available` subdirectory, # or nil if it's not supported by this Apache. def self.httpd_mods_available_directory(options = nil) config_file = httpd_default_config_file(options) return nil if !config_file # mods-available is supposed to be a Debian extension that only works # on the APT-installed Apache, so only return non-nil if we're # working against the APT-installed Apache. config_dir = File.dirname(config_file) if config_dir == "/etc/httpd" || config_dir == "/etc/apache2" if File.exist?("#{config_dir}/mods-available") && File.exist?("#{config_dir}/mods-enabled") return "#{config_dir}/mods-available" else return nil end else return nil end end memoize :httpd_mods_available_directory # Returns the path to the Apache `mods-enabled` subdirectory, # or nil if it's not supported by this Apache. def self.httpd_mods_enabled_directory(options = nil) config_file = httpd_default_config_file(options) return nil if !config_file # mods-enabled is supposed to be a Debian extension that only works # on the APT-installed Apache, so only return non-nil if we're # working against the APT-installed Apache. config_dir = File.dirname(config_file) if config_dir == "/etc/httpd" || config_dir == "/etc/apache2" if File.exist?("#{config_dir}/mods-available") && File.exist?("#{config_dir}/mods-enabled") return "#{config_dir}/mods-enabled" else return nil end else return nil end end memoize :httpd_mods_enabled_directory # The absolute path to the 'a2enmod' executable. def self.a2enmod(options = {}) apxs2 = options[:apxs2] || self.apxs2 dir = File.dirname(apxs2) # a2enmod is supposed to be a Debian extension that only works # on the APT-installed Apache, so only return non-nil if we're # working against the APT-installed Apache. if dir == "/usr/bin" || dir == "/usr/sbin" if env_defined?('A2ENMOD') return ENV['A2ENMOD'] else return find_apache2_executable("a2enmod", options) end else return nil end end memoize :a2enmod # The absolute path to the 'a2enmod' executable. def self.a2dismod(options = {}) apxs2 = options[:apxs2] || self.apxs2 dir = File.dirname(apxs2) # a2dismod is supposed to be a Debian extension that only works # on the APT-installed Apache, so only return non-nil if we're # working against the APT-installed Apache. if dir == "/usr/bin" || dir == "/usr/sbin" if env_defined?('A2DISMOD') return ENV['A2DISMOD'] else return find_apache2_executable("a2dismod", options) end end end memoize :a2dismod # The absolute path to the 'apr-config' or 'apr-1-config' executable, # or nil if not found. def self.apr_config if env_defined?('APR_CONFIG') return ENV['APR_CONFIG'] elsif apxs2.nil? return nil else filename = `#{apxs2} -q APR_CONFIG 2>/dev/null`.strip if filename.empty? apr_bindir = `#{apxs2} -q APR_BINDIR 2>/dev/null`.strip if apr_bindir.empty? return nil else return select_executable(apr_bindir, "apr-1-config", "apr-config") end elsif File.exist?(filename) return filename else return nil end end end memoize :apr_config # The absolute path to the 'apu-config' or 'apu-1-config' executable, or nil # if not found. def self.apu_config if env_defined?('APU_CONFIG') return ENV['APU_CONFIG'] elsif apxs2.nil? return nil else filename = `#{apxs2} -q APU_CONFIG 2>/dev/null`.strip if filename.empty? apu_bindir = `#{apxs2} -q APU_BINDIR 2>/dev/null`.strip if apu_bindir.empty? return nil else return select_executable(apu_bindir, "apu-1-config", "apu-config") end elsif File.exist?(filename) return filename else return nil end end end memoize :apu_config # Find an executable in the Apache 'bin' and 'sbin' directories. # Returns nil if not found. def self.find_apache2_executable(*possible_names) if possible_names.last.is_a?(Hash) options = possible_names.pop options = nil if options.empty? end if options dirs = options[:dirs] || [apache2_bindir(options), apache2_sbindir(options)] else dirs = [apache2_bindir, apache2_sbindir] end dirs.each do |bindir| if bindir.nil? next end possible_names.each do |name| filename = "#{bindir}/#{name}" if !File.exist?(filename) log "Looking for #{filename}: not found" elsif !File.file?(filename) log "Looking for #{filename}: found, but is not a file" elsif !File.executable?(filename) log "Looking for #{filename}: found, but is not executable" else log "Looking for #{filename}: found" return filename end end end return nil end ################ Directories ################ # The absolute path to the Apache 2 'bin' directory, or nil if unknown. def self.apache2_bindir(options = {}) apxs2 = options[:apxs2] || self.apxs2 if apxs2.nil? return nil else return `#{apxs2} -q BINDIR 2>/dev/null`.strip end end memoize :apache2_bindir # The absolute path to the Apache 2 'sbin' directory, or nil if unknown. def self.apache2_sbindir(options = {}) apxs2 = options[:apxs2] || self.apxs2 if apxs2.nil? return nil else return `#{apxs2} -q SBINDIR`.strip end end memoize :apache2_sbindir ################ Compiler and linker flags ################ def self.apache2_module_cflags(with_apr_flags = true) return apache2_module_c_or_cxxflags(:c, with_apr_flags) end memoize :apache2_module_cflags, true def self.apache2_module_cxxflags(with_apr_flags = true) return apache2_module_c_or_cxxflags(:cxx, with_apr_flags) end memoize :apache2_module_cxxflags, true # The C compiler flags that are necessary to compile an Apache module. # Also includes APR and APU compiler flags if with_apr_flags is true. def self.apache2_module_c_or_cxxflags(language, with_apr_flags = true) flags = [""] if (language == :c && cc_is_sun_studio?) || (language == :cxx && cxx_is_sun_studio?) flags << "-KPIC" else flags << "-fPIC" end if with_apr_flags if language == :c flags << apr_cflags flags << apu_cflags else flags << apr_cxxflags flags << apu_cxxflags end end if !apxs2.nil? apxs2_flags = `#{apxs2} -q CFLAGS`.strip << " -I" << `#{apxs2} -q INCLUDEDIR`.strip apxs2_flags.gsub!(/-O\d? /, '') if os_name_simple == "solaris" if (language == :c && !cc_is_sun_studio?) || (language == :cxx && !cxx_is_sun_studio?) # Remove flags not supported by GCC # The big problem is Coolstack apxs includes a bunch of solaris -x directives. options = apxs2_flags.split options.reject! { |f| f =~ /^\-x/ } options.reject! { |f| f =~ /^\-Xa/ } options.reject! { |f| f =~ /^\-fast/ } options.reject! { |f| f =~ /^\-mt/ } options.reject! { |f| f =~ /^\-W2/ } apxs2_flags = options.join(' ') apxs2_flags.gsub!(/ ?-Qoption cg ?/, " ") end end if os_name_simple == "linux" && linux_distro_tags.include?(:redhat) && apxs2 == "/usr/sbin/apxs" && httpd_architecture_bits == 64 # The Apache package in CentOS 5 x86_64 is broken. # 'apxs -q CFLAGS' contains directives for compiling # the module as 32-bit, even though httpd itself # is 64-bit. Fix this. apxs2_flags.gsub!('-m32 -march=i386 -mtune=generic', '') end # Some Apache installations include '-pie' in CFLAGS, which # won't work on shared libraries. # https://github.com/phusion/passenger/issues/1756 apxs2_flags.gsub!('-pie', '') apxs2_flags.strip! flags << apxs2_flags end if !httpd.nil? && os_name_simple == "macosx" # The default Apache install on OS X is a universal binary. # Figure out which architectures it's compiled for and do the same # thing for mod_passenger. We use the 'file' utility to do this. # # Running 'file' on the Apache executable usually outputs something # like this: # # /usr/sbin/httpd: Mach-O universal binary with 4 architectures # /usr/sbin/httpd (for architecture ppc7400): Mach-O executable ppc # /usr/sbin/httpd (for architecture ppc64): Mach-O 64-bit executable ppc64 # /usr/sbin/httpd (for architecture i386): Mach-O executable i386 # /usr/sbin/httpd (for architecture x86_64): Mach-O 64-bit executable x86_64 # # But on some machines, it may output just: # # /usr/sbin/httpd: Mach-O fat file with 4 architectures # # (http://code.google.com/p/phusion-passenger/issues/detail?id=236) output = `file "#{httpd}"`.strip if output =~ /Mach-O fat file/ && output !~ /for architecture/ architectures = ["i386", "ppc", "x86_64", "ppc64"] else architectures = [] output.split("\n").grep(/for architecture/).each do |line| line =~ /for architecture (.*?)\)/ architectures << $1 end end # The compiler may not support all architectures in the binary. # XCode 4 seems to have removed support for the PPC architecture # even though there are still plenty of Apache binaries around # containing PPC components. architectures.reject! do |arch| !compiler_supports_architecture?(arch) end architectures.map! do |arch| "-arch #{arch}" end flags << architectures.compact.join(' ') end return flags.compact.join(' ').strip end # Linker flags that are necessary for linking an Apache module. # Already includes APR and APU linker flags. def self.apache2_module_cxx_ldflags flags = "" if !apxs2.nil? flags << `#{apxs2} -q LDFLAGS`.strip end # We must include the cxxflags in the linker flags. On some multilib # Solaris systems, `apxs -q CFLAGS` outputs a flag that tells the compiler # which architecture to compile against, while `apxs -q LDFLAGS` doesn't. flags << " #{apache2_module_cxxflags} #{apr_cxx_ldflags} #{apu_cxx_ldflags}" if cxx_is_sun_studio? flags.gsub!("-fPIC", "-KPIC") flags << " -KPIC" if !flags.include?("-KPIC") else flags << " -fPIC" if !flags.include?("-fPIC") end flags.strip! flags end memoize :apache2_module_cxx_ldflags # The C compiler flags that are necessary for programs that use APR. def self.apr_cflags determine_apr_c_info[0] end # The C++ compiler flags that are necessary for programs that use APR. def self.apr_cxxflags determine_apr_c_info[0] end # The linker flags that are necessary for linking programs that use APR. def self.apr_c_ldflags determine_apr_c_info[1] end # The linker flags that are necessary for linking C++ programs that use APR. def self.apr_cxx_ldflags determine_apr_cxx_info[1] end # The C compiler flags that are necessary for programs that use APR-Util. def self.apu_cflags determine_apu_c_info[0] end # The C++ compiler flags that are necessary for programs that use APR-Util. def self.apu_cxxflags determine_apu_cxx_info[0] end # The linker flags that are necessary for linking C programs that use APR-Util. def self.apu_c_ldflags determine_apu_c_info[1] end # The linker flags that are necessary for linking C++ programs that use APR-Util. def self.apu_cxx_ldflags determine_apu_cxx_info[1] end ################ Miscellaneous information ################ # Returns whether it is necessary to use information outputted by # 'apr-config' and 'apu-config' in order to compile an Apache module. # When Apache is installed with --with-included-apr, the APR/APU # headers are placed into the same directory as the Apache headers, # and so 'apr-config' and 'apu-config' won't be necessary in that case. def self.apr_config_needed_for_building_apache_modules? return !try_compile("whether APR is needed for building Apache modules", :c, "#include \n", apache2_module_cflags(false)) end memoize :apr_config_needed_for_building_apache_modules?, true private def self.determine_apr_info(language) if apr_config.nil? [nil, nil] else flags = `#{apr_config} --cppflags --includes`.strip libs = `#{apr_config} --link-ld`.strip flags.gsub!(/-O\d? /, '') if os_name_simple == "solaris" if (language == :c && !cc_is_sun_studio?) || (language == :cxx && !cxx_is_sun_studio?) # Remove flags not supported by non-Sun Studio compilers flags = flags.split(/ +/).reject do |f| f =~ /^\-mt/ end flags = flags.join(' ') end elsif os_name_simple == "aix" libs << " -Wl,-G -Wl,-brtl" end [flags, libs] end end private_class_method :determine_apr_info def self.determine_apr_c_info determine_apr_info(:c) end private_class_method :determine_apr_c_info memoize :determine_apr_c_info, true def self.determine_apr_cxx_info determine_apr_info(:cxx) end private_class_method :determine_apr_cxx_info memoize :determine_apr_cxx_info, true def self.determine_apu_info(language) if apu_config.nil? [nil, nil] else flags = `#{apu_config} --includes`.strip libs = `#{apu_config} --link-ld`.strip flags.gsub!(/-O\d? /, '') if os_name_simple == "solaris" if (language == :c && !cc_is_sun_studio?) || (language == :cxx && !cxx_is_sun_studio?) # Remove flags not supported by non-Sun Studio compilers flags = flags.split(/ +/).reject do |f| f =~ /^\-mt/ end flags = flags.join(' ') end end [flags, libs] end end private_class_method :determine_apu_info def self.determine_apu_c_info determine_apu_info(:c) end private_class_method :determine_apu_c_info memoize :determine_apu_c_info, true def self.determine_apu_cxx_info determine_apu_info(:cxx) end private_class_method :determine_apu_cxx_info memoize :determine_apu_cxx_info, true def self.scan_for_included_apache2_config_files(config_file, state, options = nil) begin config = File.open(config_file, "rb") do |f| f.read end rescue Errno::EACCES state[:unreadable_files] << config_file return end found_filenames = [] config.scan(/^[ \t]*(Include(Optional)?|ServerRoot)[ \t]+(.+?)[ \t]*$/i) do |match| if match[0].downcase == "serverroot" new_root = unescape_apache_config_value(match[2], options) state[:root] = new_root if new_root else filename = unescape_apache_config_value(match[2], options) next if filename.nil? || filename.empty? if filename !~ /\A\// # Not an absolute path. Infer from root. filename = "#{state[:root]}/#{filename}" end expand_apache2_glob(filename).each do |filename2| if !state[:files].has_key?(filename2) state[:files][filename2] = true scan_for_included_apache2_config_files(filename2, state, options) end end end end end private_class_method :scan_for_included_apache2_config_files def self.expand_apache2_glob(glob) if File.directory?(glob) glob = glob.sub(/\/*$/, '') result = Dir["#{glob}/**/*"] else result = [] Dir[glob].each do |filename| if File.directory?(filename) result.concat(Dir["#{filename}/**/*"]) else result << filename end end end result.reject! do |filename| File.directory?(filename) end return result end private_class_method :expand_apache2_glob def self.unescape_apache_config_value(value, options = nil) if value =~ /^"(.*)"$/ value = unescape_c_string($1) end if value.include?("${") log "Attempting to substitute environment variables in Apache config value #{value.inspect}..." end # The Apache config file supports environment variable # substitution. Ubuntu uses this extensively. value.gsub!(/\$\{(.+?)\}/) do |varname| if substitution = httpd_infer_envvar($1, options) log "Substituted \"#{varname}\" -> \"#{substitution}\"" substitution else log "Cannot substitute \"#{varname}\"" varname end end if value.include?("${") # We couldn't substitute everything. return nil else return value end end private_class_method :unescape_apache_config_value def self.unescape_c_string(s) state = 0 res = '' backslash = "\\" s.each_char do |c| case state when 0 case c when backslash then state = 1 else res << c end when 1 case c when 'n' then res << "\n"; state = 0 when 't' then res << "\t"; state = 0 when backslash then res << backslash; state = 0 else res << backslash; res << c; state = 0 end end end return res end private_class_method :unescape_c_string end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/apache_detector.rb000644 000765 000024 00000026535 12233035540 032306 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2013-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/ruby' PhusionPassenger.require_passenger_lib 'platform_info/apache' PhusionPassenger.require_passenger_lib 'utils/ansi_colors' require 'pathname' module PhusionPassenger module PlatformInfo # Detects all possible Apache installations on the system, and presents the # autodetection information to the user in a friendly way. It turns out too # many people have multiple Apache installations on their system, but they # don't know about that, or they don't know how to compile against the # correct Apache installation. This tool helps them. # # If you use this class to log things to the terminal, then be sure to set # the terminal color to Utils::AnsiColors::DEFAULT_TERMINAL_COLOR. class ApacheDetector class Result # These are required and are never nil. attr_accessor :apxs2, :httpd, :ctl, :version # These are optional and may be nil. attr_accessor :a2enmod, :a2dismod # Ttis may be nil. It depends on whether 'apache2ctl -V' succeeds. attr_accessor :config_file # This may be nil. It depends on how well we can infer information from the config file. attr_accessor :error_log attr_writer :config_file_broken def initialize(detector) @detector = detector end def config_file_broken? @config_file_broken end def report log " * Found Apache #{version}!" log " Information:" log " apxs2 : #{apxs2}" log " Main executable: #{httpd}" log " Control command: #{ctl}" log " Config file : #{config_file || 'unknown'}" log " Error log file : #{error_log || 'unknown'}" if config_file_broken? log "" log " WARNING:" log " The configuration file seems to be broken! Please double-check it by running:" log " #{ctl} -t" end log "" log " To install #{PROGRAM_NAME} against this specific Apache version:" log " #{PlatformInfo.ruby_command} #{PhusionPassenger.bin_dir}/passenger-install-apache2-module --apxs2-path='#{apxs2}'" log "" log " To start, stop or restart this specific Apache version:" log " #{ctl} start" log " #{ctl} stop" log " #{ctl} restart" log "" if error_log log " To troubleshoot, please read the logs in this file:" log " #{error_log}" log "" end end private def log(message) @detector.send(:log, message) end end attr_reader :results def initialize(output) @output = output @results = [] @failures = 0 PlatformInfo.verbose = true PlatformInfo.log_implementation = lambda do |message| if message =~ /: found$/ log(" --> #{message}") else log(" --> #{message}") end end end def finish PlatformInfo.verbose = false PlatformInfo.log_implementation = nil end def detect_all log "Looking for possible Apache installations..." apxses = PlatformInfo.find_all_commands("apxs2") + PlatformInfo.find_all_commands("apxs") if !apxses.include?(PlatformInfo.apxs2) PlatformInfo.send(:log, "Looking for #{PlatformInfo.apxs2}: found") apxses << PlatformInfo.apxs2 end apxses = remove_symlink_duplications(apxses) log "" apxses.each do |apxs2| detect_one(apxs2) end end def detect_one(apxs2) log "Analyzing #{apxs2}..." add_result do |result| result.apxs2 = apxs2 log "Detecting main Apache executable..." result.httpd = PlatformInfo.httpd(:apxs2 => apxs2) if result.httpd log "Detecting version..." if result.version = PlatformInfo.httpd_version(:httpd => result.httpd) log " --> #{result.version}" else log " --> Cannot detect version!" result.httpd = nil end end if result.httpd log "Detecting control command..." result.ctl = PlatformInfo.apache2ctl(:apxs2 => apxs2) result.httpd = nil if !result.ctl end if result.httpd log "Detecting configuration file location..." result.config_file = PlatformInfo.httpd_default_config_file(:httpd => result.httpd) if result.config_file log " --> #{result.config_file}" else log " --> Cannot detect default config file location!" end end if result.httpd log "Detecting error log file..." result.error_log = PlatformInfo.httpd_actual_error_log(:httpd => result.httpd) if result.error_log log " --> #{result.error_log}" else log " --> Cannot detect error log file!" end end if result.httpd log "Detecting a2enmod and a2dismod..." result.a2enmod = PlatformInfo.a2enmod(:apxs2 => apxs2) result.a2dismod = PlatformInfo.a2dismod(:apxs2 => apxs2) end if result.httpd result.config_file_broken = PlatformInfo.apache2ctl_V(:apxs2 => apxs2).nil? end if result.httpd log "Found a usable Apache installation using #{apxs2}." true else log "Cannot find a usable Apache installation using #{apxs2}." false end end log "" end def report if @failures > 0 && Process.uid != 0 user = `whoami`.strip sudo_s_e = PhusionPassenger::PlatformInfo.ruby_sudo_shell_command("-E") ruby = PhusionPassenger::PlatformInfo.ruby_command log "" log "----------------------------" log "" log "Permission problems" log "" log "Sorry, this program doesn't have enough permissions to autodetect all your" log "Apache installations, because it's running as the #{`whoami`.strip} user." log "Please re-run this program with root privileges:" log "" log " export ORIG_PATH=\"$PATH\"" log " #{sudo_s_e}" log " export PATH=\"$ORIG_PATH\"" log " #{ruby} #{PhusionPassenger.bin_dir}/passenger-config --detect-apache2" return end log "Final autodetection results" @results.each do |result| result.report end if @results.empty? PhusionPassenger.require_passenger_lib 'platform_info/depcheck' PlatformInfo::Depcheck.load("depcheck_specs/apache2") apache2 = PlatformInfo::Depcheck.find("apache2") apache2_install_instructions = apache2.install_instructions.split("\n").join("\n ") # apxs2 is part of the development headers. apache2_dev = PlatformInfo::Depcheck.find("apache2-dev") apache2_dev_install_instructions = apache2_dev.install_instructions.split("\n").join("\n ") log "Sorry, this program cannot find an Apache installation." log "" log "Please install Apache and its development headers." log "" log " * To install Apache:" log " #{apache2_install_instructions}" log "" log " * To install Apache development headers:" log " #{apache2_dev_install_instructions}" log "" log "If you are sure that you have Apache installed, please read the documentation:" log "https://www.phusionpassenger.com/library/install/apache/customizing_compilation_process.html#forcing-location-of-command-line-tools-and-dependencies" elsif @results.size > 1 log "WARNING: You have multiple Apache installations on your system!" log "You are strongly recommended to read this section of the documentation:" log "https://www.phusionpassenger.com/install/apache/multiple_apache_installs.html" end end def result_for(apxs2) # All the results use realpaths, so the input must too. apxs2 = try_realpath(apxs2) return @results.find { |r| r.apxs2 == apxs2 } end private def log(message) if @output.tty? @output.puts(Utils::AnsiColors.ansi_colorize(message)) else @output.puts(Utils::AnsiColors.strip_color_tags(message)) end end # On Ubuntu, /usr/bin/apxs2 is a symlink to /usr/bin/apxs. # On recent Arch Linux releases, /bin, /sbin etc are symlinks to # /usr/bin and /usr/sbin. # We're only supposed to detect one Apache in that case so we need to # resolve symlinks. def remove_symlink_duplications(filenames) old_size = filenames.size filenames = filenames.map do |filename| try_realpath(filename) end filenames.uniq! if old_size != filenames.size log "#{old_size - filenames.size} symlink duplicate(s) detected; ignoring them." end return filenames end def add_result result = Result.new(self) if yield(result) @results << result else @failures += 1 end end def try_realpath(path) if path begin Pathname.new(path).realpath.to_s rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR path end else nil end end end end end # module Phusion Passenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/binary_compatibility.rb000644 000765 000024 00000014341 12233035540 033401 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'rbconfig' PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/ruby' PhusionPassenger.require_passenger_lib 'platform_info/operating_system' module PhusionPassenger module PlatformInfo # Returns a string that describes the current Ruby # interpreter's extension binary compatibility. A Ruby extension # compiled for a certain Ruby interpreter can also be loaded on # a different Ruby interpreter with the same binary compatibility # identifier. # # The result depends on the following factors: # - Ruby engine name. # - Ruby extension version. # This is not the same as the Ruby language version, which # identifies language-level compatibility. This is rather about # binary compatibility of extensions. # MRI seems to break source compatibility between tiny releases, # though patchlevel releases tend to be source and binary # compatible. # - Ruby extension architecture. # This is not necessarily the same as the operating system # runtime architecture or the CPU architecture. # For example, in case of JRuby, the extension architecture is # just "java" because all extensions target the Java platform; # the architecture the JVM was compiled for has no effect on # compatibility. # On systems with universal binaries support there may be multiple # architectures. In this case the architecture is "universal" # because extensions must be able to support all of the Ruby # executable's architectures. # - The operating system for which the Ruby interpreter was compiled. def self.ruby_extension_binary_compatibility_id ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby" ruby_ext_version = RUBY_VERSION if RUBY_PLATFORM =~ /darwin/ if RUBY_PLATFORM =~ /universal/ ruby_arch = "universal" else # OS X < 10.8: something like: # "/opt/ruby-enterprise/bin/ruby: Mach-O 64-bit executable x86_64" output = `file -L "#{ruby_executable}"`.strip ruby_arch = output.sub(/.* /, '') if ruby_arch == "executable" # OS X >= 10.8: something like: # "/opt/ruby-enterprise/bin/ruby: Mach-O 64-bit executable" if output =~ /Mach-O 64-bit/ ruby_arch = "x86_64" else raise "Cannot autodetect the Ruby interpreter's architecture" end end end elsif RUBY_PLATFORM == "java" ruby_arch = "java" else ruby_arch = cpu_architectures[0] end return "#{ruby_engine}-#{ruby_ext_version}-#{ruby_arch}-#{os_name_simple}" end memoize :ruby_extension_binary_compatibility_id # Returns an identifier string that describes the current # platform's binary compatibility with regard to C/C++ # binaries. Two systems with the same binary compatibility # identifiers should be able to run the same C/C++ binaries. # # The the string depends on the following factors: # - The operating system name. # - Operating system runtime identifier. # This may include the kernel version, libc version, C++ ABI version, # etc. Everything that is of interest for binary compatibility with # regard to C/C++ binaries. # - Operating system default runtime architecture. # This is not the same as the CPU architecture; some CPUs support # multiple architectures, e.g. Intel Core 2 Duo supports x86 and # x86_64. Some operating systems actually support multiple runtime # architectures: a lot of x86_64 Linux distributions also include # 32-bit runtimes, and OS X Snow Leopard is x86_64 by default but # all system libraries also support x86. # This component identifies the architecture that is used when # compiling a binary with the system's C++ compiler with its default # options. def self.cxx_binary_compatibility_id if os_name_simple == "macosx" # RUBY_PLATFORM gives us the kernel version, but we want # the OS X version. os_version_string = `sw_vers -productVersion`.strip # sw_vers returns something like "10.6.2". We're only # interested in the first two digits (MAJOR.MINOR) since # tiny releases tend to be binary compatible with each # other. components = os_version_string.split(".") os_version = "#{components[0]}.#{components[1]}" os_runtime = os_version os_arch = cpu_architectures[0] if os_version >= "10.5" && os_arch =~ /^i.86$/ # On Snow Leopard, 'uname -m' returns i386 but # we *know* that everything is x86_64 by default. os_arch = "x86_64" end else os_arch = cpu_architectures[0] os_runtime = nil end return [os_arch, os_name_simple, os_runtime].compact.join("-") end memoize :cxx_binary_compatibility_id end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/compiler.rb000644 000765 000024 00000047100 12233035540 030775 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/operating_system' module PhusionPassenger module PlatformInfo private def self.detect_language_extension(language) case language when :c return "c" when :cxx return "cpp" else raise ArgumentError, "Unsupported language #{language.inspect}" end end private_class_method :detect_language_extension def self.detect_compiler_type_name(language) case language when :c return "C" when :cxx return "C++" else raise ArgumentError, "Unsupported language #{language.inspect}" end end private_class_method :detect_compiler_type_name def self.create_compiler_command(language, flags1, flags2, link = false) case language when :c result = [cc, link ? ENV['EXTRA_PRE_LDFLAGS'] : nil, ENV['EXTRA_PRE_CFLAGS'], flags1, flags2, ENV['EXTRA_CFLAGS'], ENV['EXTRA_LDFLAGS']] when :cxx result = [cxx, link ? ENV['EXTRA_PRE_LDFLAGS'] : nil, ENV['EXTRA_PRE_CXXFLAGS'], flags1, flags2, ENV['EXTRA_CXXFLAGS'], ENV['EXTRA_LDFLAGS']] else raise ArgumentError, "Unsupported language #{language.inspect}" end return result.compact.join(" ").strip end private_class_method :create_compiler_command def self.run_compiler(description, command, source_file, source, capture_output = false) if verbose? message = "#{description}\n" << "Running: #{command}\n" if source.strip.empty? message << "Source file is empty." else message << "Source file contains:\n" << "-------------------------\n" << unindent(source) << "\n-------------------------" end log(message) end if capture_output begin output = `#{command} 2>&1` result = $?.exitstatus == 0 rescue SystemCallError => e result = nil exec_error_reason = e.message end log("Output:\n" << "-------------------------\n" << output.to_s << "\n-------------------------") elsif verbose? result = system(command) else result = system("(#{command}) >/dev/null 2>/dev/null") end if result.nil? log("Command could not be executed! #{exec_error_reason}".strip) return false elsif result log("Check succeeded") if capture_output return { :result => true, :output => output } else return true end else log("Check failed with exit status #{$?.exitstatus}") if capture_output == :always return { :result => false, :output => output } else return false end end end private_class_method :run_compiler def self.cc_or_cxx_supports_feliminate_unused_debug?(language) ext = detect_language_extension(language) compiler_type_name = detect_compiler_type_name(language) create_temp_file("passenger-compile-check.#{ext}") do |filename, f| f.close begin command = create_compiler_command(language, "-c '#{filename}' -o '#{filename}.o'", '-feliminate-unused-debug-symbols -feliminate-unused-debug-types') result = run_compiler("Checking for #{compiler_type_name} compiler '-feliminate-unused-debug-{symbols,types}' support", command, filename, '', true) return result && result[:output].empty? ensure File.unlink("#{filename}.o") rescue nil end end end private_class_method :cc_or_cxx_supports_feliminate_unused_debug? public def self.cc return string_env('CC', default_cc) end memoize :cc def self.cxx return string_env('CXX', default_cxx) end memoize :cxx def self.default_cc # On most platforms, we'll want to use the same compiler as what the rest # of the system uses, so that we generate compatible binaries. That's # most likely the 'cc' command. We used to use 'gcc' by default. # # See for example this issue with OS X Mavericks (10.9). They switched from # GCC to Clang as the default compiler. Since the Nginx by default uses 'cc' # as the compiler, we'll have to do that too. Otherwise we'll get C++ linker # errors because Nginx is compiled with Clang while Phusion Passenger is # compiled with GCC. # https://code.google.com/p/phusion-passenger/issues/detail?id=950 if PlatformInfo.find_command('cc') return 'cc' else return 'gcc' end end def self.default_cxx if PlatformInfo.find_command('c++') return 'c++' else return 'g++' end end def self.cc_is_gcc? `#{cc} -v 2>&1` =~ /gcc version/ end memoize :cc_is_gcc? def self.cxx_is_gcc? `#{cxx} -v 2>&1` =~ /gcc version/ end memoize :cxx_is_gcc? def self.cc_is_clang? `#{cc} --version 2>&1` =~ /clang version/ end memoize :cc_is_clang? def self.cxx_is_clang? `#{cxx} --version 2>&1` =~ /clang version/ end memoize :cxx_is_clang? def self.cc_is_sun_studio? `#{cc} -V 2>&1` =~ /Sun C/ || `#{cc} -flags 2>&1` =~ /Sun C/ end memoize :cc_is_sun_studio? def self.cxx_is_sun_studio? `#{cxx} -V 2>&1` =~ /Sun C/ || `#{cxx} -flags 2>&1` =~ /Sun C/ end memoize :cxx_is_sun_studio? # Looks for the given C or C++ header. This works by invoking the compiler and # searching in the compiler's header search path. Returns its full filename, # or true if this function knows that the header exists but can't find it (e.g. # because the compiler cannot tell us what its header search path is). # Returns nil if the header cannot be found. def self.find_header(header_name, language, flags = nil) extension = detect_language_extension(language) create_temp_file("passenger-compile-check.#{extension}") do |filename, f| source = %Q{ #include <#{header_name}> } f.puts(source) f.close begin command = create_compiler_command(language, "-v -c '#{filename}' -o '#{filename}.o'", flags) if result = run_compiler("Checking for #{header_name}", command, filename, source, true) result[:output] =~ /^#include <...> search starts here:$(.+?)^End of search list\.$/m search_paths = $1.to_s.strip.split("\n").map{ |line| line.strip } search_paths.each do |dir| if File.file?("#{dir}/#{header_name}") return "#{dir}/#{header_name}" end end return true else return nil end ensure File.unlink("#{filename}.o") rescue nil end end end def self.try_compile(description, language, source, flags = nil) extension = detect_language_extension(language) create_temp_file("passenger-compile-check.#{extension}") do |filename, f| f.puts(source) f.close command = create_compiler_command(language, "-c '#{filename}' -o '#{filename}.o'", flags) return run_compiler(description, command, filename, source) end end # Like try_compile, but designed for checking whether a warning flag is # supported. Compilers sometimes do not error out upon encountering an # unsupported warning flag, but merely print a warning. This method checks # for that too. def self.try_compile_with_warning_flag(description, language, source, flags = nil) extension = detect_language_extension(language) result = nil create_temp_file("passenger-compile-check.#{extension}") do |filename, f| f.puts(source) f.close command = create_compiler_command(language, "-c '#{filename}' -o '#{filename}.o'", flags) result = run_compiler(description, command, filename, source, true) result = result && result[:result] && result[:output] !~ /unknown warning option/i end return false if !result # For some reason, GCC does not complain about a warning flag # not being supported unless the source contains another error. So we # check for this. create_temp_file("passenger-compile-check.#{extension}") do |filename, f| source = %Q{ void foo() { return error; } } f.puts(source) f.close command = create_compiler_command(language, "-c '#{filename}' -o '#{filename}.o'", flags) result = run_compiler("#{description} (really)", command, filename, source, :always) end result && !result[:output].include?(flags) end def self.try_link(description, language, source, flags = nil) extension = detect_language_extension(language) create_temp_file("passenger-link-check.#{extension}") do |filename, f| f.puts(source) f.close command = create_compiler_command(language, "'#{filename}' -o '#{filename}.out'", flags, true) return run_compiler(description, command, filename, source) end end def self.try_compile_and_run(description, language, source, flags = nil) extension = detect_language_extension(language) create_temp_file("passenger-run-check.#{extension}", tmpexedir) do |filename, f| f.puts(source) f.close command = create_compiler_command(language, "'#{filename}' -o '#{filename}.out'", flags, true) if run_compiler(description, command, filename, source) log("Running #{filename}.out") begin output = `'#{filename}.out' 2>&1` rescue SystemCallError => e log("Command failed: #{e}") return false end status = $?.exitstatus log("Command exited with status #{status}. Output:\n--------------\n#{output}\n--------------") return status == 0 else return false end end end # Checks whether the compiler supports "-arch #{arch}". def self.compiler_supports_architecture?(arch) return try_compile("Checking for C compiler '-arch' support", :c, '', "-arch #{arch}") end def self.cc_supports_visibility_flag? return false if os_name_simple == "aix" return try_compile("Checking for C compiler '-fvisibility' support", :c, '', '-fvisibility=hidden') end memoize :cc_supports_visibility_flag?, true def self.cxx_supports_visibility_flag? return false if os_name_simple == "aix" return try_compile("Checking for C++ compiler '-fvisibility' support", :cxx, '', '-fvisibility=hidden') end memoize :cxx_supports_visibility_flag?, true def self.cc_supports_wno_attributes_flag? return try_compile_with_warning_flag( "Checking for C compiler '-Wno-attributes' support", :c, '', '-Wno-attributes') end memoize :cc_supports_wno_attributes_flag?, true def self.cxx_supports_wno_attributes_flag? return try_compile_with_warning_flag( "Checking for C++ compiler '-Wno-attributes' support", :cxx, '', '-Wno-attributes') end memoize :cxx_supports_wno_attributes_flag?, true def self.cc_supports_wno_missing_field_initializers_flag? return try_compile_with_warning_flag( "Checking for C compiler '-Wno-missing-field-initializers' support", :c, '', '-Wno-missing-field-initializers') end memoize :cc_supports_wno_missing_field_initializers_flag?, true def self.cxx_supports_wno_missing_field_initializers_flag? return try_compile_with_warning_flag( "Checking for C++ compiler '-Wno-missing-field-initializers' support", :cxx, '', '-Wno-missing-field-initializers') end memoize :cxx_supports_wno_missing_field_initializers_flag?, true def self.cxx_supports_wno_unused_local_typedefs_flag? return try_compile_with_warning_flag( "Checking for C++ compiler '-Wno-unused-local-typedefs' support", :cxx, '', '-Wno-unused-local-typedefs') end memoize :cxx_supports_wno_unused_local_typedefs_flag?, true def self.cc_supports_no_tls_direct_seg_refs_option? return try_compile("Checking for C compiler '-mno-tls-direct-seg-refs' support", :c, '', '-mno-tls-direct-seg-refs') end memoize :cc_supports_no_tls_direct_seg_refs_option?, true def self.cxx_supports_no_tls_direct_seg_refs_option? return try_compile("Checking for C++ compiler '-mno-tls-direct-seg-refs' support", :cxx, '', '-mno-tls-direct-seg-refs') end memoize :cxx_supports_no_tls_direct_seg_refs_option?, true def self.compiler_supports_wno_ambiguous_member_template? try_compile_with_warning_flag( "Checking for C++ compiler '-Wno-ambiguous-member-template' support", :cxx, '', '-Wno-ambiguous-member-template') end memoize :compiler_supports_wno_ambiguous_member_template?, true def self.cc_supports_feliminate_unused_debug? return cc_or_cxx_supports_feliminate_unused_debug?(:c) end memoize :cc_supports_feliminate_unused_debug?, true def self.cxx_supports_feliminate_unused_debug? return cc_or_cxx_supports_feliminate_unused_debug?(:cxx) end memoize :cxx_supports_feliminate_unused_debug?, true # Returns whether compiling C++ with -fvisibility=hidden might result # in tons of useless warnings, like this: # http://code.google.com/p/phusion-passenger/issues/detail?id=526 # This appears to be a bug in older g++ versions: # http://gcc.gnu.org/ml/gcc-patches/2006-07/msg00861.html # Warnings should be suppressed with -Wno-attributes. def self.cc_visibility_flag_generates_warnings? if os_name_simple == "linux" && `#{cc} -v 2>&1` =~ /gcc version (.*?)/ return $1 <= "4.1.2" else return false end end memoize :cc_visibility_flag_generates_warnings?, true def self.cxx_visibility_flag_generates_warnings? if os_name_simple == "linux" && `#{cxx} -v 2>&1` =~ /gcc version (.*?)/ return $1 <= "4.1.2" else return false end end memoize :cxx_visibility_flag_generates_warnings?, true def self.adress_sanitizer_flag if cc_is_clang? if `#{cc} --help` =~ /-fsanitize=/ return "-fsanitize=address" else return "-faddress-sanitizer" end else return nil end end memoize :adress_sanitizer_flag def self.cxx_11_flag # C++11 support on FreeBSD 10.0 + Clang seems to be bugged. # http://llvm.org/bugs/show_bug.cgi?id=18310 return nil if os_name_simple == "freebsd" source = %{ struct Foo { Foo(Foo &&f) { } }; } if try_compile("Checking for C++ -std=gnu++11 compiler flag", :cxx, source, '-std=gnu++11') return "-std=gnu++11" elsif try_compile("Checking for C++ -std=c++11 compiler flag", :cxx, source, '-std=c++11') return "-std=c++11" else return nil end end memoize :cxx_11_flag, true def self.has_rt_library? return try_link("Checking for -lrt support", :c, "int main() { return 0; }\n", '-lrt') end memoize :has_rt_library?, true def self.has_math_library? return try_link("Checking for -lmath support", :c, "int main() { return 0; }\n", '-lmath') end memoize :has_math_library?, true def self.has_dl_library? return try_link("Checking for -ldl support", :c, "int main() { return 0; }\n", '-ldl') end memoize :has_dl_library?, true def self.has_alloca_h? return try_compile("Checking for alloca.h", :c, '#include ') end memoize :has_alloca_h?, true def self.has_accept4? return try_compile("Checking for accept4()", :c, %Q{ #define _GNU_SOURCE #include static void *foo = accept4; }) end memoize :has_accept4?, true # C compiler flags that should be passed in order to enable debugging information. def self.debugging_cflags # According to OpenBSD's pthreads man page, pthreads do not work # correctly when an app is compiled with -g. It recommends using # -ggdb instead. # # In any case we'll always want to use -ggdb for better GDB debugging. if cc_is_gcc? return '-ggdb' else return '-g' end end def self.dmalloc_ldflags if !ENV['DMALLOC_LIBS'].to_s.empty? return ENV['DMALLOC_LIBS'] end if os_name_simple == "macosx" ['/opt/local', '/usr/local', '/usr'].each do |prefix| filename = "#{prefix}/lib/libdmallocthcxx.a" if File.exist?(filename) return filename end end return nil else return "-ldmallocthcxx" end end memoize :dmalloc_ldflags def self.electric_fence_ldflags if os_name_simple == "macosx" ['/opt/local', '/usr/local', '/usr'].each do |prefix| filename = "#{prefix}/lib/libefence.a" if File.exist?(filename) return filename end end return nil else return "-lefence" end end memoize :electric_fence_ldflags def self.export_dynamic_flags if os_name_simple == "linux" return '-rdynamic' else return nil end end def self.make return string_env('MAKE', find_command('make')) end memoize :make, true def self.gnu_make if result = string_env('GMAKE') return result else result = find_command('gmake') if !result result = find_command('make') if result if `#{result} --version 2>&1` =~ /GNU/ return result else return nil end else return nil end else return result end end end memoize :gnu_make, true def self.xcode_select_version if find_command('xcode-select') `xcode-select --version` =~ /version (.+)\./ return $1 else return nil end end end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/curl.rb000644 000765 000024 00000004167 12233035540 030136 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'platform_info' module PhusionPassenger module PlatformInfo def self.curl_flags result = `(curl-config --cflags) 2>/dev/null`.strip if result.empty? return nil else version = `curl-config --vernum`.strip if version >= '070c01' # Curl >= 7.12.1 supports curl_easy_reset() result << " -DHAS_CURL_EASY_RESET" end return result end end memoize :curl_flags def self.curl_libs result = `(curl-config --libs) 2>/dev/null`.strip if result.empty? return nil else return result end end memoize :curl_libs def self.curl_supports_ssl? features = `(curl-config --feature) 2>/dev/null` return features =~ /SSL/ end memoize :curl_supports_ssl? end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/cxx_portability.rb000644 000765 000024 00000020731 12233035540 032410 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2013 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/compiler' PhusionPassenger.require_passenger_lib 'platform_info/operating_system' module PhusionPassenger module PlatformInfo # Extra flags that should always be passed to the C compiler # when linking, to be included last in the command string. def self.portability_c_ldflags return portability_c_or_cxx_ldflags(:c) end memoize :portability_c_ldflags # Extra flags that should always be passed to the C++ compiler # when linking, to be included last in the command string. def self.portability_cxx_ldflags return portability_c_or_cxx_ldflags(:cxx) end memoize :portability_cxx_ldflags # Extra compiler flags that should always be passed to the C compiler, # last in the command string. def self.default_extra_cflags return default_extra_c_or_cxxflags(:cc) end memoize :default_extra_cflags, true # Extra compiler flags that should always be passed to the C++ compiler, # last in the command string. def self.default_extra_cxxflags return default_extra_c_or_cxxflags(:cxx) end memoize :default_extra_cxxflags, true private def self.check_unordered_map(flags, class_name, header_name, macro_name) ok = try_compile("Checking for unordered_map", :cxx, %Q{ #include <#{header_name}> int main() { #{class_name} m; return 0; } }) flags << "-D#{macro_name}" if ok return ok end private_class_method :check_unordered_map def self.check_hash_map(flags) hash_namespace = nil ok = false ['__gnu_cxx', '', 'std', 'stdext'].each do |namespace| ['hash_map', 'ext/hash_map'].each do |hash_map_header| ok = try_compile("Checking for #{hash_map_header}", :cxx, %Q{ #include <#{hash_map_header}> int main() { #{namespace}::hash_map m; return 0; } }) if ok hash_namespace = namespace flags << "-DHASH_NAMESPACE=\"#{namespace}\"" flags << "-DHASH_MAP_HEADER=\"<#{hash_map_header}>\"" flags << "-DHASH_MAP_CLASS=\"hash_map\"" break end end break if ok end ['ext/hash_fun.h', 'functional', 'tr1/functional', 'ext/stl_hash_fun.h', 'hash_fun.h', 'stl_hash_fun.h', 'stl/_hash_fun.h'].each do |hash_function_header| ok = try_compile("Checking for #{hash_function_header}", :cxx, %Q{ #include <#{hash_function_header}> int main() { #{hash_namespace}::hash()(5); return 0; } }) if ok flags << "-DHASH_FUN_H=\"<#{hash_function_header}>\"" break end end end private_class_method :check_hash_map def self.default_extra_c_or_cxxflags(cc_or_cxx) flags = ["-D_REENTRANT", "-I/usr/local/include"] if !send("#{cc_or_cxx}_is_sun_studio?") flags << "-Wall -Wextra -Wno-unused-parameter -Wno-parentheses -Wpointer-arith -Wwrite-strings -Wno-long-long" if send("#{cc_or_cxx}_supports_wno_missing_field_initializers_flag?") flags << "-Wno-missing-field-initializers" end if requires_no_tls_direct_seg_refs? && send("#{cc_or_cxx}_supports_no_tls_direct_seg_refs_option?") flags << "-mno-tls-direct-seg-refs" end # Work around Clang warnings in ev++.h. if send("#{cc_or_cxx}_is_clang?") flags << "-Wno-ambiguous-member-template" end end if !send("#{cc_or_cxx}_is_sun_studio?") if send("#{cc_or_cxx}_supports_feliminate_unused_debug?") flags << "-feliminate-unused-debug-symbols -feliminate-unused-debug-types" end if send("#{cc_or_cxx}_supports_visibility_flag?") flags << "-fvisibility=hidden -DVISIBILITY_ATTRIBUTE_SUPPORTED" if send("#{cc_or_cxx}_visibility_flag_generates_warnings?") && send("#{cc_or_cxx}_supports_wno_attributes_flag?") flags << "-Wno-attributes" end end end flags << debugging_cflags flags << '-DHAS_ALLOCA_H' if has_alloca_h? flags << '-DHAVE_ACCEPT4' if has_accept4? flags << '-DHAS_SFENCE' if supports_sfence_instruction? flags << '-DHAS_LFENCE' if supports_lfence_instruction? flags << "-DPASSENGER_DEBUG -DBOOST_DISABLE_ASSERTS" if cc_or_cxx == :cxx flags << cxx_11_flag if cxx_11_flag if cxx_supports_wno_unused_local_typedefs_flag? # Avoids some compilaton warnings with Boost on Ubuntu 14.04. flags << "-Wno-unused-local-typedefs" end # There are too many implementations of of the hash map! # Figure out the right one. check_unordered_map(flags, "std::unordered_map", "unordered_map", "HAS_UNORDERED_MAP") || check_unordered_map(flags, "std::tr1::unordered_map", "unordered_map", "HAS_TR1_UNORDERED_MAP") || check_hash_map(flags) end if os_name_simple == "solaris" if send("#{cc_or_cxx}_is_sun_studio?") flags << '-mt' else flags << '-pthreads' end if os_name_full =~ /solaris2\.11/ # skip the _XOPEN_SOURCE and _XPG4_2 definitions in later versions of Solaris / OpenIndiana flags << '-D__EXTENSIONS__ -D__SOLARIS__ -D_FILE_OFFSET_BITS=64' else flags << '-D_XOPEN_SOURCE=500 -D_XPG4_2 -D__EXTENSIONS__ -D__SOLARIS__ -D_FILE_OFFSET_BITS=64' flags << '-D__SOLARIS9__ -DBOOST__STDC_CONSTANT_MACROS_DEFINED' if os_name_full =~ /solaris2\.9/ end flags << '-DBOOST_HAS_STDINT_H' unless os_name_full =~ /solaris2\.9/ if send("#{cc_or_cxx}_is_sun_studio?") flags << '-xtarget=ultra' if RUBY_PLATFORM =~ /sparc/ else flags << '-mcpu=ultrasparc' if RUBY_PLATFORM =~ /sparc/ end elsif os_name_simple == "openbsd" flags << '-DBOOST_HAS_STDINT_H -D_GLIBCPP__PTHREADS' elsif os_name_simple == "aix" flags << '-pthread' flags << '-DOXT_DISABLE_BACKTRACES' elsif RUBY_PLATFORM =~ /(sparc-linux|arm-linux|^arm.*-linux|sh4-linux)/ # http://code.google.com/p/phusion-passenger/issues/detail?id=200 # http://groups.google.com/group/phusion-passenger/t/6b904a962ee28e5c # http://groups.google.com/group/phusion-passenger/browse_thread/thread/aad4bd9d8d200561 flags << '-DBOOST_SP_USE_PTHREADS' end return flags.compact.map{ |str| str.strip }.join(" ").strip end private_class_method :default_extra_c_or_cxxflags def self.portability_c_or_cxx_ldflags(cc_or_cxx) result = '' result << cxx_11_flag if cc_or_cxx == :cxx && cxx_11_flag if os_name_simple == "solaris" result << ' -lxnet -lsocket -lnsl -lpthread' else result << ' -lpthread' end result << ' -lrt' if has_rt_library? result << ' -lmath' if has_math_library? result << ' -ldl' if has_dl_library? result.strip! return result end private_class_method :portability_c_or_cxx_ldflags end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/depcheck.rb000644 000765 000024 00000031072 12233035540 030732 0ustar00honglistaff000000 000000 # encoding: utf-8 PhusionPassenger.require_passenger_lib 'platform_info/ruby' PhusionPassenger.require_passenger_lib 'platform_info/linux' PhusionPassenger.require_passenger_lib 'platform_info/compiler' PhusionPassenger.require_passenger_lib 'platform_info/openssl' PhusionPassenger.require_passenger_lib 'platform_info/curl' PhusionPassenger.require_passenger_lib 'platform_info/operating_system' PhusionPassenger.require_passenger_lib 'utils/ansi_colors' module PhusionPassenger module PlatformInfo # Almost all software require other software in order to run. We call those # other software 'dependencies'. Reliably checking for dependencies can be # difficult. Helping the user in case a dependency is not installed (or # doesn't seem to be installed) is more difficult still. # # The Depcheck framework seeks to make all this easier. It allows the programmer # to write "specs" which contain dependency checking code in a structured way. # The programmer defines a dependency's basic information (name, website, etc), # defines installation instructions (which may be customized per platform) and # defines code for checking whether the dependency actually exists. The Depcheck # framework: # # * Provides helpers for checking for the existance of commands, libraries, # headers, etc. # * Registers all dependency specs in a way that can be easily accessed # structurally. # * Allows user-friendly display of dependency checking progress and user help # instructions. # # Most dependency checking code (e.g. autoconf) is very straightforward: they # just check for the existance of a command, library, header, etc and either # report "found" or "not found". In our experience the world is unfortunately # not that simple. Users can have multiple versions of a dependency installed, # where some dependencies are suitable while others are not. Therefore specs # should print as many details about the dependency as possible (location, version, # etc) so that the user can override any decisions if necessary. module Depcheck THIS_DIR = File.expand_path(File.dirname(__FILE__)) @@loaded = {} @@database = {} def self.load(partial_filename) if !@@loaded[partial_filename] filename = "#{THIS_DIR}/#{partial_filename}.rb" content = File.read(filename) instance_eval(content, filename) @@loaded[partial_filename] = true end end def self.define(identifier, &block) @@database[identifier.to_s] = block end def self.find(identifier) # We lazy-initialize everything in order to save resources. This also # allows blocks to perform relatively expensive checks without hindering # startup time. identifier = identifier.to_s result = @@database[identifier] if result.is_a?(Proc) result = Dependency.new(&result) @@database[identifier] = result end result end class Dependency def initialize(&block) instance_eval(&block) check_syntax_aspect("Name must be given") { !!@name } check_syntax_aspect("A checker must be given") { !!@checker } end def check @install_comments = nil @check_result ||= @checker.call end ### DSL for specs ### def name(value = nil) value ? @name = value : @name end def website(value = nil) value ? @website = value : @website end def website_comments(value = nil) value ? @website_comments = value : @website_comments end def install_instructions(value = nil) if value @install_instructions = value else if @install_instructions @install_instructions elsif @website result = "Please download it from #{@website}" result << "\n(#{@website_comments})" if @website_comments result else "Search Google for '#{@name}'." end end end def install_comments(value = nil) value ? @install_comments = value : @install_comments end private def check_syntax_aspect(description) if !yield raise description end end ### DSL for specs ### def define_checker(&block) @checker = block end def check_for_command(name, *args) result = find_command(name, *args) if result { :found => true, "Location" => result } else false end end def check_for_ruby_tool(name) result = locate_ruby_tool(name) if result { :found => true, "Location" => result } else false end end def check_for_header(header_name, language = :c, flags = nil) if result = PlatformInfo.find_header(header_name, language, flags) { :found => true, "Location" => result } else false end end # def check_for_library(name) # check_by_compiling("int main() { return 0; }", :cxx, nil, "-l#{name}") # end # def check_by_compiling(source, language = :c, cflags = nil, linkflags = nil) # case language # when :c # source_file = "#{PlatformInfo.tmpexedir}/depcheck-#{Process.pid}-#{Thread.current.object_id}.c" # compiler = "gcc" # compiler_flags = ENV['CFLAGS'] # when :cxx # source_file = "#{PlatformInfo.tmpexedir}/depcheck-#{Process.pid}-#{Thread.current.object_id}.cpp" # compiler = "g++" # compiler_flags = "#{ENV['CFLAGS']} #{ENV['CXXFLAGS']}".strip # else # raise ArgumentError, "Unknown language '#{language}" # end # output_file = "#{PlatformInfo.tmpexedir}/depcheck-#{Process.pid}-#{Thread.current.object_id}" # begin # File.open(source_file, 'w') do |f| # f.puts(source) # end # if find_command(compiler) # command = "#{compiler} #{compiler_flags} #{cflags} " + # "#{source_file} -o #{output_file} #{linkflags}" # [!!system(command)] # else # [:unknown, "Cannot check: compiler '#{compiler}' not found."] # end # ensure # File.unlink(source_file) rescue nil # File.unlink(output_file) rescue nil # end # end def check_for_ruby_library(name) begin require(name) { :found => true } rescue LoadError if defined?(Gem) false else begin require 'rubygems' require(name) { :found => true } rescue LoadError false end end end end def on(platform) return if @on_invoked invoke = false if (linux_distro_tags || []).include?(platform) invoke = true else case platform when :linux invoke = true if PlatformInfo.os_name_simple == "linux" when :freebsd invoke = true if PlatformInfo.os_name_simple == "freebsd" when :macosx invoke = true if PlatformInfo.os_name_simple == "macosx" when :solaris invoke = true if PlatformInfo.os_name_simple == "solaris" when :other_platforms invoke = true end end if invoke yield @on_invoked = true end end def apt_get_install(package_name) install_instructions("Please install it with apt-get install #{package_name}") end def urpmi(package_name) install_instructions("Please install it with urpmi #{package_name}") end def yum_install(package_name, options = {}) if options[:epel] install_instructions("Please enable EPEL, then install with yum install #{package_name}") else install_instructions("Please install it with yum install #{package_name}") end end def emerge(package_name) install_instructions("Please install it with emerge -av #{package_name}") end def gem_install(package_name) install_instructions("Please make sure RubyGems is installed, then run " + "#{gem_command} install #{package_name}") end def brew_install(package_name) install_instructions("Please install it with brew install #{package_name}") end def install_osx_command_line_tools PhusionPassenger.require_passenger_lib 'platform_info/compiler' if PlatformInfo.xcode_select_version.to_s >= "2333" install_instructions "Please install the Xcode command line tools: " + "sudo xcode-select --install" else install_instructions "Please install Xcode, then install the command line tools " + "though the menu Xcode -> Preferences -> Downloads -> Components" end end def ruby_command PlatformInfo.ruby_command end def gem_command PlatformInfo.gem_command(:sudo => true) || 'gem' end def find_command(command, *args) PlatformInfo.find_command(command, *args) end def linux_distro_tags PlatformInfo.linux_distro_tags end def locate_ruby_tool(name) PlatformInfo.locate_ruby_tool(name) end end # class Dependency class ConsoleRunner attr_reader :missing_dependencies def initialize @stdout = STDOUT @dep_identifiers = [] end def add(identifier) @dep_identifiers << identifier end def check_all old_log_impl = PlatformInfo.log_implementation begin PlatformInfo.log_implementation = lambda do |message| message = PlatformInfo.send(:reindent, message, 10) message.sub!(/^ /, '') STDOUT.puts " -> #{message}" end @missing_dependencies = [] @dep_identifiers.each do |identifier| dep = Depcheck.find(identifier) raise "Cannot find depcheck spec #{identifier.inspect}" if !dep puts_header "Checking for #{dep.name}..." result = dep.check result = { :found => false } if !result if result[:found] && !result[:error] puts_detail "Found: yes" else if result[:error] puts_detail "Found: #{result[:found] ? "yes, but there was an error" : "no"}" puts_detail "Error: #{result[:error]}" else puts_detail "Found: #{result[:found] ? "yes" : "no"}" end @missing_dependencies << dep end result.each_pair do |key, value| if key.is_a?(String) puts_detail "#{key}: #{value}" end end end return @missing_dependencies.empty? ensure PlatformInfo.log_implementation = old_log_impl end end def print_installation_instructions_for_missing_dependencies @missing_dependencies.each do |dep| puts " * To install #{dep.name}:" puts " #{dep.install_instructions}" if dep.install_comments puts " #{dep.install_comments}" end puts end end private def puts(text = nil) if text @stdout.puts(Utils::AnsiColors.ansi_colorize(text)) else @stdout.puts end @stdout.flush end def puts_header(text) puts " * #{text}" end def puts_detail(text) puts " #{text}" end end end # module Depcheck end # module PlatformInfo end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/depcheck_specs/000755 000765 000024 00000000000 12233035540 031577 5ustar00honglistaff000000 000000 passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/linux.rb000644 000765 000024 00000006414 12233035540 030325 0ustar00honglistaff000000 000000 # encoding: binary # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/operating_system' module PhusionPassenger module PlatformInfo # An identifier for the current Linux distribution. nil if the operating system is not Linux. def self.linux_distro tags = linux_distro_tags if tags return tags.first else return nil end end # Autodetects the current Linux distribution and return a number of identifier tags. # The first tag identifies the distribution while the other tags indicate which # distributions it is likely compatible with. # Returns nil if the operating system is not Linux. def self.linux_distro_tags if os_name_simple != "linux" return nil end lsb_release = read_file("/etc/lsb-release") if lsb_release =~ /Ubuntu/ return [:ubuntu, :debian] elsif File.exist?("/etc/debian_version") return [:debian] elsif File.exist?("/etc/redhat-release") redhat_release = read_file("/etc/redhat-release") if redhat_release =~ /CentOS/ return [:centos, :redhat] elsif redhat_release =~ /Fedora/ return [:fedora, :redhat] elsif redhat_release =~ /Mandriva/ return [:mandriva, :redhat] else # On official RHEL distros, the content is in the form of # "Red Hat Enterprise Linux Server release 5.1 (Tikanga)" return [:rhel, :redhat] end elsif File.exist?("/etc/system-release") system_release = read_file("/etc/system-release") if system_release =~ /Amazon Linux/ return [:amazon, :redhat] else return [:unknown] end elsif File.exist?("/etc/suse-release") return [:suse] elsif File.exist?("/etc/gentoo-release") return [:gentoo] else return [:unknown] end # TODO: Slackware end memoize :linux_distro_tags end end # PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/openssl.rb000644 000765 000024 00000004234 12233035540 030647 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/operating_system' module PhusionPassenger module PlatformInfo def self.openssl_extra_cflags if PlatformInfo.os_name_simple == "macosx" if File.exist?("/usr/include/openssl") "" else # OS X >= 10.11 El Capitan no longer include # OpenSSL development headers. Use the one from # Homebrew. "-I/usr/local/opt/openssl/include" end else "" end end memoize :openssl_extra_cflags def self.openssl_extra_ldflags if PlatformInfo.os_name_simple == "macosx" if File.exist?("/usr/include/openssl") "" else "-L/usr/local/opt/openssl/lib" end else "" end end memoize :openssl_extra_ldflags end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/operating_system.rb000644 000765 000024 00000020270 12233035540 032556 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'rbconfig' PhusionPassenger.require_passenger_lib 'platform_info' module PhusionPassenger module PlatformInfo # Returns the operating system's name in as simple a form as possible. For example, # Linux is always identified as "linux". OS X is always identified as "macosx" (despite # the actual os name being something like "darwin"). This is useful as a stable indicator # of the os without having to worry about version numbers, etc. # N.B. unrecognized os names will just be returned as-is. def self.os_name_simple if rb_config['target_os'] =~ /darwin/ && (sw_vers = find_command('sw_vers')) 'macosx' elsif rb_config['target_os'] =~ /^linux-/ 'linux' elsif rb_config['target_os'] =~ /solaris/ 'solaris' elsif rb_config['target_os'] =~ /freebsd/ 'freebsd' elsif rb_config['target_os'] =~ /aix/ 'aix' else rb_config['target_os'] end end memoize :os_name_simple # Returns the operating system's name exactly as advertised by the system. While it is # in lowercase and contains no spaces, it can contain things like version number or # may be less intuitive (e.g. "darwin" for OS X). def self.os_name_full rb_config['target_os'] end memoize :os_name_full # The current platform's shared library extension ('so' on most Unices). def self.library_extension if os_name_simple == "macosx" return "bundle" else return "so" end end # Returns the `uname` command, or nil if `uname` cannot be found. # In addition to looking for `uname` in `PATH`, this method also looks # for `uname` in /bin and /usr/bin, just in case the user didn't # configure its PATH properly. def self.uname_command if result = find_command("uname") result elsif File.exist?("/bin/uname") return "/bin/uname" elsif File.exist?("/usr/bin/uname") return "/usr/bin/uname" else return nil end end # Returns a list of all CPU architecture names that the current machine CPU # supports. If there are multiple such architectures then the first item in # the result denotes that OS runtime's main/preferred architecture. # # This function normalizes some names. For example x86 is always reported # as "x86" regardless of whether the OS reports it as "i386" or "i686". # x86_64 is always reported as "x86_64" even if the OS reports it as "amd64". # # Please note that even if the CPU supports multiple architectures, the # operating system might not. For example most x86 CPUs nowadays also # support x86_64, but x86_64 Linux systems require various x86 compatibility # libraries to be installed before x86 executables can be run. This function # does not detect whether these compatibility libraries are installed. # The only guarantee that you have is that the OS can run executables in # the architecture denoted by the first item in the result. # # For example, on x86_64 Linux this function can return ["x86_64", "x86"]. # This indicates that the CPU supports both of these architectures, and that # the OS's main/preferred architecture is x86_64. Most executables on the # system are thus be x86_64. It is guaranteed that the OS can run x86_64 # executables, but not x86 executables per se. # # Another example: on MacOS X this function can return either # ["x86_64", "x86"] or ["x86", "x86_64"]. The former result indicates # OS X 10.6 (Snow Leopard) and beyond because starting from that version # everything is 64-bit by default. The latter result indicates an OS X # version older than 10.6. def self.cpu_architectures uname = uname_command raise "The 'uname' command cannot be found" if !uname if os_name_simple == "macosx" arch = `#{uname} -p`.strip if arch == "i386" # Macs have been x86 since around 2007. I think all of them come with # a recent enough Intel CPU that supports both x86 and x86_64, and I # think every OS X version has both the x86 and x86_64 runtime installed. major, minor, *rest = `sw_vers -productVersion`.strip.split(".") major = major.to_i minor = minor.to_i if major >= 10 || (major == 10 && minor >= 6) # Since Snow Leopard x86_64 is the default. ["x86_64", "x86"] else # Before Snow Leopard x86 was the default. ["x86", "x86_64"] end else arch end else arch = `#{uname} -p`.strip # On some systems 'uname -p' returns something like # 'Intel(R) Pentium(R) M processor 1400MHz' or # 'Intel(R)_Xeon(R)_CPU___________X7460__@_2.66GHz'. if arch == "unknown" || arch =~ / / || arch =~ /Hz$/ arch = `#{uname} -m`.strip end if arch =~ /^i.86$/ arch = "x86" elsif arch == "amd64" arch = "x86_64" end if arch == "x86" # Most x86 operating systems nowadays are probably running on # a CPU that supports both x86 and x86_64, but we're not gonna # go through the trouble of checking that. The main architecture # is what we usually care about. ["x86"] elsif arch == "x86_64" # I don't think there's a single x86_64 CPU out there # that doesn't support x86 as well. ["x86_64", "x86"] else [arch] end end end memoize :cpu_architectures, true # Returns whether the flock() function is supported on this OS. def self.supports_flock? defined?(File::LOCK_EX) && os_name_simple != 'solaris' end # Returns whether the OS's main CPU architecture supports the # x86/x86_64 sfence instruction. def self.supports_sfence_instruction? arch = cpu_architectures[0] return arch == "x86_64" || (arch == "x86" && try_compile_and_run("Checking for sfence instruction support", :c, %Q{ int main() { __asm__ __volatile__ ("sfence" ::: "memory"); return 0; } })) end memoize :supports_sfence_instruction?, true # Returns whether the OS's main CPU architecture supports the # x86/x86_64 lfence instruction. def self.supports_lfence_instruction? arch = cpu_architectures[0] return arch == "x86_64" || (arch == "x86" && try_compile_and_run("Checking for lfence instruction support", :c, %Q{ int main() { __asm__ __volatile__ ("lfence" ::: "memory"); return 0; } })) end memoize :supports_lfence_instruction?, true def self.requires_no_tls_direct_seg_refs? return File.exists?("/proc/xen/capabilities") && cpu_architectures[0] == "x86" end memoize :requires_no_tls_direct_seg_refs?, true end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/ruby.rb000644 000765 000024 00000044713 12233035540 030153 0ustar00honglistaff000000 000000 # encoding: binary # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'rbconfig' require 'etc' PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/operating_system' module PhusionPassenger module PlatformInfo # Store original $GEM_HOME value so that even if the app customizes # $GEM_HOME we can still work with the original value. gem_home = ENV['GEM_HOME'] if gem_home gem_home = gem_home.strip.freeze gem_home = nil if gem_home.empty? end GEM_HOME = gem_home # Ditto for $GEM_PATH gem_path = ENV['GEM_PATH'] if gem_path gem_path = gem_path.strip.freeze gem_path = nil if gem_path.empty? end GEM_PATH = gem_path if defined?(::RUBY_ENGINE) RUBY_ENGINE = ::RUBY_ENGINE else RUBY_ENGINE = "ruby" end # Returns correct command for invoking the current Ruby interpreter. # In case of RVM this function will return the path to the RVM wrapper script # that executes the current Ruby interpreter in the currently active gem set. def self.ruby_command # Detect usage of gem-wrappers: https://github.com/rvm/gem-wrappers # This is currently used by RVM >= 1.25, although it's not exclusive to RVM. if GEM_HOME && File.exist?("#{GEM_HOME}/wrappers/ruby") return "#{GEM_HOME}/wrappers/ruby" end if in_rvm? # Detect old-school RVM wrapper script location. name = rvm_ruby_string dirs = rvm_paths if name && dirs dirs.each do |dir| filename = "#{dir}/wrappers/#{name}/ruby" if File.exist?(filename) contents = File.open(filename, 'rb') do |f| f.read end # Old wrapper scripts reference $HOME which causes # things to blow up when run by a different user. if contents.include?("$HOME") filename = nil end else filename = nil end if filename return filename end end # Correctness of these commands are confirmed by mpapis. # If we ever encounter a case for which this logic is not sufficient, # try mpapis' pseudo code: # # rvm_update_prefix = write_to rvm_path ? "" : "rvmsudo" # rvm_gemhome_prefix = write_to GEM_HOME ? "" : "rvmsudo" # repair_command = "#{rvm_update_prefix} rvm get stable && rvm reload && #{rvm_gemhome_prefix} rvm repair all" # wrapper_command = "#{rvm_gemhome_prefix} rvm wrapper #{rvm_ruby_string} --no-prefix --all" case rvm_installation_mode when :single repair_command = "rvm get stable && rvm reload && rvm repair all" wrapper_command = "rvm wrapper #{rvm_ruby_string} --no-prefix --all" when :multi repair_command = "rvmsudo rvm get stable && rvm reload && rvmsudo rvm repair all" wrapper_command = "rvmsudo rvm wrapper #{rvm_ruby_string} --no-prefix --all" when :mixed repair_command = "rvmsudo rvm get stable && rvm reload && rvm repair all" wrapper_command = "rvm wrapper #{rvm_ruby_string} --no-prefix --all" end STDERR.puts "Your RVM wrapper scripts are too old, or some " + "wrapper scripts are missing. Please update/regenerate " + "them first by running:\n\n" + " #{repair_command}\n\n" + "If that doesn't seem to work, please run:\n\n" + " #{wrapper_command}" exit 1 else # Something's wrong with the user's RVM installation. # Raise an error so that the user knows this instead of # having things fail randomly later on. # 'name' is guaranteed to be non-nil because rvm_ruby_string # already raises an exception on error. STDERR.puts "Your RVM installation appears to be broken: the RVM " + "path cannot be found. Please fix your RVM installation " + "or contact the RVM developers for support." exit 1 end else return ruby_executable end end memoize :ruby_command # Returns the full path to the current Ruby interpreter's executable file. # This might not be the actual correct command to use for invoking the Ruby # interpreter; use ruby_command instead. def self.ruby_executable @@ruby_executable ||= rb_config['bindir'] + '/' + rb_config['RUBY_INSTALL_NAME'] + rb_config['EXEEXT'] end # Returns whether the Ruby interpreter supports process forking. def self.ruby_supports_fork? # MRI >= 1.9.2's respond_to? returns false for methods # that are not implemented. return Process.respond_to?(:fork) && RUBY_ENGINE != "jruby" && RUBY_ENGINE != "macruby" && rb_config['target_os'] !~ /mswin|windows|mingw/ end # Returns whether Phusion Passenger needs Ruby development headers to # be available for the current Ruby implementation. def self.passenger_needs_ruby_dev_header? # Too much of a trouble for JRuby. We can do without it. return RUBY_ENGINE != "jruby" end # Returns the correct 'gem' command for this Ruby interpreter. # If `:sudo => true` is given, then the gem command is prefixed by a # sudo command if filesystem permissions require this. def self.gem_command(options = {}) command = ruby_tool_command('gem') if options[:sudo] && gem_install_requires_sudo? command = "#{ruby_sudo_command} #{command}" end return command end memoize :gem_command # Returns whether running 'gem install' as the current user requires sudo. def self.gem_install_requires_sudo? `#{gem_command} env` =~ /INSTALLATION DIRECTORY: (.+)/ if install_dir = $1 return !File.writable?(install_dir) else return nil end end memoize :gem_install_requires_sudo? # Returns the absolute path to the Rake executable that # belongs to the current Ruby interpreter. Returns nil if it # doesn't exist. # # The return value may not be the actual correct invocation # for Rake. Use `rake_command` for that. def self.rake return locate_ruby_tool('rake') end memoize :rake # Returns the correct command string for invoking the Rake executable # that belongs to the current Ruby interpreter. Returns nil if Rake is # not found. def self.rake_command ruby_tool_command('rake') end memoize :rake_command # Returns the absolute path to the RSpec runner program that # belongs to the current Ruby interpreter. Returns nil if it # doesn't exist. def self.rspec return locate_ruby_tool('rspec') end memoize :rspec # Returns whether the current Ruby interpreter is managed by RVM. def self.in_rvm? bindir = rb_config['bindir'] return bindir.include?('/.rvm/') || bindir.include?('/rvm/') end # If the current Ruby interpreter is managed by RVM, returns all # directories in which RVM places its working files. This is usually # ~/.rvm or /usr/local/rvm, but in mixed-mode installations there # can be multiple such paths. # # Otherwise returns nil. def self.rvm_paths if in_rvm? result = [] [ENV['rvm_path'], "#{PhusionPassenger.home_dir}/.rvm", "/usr/local/rvm"].each do |path| next if path.nil? rubies_path = File.join(path, 'rubies') wrappers_path = File.join(path, 'wrappers') gems_path = File.join(path, 'gems') if File.directory?(path) && (File.directory?(rubies_path) || File.directory?(wrappers_path) || File.directory?(gems_path)) result << path end end if result.empty? # Failure to locate the RVM path is probably caused by the # user customizing $rvm_path. Older RVM versions don't # export $rvm_path, making us unable to detect its value. STDERR.puts "Unable to locate the RVM path. Your RVM installation " + "is probably too old. Please update it with " + "'rvm get head && rvm reload && rvm repair all'." exit 1 else return result end else return nil end end memoize :rvm_paths # If the current Ruby interpreter is managed by RVM, returns the # RVM name which identifies the current Ruby interpreter plus the # currently active gemset, e.g. something like this: # "ruby-1.9.2-p0@mygemset" # # Returns nil otherwise. def self.rvm_ruby_string if in_rvm? # RVM used to export the necessary information through # environment variables, but doesn't always do that anymore # in the latest versions in order to fight env var pollution. # Scanning $LOAD_PATH seems to be the only way to obtain # the information. # Getting the RVM name of the Ruby interpreter ("ruby-1.9.2") # isn't so hard, we can extract it from the #ruby_executable # string. Getting the gemset name is a bit harder, so let's # try various strategies... # $GEM_HOME usually contains the gem set name. # It may be something like: # /Users/hongli/.rvm/gems/ruby-1.9.3-p392 # But also: # /home/bitnami/.rvm/gems/ruby-1.9.3-p385-perf@njist325/ruby/1.9.1 if GEM_HOME && GEM_HOME =~ %r{rvm/gems/(.+)} return $1.sub(/\/.*/, '') end # User might have explicitly set GEM_HOME to a custom directory, # or might have nuked $GEM_HOME. Extract info from $GEM_PATH. if GEM_PATH GEM_PATH.split(':').each do |gem_path| if gem_path =~ %r{rvm/gems/(.+)} return $1.sub(/\/.*/, '') end end end # That failed too. Try extracting info from from $LOAD_PATH. matching_path = $LOAD_PATH.find_all do |item| item.include?("rvm/gems/") end if matching_path && !matching_path.empty? subpath = matching_path.to_s.gsub(/^.*rvm\/gems\//, '') result = subpath.split('/').first return result if result end # On Ruby 1.9, $LOAD_PATH does not contain any gem paths until # at least one gem has been required so the above can fail. # We're out of options now, we can't detect the gem set. # Raise an exception so that the user knows what's going on # instead of having things fail in obscure ways later. STDERR.puts "Unable to autodetect the currently active RVM gem " + "set name. This could happen if you ran this program using 'sudo' " + "instead of 'rvmsudo'. When using RVM, you're always supposed to " + "use 'rvmsudo' instead of 'sudo!'.\n\n" + "Please try rerunning this program using 'rvmsudo'. If that " + "doesn't help, please contact this program's author for support." exit 1 end return nil end memoize :rvm_ruby_string # Returns the RVM installation mode: # :single - RVM is installed in single-user mode. # :multi - RVM is installed in multi-user mode. # :mixed - RVM is in a mixed-mode installation. # nil - The current Ruby interpreter is not using RVM. def self.rvm_installation_mode if in_rvm? if ENV['rvm_path'] =~ /\.rvm/ return :single else if GEM_HOME =~ /\.rvm/ return :mixed else return :multi end end else return nil end end # Returns either 'sudo' or 'rvmsudo' depending on whether the current # Ruby interpreter is managed by RVM. def self.ruby_sudo_command if in_rvm? return "rvmsudo" else return "sudo" end end # Returns a `sudo` or `rvmsudo` command that spawns a shell, depending # on whether the current Ruby interpreter is managed by RVM. def self.ruby_sudo_shell_command(args = nil) if in_rvm? shell = ENV['SHELL'].to_s if shell.empty? begin user = Etc.getpwuid(0) rescue ArgumentError user = nil end shell = user.shell if user shell = "bash" if !shell || shell.empty? end result = "rvmsudo " result << "#{args} " if args result << shell return result else return "sudo -s #{args}".strip end end # Locates a Ruby tool, e.g. 'gem', 'rake', 'bundle', etc. Instead of # naively looking in $PATH, this function uses a variety of search heuristics # to find the tool that's really associated with the current Ruby interpreter. # It should never locate a tool that's actually associated with a different # Ruby interpreter. # Returns nil when nothing's found. # # This method only returns the path to the tool script. Running this script # directly does not necessarily mean that it's run under the correct Ruby # interpreter. You may have to prepend it with the Ruby command. Use # `ruby_tool_command` if you want to get a command that you can execute. def self.locate_ruby_tool(name) result = locate_ruby_tool_by_basename(name) if !result exeext = rb_config['EXEEXT'] exeext = nil if exeext.empty? if exeext result = locate_ruby_tool_by_basename("#{name}#{exeext}") end if !result result = locate_ruby_tool_by_basename(transform_according_to_ruby_exec_format(name)) end if !result && exeext result = locate_ruby_tool_by_basename(transform_according_to_ruby_exec_format(name) + exeext) end end return result end # Locates a Ruby tool command, e.g. 'gem', 'rake', 'bundle', etc. Instead of # naively looking in $PATH, this function uses a variety of search heuristics # to find the command that's really associated with the current Ruby interpreter. # It should never locate a command that's actually associated with a different # Ruby interpreter. # Returns nil when nothing's found. # # Unlike `locate_ruby_tool`, which only returns the path to the tool script, # this method returns a full command that you can execute. The returned command # guarantees that the tool is run under the correct Ruby interpreter. def self.ruby_tool_command(name) path = locate_ruby_tool(name) if path if is_ruby_program?(path) "#{ruby_command} #{path}" else # The found tool is a wrapper script, e.g. in RVM's ~/.rvm/wrappers. # In this case, don't include the Ruby command in the result. path end else nil end end private def self.locate_ruby_tool_by_basename(name) if os_name_simple == "macosx" && ruby_command =~ %r(\A/System/Library/Frameworks/Ruby.framework/Versions/.*?/usr/bin/ruby\Z) # On OS X we must look for Ruby binaries in /usr/bin. # RubyGems puts executables (e.g. 'rake') in there, not in # /System/Libraries/(...)/bin. filename = "/usr/bin/#{name}" else filename = File.dirname(ruby_command) + "/#{name}" end if !File.file?(filename) || !File.executable?(filename) # RubyGems might put binaries in a directory other # than Ruby's bindir. Debian packaged RubyGems and # DebGem packaged RubyGems are the prime examples. begin require 'rubygems' unless defined?(Gem) filename = Gem.bindir + "/#{name}" rescue LoadError filename = nil end end if !filename || !File.file?(filename) || !File.executable?(filename) # Looks like it's not in the RubyGems bindir. Search in $PATH, but # be very careful about this because whatever we find might belong # to a different Ruby interpreter than the current one. ENV['PATH'].split(':').each do |dir| filename = "#{dir}/#{name}" if File.file?(filename) && File.executable?(filename) shebang = File.open(filename, 'rb') do |f| f.readline.strip end if shebang == "#!#{ruby_command}" # Looks good. break end end # Not found. Try next path. filename = nil end end filename end private_class_method :locate_ruby_tool_by_basename def self.is_ruby_program?(filename) File.open(filename, 'rb') do |f| return f.readline =~ /ruby/ end rescue EOFError return false end private_class_method :is_ruby_program? # Deduce Ruby's --program-prefix and --program-suffix from its install name # and transforms the given input name accordingly. # # transform_according_to_ruby_exec_format("rake") => "jrake", "rake1.8", etc def self.transform_according_to_ruby_exec_format(name) install_name = rb_config['RUBY_INSTALL_NAME'] if install_name.include?('ruby') format = install_name.sub('ruby', '%s') return sprintf(format, name) else return name end end private_class_method :transform_according_to_ruby_exec_format end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/zlib.rb000644 000765 000024 00000002764 12233035540 030132 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'platform_info' module PhusionPassenger module PlatformInfo def self.zlib_flags return nil end def self.zlib_libs return '-lz' end end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/depcheck_specs/apache2.rb000644 000765 000024 00000004761 12233035540 033437 0ustar00honglistaff000000 000000 define 'apache2' do name 'Apache 2' website 'http://httpd.apache.org/' define_checker do PhusionPassenger.require_passenger_lib 'platform_info/apache' if check_for_command(PlatformInfo.httpd) { :found => true, "Location of httpd" => PlatformInfo.httpd, "Apache version" => PlatformInfo.httpd_version } else false end end on :debian do apt_get_install "apache2-mpm-worker" end on :mandriva do urpmi "apache" end on :redhat do yum_install "httpd" end on :gentoo do emerge "apache" end end define 'apache2-dev' do name "Apache 2 development headers" website "http://httpd.apache.org/" define_checker do PhusionPassenger.require_passenger_lib 'platform_info/apache' if PlatformInfo.apxs2 { :found => true, "Location of apxs2" => PlatformInfo.apxs2 } else false end end on :debian do apt_get_install "apache2-threaded-dev" end on :mandriva do urpmi "apache-devel" end on :redhat do yum_install "httpd-devel" end on :gentoo do emerge "apache" end on :macosx do install_osx_command_line_tools end end define 'apr-dev' do name "Apache Portable Runtime (APR) development headers" website "http://httpd.apache.org/" define_checker do PhusionPassenger.require_passenger_lib 'platform_info/apache' if PlatformInfo.apr_config { :found => true, "Location" => PlatformInfo.apr_config, "Version" => `#{PlatformInfo.apr_config} --version`.strip } else false end end on :debian do apt_get_install "libapr1-dev" end on :mandriva do urpmi "libapr-devel" end on :redhat do yum_install "apr-devel" end on :gentoo do emerge "apr" end on :macosx do install_osx_command_line_tools end end define 'apu-dev' do name "Apache Portable Runtime Utility (APU) development headers" website "http://httpd.apache.org/" define_checker do PhusionPassenger.require_passenger_lib 'platform_info/apache' if PlatformInfo.apu_config { :found => true, "Location" => PlatformInfo.apu_config, "Version" => `#{PlatformInfo.apu_config} --version`.strip } else false end end on :debian do apt_get_install "libaprutil1-dev" end on :mandriva do urpmi "libapr-util-devel" end on :redhat do yum_install "apr-util-devel" end on :macosx do install_osx_command_line_tools end end src/ruby_supportlib/phusion_passenger/platform_info/depcheck_specs/compiler_toolchain.rb000644 000765 000024 00000003465 12233035540 035727 0ustar00honglistaff000000 000000 passenger-5.0.30define 'cc' do name "C compiler" website "http://gcc.gnu.org/" define_checker do PhusionPassenger.require_passenger_lib 'platform_info/compiler' check_for_command(PlatformInfo.cc, false) end on :debian do apt_get_install "build-essential" end on :mandriva do urpmi "gcc" end on :redhat do yum_install "gcc" end on :gentoo do emerge "gcc" end on :macosx do install_osx_command_line_tools end end define 'c++' do name "C++ compiler" website "http://gcc.gnu.org/" define_checker do PhusionPassenger.require_passenger_lib 'platform_info/compiler' check_for_command(PlatformInfo.cxx, false) end on :debian do apt_get_install "build-essential" end on :mandriva do urpmi "gcc-c++" end on :redhat do yum_install "gcc-c++" end on :gentoo do emerge "gcc" end on :macosx do install_osx_command_line_tools end end define 'make' do name "The 'make' tool" define_checker do PhusionPassenger.require_passenger_lib 'platform_info/compiler' check_for_command(PlatformInfo.make) end on :debian do apt_get_install "build-essential" end on :mandriva do urpmi "make" end on :redhat do yum_install "make" end on :macosx do install_osx_command_line_tools end on :other_platforms do website "http://www.gnu.org/software/make/" end end define 'gmake' do name "GNU make" define_checker do PhusionPassenger.require_passenger_lib 'platform_info/compiler' check_for_command(PlatformInfo.gnu_make) end on :debian do apt_get_install "build-essential" end on :mandriva do urpmi "make" end on :redhat do yum_install "make" end on :macosx do install_osx_command_line_tools end on :other_platforms do website "http://www.gnu.org/software/make/" end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/depcheck_specs/gems.rb000644 000765 000024 00000000403 12233035540 033054 0ustar00honglistaff000000 000000 define 'fastthread' do name 'fastthread' define_checker do check_for_ruby_library('fastthread') end gem_install 'fastthread' end define 'rack' do name 'rack' define_checker do check_for_ruby_library('rack') end gem_install 'rack' end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/depcheck_specs/libs.rb000644 000765 000024 00000006360 12233035540 033062 0ustar00honglistaff000000 000000 define 'openssl-dev' do name "OpenSSL development headers" website "http://www.openssl.org/" define_checker do check_for_header('openssl/ssl.h', :c, PlatformInfo.openssl_extra_cflags) end on :debian do apt_get_install "libssl-dev" end on :redhat do yum_install "openssl-devel" end on :macosx do brew_install "openssl" end end define 'libcurl-dev' do name "Curl development headers with SSL support" website "http://curl.haxx.se/libcurl" define_checker do PhusionPassenger.require_passenger_lib 'platform_info/curl' result = { :found => false } if !(curl_config = PlatformInfo.find_command('curl-config')) result[:error] = "Cannot find the `curl-config` command." next result else result["curl-config location"] = curl_config end if !(header = PlatformInfo.find_header("curl/curl.h", :c, PlatformInfo.curl_flags)) result[:error] = "Cannot find the curl/curl.h header file." next result else result[:found] = true result["Header location"] = header == true ? "somewhere, not sure where" : header end begin result["Version"] = `#{curl_config} --version`.strip rescue SystemCallError => e result[:error] = "Cannot run `curl-config --version`: #{e}" next result end source = %Q{ #include int main() { curl_global_init(CURL_GLOBAL_ALL); return 0; } } ret = PlatformInfo.try_compile_and_run("Checking for libcurl usability", :c, source, "#{PlatformInfo.curl_flags} #{PlatformInfo.curl_libs}") result["Usable"] = ret ? "yes" : "no" if !ret result[:error] = "libcurl was found, but it isn't usable. Set VERBOSE=1 to see why." next result end result["Supports SSL"] = PlatformInfo.curl_supports_ssl? ? "yes" : "no" if !PlatformInfo.curl_supports_ssl? result[:error] = "libcurl was found, but it doesn't support SSL. Please reinstall it with SSL support." next result end result end install_instructions "Please download Curl from #{website} " + "and make sure you install it with SSL support." on :debian do install_instructions "Please run " + "apt-get install libcurl4-openssl-dev " + "or libcurl4-gnutls-dev, whichever you prefer." end on :redhat do release = PlatformInfo.read_file("/etc/redhat-release") if release =~ /release 4/ # http://code.google.com/p/phusion-passenger/issues/detail?id=554 yum_install "curl-devel zlib-devel e2fsprogs-devel krb5-devel libidn-devel" elsif release =~ /release 5/ yum_install "curl-devel" else yum_install "libcurl-devel" end end end define 'zlib-dev' do name "Zlib development headers" website "http://www.zlib.net/" define_checker do check_for_header('zlib.h') end on :debian do apt_get_install "zlib1g-dev" end on :mandriva do urpmi "zlib1-devel" end on :redhat do yum_install "zlib-devel" end end define 'pcre-dev' do name "PCRE development headers" website "http://www.pcre.org/" define_checker do check_for_header('pcre.h') end on :debian do apt_get_install "libpcre3-dev" end on :redhat do yum_install 'pcre-devel' end endpassenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/depcheck_specs/ruby.rb000644 000765 000024 00000010077 12233035540 033112 0ustar00honglistaff000000 000000 define 'ruby-dev' do name "Ruby development headers" website "http://www.ruby-lang.org/" define_checker do require 'rbconfig' begin require 'mkmf' rb_config = PlatformInfo.rb_config header_dir = rb_config['rubyhdrdir'] || rb_config['archdir'] filename = "#{header_dir}/ruby.h" if File.exist?(filename) { :found => true, "Location" => filename } else false end rescue LoadError, SystemExit # On RedHat/Fedora/CentOS, if ruby-devel is not installed then # mkmf.rb will print an error and call 'exit'. So here we # catch SystemExit as well. false rescue NotImplementedError # JRuby raises this. false end end if ruby_command =~ %r(^/usr/bin/ruby) || ruby_command =~ %r(^/System/Library/Frameworks/Ruby.framework) # Only tell user to install the headers with the system's package manager # if Ruby itself was installed with the package manager. on :debian do apt_get_install "ruby-dev" end on :mandriva do urpmi "ruby-devel" end on :redhat do yum_install "ruby-devel" end on :macosx do install_osx_command_line_tools end end on :other_platforms do install_instructions "Please (re)install Ruby by downloading it from #{website}" end end define 'ruby-openssl' do name "OpenSSL support for Ruby" if RUBY_PLATFORM =~ /java/ website "http://jruby.org/openssl" install_instructions "Please install OpenSSL support for JRuby: #{website}" else website "http://www.ruby-lang.org/" install_instructions "Please (re)install Ruby with OpenSSL support." end define_checker do begin require 'openssl' { :found => true } rescue LoadError false end end if ruby_command =~ %r(^/usr/bin/ruby) # Only tell user to install ruby-openssl with the system's package manager # if Ruby itself was installed with the package manager. on :debian do apt_get_install "libopenssl-ruby" end end end define 'rubygems' do name "RubyGems" website "http://rubyforge.org/frs/?group_id=126" define_checker do begin require 'rubygems' { :found => true } rescue LoadError false end end install_instructions "Please download it from #{website}. " + "Extract the tarball, and run ruby setup.rb" if ruby_command =~ %r(^/usr/bin/ruby) # Only tell user to install RubyGems with the system's package manager # if Ruby itself was installed with the package manager. # # Older versions of Debian have totally messed up RubyGems by patching it to install binaries # to /var/lib/gems/bin instead of /usr/bin or even /usr/local/bin. That # wouldn't be so much of a problem were it not for the fact that # /var/lib/gems/bin is not in $PATH by default, so on a regular basis people # ask various Ruby/Rails support forums why they get a 'foo: command not found' # after typing 'gem install foo'. # # Luckily newer Debian versions fixed this problem. on :debian do apt_get_install "rubygems" end end end # The 'rake' spec looks for a Rake instance that's installed for the same # Ruby interpreter as the one that's currently running. # For example if you're running this 'rake.rb' file with Ruby 1.8, then # this checker will not find Ruby 1.9's Rake or JRuby's Rake. define 'rake' do name "Rake (associated with #{ruby_command})" website "http://rake.rubyforge.org/" define_checker do PhusionPassenger.require_passenger_lib 'platform_info/ruby' if result = PlatformInfo.rake_command { :found => true, "Location" => result } else false end end if ruby_command =~ %r(^/usr/bin/ruby) # Only tell user to install Rake with the system's package manager # if Ruby itself was installed with the package manager. on :debian do apt_get_install "rake" end on :mandriva do urpmi "rake" end on :redhat do yum_install "rubygem-rake", :epel => true end end on :other_platforms do gem_install "rake" end end passenger-5.0.30/src/ruby_supportlib/phusion_passenger/platform_info/depcheck_specs/utilities.rb000644 000765 000024 00000000642 12233035540 034141 0ustar00honglistaff000000 000000 define 'download-tool' do name "A download tool like 'wget' or 'curl'" define_checker do check_for_command('wget') || check_for_command('curl') end on :debian do apt_get_install "wget curl" end on :redhat do yum_install "wget curl" end on :other_platforms do install_instructions "Please install either wget (http://www.gnu.org/software/wget/) or curl (http://curl.haxx.se/)." end endpassenger-5.0.30/src/ruby_supportlib/phusion_passenger/nginx/config_options.rb000644 000765 000024 00000030562 12233035540 030473 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2013-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. # This file defines all supported Nginx per-location configuration options. The # build system automatically generates the corresponding Nginx module boilerplate # code from the definitions in this file. # # Main configuration options are not defined in this file, but are defined in # src/nginx_module/Configuration.c instead. # # The following boilerplate code is generated: # # * ngx_command_t array members (ConfigurationCommands.c.erb) # * Location configuration structure definition (ConfigurationFields.h.erb) # * Location configuration structure initialization (CreateLocationConfig.c.erb) # * Location configuration merging (MergeLocationConfig.c.erb) # * Conversion of configuration options to CGI headers (CacheLocationConfig.c.erb) # # Options: # # * name - The configuration option name. Required. # * context - The context in which this configuration option is valid. # Defaults to [:main, :srv, :loc, :lif] # * type - This configuration option's value type. Allowed types: # :string, :integer, :uinteger, :flag, :string_array, :string_keyval, # :path # * take - Tells Nginx how many parameters and what kind of parameter # this configuration option takes. It should be set to a string # such as "NGX_CONF_FLAG". # By default this is automatically inferred from `type`: for # example if `type` is :string then ConfigurationCommands.c.erb # will infer that `NGX_CONF_TAKE1` should be used. # * function - The name of the function that should be used to store the # configuration value into the corresponding structure. This function # is not auto-generated, so it must be the name of an existing # function. By default, the function name is automatically inferred # from `type`. For example if `type` is string then `function` is # inferred to be `ngx_conf_set_str_slot`. # If you set this to a string then you are responsible for defining # said function in Configuration.c. # * struct - The type of the struct that the field is contained in. Something like # "NGX_HTTP_LOC_CONF_OFFSET" (which is also the default). # * field - The name that should be used for the auto-generated field in # the location configuration structure. Defaults to the configuration # name without the 'passenger_' prefix. Set this to nil if you do not # want a structure field to be auto-generated. If the field name contains # a dot (.e.g `upstream_config.pass_headers`) then the structure field will # also not be auto-generated, because it is assumed to belong to an existing # structure field. # * post - The extra information needed by function for post-processing. # * header - The name of the corresponding CGI header. By default CGI header # generation code is automatically generated, using the configuration # option's name in uppercase as the CGI header name. # Setting this to nil, or setting `field` to a value containing a dot, # will disable auto-generation of CGI header generation code. You are # then responsible for writing CGI header passing code yourself in # ContentHandler.c. # * auto_generate_nginx_merge_code - Whether location configuration merging # code should be automatically generated. Defaults to true. If you set # this to false then you are responsible for writing merging code # yourself in Configuration.c. # * alias_for - Set this if this configuration option is an alias for another # option. Alias definitions must only have the `name` and `alias_for` # fields, nothing else. LOCATION_CONFIGURATION_OPTIONS = [ { :name => 'passenger_socket_backlog', :type => :integer, :context => [:main], :struct => "NGX_HTTP_MAIN_CONF_OFFSET" }, { :name => 'passenger_core_file_descriptor_ulimit', :type => :uinteger, :context => [:main], :struct => 'NGX_HTTP_MAIN_CONF_OFFSET' }, { :name => 'passenger_app_file_descriptor_ulimit', :type => :uinteger }, { :name => 'passenger_enabled', :context => [:srv, :loc, :lif], :type => :flag, :function => 'passenger_enabled', :field => 'enabled', :header => nil }, { :name => 'passenger_ruby', :context => [:srv, :loc, :lif], :type => :string }, { :name => 'passenger_python', :type => :string }, { :name => 'passenger_nodejs', :type => :string }, { :name => 'passenger_meteor_app_settings', :type => :string }, { :name => 'passenger_app_env', :type => :string, :field => 'environment' }, { :name => 'passenger_friendly_error_pages', :type => :flag }, { :name => 'passenger_min_instances', :type => :integer, :header => 'PASSENGER_MIN_PROCESSES' }, { :name => 'passenger_max_instances_per_app', :context => [:main], :type => :integer, :header => 'PASSENGER_MAX_PROCESSES' }, { :name => 'passenger_max_requests', :type => :integer }, { :name => 'passenger_start_timeout', :type => :integer }, { :name => 'passenger_base_uri', :type => :string_array, :field => 'base_uris', :header => nil }, { :name => 'passenger_document_root', :type => :string, :header => nil }, { :name => 'passenger_user', :type => :string }, { :name => 'passenger_group', :type => :string }, { :name => 'passenger_app_group_name', :type => :string }, { :name => 'passenger_app_root', :type => :string }, { :name => 'passenger_app_rights', :type => :string }, { :name => 'union_station_support', :type => :flag }, { :name => 'union_station_filter', :take => 'NGX_CONF_TAKE1', :type => :string_array, :function => 'union_station_filter', :field => 'union_station_filters', :header => nil }, { :name => 'passenger_debugger', :type => :flag }, { :name => 'passenger_max_preloader_idle_time', :type => :integer }, { :name => 'passenger_ignore_headers', :take => 'NGX_CONF_1MORE', :function => 'ngx_conf_set_bitmask_slot', :field => 'upstream_config.ignore_headers', :post => '&ngx_http_upstream_ignore_headers_masks' }, { :name => 'passenger_env_var', :type => :string_keyval, :field => 'env_vars', :header => nil }, { :name => 'passenger_set_header', :type => :string_keyval, :field => 'headers_source', :header => nil, :auto_generate_nginx_create_code => false, :auto_generate_nginx_merge_code => false }, { :name => 'passenger_pass_header', :type => :string_array, :field => 'upstream_config.pass_headers' }, { :name => 'passenger_headers_hash_max_size', :type => :uinteger, :header => nil, :default => 512 }, { :name => 'passenger_headers_hash_bucket_size', :type => :uinteger, :header => nil, :default => 64 }, { :name => 'passenger_ignore_client_abort', :type => :flag, :field => 'upstream_config.ignore_client_abort' }, { :name => 'passenger_buffer_response', :type => :flag, :field => 'upstream_config.buffering' }, { :name => 'passenger_buffer_size', :take => 'NGX_CONF_TAKE1', :function => 'ngx_conf_set_size_slot', :field => 'upstream_config.buffer_size' }, { :name => 'passenger_buffers', :take => 'NGX_CONF_TAKE2', :function => 'ngx_conf_set_bufs_slot', :field => 'upstream_config.bufs' }, { :name => 'passenger_busy_buffers_size', :take => 'NGX_CONF_TAKE1', :function => 'ngx_conf_set_size_slot', :field => 'upstream_config.busy_buffers_size_conf' }, { :name => 'passenger_intercept_errors', :type => :flag, :field => 'upstream_config.intercept_errors' }, { :name => 'passenger_spawn_method', :type => :string }, { :name => 'passenger_load_shell_envvars', :type => :flag }, { :name => 'union_station_key', :type => :string }, { :name => 'passenger_max_request_queue_size', :type => :integer }, { :name => 'passenger_request_queue_overflow_status_code', :type => :integer }, { :name => 'passenger_restart_dir', :type => :string }, { :name => 'passenger_app_type', :type => :string, :header => nil }, { :name => 'passenger_startup_file', :type => :string }, { :name => 'passenger_sticky_sessions', :type => :flag }, { :name => 'passenger_sticky_sessions_cookie_name', :type => :string }, { :name => 'passenger_vary_turbocache_by_cookie', :type => :string }, { :name => 'passenger_abort_websockets_on_process_shutdown', :type => :flag }, { :name => 'passenger_force_max_concurrent_requests_per_process', :type => :integer }, ###### Enterprise features ###### { :context => [:main], :name => 'passenger_fly_with', :type => :string, :struct => "NGX_HTTP_MAIN_CONF_OFFSET", :function => 'passenger_enterprise_only', :field => nil }, { :name => 'passenger_max_instances', :type => :integer, :function => 'passenger_enterprise_only', :field => nil }, { :name => 'passenger_max_request_time', :type => :integer, :function => 'passenger_enterprise_only', :field => nil }, { :name => 'passenger_memory_limit', :type => :integer, :function => 'passenger_enterprise_only', :field => nil }, { :name => 'passenger_concurrency_model', :type => :string, :function => 'passenger_enterprise_only', :field => nil }, { :name => 'passenger_thread_count', :type => :integer, :function => 'passenger_enterprise_only', :field => nil }, { :name => 'passenger_rolling_restarts', :type => :flag, :function => 'passenger_enterprise_only', :field => nil }, { :name => 'passenger_resist_deployment_errors', :type => :flag, :function => 'passenger_enterprise_only', :field => nil }, ###### Aliases for backwards compatibility ###### { :name => 'rails_spawn_method', :alias_for => 'passenger_spawn_method' }, { :name => 'rails_env', :alias_for => 'passenger_app_env' }, { :name => 'rack_env', :alias_for => 'passenger_app_env' }, { :name => 'rails_app_spawner_idle_time', :alias_for => 'passenger_max_preloader_idle_time' }, ###### Obsolete options ###### { :name => 'rails_framework_spawner_idle_time', :take => 'NGX_CONF_TAKE1', :function => 'rails_framework_spawner_idle_time', :field => nil }, { :name => 'passenger_use_global_queue', :take => 'NGX_CONF_FLAG', :function => 'passenger_use_global_queue', :field => nil } ] passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/about_command.rb000644 000765 000024 00000024032 12233035540 030400 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'config/command' module PhusionPassenger module Config class AboutCommand < Command def self.help puts "Usage: passenger-config about " puts puts " Show information about #{PROGRAM_NAME}." puts puts "Available subcommands:" puts " root Show #{PROGRAM_NAME}'s root." puts " ruby-libdir Show #{PROGRAM_NAME}'s Ruby library directory." puts " includedir Show the Nginx runtime library headers directory." puts " nginx-addon-dir Show #{PROGRAM_NAME}'s Nginx addon directory." puts " nginx-libs Show paths to #{PROGRAM_NAME}'s libraries" puts " for static linking to Nginx runtime." puts " nginx-dynamic-libs Show paths to #{PROGRAM_NAME}'s libraries" puts " for dynamic linking to Nginx runtime." puts " nginx-dynamic-compiled Check whether runtime libraries for dynamic" puts " linking to Nginx are compiled." puts " resourcesdir Show #{PROGRAM_NAME}'s resources directory." puts " support-binaries-dir Show #{PROGRAM_NAME}'s support binaries dir." puts " compiled Check whether runtime libraries are compiled." puts " custom-packaged Check whether Phusion Passenger is custom" puts " packaged." puts " installed-from-release-package Check whether this installation came from" puts " an official release package." puts " make-locations-ini Generate a locations.ini based on the current" puts " install paths." puts " detect-apache2 Autodetect Apache installations." puts " ruby-command Show the correct command for invoking the Ruby" puts " interpreter." puts " rubyext-compat-id Show the Ruby extension binary compatibility ID." puts " cxx-compat-id Show the C++ binary compatibility ID." puts " version Show the version number." end def run PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/compiler' subcommand = @argv[0].to_s.dup # Compatibility with version <= 4.0.29: accept both # 'subcommand' and '--subcommand'. subcommand = "--#{subcommand}" if subcommand !~ /^--/ case subcommand when "--root" puts PhusionPassenger.install_spec when "--ruby-libdir" puts PhusionPassenger.ruby_libdir when "--includedir" puts PhusionPassenger.include_dir when "--nginx-addon-dir" puts PhusionPassenger.nginx_module_source_dir when "--nginx-libs" puts "#{common_library.link_objects_as_string} #{PhusionPassenger.lib_dir}/common/libboost_oxt.a" when "--nginx-dynamic-libs" puts "#{common_dynamic_library.link_objects_as_string} #{PhusionPassenger.lib_dir}/nginx_dynamic/libboost_oxt.a" when "--resourcesdir" puts PhusionPassenger.resources_dir when "--support-binaries-dir" puts PhusionPassenger.support_binaries_dir when "--compiled" common_library.link_objects.each do |filename| if !File.exist?(filename) exit 1 end end if File.exist?("#{PhusionPassenger.lib_dir}/common/libboost_oxt.a") exit 0 else exit 1 end when "--nginx-dynamic-compiled" common_dynamic_library.link_objects.each do |filename| if !File.exist?(filename) exit 1 end end if File.exist?("#{PhusionPassenger.lib_dir}/nginx_dynamic/libboost_oxt.a") exit 0 else exit 1 end when "--custom-packaged" if PhusionPassenger.custom_packaged? exit 0 else exit 1 end when "--installed-from-release-package" if PhusionPassenger.installed_from_release_package? exit 0 else exit 1 end when "--make-locations-ini" if @argv[1] =~ /^--for-(native-)?packaging-method=(.*)/ packaging_method = $2 else packaging_method = nil end puts "[locations]" if packaging_method puts "packaging_method=#{packaging_method}" else if PhusionPassenger.custom_packaged? puts "packaging_method=#{PhusionPassenger.packaging_method}" else puts "packaging_method=unknown" end end PhusionPassenger::REQUIRED_LOCATIONS_INI_FIELDS.each do |field| puts "#{field}=#{PhusionPassenger.send(field)}" end PhusionPassenger::OPTIONAL_LOCATIONS_INI_FIELDS.each do |field| value = PhusionPassenger.send(field) puts "#{field}=#{value}" if value end when "--detect-apache2" PhusionPassenger.require_passenger_lib 'platform_info/apache_detector' detector = PhusionPassenger::PlatformInfo::ApacheDetector.new(STDOUT) STDOUT.write(Utils::AnsiColors::DEFAULT_TERMINAL_COLOR) STDOUT.flush begin detector.detect_all detector.report ensure detector.finish STDOUT.write(Utils::AnsiColors::RESET) STDOUT.flush end when "--ruby-command" PhusionPassenger.require_passenger_lib 'platform_info/ruby' ruby = PhusionPassenger::PlatformInfo.ruby_command puts "passenger-config was invoked through the following Ruby interpreter:" puts " Command: #{ruby}" STDOUT.write " Version: " STDOUT.flush system("/bin/sh -c '#{ruby} -v'") puts " To use in Apache: PassengerRuby #{ruby}" puts " To use in Nginx : passenger_ruby #{ruby}" puts " To use with Standalone: #{ruby} #{PhusionPassenger.bin_dir}/passenger start" puts ruby = PhusionPassenger::PlatformInfo.find_command('ruby') if ruby && !ruby.include?("rvm/rubies/") # If this is an RVM Ruby executable then we don't show it. We want people to # use the RVM wrapper scripts only. puts "The following Ruby interpreter was found first in $PATH:" puts " Command: #{ruby}" STDOUT.write " Version: " STDOUT.flush system("/bin/sh -c '#{ruby} -v'") puts " To use in Apache: PassengerRuby #{ruby}" puts " To use in Nginx : passenger_ruby #{ruby}" puts " To use with Standalone: #{ruby} #{PhusionPassenger.bin_dir}/passenger start" elsif !ruby.include?("rvm/rubies/") puts "No Ruby interpreter found in $PATH." end puts puts "## Notes for RVM users" puts "Do you want to know which command to use for a different Ruby interpreter? 'rvm use' that Ruby interpreter, then re-run 'passenger-config about ruby-command'." when "--rubyext-compat-id" PhusionPassenger.require_passenger_lib 'platform_info/binary_compatibility' puts PhusionPassenger::PlatformInfo.ruby_extension_binary_compatibility_id when "--cxx-compat-id" PhusionPassenger.require_passenger_lib 'platform_info/binary_compatibility' puts PhusionPassenger::PlatformInfo.cxx_binary_compatibility_id when "--version" if defined?(PhusionPassenger::PASSENGER_IS_ENTERPRISE) name = "#{PhusionPassenger::PROGRAM_NAME} Enterprise" else name = PhusionPassenger::PROGRAM_NAME end puts "#{name} #{PhusionPassenger::VERSION_STRING}" when "--help" self.class.help else self.class.help exit 1 end end private def common_library PhusionPassenger.require_passenger_lib 'common_library' return COMMON_LIBRARY. only(*NGINX_LIBS_SELECTOR). set_output_dir("#{PhusionPassenger.lib_dir}/common/libpassenger_common") end def common_dynamic_library PhusionPassenger.require_passenger_lib 'common_library' return COMMON_LIBRARY. only(*NGINX_LIBS_SELECTOR). set_output_dir("#{PhusionPassenger.lib_dir}/nginx_dynamic/module_libpassenger_common") end end end # module Config end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/agent_compiler.rb000644 000765 000024 00000013242 12233035540 030561 0ustar00honglistaff000000 000000 # encoding: utf-8 # # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'fileutils' require 'logger' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'abstract_installer' PhusionPassenger.require_passenger_lib 'config/installation_utils' PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'utils/shellwords' PhusionPassenger.require_passenger_lib 'utils/progress_bar' PhusionPassenger.require_passenger_lib 'utils/tmpio' module PhusionPassenger module Config class AgentCompiler < AbstractInstaller protected def dependencies specs = [ 'depcheck_specs/compiler_toolchain', 'depcheck_specs/ruby', 'depcheck_specs/gems', 'depcheck_specs/libs', 'depcheck_specs/utilities' ] ids = [ 'cc', 'c++', 'rake', 'libcurl-dev', 'openssl-dev', 'zlib-dev' ].compact return [specs, ids] end def install_doc_url "https://www.phusionpassenger.com/library/install/standalone/" end def troubleshooting_doc_url "https://www.phusionpassenger.com/library/admin/standalone/troubleshooting/" end def run_steps check_source_code_available! if !@force check_whether_os_is_broken check_whether_system_has_enough_ram InstallationUtils.check_for_download_tool! end check_dependencies(false) || abort puts @destdir = InstallationUtils.find_or_create_writable_support_binaries_dir! confirm_enable_optimizations compile_agent end def before_install super if !@working_dir @working_dir = PhusionPassenger::Utils.mktmpdir("passenger-install.", PlatformInfo.tmpexedir) @owns_working_dir = true end end def after_install super FileUtils.remove_entry_secure(@working_dir) if @owns_working_dir end private def check_source_code_available! if PhusionPassenger.build_system_dir.nil? puts "Cannot compile agent binary" puts puts "This #{PROGRAM_NAME} installation does not " + "come with any source code, so the agent binary cannot " + "be compiled. So instead, please try to download a " + "precompiled agent binary from the #{PROGRAM_NAME} website, " + "by running:\n\n" + " passenger-config download-agent" abort end end def confirm_enable_optimizations if @auto if @optimize puts "Compiling with optimizations." else puts "Not compiling with optimizations." end else if @optimize puts "Compiling with optimizations." else new_screen render_template 'config/agent_compiler/confirm_enable_optimizations', :total_ram => total_ram_gb puts @optimize = prompt_confirmation('Compile with optimizations?') puts end end end def compile_agent puts "Compiling #{PROGRAM_NAME} agent..." progress_bar = ProgressBar.new e_working_dir = Shellwords.escape(@working_dir) args = "#{e_working_dir}/support-binaries/#{AGENT_EXE}" + " CACHING=false" + " OUTPUT_DIR=#{e_working_dir} " " OPTIMIZE=#{!!@optimize}" begin progress_bar.set(0) Dir.chdir(PhusionPassenger.build_system_dir) do InstallationUtils.run_rake_task!(args) do |progress, total| progress_bar.set(0.05 + (progress / total.to_f) * 0.95) end end progress_bar.set(1) ensure progress_bar.finish end FileUtils.cp("#{@working_dir}/support-binaries/#{AGENT_EXE}", "#{@destdir}/#{AGENT_EXE}") puts "Compilation finished!" end def total_ram_gb begin meminfo = File.read("/proc/meminfo") if meminfo =~ /^MemTotal: *(\d+) kB$/ return sprintf("%.1f", $1.to_i / 1024 / 1024) end rescue Errno::ENOENT, Errno::EACCES # Don't do anything on systems without memory information. return nil end end end end # module Standalone end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/api_call_command.rb000644 000765 000024 00000020724 12233035540 031036 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'optparse' require 'net/http' require 'socket' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'admin_tools/instance_registry' PhusionPassenger.require_passenger_lib 'config/command' PhusionPassenger.require_passenger_lib 'config/utils' PhusionPassenger.require_passenger_lib 'utils/json' module PhusionPassenger module Config class ApiCallCommand < Command include PhusionPassenger::Config::Utils def self.create_default_options { :agent_name => "core_api" } end def run parse_options initialize_objects infer_socket_path_and_credentials invoke end private def self.create_option_parser(options) OptionParser.new do |opts| nl = "\n" + ' ' * 37 opts.banner = "Usage: passenger-config api-call [OPTIONS]\n" opts.separator "" opts.separator " Makes an API call to one of the #{PROGRAM_NAME} agents. #{PROGRAM_NAME}" opts.separator " listens on a local HTTP server for admin commands. Many `passenger-config`" opts.separator " commands are just shortcuts for sending HTTP API calls to the" opts.separator " #{PROGRAM_NAME} admin HTTP server. `passenger-config api-call` allows" opts.separator " you to send API calls directly." opts.separator "" opts.separator " METHOD is an HTTP verb, like 'GET', 'POST', 'PUT' or 'DELETE'." opts.separator " PATH is the admin URI. You can pass POST data with '-d'." opts.separator "" opts.separator " Example 1: passenger-config api-call GET /server.json" opts.separator " Sends the 'GET /server.json' command to the Passenger core process." opts.separator "" opts.separator " Example 2: passenger-config api-call PUT /config.json \\" opts.separator " -d '{\"log_level\", 7}'" opts.separator " Sends the 'PUT /config.json' command to the Passenger core process, with" opts.separator " the given PUT data." opts.separator "" opts.separator " Example 3: passenger-config api-call POST /shutdown.json -a watchdog_api" opts.separator " Sends the 'POST /shutdown.json' command to the watchdog, with no POST data." opts.separator "" opts.separator " Example 4: passenger-config api-call POST /shutdown.json \\" opts.separator " -S /tmp/watchdog.sock" opts.separator " Sends the 'POST /shutdown.json' command to the watchdog listening at the" opts.separator " specific socket file /tmp/watchdog.sock. No POST data." opts.separator "" opts.separator "Options:" opts.on("-d", "--data DATA", String, "Specify HTTP request body data") do |value| options[:data] = value end opts.on("-i", "--stdin", "Read HTTP request body data from stdin") do options[:data_source] = :stdin end opts.on("-f", "--data-file PATH", String, "Read HTTP request body data from the given#{nl}" + "file") do |value| options[:data_source] = value end opts.on("-a", "--agent NAME", String, "The name of the socket to send the command#{nl}" + "to. This specifies which agent the request#{nl}" + "is sent to. Choices: watchdog_api,#{nl}" + "core_api, ust_router_api.#{nl}" + "Default: core_api") do |val| options[:agent_name] = val end opts.on("-S", "--socket PATH", String, "Instead of inferring the socket path from#{nl}" + "the #{PROGRAM_NAME} instance directory#{nl}" + "and agent name, send the command to a#{nl}" + "specific Unix domain socket directly") do |val| options[:socket_path] = val end opts.on("--show-headers", "Show HTTP response headers") do options[:show_headers] = true end opts.on("--ignore-response-code", "Exit successfully even if a non-2xx#{nl}" + "response was returned") do options[:ignore_response_code] = true end opts.on("--instance NAME", String, "The #{PROGRAM_NAME} instance to select") do |value| options[:instance] = value end opts.on("-h", "--help", "Show this help") do options[:help] = true end end end def initialize_objects if @argv.size != 2 abort "You've passed to few arguments. See --help for more information." end @method = @argv[0] @path = @argv[1] case @method.upcase when "GET" @request = Net::HTTP::Get.new(@path) when "POST" @request = Net::HTTP::Post.new(@path) when "PUT" @request = Net::HTTP::Put.new(@path) when "DELETE" @request = Net::HTTP::Delete.new(@path) else abort "Unknown method #{@method.inspect}. Please specify either GET, POST, PUT or DELETE." end if @path !~ /\A\// abort "The path must start with a slash (/). See --help for more information." end if @options[:data] && @options[:data_source] abort "You cannot specify both --data and --stdin/--data-file. Please choose either one." end if @options[:data_source] == :stdin STDIN.binmode @options[:data] = STDIN.read elsif @options[:data_source] File.open(@options[:data_source], "rb") do |f| @options[:data] = f.read end end end def infer_socket_path_and_credentials if @options[:socket_path] @socket_path = @options[:socket_path] else select_passenger_instance @socket_path = "#{@instance.path}/agents.s/#{@options[:agent_name]}" begin @password = @instance.full_admin_password rescue Errno::EACCES end end end def invoke if @password @request.basic_auth("admin", @password) end @request["connection"] = "close" if @options[:data] @request.content_type = "application/json" @request.body = @options[:data] end sock = Net::BufferedIO.new(UNIXSocket.new(@socket_path)) begin @request.exec(sock, "1.1", @request.path) done = false while !done response = Net::HTTPResponse.read_new(sock) done = !response.kind_of?(Net::HTTPContinue) end response.reading_body(sock, @request.response_body_permitted?) do # Nothing end ensure sock.close end if @options[:show_headers] print_headers(response) end puts response.body if !@options[:ignore_response_code] && response.code.to_i / 100 != 2 abort end end def print_headers(response) puts "HTTP/1.1 #{response.message} #{response.code}" response.each_header do |name, val| puts "#{name}: #{val}" end puts end end end # module Config end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/build_native_support_command.rb000644 000765 000024 00000006202 12233035540 033526 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'optparse' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'config/command' module PhusionPassenger module Config class BuildNativeSupportCommand < Command def run parse_options PhusionPassenger.require_passenger_lib 'native_support' end private def self.create_option_parser(options) PhusionPassenger.require_passenger_lib 'platform_info/ruby' OptionParser.new do |opts| nl = "\n" + ' ' * 37 opts.banner = "Usage: passenger-config build-native-support [OPTIONS]\n" opts.separator "" opts.separator " #{PROGRAM_NAME} utilizes a Ruby native extension, called native_support," opts.separator " for improving Ruby performance. The extension depends on the" opts.separator " #{PROGRAM_NAME} version and the Ruby version. Normally, every time you run" opts.separator " a #{PROGRAM_NAME} version with a Ruby version that it hasn't encountered" opts.separator " before, it will rebuild the native_support library for that Ruby version." opts.separator " By running this command, you can force the native_support to be built for" opts.separator " the current Ruby interpreter." opts.separator "" opts.separator " The current Ruby interpreter is:" opts.separator " Path: #{PlatformInfo.ruby_command}" opts.separator " Version: #{RUBY_VERSION}" opts.separator "" opts.separator "Options:" opts.on("-h", "--help", "Show this help") do options[:help] = true end end end def help puts @parser end def parse_options super if @argv.size > 0 help abort end end end end # module Config end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/command.rb000644 000765 000024 00000004062 12233035540 027207 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2013 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module PhusionPassenger module Config class Command def initialize(argv) @argv = argv.dup @options = self.class.create_default_options end def run_and_get_exit_code begin run exit(0) rescue SystemExit => e e.status end end private def self.create_default_options return {} end def parse_options @parser = self.class.create_option_parser(@options) begin @parser.parse!(@argv) rescue OptionParser::ParseError => e STDERR.puts "*** ERROR: #{e}" abort @parser.to_s end if @options[:help] puts @parser exit end end end end # module Config end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/compile_agent_command.rb000644 000765 000024 00000007435 12233035540 032104 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'optparse' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'config/command' PhusionPassenger.require_passenger_lib 'config/agent_compiler' PhusionPassenger.require_passenger_lib 'utils/ansi_colors' module PhusionPassenger module Config class CompileAgentCommand < Command include InstallationUtils def run @options = { :auto => !STDIN.tty? || !STDOUT.tty?, :colorize => :auto, :force_tip => true } parse_options initialize_objects sanity_check if !AgentCompiler.new(@options).run abort end end private def self.create_option_parser(options) OptionParser.new do |opts| nl = "\n" + ' ' * 37 opts.banner = "Usage: passenger-config compile-agent [OPTIONS]\n" opts.separator "" opts.separator " Compile the #{PROGRAM_NAME} agent binary. The agent binary is required for" opts.separator " #{PROGRAM_NAME} to function properly." opts.separator "" opts.separator "Options:" opts.on("--working-dir PATH", String, "Store temporary files in the given#{nl}" + "directory, instead of creating one") do |val| options[:working_dir] = val end opts.on("--auto", "Run in non-interactive mode. Default when#{nl}" + "stdin or stdout is not a TTY") do options[:auto] = true end opts.on("--optimize", "Compile agent with optimizations") do options[:optimize] = true end opts.on("-f", "--force", "Skip sanity checks") do options[:force] = true end opts.on("--no-force-tip", "Do not print any tips regarding the --force parameter") do options[:force_tip] = false end opts.on("-h", "--help", "Show this help") do options[:help] = true end end end def help puts @parser end def initialize_objects @colors = Utils::AnsiColors.new(@options[:colorize]) end def sanity_check return if @options[:force] if PhusionPassenger.find_support_binary(AGENT_EXE) puts "#{@colors.green}The #{PROGRAM_NAME} agent is already installed.#{@colors.reset}" if @options[:force_tip] puts "If you want to recompile it, re-run this program with the --force parameter." end exit end end end end # module Config end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/compile_nginx_engine_command.rb000644 000765 000024 00000011462 12233035540 033451 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'optparse' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'config/command' PhusionPassenger.require_passenger_lib 'config/nginx_engine_compiler' PhusionPassenger.require_passenger_lib 'utils/ansi_colors' module PhusionPassenger module Config class CompileNginxEngineCommand < Command include InstallationUtils def run @options = { :colorize => :auto, :force_tip => true, :connect_timeout => 30, :idle_timeout => 30 } parse_options initialize_objects sanity_check if !NginxEngineCompiler.new(@options).run abort end end private def self.create_option_parser(options) OptionParser.new do |opts| nl = "\n" + ' ' * 37 opts.banner = "Usage: passenger-config compile-nginx-engine [OPTIONS]\n" opts.separator "" opts.separator " Compile an Nginx engine, for use in #{PROGRAM_NAME} Standalone." opts.separator "" opts.separator "Options:" opts.on("--working-dir PATH", String, "Store temporary files in the given#{nl}" + "directory, instead of creating one") do |val| options[:working_dir] = val end opts.on("--nginx-version VERSION", String, "Nginx version to compile. " + "Default: #{PREFERRED_NGINX_VERSION}") do |val| options[:nginx_version] = val end opts.on("--nginx-tarball PATH", String, "Use the given Nginx tarball instead of#{nl}" + "downloading it. You MUST also specify the#{nl}" + "Nginx version with --nginx-version") do |val| options[:nginx_tarball] = val end opts.on("-f", "--force", "Skip sanity checks") do options[:force] = true end opts.on("--no-force-tip", "Do not print any tips regarding the --force#{nl}" + "parameter") do options[:force_tip] = false end opts.on("--connect-timeout SECONDS", Integer, "The maximum amount of time to spend on DNS#{nl}" + "lookup and establishing the TCP connection.#{nl}" + "Default: 30") do |val| options[:connect_timeout] = val end opts.on("--idle-timeout SECONDS", Integer, "The maximum idle read time. Default: 30") do |val| options[:idle_timeout] = val end opts.on("-h", "--help", "Show this help") do options[:help] = true end end end def help puts @parser end def initialize_objects @colors = Utils::AnsiColors.new(@options[:colorize]) if !@options[:nginx_version] if @options[:nginx_tarball] abort "#{@colors.red}Error: if you specify --nginx-tarball, " + "you must also specify --nginx-version.#{@colors.reset}" else @options[:nginx_version] = PREFERRED_NGINX_VERSION end end end def sanity_check return if @options[:force] if PhusionPassenger.find_support_binary("nginx-#{@options[:nginx_version]}") puts "#{@colors.green}The Nginx engine (version #{@options[:nginx_version]}) " + "is already installed.#{@colors.reset}" if @options[:force_tip] puts "If you want to recompile it, re-run this program with the --force parameter." end exit end end end end # module Config end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/detach_process_command.rb000644 000765 000024 00000011000 12233035540 032243 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'optparse' require 'net/http' require 'socket' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'admin_tools/instance_registry' PhusionPassenger.require_passenger_lib 'config/command' PhusionPassenger.require_passenger_lib 'config/utils' PhusionPassenger.require_passenger_lib 'utils/json' module PhusionPassenger module Config class DetachProcessCommand < Command include PhusionPassenger::Config::Utils def run parse_options select_passenger_instance perform_detach end private def self.create_option_parser(options) OptionParser.new do |opts| nl = "\n" + ' ' * 37 opts.banner = "Usage: passenger-config detach-process [OPTIONS] \n" opts.separator "" opts.separator " Remove an application process from the #{PROGRAM_NAME} process pool, and" opts.separator " shut it down. Has a similar effect to killing the application process" opts.separator " directly with `kill `. But `kill` aborts any active requests, while" opts.separator " this command shuts down the process after active requests are finished." opts.separator "" opts.separator " If you want to force abort a process and its active requests, just use `kill`." opts.separator "" opts.separator "Options:" opts.on("--instance NAME", String, "The #{PROGRAM_NAME} instance to select") do |value| options[:instance] = value end opts.on("--ignore-nonexistant-pid", "Exit successfully even if the specified#{nl}" + "PID is not in the process pool.") do options[:ignore_nonexistant_pid] = true end opts.on("-h", "--help", "Show this help") do options[:help] = true end end end def help puts @parser end def parse_options super if @argv.empty? abort "Please pass a PID. " + "See --help for more information." elsif @argv.size == 1 @pid = @argv[0].to_i elsif @argv.size > 1 help abort end end def perform_detach request = Net::HTTP::Post.new("/pool/detach_process.json") try_performing_full_admin_basic_auth(request, @instance) request.content_type = "application/json" request.body = PhusionPassenger::Utils::JSON.generate(:pid => @pid) response = @instance.http_request("agents.s/core_api", request) if response.code.to_i / 100 == 2 body = PhusionPassenger::Utils::JSON.parse(response.body) if body['detached'] puts "Process #{@pid} detached." elsif @options[:ignore_nonexistant_pid] puts "Could not detach process #{@pid}." else abort "Could not detach process #{@pid}." end elsif response.code.to_i == 401 print_full_admin_command_permission_error abort else STDERR.puts "*** An error occured while communicating with the #{PROGRAM_NAME} server:" STDERR.puts response.body abort end end end end # module Config end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/download_agent_command.rb000644 000765 000024 00000027622 12233035540 032263 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'optparse' require 'logger' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'config/command' PhusionPassenger.require_passenger_lib 'config/installation_utils' PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/binary_compatibility' PhusionPassenger.require_passenger_lib 'utils/download' PhusionPassenger.require_passenger_lib 'utils/ansi_colors' PhusionPassenger.require_passenger_lib 'utils/shellwords' PhusionPassenger.require_passenger_lib 'utils/tmpio' module PhusionPassenger module Config class DownloadAgentCommand < Command include InstallationUtils BINARY_NOT_USABLE_EXIT_CODE = 3 def run @options = { :log_level => Logger::INFO, :colors => :auto, :error_colors => true, :show_download_progress => STDOUT.tty?, :compilation_tip => true, :force_tip => true, :use_cache => true, :connect_timeout => 30, :idle_timeout => 30 } parse_options initialize_objects sanity_check download_and_extract end private def self.create_option_parser(options) OptionParser.new do |opts| nl = "\n" + ' ' * 37 opts.banner = "Usage: passenger-config download-agent [OPTIONS]\n" opts.separator "" opts.separator " Download a precompiled #{PROGRAM_NAME} agent binary from the" opts.separator " #{PROGRAM_NAME} website. The agent binary is required for #{PROGRAM_NAME}" opts.separator " to function properly. Precompiled binaries are only available for Linux and" opts.separator " OS X." opts.separator "" opts.separator "Options:" opts.on("--url-root URL", String, "Download the binary from a custom URL") do |value| options[:url_root] = value end opts.on("--log-prefix PREFIX", String, "Prefix all logs with the given string") do |value| options[:log_prefix] = value end opts.on("--log-level LEVEL", String, "Set log level (fatal,error,warn,info,#{nl}" + "debug). Default: info") do |value| case value when "fatal" options[:log_level] = Logger::FATAL when "error" options[:log_level] = Logger::ERROR when "warn" options[:log_level] = Logger::WARN when "info" options[:log_level] = Logger::INFO when "debug" options[:log_level] = Logger::DEBUG else abort "Invalid log level #{value.inspect}" end end opts.on("-f", "--force", "Skip sanity checks") do options[:force] = true end opts.on("--no-colors", "Never output colors") do options[:colors] = false end opts.on("--no-error-colors", "Do not colorized error messages") do options[:error_colors] = false end opts.on("--no-download-progress", "Never show download progress") do options[:show_download_progress] = false end opts.on("--no-compilation-tip", "Do not present compilation as an#{nl}" + "alternative way to install the agent") do options[:compilation_tip] = false end opts.on("--no-force-tip", "Do not print any tips regarding the#{nl}" + "--force parameter") do options[:force_tip] = false end opts.on("--skip-cache", "Do not copy the agent binary from cache") do options[:use_cache] = false end opts.on("--suppress-binary-unusable-message", "Do not print anything if the downloaded#{nl}" + "binary turns out to be unusable") do options[:suppress_binary_unusable_message] = true end opts.on("--dry-run", "Do everything except actually installing#{nl}" + "the binary") do options[:dry_run] = true end opts.on("--connect-timeout SECONDS", Integer, "The maximum amount of time to spend on DNS#{nl}" + "lookup and establishing the TCP connection.#{nl}" + "Default: 30") do |val| options[:connect_timeout] = val end opts.on("--idle-timeout SECONDS", Integer, "The maximum idle read time. Default: 30") do |val| options[:idle_timeout] = val end opts.on("-h", "--help", "Show this help") do options[:help] = true end end end def help puts @parser end def initialize_objects if @options[:url_root] @sites = [{ :url => @options[:url_root] }] else @sites = PhusionPassenger.binaries_sites end @colors = Utils::AnsiColors.new(@options[:colors]) @logger = Logger.new(STDOUT) @logger.level = @options[:log_level] @logger.formatter = proc do |severity, datetime, progname, msg| if @options[:error_colors] && (severity == "FATAL" || severity == "ERROR") color = @colors.red else color = nil end result = "" msg.split("\n", -1).map do |line| result << "#{color}#{@options[:log_prefix]}#{line}#{@colors.reset}\n" end result end end def sanity_check return if @options[:force] if PhusionPassenger.find_support_binary(AGENT_EXE) @logger.warn "#{@colors.green}The #{PROGRAM_NAME} agent is already installed." if @options[:force_tip] @logger.warn "If you want to redownload it, re-run this program with the --force parameter." end exit end if !PhusionPassenger.installed_from_release_package? @logger.fatal("#{PROGRAM_NAME} was not installed from an official release " + "package, so you cannot download our precompiled agent binary." + compile_tip_message) if @options[:force_tip] @logger.warn "If you want to download it anyway, re-run this program with the --force parameter." end abort end check_for_download_tool! end def download_and_extract destdir = find_or_create_writable_support_binaries_dir! exit if @options[:dry_run] PhusionPassenger::Utils.mktmpdir("passenger-install.", PlatformInfo.tmpexedir) do |tmpdir| basename = "agent-#{PlatformInfo.cxx_binary_compatibility_id}.tar.gz" tarball = "#{tmpdir}/#{basename}" if !download_support_file(basename, tarball) @logger.error "#{@colors.reset}------------------------------------------" @logger.fatal("Sorry, no precompiled agent binary is available for " + "your platform." + compile_tip_message) abort end @logger.info "Extracting precompiled agent binary to #{destdir}..." e_tmpdir = Shellwords.escape(tmpdir) e_tarball = Shellwords.escape(tarball) if !system("cd #{e_tmpdir} && tar xzf #{e_tarball}") @logger.fatal "The downloaded archive file could not be extracted." abort end if !File.exist?("#{tmpdir}/#{AGENT_EXE}") @logger.fatal "The downloaded archive file does not seem to " + "contain an agent binary. This is probably a problem in " + "the #{PROGRAM_NAME} website. Please report this problem to " + "the #{PROGRAM_NAME} authors." abort end @logger.info "Checking whether the downloaded binary is usable..." if test_binary("#{tmpdir}/#{AGENT_EXE}") @logger.info "The downloaded binary is usable." else if !@options[:suppress_binary_unusable_message] @logger.fatal "Sorry, the precompiled agent binary can not be run " + "your system.#{compile_tip_message}" end exit(BINARY_NOT_USABLE_EXIT_CODE) end FileUtils.mv("#{tmpdir}/#{AGENT_EXE}", "#{destdir}/#{AGENT_EXE}") @logger.info "#{@colors.green}Agent binary successfully download and installed." end end def download_support_file(name, output) @sites.each_with_index do |site, i| if real_download_support_file(site, name, output) if i > 0 @logger.warn "#{@colors.green}Download OK!" else @logger.info "#{@colors.green}Download OK!" end return true elsif i != @sites.size - 1 @logger.warn "Trying next mirror..." end end return false end def real_download_support_file(site, name, output) url = "#{site[:url]}/#{VERSION_STRING}/#{name}" options = { :cacert => site[:cacert], :logger => @logger, :use_cache => @options[:use_cache] } # connect_timeout and idle_timeout may be nil or 0, which means # that the default Utils::Download timeouts should be used. if @options[:connect_timeout] && @options[:connect_timeout] != 0 options[:connect_timeout] = @options[:connect_timeout] end if @options[:idle_timeout] && @options[:idle_timeout] != 0 options[:idle_timeout] = @options[:idle_timeout] end return PhusionPassenger::Utils::Download.download(url, output, options) end def test_binary(filename) output = `env LD_BIND_NOW=1 DYLD_BIND_AT_LAUNCH=1 #{Shellwords.escape(filename)} test-binary` return $? && $?.exitstatus == 0 && output == "PASS\n" end def compile_tip_message return "" if !@options[:compilation_tip] if PhusionPassenger.build_system_dir result = " Please compile the agent from source instead, by running:\n\n" result << " passenger-config compile-agent" else result = " Furthermore, this #{PROGRAM_NAME} installation does not " result << "come with any source code, so the agent binary cannot " result << "be compiled either. Please contact the person or " result << "organization who packaged #{PROGRAM_NAME} for help on this " result << "problem." end return result end # Override InstallationUtils def print_installation_error_header @logger.warn "------------------------------------------" @logger.fatal "Cannot store agent binary" @logger.fatal "" end end end # module Config end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/download_nginx_engine_command.rb000644 000765 000024 00000030242 12233035540 033625 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'optparse' require 'logger' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'config/command' PhusionPassenger.require_passenger_lib 'config/installation_utils' PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/binary_compatibility' PhusionPassenger.require_passenger_lib 'utils/download' PhusionPassenger.require_passenger_lib 'utils/ansi_colors' PhusionPassenger.require_passenger_lib 'utils/shellwords' PhusionPassenger.require_passenger_lib 'utils/tmpio' module PhusionPassenger module Config class DownloadNginxEngineCommand < Command include InstallationUtils BINARY_NOT_USABLE_EXIT_CODE = 3 def run @options = { :log_level => Logger::INFO, :colors => :auto, :error_colors => true, :show_download_progress => STDOUT.tty?, :compilation_tip => true, :force_tip => true, :use_cache => true, :connect_timeout => 30, :idle_timeout => 30 } parse_options initialize_objects sanity_check download_and_extract end private def self.create_option_parser(options) OptionParser.new do |opts| nl = "\n" + ' ' * 37 opts.banner = "Usage: passenger-config download-nginx-engine [OPTIONS]\n" opts.separator "" opts.separator " Download an Nginx #{PREFERRED_NGINX_VERSION} engine " + "from the #{PROGRAM_NAME} website," opts.separator " for use with #{PROGRAM_NAME} Standalone. Precompiled engines are only" opts.separator " available for Linux and OS X." opts.separator "" opts.separator " It is not possible to customize the Nginx engine version. " + "It must be #{PREFERRED_NGINX_VERSION}." opts.separator "" opts.separator "Options:" opts.on("--url-root URL", String, "Download the engine from a custom URL") do |value| options[:url_root] = value end opts.on("--log-prefix PREFIX", String, "Prefix all logs with the given string") do |value| options[:log_prefix] = value end opts.on("--log-level LEVEL", String, "Set log level (fatal,error,warn,info,#{nl}" + "debug). Default: info") do |value| case value when "fatal" options[:log_level] = Logger::FATAL when "error" options[:log_level] = Logger::ERROR when "warn" options[:log_level] = Logger::WARN when "info" options[:log_level] = Logger::INFO when "debug" options[:log_level] = Logger::DEBUG else abort "Invalid log level #{value.inspect}" end end opts.on("-f", "--force", "Skip sanity checks") do options[:force] = true end opts.on("--no-colors", "Never output colors") do options[:colors] = false end opts.on("--no-error-colors", "Do not colorized error messages") do options[:error_colors] = false end opts.on("--no-download-progress", "Never show download progress") do options[:show_download_progress] = false end opts.on("--no-compilation-tip", "Do not present compilation as an#{nl}" + "alternative way to install the agent") do options[:compilation_tip] = false end opts.on("--no-force-tip", "Do not print any tips regarding the#{nl}" + "--force parameter") do options[:force_tip] = false end opts.on("--skip-cache", "Do not copy the Nginx engine from cache") do options[:use_cache] = false end opts.on("--suppress-binary-unusable-message", "Do not print anything if the downloaded#{nl}" + "binary turns out to be unusable") do options[:suppress_binary_unusable_message] = true end opts.on("--dry-run", "Do everything except actually installing#{nl}" + "the engine") do options[:dry_run] = true end opts.on("--connect-timeout SECONDS", Integer, "The maximum amount of time to spend on DNS#{nl}" + "lookup and establishing the TCP connection.#{nl}" + "Default: 30") do |val| options[:connect_timeout] = val end opts.on("--idle-timeout SECONDS", Integer, "The maximum idle read time. Default: 30") do |val| options[:idle_timeout] = val end opts.on("-h", "--help", "Show this help") do options[:help] = true end end end def help puts @parser end def initialize_objects if @options[:url_root] @sites = [{ :url => @options[:url_root] }] else @sites = PhusionPassenger.binaries_sites end @colors = Utils::AnsiColors.new(@options[:colors]) @logger = Logger.new(STDOUT) @logger.level = @options[:log_level] @logger.formatter = proc do |severity, datetime, progname, msg| if @options[:error_colors] && (severity == "FATAL" || severity == "ERROR") color = @colors.red else color = nil end result = "" msg.split("\n", -1).map do |line| result << "#{color}#{@options[:log_prefix]}#{line}#{@colors.reset}\n" end result end end def sanity_check return if @options[:force] if PhusionPassenger.find_support_binary("nginx-#{PREFERRED_NGINX_VERSION}") @logger.warn "#{@colors.green}The Nginx engine (version #{PREFERRED_NGINX_VERSION}) is already installed." if @options[:force_tip] @logger.warn "If you want to redownload it, re-run this program with the --force parameter." end exit end if !PhusionPassenger.installed_from_release_package? @logger.fatal("#{PROGRAM_NAME} was not installed from an official release " + "package, so you cannot download our precompiled Nginx engine." + compile_tip_message) if @options[:force_tip] @logger.warn "If you want to download it anyway, re-run this program with the --force parameter." end abort end check_for_download_tool! end def download_and_extract destdir = find_or_create_writable_support_binaries_dir! exit if @options[:dry_run] PhusionPassenger::Utils.mktmpdir("passenger-install.", PlatformInfo.tmpexedir) do |tmpdir| basename = "nginx-#{PREFERRED_NGINX_VERSION}-#{PlatformInfo.cxx_binary_compatibility_id}.tar.gz" tarball = "#{tmpdir}/#{basename}" if !download_support_file(basename, tarball) @logger.error "#{@colors.reset}------------------------------------------" @logger.fatal("Sorry, no precompiled Nginx #{PREFERRED_NGINX_VERSION} engine is available for " + "your platform." + compile_tip_message) abort end @logger.info "Extracting precompiled Nginx #{PREFERRED_NGINX_VERSION} engine to #{destdir}" e_tmpdir = Shellwords.escape(tmpdir) e_tarball = Shellwords.escape(tarball) if !system("cd #{e_tmpdir} && tar xzf #{e_tarball}") @logger.fatal "The downloaded archive file could not be extracted." abort end if !File.exist?("#{tmpdir}/nginx-#{PREFERRED_NGINX_VERSION}") @logger.fatal "The downloaded archive file does not seem to " + "contain an Nginx #{PREFERRED_NGINX_VERSION} engine. This is probably " + "a problem in the #{PROGRAM_NAME} website. Please report this problem to " + "the #{PROGRAM_NAME} authors." abort end @logger.info "Checking whether the downloaded engine is usable..." if test_binary("#{tmpdir}/nginx-#{PREFERRED_NGINX_VERSION}") @logger.info "The downloaded engine is usable." else if !@options[:suppress_binary_unusable_message] @logger.fatal "Sorry, the precompiled Nginx engine can not be run " + "your system.#{compile_tip_message}" end exit(BINARY_NOT_USABLE_EXIT_CODE) end FileUtils.mv("#{tmpdir}/nginx-#{PREFERRED_NGINX_VERSION}", "#{destdir}/nginx-#{PREFERRED_NGINX_VERSION}") @logger.info "#{@colors.green}Nginx #{PREFERRED_NGINX_VERSION} engine successfully download and installed." end end def download_support_file(name, output) @sites.each_with_index do |site, i| if real_download_support_file(site, name, output) if i > 0 @logger.warn "#{@colors.green}Download OK!" else @logger.info "#{@colors.green}Download OK!" end return true elsif i != @sites.size - 1 @logger.warn "Trying next mirror..." end end return false end def real_download_support_file(site, name, output) url = "#{site[:url]}/#{VERSION_STRING}/#{name}" options = { :cacert => site[:cacert], :logger => @logger, :use_cache => @options[:use_cache], :show_progress => @options[:show_download_progress] } # connect_timeout and idle_timeout may be nil or 0, which means # that the default Utils::Download timeouts should be used. if @options[:connect_timeout] && @options[:connect_timeout] != 0 options[:connect_timeout] = @options[:connect_timeout] end if @options[:idle_timeout] && @options[:idle_timeout] != 0 options[:idle_timeout] = @options[:idle_timeout] end return PhusionPassenger::Utils::Download.download(url, output, options) end def test_binary(filename) output = `env LD_BIND_NOW=1 DYLD_BIND_AT_LAUNCH=1 #{Shellwords.escape(filename)} -v 2>&1` return $? && $?.exitstatus == 0 && output =~ /nginx version:/ end def compile_tip_message return "" if !@options[:compilation_tip] if PhusionPassenger.build_system_dir result = " Please compile the Nginx engine from source instead, by running:\n\n" result << " passenger-config compile-nginx-engine" else result = " Furthermore, this #{PROGRAM_NAME} installation does not " result << "come with any source code, so the Nginx engine cannot " result << "be compiled either. Please contact the person or " result << "organization who packaged #{PROGRAM_NAME} for help on this " result << "problem." end return result end end end # module Config end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/install_agent_command.rb000644 000765 000024 00000016322 12233035540 032115 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'optparse' require 'logger' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'config/command' PhusionPassenger.require_passenger_lib 'config/download_agent_command' PhusionPassenger.require_passenger_lib 'config/compile_agent_command' PhusionPassenger.require_passenger_lib 'utils/ansi_colors' module PhusionPassenger module Config class InstallAgentCommand < Command def run @options = { :log_level => Logger::INFO, :colorize => :auto, :force => false, :force_tip => true, :compile => true, :download_args => [ "--no-error-colors", "--no-compilation-tip" ], :compile_args => [] } parse_options initialize_objects sanity_check if !download compile end end private def self.create_option_parser(options) OptionParser.new do |opts| nl = "\n" + ' ' * 37 opts.banner = "Usage: passenger-config install-agent [OPTIONS]\n" opts.separator "" opts.separator " Install the #{PROGRAM_NAME} agent binary. The agent binary is required for" opts.separator " #{PROGRAM_NAME} to function properly. Installation is done either by" opts.separator " downloading it from the #{PROGRAM_NAME} website, or by compiling it from" opts.separator " source." opts.separator "" opts.separator "Options:" opts.on("--working-dir PATH", String, "Store temporary files in the given#{nl}" + "directory, instead of creating one") do |val| options[:compile_args] << "--working-dir" options[:compile_args] << val end opts.on("--url-root URL", String, "Download the binary from a custom URL") do |value| options[:download_args] << "--url-root" options[:download_args] << value end opts.on("--brief", "Report progress in a brief style") do options[:brief] = true options[:download_args] << "--log-level" options[:download_args] << "warn" options[:download_args] << "--log-prefix" options[:download_args] << " " options[:download_args] << "--no-download-progress" end opts.on("--auto", "Run in non-interactive mode. Default when#{nl}" + "stdin or stdout is not a TTY") do options[:compile_args] << "--auto" end opts.on("-f", "--force", "Skip sanity checks") do options[:force] = true options[:download_args] << "--force" options[:compile_args] << "--force" end opts.on("--no-force-tip", "Do not print any tips regarding the#{nl}" + "--force parameter") do options[:force_tip] = false options[:download_args] << "--no-force-tip" options[:compile_args] << "--no-force-tip" end opts.on("--no-compile", "Download, but do not compile") do options[:compile] = false end opts.on("--skip-cache", "Do not copy the agent binary from cache") do options[:download_args] << "--skip-cache" end opts.on("--connect-timeout SECONDS", Integer, "The maximum amount of time to spend on DNS#{nl}" + "lookup and establishing the TCP connection.#{nl}" + "Default: 30") do |val| options[:download_args] << "--connect-timeout" options[:download_args] << val.to_s end opts.on("--idle-timeout SECONDS", Integer, "The maximum idle read time. Default: 30") do |val| options[:download_args] << "--idle-timeout" options[:download_args] << val.to_s end opts.on("-h", "--help", "Show this help") do options[:help] = true end end end def help puts @parser end def initialize_objects @colors = Utils::AnsiColors.new(@options[:colorize]) @logger = Logger.new(STDOUT) @logger.level = @options[:log_level] @logger.formatter = proc do |severity, datetime, progname, msg| if severity == "FATAL" || severity == "ERROR" color = @colors.red else color = nil end result = "" msg.split("\n", -1).map do |line| result << "#{color}#{@options[:log_prefix]}#{line}#{@colors.reset}\n" end result end end def sanity_check return if @options[:force] if PhusionPassenger.find_support_binary(AGENT_EXE) @logger.warn "#{@colors.green}The #{PROGRAM_NAME} agent is already installed." if @options[:force_tip] @logger.warn "If you want to redownload it, re-run this program with the --force parameter." end exit end end def download if @options[:brief] puts " --> Downloading a #{PROGRAM_NAME} agent binary for your platform" else puts "#{@colors.blue_bg}#{@colors.yellow}#{@colors.bold}Downloading a #{PROGRAM_NAME} agent " + "binary for your platform#{@colors.reset}" puts end begin DownloadAgentCommand.new(@options[:download_args]).run return true rescue SystemExit => e return e.success? end end def compile puts puts "---------------------------------------" puts if @options[:compile] puts "The #{PROGRAM_NAME} agent binary could not be downloaded. Compiling it from source instead." puts CompileAgentCommand.new(@options[:compile_args]).run else abort "No precompiled agent binary could be downloaded. Refusing to compile because --no-compile is given." end end end end # module Config end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/install_standalone_runtime_command.rb000644 000765 000024 00000024505 12233035540 034714 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'optparse' require 'logger' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'config/command' PhusionPassenger.require_passenger_lib 'config/install_agent_command' PhusionPassenger.require_passenger_lib 'config/download_nginx_engine_command' PhusionPassenger.require_passenger_lib 'config/compile_nginx_engine_command' PhusionPassenger.require_passenger_lib 'utils/ansi_colors' PhusionPassenger.require_passenger_lib 'utils/tmpio' module PhusionPassenger module Config class InstallStandaloneRuntimeCommand < Command def run @options = { :log_level => Logger::INFO, :colorize => :auto, :force => false, :force_tip => true, :compile => true, :install_agent => true, :install_agent_args => [], :download_args => [ "--no-error-colors", "--no-compilation-tip" ], :compile_args => [] } parse_options initialize_objects sanity_check PhusionPassenger::Utils.mktmpdir("passenger-install.", PlatformInfo.tmpexedir) do |tmpdir| install_agent(tmpdir) if !download_nginx_engine compile_nginx_engine(tmpdir) end end end private def self.create_option_parser(options) OptionParser.new do |opts| nl = "\n" + ' ' * 37 opts.banner = "Usage: passenger-config install-standalone-runtime [OPTIONS]\n" opts.separator "" opts.separator " Install the #{PROGRAM_NAME} Standalone runtime. This runtime consists of" opts.separator " the #{PROGRAM_NAME} agent, and an Nginx engine. Installation is done either" opts.separator " by downloading the necessary files from the #{PROGRAM_NAME} website, or" opts.separator " by compiling them from source." opts.separator "" opts.separator "Options:" opts.on("--working-dir PATH", String, "Store temporary files in the given#{nl}" + "directory, instead of creating one") do |val| options[:install_agent_args] << "--working-dir" options[:install_agent_args] << val options[:compile_args] << "--working-dir" options[:compile_args] << val end opts.on("--url-root URL", String, "Download binaries from a custom URL") do |value| options[:install_agent_args] << "--url-root" options[:install_agent_args] << value options[:download_args] << "--url-root" options[:download_args] << value end opts.on("--nginx-version VERSION", String, "Nginx version to compile. " + "Default: #{PREFERRED_NGINX_VERSION}") do |val| options[:nginx_version] = val options[:compile_args] << "--nginx-version" options[:compile_args] << val end opts.on("--nginx-tarball PATH", String, "Use the given Nginx tarball instead of#{nl}" + "downloading it. You MUST also specify the#{nl}" + "Nginx version with --nginx-version") do |val| options[:nginx_tarball] = val end opts.on("--brief", "Report progress in a brief style") do options[:brief] = true options[:install_agent_args] << "--brief" options[:download_args] << "--log-level" options[:download_args] << "warn" options[:download_args] << "--log-prefix" options[:download_args] << " " options[:download_args] << "--no-download-progress" end opts.on("--auto", "Run in non-interactive mode. Default when#{nl}" + "stdin or stdout is not a TTY") do options[:install_agent_args] << "--auto" end opts.on("-f", "--force", "Skip sanity checks") do options[:force] = true options[:install_agent_args] << "--force" options[:download_args] << "--force" options[:compile_args] << "--force" end opts.on("--no-force-tip", "Do not print any tips regarding the#{nl}" + "--force parameter") do options[:force_tip] = false options[:install_agent_args] << "--no-force-tip" options[:download_args] << "--no-force-tip" options[:compile_args] << "--no-force-tip" end opts.on("--no-compile", "Download, but do not compile") do options[:compile] = false options[:install_agent_args] << "--no-compile" end opts.on("--skip-agent", "Do not install the agent") do options[:install_agent] = false end opts.on("--skip-cache", "Do not copy the binaries from cache") do options[:install_agent_args] << "--skip-cache" options[:download_args] << "--skip-cache" end opts.on("--connect-timeout SECONDS", Integer, "The maximum amount of time to spend on DNS#{nl}" + "lookup and establishing the TCP connection.#{nl}" + "Default: 30") do |val| options[:install_agent_args] << "--connect-timeout" options[:install_agent_args] << val.to_s options[:download_args] << "--connect-timeout" options[:download_args] << val.to_s options[:compile_args] << "--connect-timeout" options[:compile_args] << val.to_s end opts.on("--idle-timeout SECONDS", Integer, "The maximum idle read time. Default: 30") do |val| options[:install_agent_args] << "--idle-timeout" options[:install_agent_args] << val.to_s options[:download_args] << "--idle-timeout" options[:download_args] << val.to_s options[:compile_args] << "--idle-timeout" options[:compile_args] << val.to_s end opts.on("-h", "--help", "Show this help") do options[:help] = true end end end def help puts @parser end def initialize_objects @colors = Utils::AnsiColors.new(@options[:colorize]) @logger = Logger.new(STDOUT) @logger.level = @options[:log_level] @logger.formatter = proc do |severity, datetime, progname, msg| if severity == "FATAL" || severity == "ERROR" color = @colors.red else color = nil end result = "" msg.split("\n", -1).map do |line| result << "#{color}#{@options[:log_prefix]}#{line}#{@colors.reset}\n" end result end if !@options[:nginx_version] if @options[:nginx_tarball] abort "#{@colors.red}Error: if you specify --nginx-tarball, " + "you must also specify --nginx-version.#{@colors.reset}" else @options[:nginx_version] = PREFERRED_NGINX_VERSION end end end def sanity_check return if @options[:force] all_installed = PhusionPassenger.find_support_binary(AGENT_EXE) && PhusionPassenger.find_support_binary("nginx-#{@options[:nginx_version]}") if all_installed @logger.warn "#{@colors.green}The #{PROGRAM_NAME} Standalone runtime is already installed." if @options[:force_tip] @logger.warn "If you want to redownload it, re-run this program with the --force parameter." end exit end end def install_agent(tmpdir) if @options[:install_agent] args = @options[:install_agent_args].dup args << "--working-dir" args << tmpdir begin InstallAgentCommand.new(args).run rescue SystemExit => e raise e if !e.success? end puts end end def download_nginx_engine if @options[:nginx_version] != PREFERRED_NGINX_VERSION return false end if @options[:brief] puts " --> Installing Nginx #{@options[:nginx_version]} engine" else puts "#{@colors.blue_bg}#{@colors.yellow}#{@colors.bold}" + "Downloading an Nginx #{@options[:nginx_version]} engine " + "for your platform#{@colors.reset}" puts end begin DownloadNginxEngineCommand.new(@options[:download_args]).run true rescue SystemExit => e e.success? end end def compile_nginx_engine(tmpdir) puts puts "---------------------------------------" puts if @options[:compile] puts "No precompiled Nginx engine could be downloaded. Compiling it from source instead." puts args = @options[:compile_args].dup args << "--working-dir" args << tmpdir CompileNginxEngineCommand.new(args).run else abort "No precompiled Nginx engine could be downloaded. Refusing to compile because --no-compile is given." end end end end # module Config end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/installation_utils.rb000644 000765 000024 00000023410 12233035540 031510 0ustar00honglistaff000000 000000 # encoding: utf-8 # # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'fileutils' require 'pathname' require 'etc' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'ruby_core_enhancements' PhusionPassenger.require_passenger_lib 'console_text_template' PhusionPassenger.require_passenger_lib 'platform_info/ruby' PhusionPassenger.require_passenger_lib 'platform_info/depcheck' PhusionPassenger.require_passenger_lib 'utils/ansi_colors' PhusionPassenger.require_passenger_lib 'utils/tmpio' module PhusionPassenger module Config module InstallationUtils extend self # Make methods available as class methods. def self.included(klass) # When included into another class, make sure that Utils # methods are made private. public_instance_methods(false).each do |method_name| klass.send(:private, method_name) end end def find_or_create_writable_support_binaries_dir! if File.exist?(PhusionPassenger.support_binaries_dir) result = directory_writable?(PhusionPassenger.support_binaries_dir) if result == true # return value can be a SystemCallError return PhusionPassenger.support_binaries_dir end if Process.euid == 0 if result == false print_installation_error_header render_template 'installation_utils/support_binaries_dir_not_writable_despite_running_as_root', :dir => PhusionPassenger.support_binaries_dir, :myself => myself else render_template 'installation_utils/unexpected_filesystem_problem', :dir => PhusionPassenger.support_binaries_dir, :exception => result end abort else return find_or_create_writable_user_support_binaries_dir! end else if Process.euid == 0 mkdir_p_preserve_parent_owner(PhusionPassenger.support_binaries_dir) return PhusionPassenger.support_binaries_dir else return find_or_create_writable_user_support_binaries_dir! end end end def check_for_download_tool! PlatformInfo::Depcheck.load('depcheck_specs/utilities') result = PlatformInfo::Depcheck.find('download-tool').check # Don't output anything if there is a download tool. # We want to be as quiet as possible. return if result && result[:found] colors = @colors || Utils::AnsiColors.new puts colors.ansi_colorize("Checking for basic prerequities...") puts runner = PlatformInfo::Depcheck::ConsoleRunner.new runner.add('download-tool') result = runner.check_all puts if !result puts "---------------------------------------" puts render_template 'installation_utils/download_tool_missing', :runner => runner abort end end # Override this method to print a different header def print_installation_error_header if @colors red = @colors.red reset = @colors.reset else red = nil reset = nil end @logger.warn "------------------------------------------" if @logger puts "#{red}Cannot proceed with installation#{reset}" puts end def rake return "env NOEXEC_DISABLE=1 #{PlatformInfo.rake_command}" end def run_rake_task!(target) total_lines = `#{rake} #{target} --dry-run STDERR_TO_STDOUT=1 2>&1`.split("\n").size - 1 partial_backlog = "" logfile = PhusionPassenger::Utils::TmpIO.new("passenger-install-log", :mode => File::WRONLY, :unlink_immediately => false) begin command = "#{rake} #{target} --trace STDERR_TO_STDOUT=1 2>&1" IO.popen(command, "rb") do |io| progress = 1 while !io.eof? line = io.readline logfile.write(line) yield(progress, total_lines) if line =~ /^\*\* / partial_backlog.replace("") progress += 1 else partial_backlog << line end end end if $?.exitstatus != 0 colors = @colors || PhusionPassenger::Utils::AnsiColors.new stderr = @stderr || STDERR stderr.puts stderr.puts "#{colors.red}*** ERROR: a Rake command failed. You can find the full " + "log in #{logfile.path}. Below, you can find the last few lines of the command's output.#{colors.reset}" stderr.puts "#{colors.red}------------- Begin command output snippet -------------#{colors.reset}" stderr.puts(partial_backlog) stderr.puts "#{colors.red}------------- End command output snippet -------------#{colors.reset}" stderr.puts "#{colors.red}The full log can be found in #{logfile.path}#{colors.reset}" exit 1 end ensure logfile.close end end private # We can't use File.writable() and friends here because they # don't always work right with ACLs. Instead of we use 'real' # checks. def directory_writable?(path) filename = "#{path}/.__test_#{object_id}__.txt" @logger.debug "Checking whether we can write to #{path}..." if @logger begin File.new(filename, "w").close @logger.debug "Yes" if @logger return true rescue Errno::EACCES @logger.debug "No" if @logger return false rescue SystemCallError => e @logger.warn "Unable to check whether we can write to #{path}: #{e}" if @logger return e ensure File.unlink(filename) rescue nil end end def find_or_create_writable_user_support_binaries_dir! if !File.exist?(PhusionPassenger.user_support_binaries_dir) create_user_support_binaries_dir! end result = directory_writable?(PhusionPassenger.user_support_binaries_dir) case result when true return PhusionPassenger.user_support_binaries_dir when false print_installation_error_header render_template 'installation_utils/user_support_binaries_dir_not_writable' abort else print_installation_error_header render_template 'installation_utils/unexpected_filesystem_problem', :dir => PhusionPassenger.support_binaries_dir, :exception => result abort end end def create_user_support_binaries_dir! dir = PhusionPassenger.user_support_binaries_dir begin mkdir_p_preserve_parent_owner(dir) rescue Errno::EACCES print_installation_error_header render_template 'installation_utils/cannot_create_user_support_binaries_dir', :dir => dir, :myself => myself abort rescue SystemCallError print_installation_error_header render_template 'installation_utils/unexpected_filesystem_problem', :dir => dir, :exception => result abort end end # When creating PhusionPassenger.support_binaries_dir, preserve the # parent directory's UID and GID. This way, running `passenger-config compile-agent` # with sudo privileged, even though Phusion Passenger isn't installed as root, # won't mess up permissions. def mkdir_p_preserve_parent_owner(path) Pathname.new(path).descend do |subpath| if !subpath.exist? stat = subpath.parent.stat Dir.mkdir(subpath.to_s) if Process.euid == 0 File.chown(stat.uid, stat.gid, subpath.to_s) end end end end def myself return `whoami`.strip end def render_template(name, options = {}) options.merge!(:colors => @colors || PhusionPassenger::Utils::AnsiColors.new) # This check here is necessary for NginxEngineCompiler. NginxEngineCompiler # derives from AbstractInstaller but also includes InstallationUtils. We want # the AbstractInstaller methods to work when they call render_template. if !File.exist?("#{PhusionPassenger.resources_dir}/templates/#{name}.txt.erb") name = "config/#{name}" end puts ConsoleTextTemplate.new({ :file => name }, options).result end end end # module Config end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/list_instances_command.rb000644 000765 000024 00000006552 12233035540 032317 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'optparse' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'admin_tools/instance_registry' PhusionPassenger.require_passenger_lib 'config/command' PhusionPassenger.require_passenger_lib 'config/utils' PhusionPassenger.require_passenger_lib 'utils/json' module PhusionPassenger module Config class ListInstancesCommand < Command include PhusionPassenger::Config::Utils def run parse_options instances = AdminTools::InstanceRegistry.new.list if @options[:json] print_json(instances) elsif instances.empty? print_no_instances_running else print_instances(instances) end end private def self.create_option_parser(options) OptionParser.new do |opts| nl = "\n" + ' ' * 37 opts.banner = "Usage: passenger-config list-instances [OPTIONS] \n" opts.separator "" opts.separator " List all running #{PROGRAM_NAME} instances." opts.separator "" opts.on("--json", "Print output in JSON format") do options[:json] = true end opts.on("-q", "--quiet", "Don't print anything if there are no #{PROGRAM_NAME} instances running") do options[:quiet] = true end opts.on("-h", "--help", "Show this help") do options[:help] = true end end end def help puts @parser end def parse_options super if !@argv.empty? help abort end end def print_no_instances_running if !@options[:quiet] puts "There are no #{PROGRAM_NAME} instances running." end end def print_json(instances) result = [] instances.each do |instance| result << instance.as_json end puts PhusionPassenger::Utils::JSON.generate(result) end def print_instances(instances) list_all_passenger_instances(instances, false) end end end # module Config end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/main.rb000644 000765 000024 00000015356 12233035540 026525 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2013-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'constants' module PhusionPassenger # Core of the `passenger-config` command. Dispatches a subcommand to a specific class. module Config KNOWN_COMMANDS = [ ["detach-process", "DetachProcessCommand"], ["restart-app", "RestartAppCommand"], ["list-instances", "ListInstancesCommand"], ["reopen-logs", "ReopenLogsCommand"], ["api-call", "ApiCallCommand"], ["validate-install", "ValidateInstallCommand"], ["build-native-support", "BuildNativeSupportCommand"], ["install-agent", "InstallAgentCommand"], ["install-standalone-runtime", "InstallStandaloneRuntimeCommand"], ["download-agent", "DownloadAgentCommand"], ["download-nginx-engine", "DownloadNginxEngineCommand"], ["compile-agent", "CompileAgentCommand"], ["compile-nginx-engine", "CompileNginxEngineCommand"], ["system-metrics", "SystemMetricsCommand"], ["about", "AboutCommand"] ] ABOUT_OPTIONS = [ "root", "includedir", "nginx-addon-dir", "nginx-libs", "nginx-dynamic-libs", "nginx-dynamic-compiled", "compiled", "custom-packaged", "installed-from-release-package", "make-locations-ini", "detect-apache2", "ruby-command", "ruby-libdir", "rubyext-compat-id", "cxx-compat-id", "version" ] def self.run!(argv) command_class, new_argv = lookup_command_class_by_argv(argv) if help_requested?(argv) help elsif help_all_requested?(argv) help(true) elsif command_class command = command_class.new(new_argv) command.run else help abort end end def self.help(all = false) puts "Usage: passenger-config [OPTIONS...]" puts puts " Tool for managing, controlling and configuring a #{PROGRAM_NAME} instance" puts " or installation." puts puts "Management commands:" puts " detach-process Detach an application process from the process pool" puts " restart-app Restart an application" puts " reopen-logs Instruct #{PROGRAM_NAME} agents to reopen their log" puts " files" puts " api-call Makes an API call to a #{PROGRAM_NAME} agent." puts puts "Informational commands:" puts " list-instances List running #{PROGRAM_NAME} instances" puts " about Show information about #{PROGRAM_NAME}" puts puts "#{PROGRAM_NAME} installation management:" puts " validate-install Validate this #{PROGRAM_NAME} installation" puts " build-native-support Ensure that the native_support library for the current" puts " Ruby interpreter is built" puts " install-agent Install the #{PROGRAM_NAME} agent binary" puts " install-standalone-runtime" puts " Install the #{PROGRAM_NAME} Standalone" puts " runtime" if all puts " download-agent Download the #{PROGRAM_NAME} agent binary" puts " download-nginx-engine Download the Nginx engine for use with" puts " #{PROGRAM_NAME} Standalone" puts " compile-agent Compile the #{PROGRAM_NAME} agent binary" puts " compile-nginx-engine Compile an Nginx engine for use with #{PROGRAM_NAME}" puts " Standalone" end puts puts "Miscellaneous commands:" puts " system-metrics Display system metrics" puts puts "Run 'passenger-config --help' for more information about each" puts "command." if !all puts puts "There are also some advanced commands not shown in this help message. Run" puts "'passenger-config --help-all' to learn more about them." end end private def self.help_requested?(argv) return argv.size == 1 && (argv[0] == "--help" || argv[0] == "-h" || argv[0] == "help") end def self.help_all_requested?(argv) return argv.size == 1 && (argv[0] == "--help-all" || argv[0] == "help-all") end def self.lookup_command_class_by_argv(argv) return nil if argv.empty? # Compatibility with version <= 4.0.29: try to pass all # --switch invocations to AboutCommand. if argv[0] =~ /^--/ name = argv[0].sub(/^--/, '') if ABOUT_OPTIONS.include?(name) command_class = lookup_command_class_by_class_name("AboutCommand") return [command_class, argv] else return nil end end # Convert "passenger-config help " to "passenger-config --help". if argv.size == 2 && argv[0] == "help" argv = [argv[1], "--help"] end KNOWN_COMMANDS.each do |props| if argv[0] == props[0] command_class = lookup_command_class_by_class_name(props[1]) new_argv = argv[1 .. -1] return [command_class, new_argv] end end return nil end def self.lookup_command_class_by_class_name(class_name) base_name = class_name.gsub(/[A-Z]/) do |match| "_" + match[0..0].downcase end base_name.sub!(/^_/, '') base_name << ".rb" PhusionPassenger.require_passenger_lib("config/#{base_name}") return PhusionPassenger::Config.const_get(class_name) end end end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/nginx_engine_compiler.rb000644 000765 000024 00000033651 12233035540 032141 0ustar00honglistaff000000 000000 # encoding: utf-8 # # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'fileutils' require 'logger' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'abstract_installer' PhusionPassenger.require_passenger_lib 'common_library' PhusionPassenger.require_passenger_lib 'config/installation_utils' PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/ruby' PhusionPassenger.require_passenger_lib 'platform_info/openssl' PhusionPassenger.require_passenger_lib 'platform_info/compiler' PhusionPassenger.require_passenger_lib 'utils/shellwords' PhusionPassenger.require_passenger_lib 'utils/progress_bar' PhusionPassenger.require_passenger_lib 'utils/tmpio' module PhusionPassenger module Config class NginxEngineCompiler < AbstractInstaller include InstallationUtils def self.configure_script_options extra_cflags = "-Wno-error #{PlatformInfo.openssl_extra_cflags}".strip result = "--with-cc-opt=#{Shellwords.escape extra_cflags} " extra_ldflags = PlatformInfo.openssl_extra_ldflags if !extra_ldflags.empty? result << "--with-ld-opt=#{Shellwords.escape extra_ldflags} " end result << "--without-http_fastcgi_module " \ "--without-http_scgi_module " \ "--without-http_uwsgi_module " \ "--with-http_gzip_static_module " \ "--with-http_stub_status_module " \ "--with-http_ssl_module " \ "--with-http_realip_module" result end protected def dependencies specs = [ 'depcheck_specs/compiler_toolchain', 'depcheck_specs/ruby', 'depcheck_specs/libs', 'depcheck_specs/utilities' ] ids = [ 'cc', 'c++', 'gmake', 'rake', 'openssl-dev', 'zlib-dev', 'pcre-dev' ].compact return [specs, ids] end def install_doc_url "https://www.phusionpassenger.com/library/install/standalone/" end def troubleshooting_doc_url "https://www.phusionpassenger.com/library/admin/standalone/troubleshooting/" end def run_steps check_source_code_available! check_precompiled_support_libs_available! if !@force check_whether_os_is_broken check_whether_system_has_enough_ram check_for_download_tool! end check_dependencies(false) || abort puts @destdir = find_or_create_writable_support_binaries_dir! puts "Installing..." download_and_extract_nginx_sources determine_support_libraries if PhusionPassenger.build_system_dir compile_support_libraries end configure_and_compile_nginx end def before_install super if !@working_dir @working_dir = PhusionPassenger::Utils.mktmpdir("passenger-install.", PlatformInfo.tmpexedir) @owns_working_dir = true end @nginx_version ||= PREFERRED_NGINX_VERSION end def after_install super FileUtils.remove_entry_secure(@working_dir) if @owns_working_dir end private def check_source_code_available! return if File.exist?(PhusionPassenger.nginx_module_source_dir) if PhusionPassenger.originally_packaged? puts "Broken #{PROGRAM_NAME} installation detected" puts puts "This program requires the #{PROGRAM_NAME} Nginx module sources " + "before it can compile an Nginx engine. However, the #{PROGRAM_NAME} " + "Nginx module sources are not installed, even though they should have been. " + "This probably means that your #{PROGRAM_NAME} installation has somehow " + "become corrupted. Please re-install #{PROGRAM_NAME}." abort else case PhusionPassenger.packaging_method when "deb" command = "sudo sh -c 'apt-get update && apt-get install #{DEB_DEV_PACKAGE}'" when "rpm" command = "sudo yum install #{RPM_DEV_PACKAGE}-#{VERSION_STRING}" end if command if STDIN.tty? puts " --> Installing #{PROGRAM_NAME} Nginx module sources" puts " Running: #{command}" if !system(command) puts " *** Command failed: #{command}" abort end else puts " --> #{PROGRAM_NAME} Nginx module sources not installed" puts " Please install them first: #{command}" abort end else puts " --> #{PROGRAM_NAME} Nginx module sources not installed" puts " Please ask your #{PROGRAM_NAME} packager or operating " + "system vendor how to install these." abort end end end def check_precompiled_support_libs_available! return if PhusionPassenger.build_system_dir || File.exist?("#{PhusionPassenger.lib_dir}/common/libboost_oxt.a") if PhusionPassenger.originally_packaged? puts "Broken #{PROGRAM_NAME} installation detected" puts puts "This program requires the #{PROGRAM_NAME} support libraries " + "before it can compile an Nginx engine. However, the #{PROGRAM_NAME} " + "support libraries are not installed, even though they should have been. " + "This probably means that your #{PROGRAM_NAME} installation has somehow " + "become corrupted. Please re-install #{PROGRAM_NAME}." abort else case PhusionPassenger.packaging_method when "deb" command = "sudo sh -c 'apt-get update && apt-get install #{DEB_DEV_PACKAGE}'" when "rpm" command = "sudo yum install #{RPM_DEV_PACKAGE}-#{VERSION_STRING}" end if command if STDIN.tty? puts " --> Installing #{PROGRAM_NAME} support libraries" puts " Running: #{command}" if !system(command) puts " *** Command failed: #{command}" abort end else puts " --> #{PROGRAM_NAME} support libraries not installed" puts " Please install them first: #{command}" abort end else puts " --> #{PROGRAM_NAME} support libraries not installed" puts " Please ask your #{PROGRAM_NAME} packager or operating " + "system vendor how to install these." abort end end end def download_and_extract_nginx_sources if @nginx_tarball tarball = @nginx_tarball else puts "Downloading Nginx #{@nginx_version} source code..." basename = "nginx-#{@nginx_version}.tar.gz" tarball = "#{@working_dir}/#{basename}" options = { :show_progress => @stdout.tty? } if @connect_timeout && @connect_timeout != 0 options[:connect_timeout] = @connect_timeout end if @idle_timeout && @idle_timeout != 0 options[:idle_timeout] = @idle_timeout end result = download("http://nginx.org/download/#{basename}", tarball, options) if !result puts show_possible_solutions_for_download_and_extraction_problems abort end end nginx_sources_name = "nginx-#{@nginx_version}" puts "Extracting tarball..." e_working_dir = Shellwords.escape(@working_dir) e_tarball = Shellwords.escape(tarball) system("cd #{e_working_dir} && tar xzf #{e_tarball}") end def show_possible_solutions_for_download_and_extraction_problems new_screen render_template "nginx_engine_compiler/possible_solutions_for_download_and_extraction_problems" puts end def determine_support_libraries if PhusionPassenger.build_system_dir lib_dir = "#{@working_dir}/common/libpassenger_common" @support_libs = COMMON_LIBRARY.only(*NGINX_LIBS_SELECTOR). set_output_dir(lib_dir). link_objects @support_libs << "#{@working_dir}/common/libboost_oxt.a" else @support_libs = COMMON_LIBRARY.only(*NGINX_LIBS_SELECTOR). set_output_dir("#{PhusionPassenger.lib_dir}/common/libpassenger_common"). link_objects @support_libs << "#{PhusionPassenger.lib_dir}/common/libboost_oxt.a" end @support_libs_string = @support_libs.join(" ") end def compile_support_libraries puts "Compiling support libraries (step 1 of 2)..." progress_bar = ProgressBar.new e_working_dir = Shellwords.escape(@working_dir) args = "#{@support_libs_string} CACHING=false OUTPUT_DIR=#{e_working_dir}" begin progress_bar.set(0.05) Dir.chdir(PhusionPassenger.build_system_dir) do run_rake_task!(args) do |progress, total| progress_bar.set(0.05 + (progress / total.to_f) * 0.95) end end progress_bar.set(1) ensure progress_bar.finish end end def configure_and_compile_nginx puts "Compiling Nginx engine (step 2 of 2)..." progress_bar = ProgressBar.new progress_bar.set(0) begin configure_nginx do progress_bar.set(0.25) end compile_nginx do |progress| progress_bar.set(0.25 + progress * 0.75) end progress_bar.set(1) ensure progress_bar.finish end FileUtils.cp("#{@working_dir}/nginx-#{@nginx_version}/objs/nginx", "#{@destdir}/nginx-#{@nginx_version}") puts "Compilation finished!" end def configure_nginx shell = PlatformInfo.find_command('bash') || "sh" e_nginx_source_dir = Shellwords.escape("#{@working_dir}/nginx-#{@nginx_version}") command = "cd #{e_nginx_source_dir} && " command << "env PASSENGER_INCLUDEDIR=#{Shellwords.escape PhusionPassenger.include_dir} " << "PASSENGER_LIBS=#{Shellwords.escape(@support_libs_string)} " # RPM thinks it's being smart by scanning binaries for # paths and refusing to create package if it detects any # hardcoded thats that point to /usr or other important # locations. For Phusion Passenger Standalone we do not # care at all what the Nginx configured prefix is because # we pass it its resource locations during runtime, so # work around the problem by configure Nginx with prefix # /tmp. command << "#{shell} ./configure --prefix=/tmp " + "#{self.class.configure_script_options} " + "--add-module=#{Shellwords.escape PhusionPassenger.nginx_module_source_dir}" run_command_yield_activity(command) do yield end end def compile_nginx backlog = "" e_nginx_source_dir = Shellwords.escape("#{@working_dir}/nginx-#{@nginx_version}") # Capture and index the `make --dry-run` output for # progress determination. total_lines = 0 dry_run_output = {} `cd #{e_nginx_source_dir} && #{PlatformInfo.gnu_make} --dry-run`.split("\n").each do |line| total_lines += 1 dry_run_output[line] = true end IO.popen("cd #{e_nginx_source_dir} && #{PlatformInfo.gnu_make} 2>&1", "r") do |io| progress = 1 while !io.eof? line = io.readline backlog << line # If the output is part of what we saw when dry-running, # then increase progress bar. Otherwise it could be compiler # warnings or something, so ignore those. if dry_run_output[line.chomp] yield(progress / total_lines.to_f) progress += 1 end end end if $?.exitstatus != 0 @stderr.puts @stderr.puts "*** ERROR: unable to compile web helper." @stderr.puts backlog exit 1 end end def run_command_yield_activity(command) backlog = "" IO.popen("#{command} 2>&1", "rb") do |io| while !io.eof? backlog << io.readline yield end end if $?.exitstatus != 0 @stderr.puts @stderr.puts backlog @stderr.puts "*** ERROR: command failed: #{command}" exit 1 end end end end # module Standalone end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/reopen_logs_command.rb000644 000765 000024 00000013327 12233035540 031607 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'optparse' require 'net/http' require 'socket' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'admin_tools/instance_registry' PhusionPassenger.require_passenger_lib 'config/command' PhusionPassenger.require_passenger_lib 'config/utils' PhusionPassenger.require_passenger_lib 'utils/json' module PhusionPassenger module Config class ReopenLogsCommand < Command include PhusionPassenger::Config::Utils def run parse_options select_passenger_instance perform_reopen_logs end private def self.create_option_parser(options) OptionParser.new do |opts| nl = "\n" + ' ' * 37 opts.banner = "Usage: passenger-config reopen-logs [OPTIONS]\n" opts.separator "" opts.separator " Instruct #{PROGRAM_NAME} agent processes to reopen their log files. This" opts.separator " should be invoked after you've rotated log files. This command returns after" opts.separator " the log files have been reopened." opts.separator "" opts.separator "Options:" opts.on("--ignore-logs-not-available", "Exit successfully if #{PROGRAM_NAME}#{nl}" + "was not configured with a log file") do options[:ignore_logs_not_available] = true end opts.on("--instance NAME", String, "The #{PROGRAM_NAME} instance to select") do |value| options[:instance] = value end opts.on("-h", "--help", "Show this help") do options[:help] = true end end end def perform_reopen_logs perform_reopen_logs_on("watchdog", "watchdog_api") perform_reinherit_logs_on("core", "core_api") perform_reinherit_logs_on("UstRouter", "ust_router_api") if using_standalone_nginx_engine? perform_reopen_logs_on_nginx end puts "All done" end def perform_reopen_logs_on(name, socket_name) puts "Reopening logs for #{PROGRAM_NAME} #{name}" request = Net::HTTP::Post.new("/reopen_logs.json") try_performing_full_admin_basic_auth(request, @instance) request.content_type = "application/json" response = @instance.http_request("agents.s/#{socket_name}", request) if response.code.to_i == 401 print_full_admin_command_permission_error abort elsif response["content-type"] == "application/json" if response.code.to_i / 100 != 2 handle_error(name, response) end else STDERR.puts "*** An error occured while communicating with the #{PROGRAM_NAME} #{name} (code #{response.code}):" STDERR.puts response.body abort end end def perform_reinherit_logs_on(name, socket_name) puts "Reopening logs for #{PROGRAM_NAME} #{name} (through reinheritance)" request = Net::HTTP::Post.new("/reinherit_logs.json") try_performing_full_admin_basic_auth(request, @instance) request.content_type = "application/json" response = @instance.http_request("agents.s/#{socket_name}", request) if response.code.to_i == 401 print_full_admin_command_permission_error abort elsif response["content-type"] == "application/json" if response.code.to_i / 100 != 2 handle_error(name, response) end else STDERR.puts "*** An error occured while communicating with the #{PROGRAM_NAME} #{name} (code #{response.code}):" STDERR.puts response.body abort end end def using_standalone_nginx_engine? @instance.properties["integration_mode"] == "standalone" && @instance.properties["standalone_engine"] == "nginx" end def perform_reopen_logs_on_nginx puts "Reopening logs for Nginx engine" Process.kill('USR1', @instance.web_server_control_process_pid) end def handle_error(name, response) json = PhusionPassenger::Utils::JSON.parse(response.body) if !should_ignore_error?(json) STDERR.puts "*** An error occured while communicating with the #{PROGRAM_NAME} #{name} (code #{response.code}):" STDERR.puts json['message'] abort end end def should_ignore_error?(json) return @options[:ignore_logs_not_available] && json["code"] == "NO_LOG_FILE" end end end # module Config end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/restart_app_command.rb000644 000765 000024 00000025165 12233035540 031622 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2013-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'optparse' require 'net/http' require 'socket' require 'rexml/document' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'admin_tools/instance_registry' PhusionPassenger.require_passenger_lib 'config/command' PhusionPassenger.require_passenger_lib 'config/utils' PhusionPassenger.require_passenger_lib 'utils/json' PhusionPassenger.require_passenger_lib 'utils/ansi_colors' PhusionPassenger.require_passenger_lib 'utils/terminal_choice_menu' module PhusionPassenger module Config class RestartAppCommand < Command include PhusionPassenger::Config::Utils def run parse_options select_passenger_instance select_app_group_name perform_restart end private def self.create_option_parser(options) OptionParser.new do |opts| nl = "\n" + ' ' * 37 opts.banner = "Usage 1: passenger-config restart-app [OPTIONS]\n" + "Usage 2: passenger-config restart-app . [OPTIONS]\n" + "Usage 3: passenger-config restart-app --name [OPTIONS]" opts.separator "" opts.separator " Restart an application. The syntax determines how the application that is to" opts.separator " be restarted, will be selected." opts.separator "" opts.separator " 1. Selects all applications whose paths begin with the given prefix." opts.separator "" opts.separator " Example: passenger-config restart-app /webapps" opts.separator " Restarts all apps whose path begin with /webapps, such as /webapps/foo," opts.separator " /webapps/bar and /webapps123." opts.separator "" opts.separator " 2. Selects all application whose paths fall under the current working" opts.separator " directory." opts.separator " Example: passenger-config restart-app ." opts.separator " If the current working directory is /webapps, restarts all apps whose path" opts.separator " begin with /webapps, such as /webapps/foo, /webapps/bar and /webapps123." opts.separator "" opts.separator " 3. Selects a specific application based on an exact match of its app group" opts.separator " name." opts.separator "" opts.separator " Example: passenger-config restart-app --name /webapps/foo" opts.separator " Restarts only /webapps/foo, but not for example /webapps/foo/bar or" opts.separator " /webapps/foo123." opts.separator "" opts.separator "Options:" opts.on("--name APP_GROUP_NAME", String, "The app group name to select") do |value| options[:app_group_name] = value end opts.on("--rolling-restart", "Perform a rolling restart instead of a#{nl}" + "regular restart (Enterprise only). The#{nl}" + "default is a blocking restart") do |value| if Config::Utils.is_enterprise? options[:rolling_restart] = true else abort "--rolling-restart is only available in #{PROGRAM_NAME} Enterprise: #{ENTERPRISE_URL}" end end opts.on("--ignore-app-not-running", "Exit successfully if the specified#{nl}" + "application is not currently running. The#{nl}" + "default is to exit with an error") do options[:ignore_app_not_running] = true end opts.on("--ignore-passenger-not-running", "Exit successfully if #{PROGRAM_NAME}#{nl}" + "is not currently running. The default is to#{nl}" + "exit with an error") do options[:ignore_passenger_not_running] = true end opts.on("--instance NAME", String, "The #{PROGRAM_NAME} instance to select") do |value| options[:instance] = value end opts.on("-h", "--help", "Show this help") do options[:help] = true end end end def help puts @parser end def parse_options super case @argv.size when 0 if !@options[:app_group_name] && !STDIN.tty? abort "Please pass either an app path prefix or an app group name. " + "See --help for more information." end when 1 if @options[:app_group_name] abort "You've passed an app path prefix, but you cannot also pass an " + "app group name. Please use only either one of them. See --help " + "for more information." end else help abort end end def select_app_group_name @groups = [] if app_group_name = @options[:app_group_name] select_app_group_name_exact(app_group_name) elsif @argv.empty? # STDIN is guaranteed to be a TTY thanks to the check in #parse_options. select_app_group_name_interactively else select_app_group_name_by_app_root_regex(@argv.first) end end def select_app_group_name_exact(name) query_pool_xml.elements.each("info/supergroups/supergroup/group") do |group| if group.elements["name"].text == name @groups << group end end if @groups.empty? abort_app_not_found "There is no #{PROGRAM_NAME}-served application running " + "with the app group name '#{name}'." end end def query_group_names result = [] query_pool_xml.elements.each("info/supergroups/supergroup/group") do |group| result << group.elements["name"].text end result << "Cancel" result end def select_app_group_name_interactively colors = PhusionPassenger::Utils::AnsiColors.new choices = query_group_names if choices.size == 1 # No running apps abort_app_not_found "#{PROGRAM_NAME} is currently not serving any applications." end puts "Please select the application to restart." puts colors.ansi_colorize("Tip: re-run this command with --help to learn how to automate it.") puts colors.ansi_colorize("If the menu doesn't display correctly, press '!'") puts menu = PhusionPassenger::Utils::TerminalChoiceMenu.new(choices, :single_choice) begin index, name = menu.query rescue Interrupt abort ensure STDOUT.write(colors.reset) STDOUT.flush end if index == choices.size - 1 abort else puts select_app_group_name_exact(name) end end def select_app_group_name_by_app_root_regex(app_root) if app_root == "." app_root = Dir.pwd end regex = /^#{Regexp.escape(app_root)}/ query_pool_xml.elements.each("info/supergroups/supergroup/group") do |group| if group.elements["app_root"].text =~ regex @groups << group end end if @groups.empty? abort_app_not_found "There are no #{PROGRAM_NAME}-served applications running " + "whose paths begin with '#{app_root}'." end end def perform_restart restart_method = @options[:rolling_restart] ? "rolling" : "blocking" @groups.each do |group| group_name = group.elements["name"].text puts "Restarting #{group_name}" request = Net::HTTP::Post.new("/pool/restart_app_group.json") try_performing_full_admin_basic_auth(request, @instance) request.content_type = "application/json" request.body = PhusionPassenger::Utils::JSON.generate( :name => group_name, :restart_method => restart_method) response = @instance.http_request("agents.s/core_api", request) if response.code.to_i / 100 == 2 response.body elsif response.code.to_i == 401 print_full_admin_command_permission_error abort else STDERR.puts "*** An error occured while communicating with the #{PROGRAM_NAME} server:" STDERR.puts response.body abort end end end def abort_app_not_found(message) if @options[:ignore_app_not_running] STDERR.puts(message) exit else abort(message) end end def query_pool_xml request = Net::HTTP::Get.new("/pool.xml") try_performing_ro_admin_basic_auth(request, @instance) response = @instance.http_request("agents.s/core_api", request) if response.code.to_i / 100 == 2 REXML::Document.new(response.body) elsif response.code.to_i == 401 if response["pool-empty"] == "true" REXML::Document.new('') elsif @options[:ignore_app_not_running] print_instance_querying_permission_error exit else print_instance_querying_permission_error abort end else STDERR.puts "*** An error occured while querying the #{PROGRAM_NAME} server:" STDERR.puts response.body abort end end end end # module Config end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/system_metrics_command.rb000644 000765 000024 00000003540 12233035540 032341 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'config/command' module PhusionPassenger module Config class SystemMetricsCommand < Command def run agent_exe = PhusionPassenger.find_support_binary(AGENT_EXE) if agent_exe exec(agent_exe, "system-metrics", *@argv) else abort "This command requires the #{PROGRAM_NAME} agent to be installed. " + "Please install it by running `passenger-config install-agent`." end end end end # module Config end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/utils.rb000644 000765 000024 00000015454 12233035540 026740 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'constants' module PhusionPassenger module Config module Utils extend self # Make methods available as class methods. def self.included(klass) # When included into another class, make sure that Utils # methods are made private. public_instance_methods(false).each do |method_name| klass.send(:private, method_name) end end def select_passenger_instance if name = @options[:instance] @instance = AdminTools::InstanceRegistry.new.find_by_name_prefix(name) if !@instance if @options[:ignore_passenger_not_running] message_type = "WARNING" else message_type = "ERROR" end STDERR.puts "*** #{message_type}: there doesn't seem to be a #{PROGRAM_NAME} instance running with the name '#{name}'." list_all_passenger_instances(AdminTools::InstanceRegistry.new.list) STDERR.puts STDERR.puts "Please pass `--instance ` to select a specific #{PROGRAM_NAME} instance." if @options[:ignore_passenger_not_running] exit else abort end elsif @instance == :ambigious abort "*** ERROR: there are multiple instances whose name start with '#{name}'. Please specify the full name." end else instances = AdminTools::InstanceRegistry.new.list if instances.empty? if @options[:ignore_passenger_not_running] message_type = "WARNING" else message_type = "ERROR" end STDERR.puts "*** #{message_type}: #{PROGRAM_NAME} doesn't seem to be running. If you are sure that it" STDERR.puts "is running, then the causes of this problem could be one of:" STDERR.puts STDERR.puts " 1. You customized the instance registry directory using Apache's" STDERR.puts " PassengerInstanceRegistryDir option, Nginx's" STDERR.puts " passenger_instance_registry_dir option, or #{PROGRAM_NAME} Standalone's" STDERR.puts " --instance-registry-dir command line argument. If so, please set the" STDERR.puts " environment variable PASSENGER_INSTANCE_REGISTRY_DIR to that directory" STDERR.puts " and run this command again." STDERR.puts " 2. The instance directory has been removed by an operating system background" STDERR.puts " service. Please set a different instance registry directory using Apache's" STDERR.puts " PassengerInstanceRegistryDir option, Nginx's passenger_instance_registry_dir" STDERR.puts " option, or #{PROGRAM_NAME} Standalone's --instance-registry-dir command" STDERR.puts " line argument." if @options[:ignore_passenger_not_running] exit else abort end elsif instances.size == 1 @instance = instances.first else complain_that_multiple_passenger_instances_are_running(instances) abort end end end def complain_that_multiple_passenger_instances_are_running(instances) puts "It appears that multiple #{PROGRAM_NAME} instances are running. Please select" puts "a specific one by passing:" puts puts " --instance " puts list_all_passenger_instances(instances) abort end def list_all_passenger_instances(instances, print_preamble = true) if print_preamble puts "The following #{PROGRAM_NAME} instances are running:" puts end printf "%-25s %-7s %s\n", "Name", "PID", "Description" puts "--------------------------------------------------------------------------" if instances.empty? printf "%-25s %-7s %s\n", "(list empty)", "-", "-" else instances.each do |instance| printf "%-25s %-7s %s\n", instance.name, instance.watchdog_pid, instance.server_software end end end def try_performing_ro_admin_basic_auth(request, instance) begin password = instance.read_only_admin_password rescue Errno::EACCES return end request.basic_auth("ro_admin", password) end def try_performing_full_admin_basic_auth(request, instance) begin password = instance.full_admin_password rescue Errno::EACCES return end request.basic_auth("admin", password) end def print_instance_querying_permission_error PhusionPassenger.require_passenger_lib 'platform_info/ruby' STDERR.puts "*** ERROR: You are not authorized to query the status for this " + "#{PROGRAM_NAME} instance. Please try again with " + "'#{PhusionPassenger::PlatformInfo.ruby_sudo_command}'." end def print_full_admin_command_permission_error PhusionPassenger.require_passenger_lib 'platform_info/ruby' STDERR.puts "*** ERROR: You are not authorized to perform this particular " + "administration command on this #{PROGRAM_NAME} instance. Please try again with " + "'#{PhusionPassenger::PlatformInfo.ruby_sudo_command}'." end def is_enterprise? return defined?(PhusionPassenger::PASSENGER_IS_ENTERPRISE) && PhusionPassenger::PASSENGER_IS_ENTERPRISE end end end # module Config end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/config/validate_install_command.rb000644 000765 000024 00000063537 12233035540 032622 0ustar00honglistaff000000 000000 # encoding: utf-8 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'optparse' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'config/command' module PhusionPassenger module Config class ValidateInstallCommand < Command # Signifies that there is at least 1 error. FAIL_EXIT_CODE = 1 # Signifies that there are no error, but at least 1 warning. WARN_EXIT_CODE = 2 # Internal error occurred. INTERNAL_ERROR_CODE = 9 def run @orig_argv = @argv.dup parse_options prepare begin if !@options[:auto] && !@options[:invoked_from_installer] ask_what_to_validate end if @options[:validate_apache2] initialize_apache_envvars if !@options[:auto] && !@options[:invoked_from_installer] check_whether_there_are_multiple_apache_installs end end if @options[:validate_passenger] check_tools_in_path check_no_other_installs_in_path end if @options[:validate_apache2] if check_apache2_installed check_apache2_load_module_config end end if @options[:summary] summarize end exit(FAIL_EXIT_CODE) if @error_count > 0 exit(WARN_EXIT_CODE) if @warning_count > 0 ensure reset_terminal end end private def self.create_default_options return { :auto => !STDIN.tty?, :validate_passenger => true, :colors => STDOUT.tty?, :summary => true } end def self.create_option_parser(options) OptionParser.new do |opts| nl = "\n" + ' ' * 37 opts.banner = "Usage: passenger-config validate-install [OPTIONS]\n" opts.separator "" opts.separator " Validate this #{SHORT_PROGRAM_NAME} installation and/or its integration with web servers." opts.separator " If you run this command in a terminal, it will run interactively and ask you questions." opts.separator " You can run this command non-interactively either by running it without a terminal, " opts.separator " or by passing --auto." opts.separator "" opts.separator " When running non-interactively, the default is to validate the #{SHORT_PROGRAM_NAME}" opts.separator " installation only (so e.g. Apache is not validated). You can customize this" opts.separator " using the appropriate command line options." opts.separator "" opts.separator " The exit codes are as follows:" opts.separator " 0 - All checks passed. No errors, no warnings." opts.separator " #{FAIL_EXIT_CODE} - Some checks failed with an error." opts.separator " #{WARN_EXIT_CODE} - No checks failed with an error, but some produced warnings." opts.separator " #{INTERNAL_ERROR_CODE} - Some internal error occurred." opts.separator "" opts.separator "Options:" opts.on("--auto", "Run non-interactively") do options[:auto] = true end opts.on("--no-validate-passenger", "Do not validate the #{SHORT_PROGRAM_NAME} installation#{nl}" + "itself") do options[:validate_passenger] = false end opts.on("--validate-apache2", "Validate Apache 2 integration") do options[:validate_apache2] = true end opts.on("--apxs2-path=PATH", String, "Apache installation to validate") do |val| ENV['APXS2'] = val end opts.separator "" opts.on("--no-colors", "Never output colors") do options[:colors] = false end opts.on("--no-summary", "Do not display a summary") do options[:summary] = false end opts.separator "" opts.on("-h", "--help", "Show this help") do options[:help] = true end opts.separator "" opts.separator "Internal options:" opts.on("--invoked-from-installer", "Indicate that this program is invoked from#{nl}" + "passenger-install-apache2-module") do options[:invoked_from_installer] = true end end end def prepare begin require 'rubygems' rescue LoadError end PhusionPassenger.require_passenger_lib 'utils/ansi_colors' PhusionPassenger.require_passenger_lib 'utils/terminal_choice_menu' PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/ruby' PhusionPassenger.require_passenger_lib 'platform_info/apache' PhusionPassenger.require_passenger_lib 'platform_info/apache_detector' PhusionPassenger.require_passenger_lib 'platform_info/depcheck' require 'stringio' require 'pathname' @error_count = 0 @warning_count = 0 @colors = Utils::AnsiColors.new(@options[:colors]) prepare_terminal end def prepare_terminal STDOUT.write(@colors.default_terminal_color) STDOUT.flush end def reset_terminal STDOUT.write(@colors.reset) STDOUT.flush end def ask_what_to_validate log "What would you like to validate?" log "Use to select." log "If the menu doesn't display correctly, press '!'" puts menu = Utils::TerminalChoiceMenu.new([ "#{SHORT_PROGRAM_NAME} itself", "Apache" ]) menu["#{SHORT_PROGRAM_NAME} itself"].checked = @options[:validate_passenger] menu["Apache"].checked = @options[:validate_apache2] begin menu.query rescue Interrupt exit(INTERNAL_ERROR_CODE) end display_separator @options[:validate_passenger] = menu.selected_choices.include?("#{SHORT_PROGRAM_NAME} itself") @options[:validate_apache2] = menu.selected_choices.include?("Apache") end def initialize_apache_envvars # The Apache executable may be located in an 'sbin' folder. We add # the 'sbin' folders to $PATH just in case. On some systems # 'sbin' isn't in $PATH unless the user is logged in as root from # the start (i.e. not via 'su' or 'sudo'). ENV["PATH"] += ":/usr/sbin:/sbin:/usr/local/sbin" end def check_tools_in_path checking "whether this #{SHORT_PROGRAM_NAME} install is in PATH" paths = ENV['PATH'].to_s.split(':') if paths.include?(gem_bindir) || paths.include?(homebrew_bindir) || paths.include?(PhusionPassenger.bin_dir) check_ok else check_warning suggest %Q{ Please add #{PhusionPassenger.bin_dir} to PATH. Otherwise you will get "command not found" errors upon running any Passenger commands. Learn more at about PATH at: https://www.phusionpassenger.com/library/indepth/environment_variables.html#the-path-environment-variable } end end def check_no_other_installs_in_path checking "whether there are no other #{SHORT_PROGRAM_NAME} installations" paths = ENV['PATH'].to_s.split(':') if Process.uid == 0 && (sudo_user = ENV['SUDO_USER']) && (bash = PlatformInfo.find_command("bash")) && PlatformInfo.find_command("sudo") # If we were invoked through sudo then we need to check the original user's PATH too. output = `sudo -u #{sudo_user} #{bash} -lc 'echo; echo PATH FOLLOWS; echo "$PATH"' 2>&1` output.sub!(/.*\nPATH FOLLOWS\n/m, '') output.strip! paths.concat(output.split(':')) end # These may not be in PATH if the user did not run this command through sudo. paths << "/usr/bin" paths << "/usr/sbin" # Some of the paths may be symlinks, so we take the realpaths when # possible and remove duplicates. This is especially important on # Red Hat 7, where /bin is a symlink to /usr/bin. paths.map! do |path| try_realpath(path) end paths.delete(try_realpath(gem_bindir)) paths.delete(try_realpath(homebrew_bindir)) paths.delete(try_realpath(rbenv_shims_dir)) paths.delete(try_realpath(PhusionPassenger.bin_dir)) paths.uniq! other_installs = [] paths.each do |path| filename = "#{path}/passenger" if File.exist?(filename) other_installs << filename end end if other_installs.empty? check_ok else check_warning suggest %Q{ You are currently validating against #{PROGRAM_NAME} #{VERSION_STRING}, located in: #{PhusionPassenger.bin_dir}/passenger Besides this #{SHORT_PROGRAM_NAME} installation, the following other #{SHORT_PROGRAM_NAME} installations have also been detected: #{other_installs.join("\n ")} Please uninstall these other #{SHORT_PROGRAM_NAME} installations to avoid confusion or conflicts. } end end def check_whether_there_are_multiple_apache_installs if PlatformInfo.httpd.nil? || PlatformInfo.apxs2.nil? # check_apache2_installed will handle this. return end log 'Checking whether there are multiple Apache installations...' output = StringIO.new detector = PlatformInfo::ApacheDetector.new(output) begin detector.detect_all detector.report apache2 = detector.result_for(PlatformInfo.apxs2) if apache2.nil? # Print an extra newline because the autodetection routines # may have run some commands which printed stuff to stderr. puts if Process.uid == 0 # More information will be displayed in #check_no_duplicate_apache2_load_module_config, # which should also fail. log "Your Apache installation appears to be broken. More information will be displayed later." else whoami = `whoami`.strip sudo = PhusionPassenger::PlatformInfo.ruby_sudo_command selfcommand = "#{PhusionPassenger.bin_dir}/passenger-config validate-install #{@orig_argv.join(' ')}" log "Permission problems" log "This program must be able to analyze your Apache installation. But it can't" log "do that, because you're running the installer as #{whoami}." log "Please give this program root privileges, by re-running it with #{sudo}:" log "" log " export ORIG_PATH=\"$PATH\"" log " #{sudo_s_e}" log " export PATH=\"$ORIG_PATH\"" log " #{ruby_command} #{selfcommand}" exit(INTERNAL_ERROR_CODE) end elsif detector.results.size > 1 other_installs = detector.results - [apache2] log "Multiple Apache installations detected!" log "" log "You are about to validate #{SHORT_PROGRAM_NAME} against the following" log "Apache installation:" log "" log " Apache #{apache2.version}" log " apxs2 : #{apache2.apxs2}" log " Executable: #{apache2.httpd}" log "" log "However, #{other_installs.size} other Apache installation(s) have been found on your system:" log "" other_installs.each do |result| log " Apache #{result.version}" log " apxs2 : #{result.apxs2}" log " Executable: #{result.httpd}" log "" end result = prompt_confirmation "Are you sure you want to validate " + "against Apache #{apache2.version} (#{apache2.apxs2})?" if !result puts display_separator other_installs.each do |result| log " * To validate against Apache #{result.version} (#{result.apxs2}):" log " Re-run this program with: --apxs2-path '#{result.apxs2}'" end log "" log "You may also want to read the \"Installation\" section of Passenger Library" log "installation troubleshooting:" log "" log " https://www.phusionpassenger.com/library/install/apache/" log "" log "If you keep having problems installing, please visit the following website for" log "support:" log "" log " #{SUPPORT_URL}" exit(INTERNAL_ERROR_CODE) end else log 'Only a single installation detected. This is good.' end display_separator ensure detector.finish end end def check_apache2_installed checking "whether Apache is installed" if PlatformInfo.httpd if PlatformInfo.apxs2 check_ok true else check_error PlatformInfo::Depcheck.load("depcheck_specs/apache2") dep = PlatformInfo::Depcheck.find("apache2-dev") install_instructions = dep.install_instructions.split("\n").join("\n ") if !@options[:invoked_from_installer] next_step = "When done, please re-run this program." end suggest %Q{ Unable to validate your Apache installation: more software required This program requires the apxs2 tool in order to be able to validate your Apache installation. This tool is currently not installed. You can solve this as follows: #{install_instructions} #{next_step} } false end else check_error PlatformInfo::Depcheck.load("depcheck_specs/apache2") dep = PlatformInfo::Depcheck.find("apache2") install_instructions = dep.install_instructions.split("\n").join("\n ") suggest %Q{ Apache is not installed. You can solve this as follows: #{install_instructions} } false end end def check_apache2_load_module_config checking "whether the Passenger module is correctly configured in Apache" if PlatformInfo.httpd_default_config_file.nil? check_error passenger_config = "#{PhusionPassenger.bin_dir}/passenger-config" suggest %Q{ Your Apache installation might be broken You are about to validate #{PROGRAM_NAME} against the following Apache installation: apxs2: #{PlatformInfo.apxs2} However, this Apache installation appears to be broken, so this program cannot continue. To find out why this program thinks the above Apache installation is broken, run: export ORIG_PATH="$PATH" #{sudo_s_e} export PATH="$ORIG_PATH" #{ruby_command} #{passenger_config} --detect-apache2 } return end result = PlatformInfo.httpd_included_config_files( PlatformInfo.httpd_default_config_file) if !result[:unreadable_files].empty? check_error if Process.uid == 0 suggest %Q{ Permission problems This program must be able to analyze your Apache installation. But it can't do that despite running with root privileges. In particular, it failed to read the following files: #{result[:unreadable_files].join("\n ")} This program doesn't know why this error occurred. Your system is probably secured using some mechanism that this program is not familiar with. Please consult your operating system's manual to learn which security mechanisms may be preventing this program from accessing the above files. On Linux systems, SELinux and AppArmor might be responsible. When you've solved the problem, please re-run this program. } else whoami = `whoami`.strip sudo = PhusionPassenger::PlatformInfo.ruby_sudo_command selfcommand = "#{PhusionPassenger.bin_dir}/passenger-config validate-install #{@orig_argv.join(' ')}" suggest %Q{ Permission problems This program must be able to analyze your Apache installation. But it can't do that, because you're running the installer as '#{whoami}'. In particular, it failed to read the following files: #{result[:unreadable_files].join("\n ")} Please give this program root privileges, by re-running it with '#{sudo}': export ORIG_PATH=\"$PATH\" #{sudo_s_e} export PATH=\"$ORIG_PATH\" #{ruby_command} #{selfcommand} } end return end occurrences = 0 occurrence_files = [] module_path = nil result[:files].each do |path| lines = File.open(path, "rb") do |f| f.read.split("\n") end lines.each do |line| # Get rid of trailing CR line = line.strip if line !~ /^[\s\t]*#/ && line =~ /LoadModule[\s\t]+passenger_module[\s\t]+(.*)/ module_path = $1 occurrences += 1 occurrence_files << path end end end if occurrences == 1 if module_path !~ /^\// # Non-absolute path. Absolutize using ServerRoot. module_path = "#{PlatformInfo.httpd_default_root}/#{module_path}" end # Resolve symlinks. module_path = try_realpath(module_path) expected_module_path = try_realpath(PhusionPassenger.apache2_module_path) if module_path == expected_module_path check_ok else check_error suggest %Q{ Incorrect #{SHORT_PROGRAM_NAME} module path detected #{PROGRAM_NAME} for Apache requires a 'LoadModule passenger_module' directive inside an Apache configuration file. This directive has been detected in the following config file: #{occurrence_files[0]} However, the directive refers to the following Apache module, which is wrong: #{module_path} Please edit the config file and change the directive to this instead: LoadModule passenger_module #{PhusionPassenger.apache2_module_path} } end elsif occurrences == 0 if @options[:invoked_from_installer] check_warning suggest %Q{ You did not specify 'LoadModule passenger_module' in any of your Apache configuration files. Please paste the configuration snippet that this installer printed earlier, into one of your Apache configuration files, such as #{PlatformInfo.httpd_default_config_file}. } else check_error installer_command = "#{PhusionPassenger.bin_dir}/passenger-install-apache2-module" suggest %Q{ You did not specify 'LoadModule passenger_module' in any of your Apache configuration files. This means that #{PROGRAM_NAME} for Apache is not installed or not active. Please run the #{PROGRAM_NAME} Apache module installer: #{ruby_command} #{installer_command} --apxs2=#{PlatformInfo.apxs2} } end else check_error suggest %Q{ You have #{occurrences} 'LoadModule passenger_module' directives in your Apache configuration files. However, you are only supposed to have one such directive. Please fix this by removing all 'LoadModule passenger_module' directives besides the one for #{SHORT_PROGRAM_NAME} version #{VERSION_STRING}. The directives were found in these files: #{occurrence_files.uniq.join("\n ")} Note: 'LoadModule passenger_module' may be placed inside the global context only (so not within a VirtualHost). } end end def summarize puts if @error_count == 0 && @warning_count == 0 log "Everything looks good. :-)" elsif @error_count == 0 log "Detected 0 error(s), #{@warning_count} warning(s)." else log "Detected #{@error_count} error(s), #{@warning_count} warning(s)." end end # Returns the RubyGems bin dir, if Phusion Passenger is installed through RubyGems. def gem_bindir if defined?(Gem) && PhusionPassenger.originally_packaged? && PhusionPassenger.build_system_dir =~ /^#{Regexp.escape Gem.dir}\// && File.exist?("#{Gem.bindir}/passenger-config") return Gem.bindir else return nil end end # Returns the Homebrew bin dir, if Phusion Passenger is installed through Homebrew. def homebrew_bindir if PhusionPassenger.packaging_method == "homebrew" return "/usr/local/bin" else return nil end end # Returns the ~/.rbenv/shims directory if it exists. def rbenv_shims_dir home = PhusionPassenger.home_dir "#{home}/.rbenv/shims" end def logn(message) STDOUT.write(@colors.ansi_colorize(message)) STDOUT.flush end def log(message) STDOUT.puts(@colors.ansi_colorize(message)) end def display_separator puts puts "-------------------------------------------------------------------------" puts end def checking(message) logn " * Checking #{message}... " end def check_ok(message = "✓") log "#{message}" end def check_error(message = "✗") log "#{message}" @error_count += 1 end def check_warning(message = "(!)") log "#{message}" @warning_count += 1 end def suggest(message) puts log reindent(unindent(message), 3) puts end def prompt_confirmation(message) result = prompt("#{message} [y/n]") do |value| if value.downcase == 'y' || value.downcase == 'n' true else log "Invalid input '#{value}'; please enter either 'y' or 'n'." false end end result.downcase == 'y' rescue Interrupt exit(INTERNAL_ERROR_CODE) end def prompt(message, default_value = nil) done = false while !done logn "#{message}: " if default_value puts default_value return default_value end begin result = STDIN.readline rescue EOFError exit(INTERNAL_ERROR_CODE) end result.strip! if result.empty? if default_value result = default_value done = true else done = !block_given? || yield(result) end else done = !block_given? || yield(result) end end result rescue Interrupt exit(INTERNAL_ERROR_CODE) end def unindent(text) return PlatformInfo.send(:unindent, text) end def reindent(text, level) return PlatformInfo.send(:reindent, text, level) end def sudo_s_e PlatformInfo.ruby_sudo_shell_command("-E") end def ruby_command PlatformInfo.ruby_command end def try_realpath(path) if path begin Pathname.new(path).realpath.to_s rescue Errno::ENOENT, Errno::EACCES, Errno::ENOTDIR path end else nil end end end end # module Config end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/apache2/config_options.rb000644 000765 000024 00000021240 12233035540 030644 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. # This file defines all supported Apache per-directory configuration options. The # build system automatically generates the corresponding Apache module boilerplate # code from the definitions in this file. # # Main configuration options are not defined in this file, but are defined in # src/apache2_module/Configuration.cpp instead. # # The following boilerplate code is generated: # # * command_rec array members (ConfigurationCommands.cpp.erb) # # Options: # # * name - The configuration option name. Required. # * context - The context in which this configuration option is valid. # Defaults to ["OR_OPTIONS", "ACCESS_CONF", "RSRC_CONF"] # * type - This configuration option's value type. Allowed types: # :string, :integer, :flag # * min_value - If `type` is :integer, then this specifies the minimum # allowed value. When nil (the default), there is no minimum. # * desc - A description for this configuration option. Required. # * header - The name of the corresponding CGI header. By default CGI header # generation code is automatically generated, using the configuration # option's name in uppercase as the CGI header name. # Setting this to nil will disable auto-generation of CGI header # generation code. You are then responsible for writing CGI header # passing code yourself in Hooks.cpp. # * header_expression - The expression to be passed to `addHeader()`. # * function - If nil, a setter function will be automatically generated. If # non-nil, must be the name of the setter function. PhusionPassenger.require_passenger_lib 'constants' APACHE2_DIRECTORY_CONFIGURATION_OPTIONS = [ { :name => "PassengerRuby", :type => :string, :desc => "The Ruby interpreter to use.", :header_expression => "config->ruby ? config->ruby : serverConfig.defaultRuby" }, { :name => "PassengerPython", :type => :string, :desc => "The Python interpreter to use." }, { :name => "PassengerNodejs", :type => :string, :desc => "The Node.js command to use." }, { :name => "PassengerMeteorAppSettings", :type => :string, :desc => "Settings file for (non-bundled) Meteor apps." }, { :name => "PassengerAppEnv", :type => :string, :desc => "The environment under which applications are run." }, { :name => "PassengerMinInstances", :type => :integer, :context => ["OR_LIMIT", "ACCESS_CONF", "RSRC_CONF"], :min_value => 0, :header => "PASSENGER_MIN_PROCESSES", :desc => "The minimum number of application instances to keep when cleaning idle instances." }, { :name => "PassengerMaxInstancesPerApp", :type => :integer, :context => ["RSRC_CONF"], :header => "PASSENGER_MAX_PROCESSES", :desc => "The maximum number of simultaneously alive application instances a single application may occupy." }, { :name => "PassengerUser", :type => :string, :context => ["ACCESS_CONF", "RSRC_CONF"], :desc => "The user that Ruby applications must run as." }, { :name => "PassengerGroup", :type => :string, :context => ["ACCESS_CONF", "RSRC_CONF"], :desc => "The group that Ruby applications must run as." }, { :name => "PassengerErrorOverride", :type => :flag, :context => ["OR_ALL"], :desc => "Allow Apache to handle error response.", :header => nil }, { :name => "PassengerMaxRequests", :type => :integer, :context => ["OR_LIMIT", "ACCESS_CONF", "RSRC_CONF"], :min_value => 0, :desc => "The maximum number of requests that an application instance may process." }, { :name => "PassengerStartTimeout", :type => :integer, :context => ["OR_LIMIT", "ACCESS_CONF", "RSRC_CONF"], :min_value => 1, :desc => "A timeout for application startup." }, { :name => "PassengerHighPerformance", :type => :flag, :context => ["OR_ALL"], :desc => "Enable or disable Passenger's high performance mode.", :header => nil }, { :name => "PassengerEnabled", :type => :flag, :context => ["OR_ALL"], :desc => "Enable or disable Phusion Passenger.", :header => nil }, { :name => "PassengerMaxRequestQueueSize", :type => :integer, :min_value => 0, :context => ["OR_ALL"], :desc => "The maximum number of queued requests." }, { :name => "PassengerMaxPreloaderIdleTime", :type => :integer, :min_value => 0, :context => ["RSRC_CONF"], :desc => "The maximum number of seconds that a preloader process may be idle before it is shutdown." }, { :name => "PassengerLoadShellEnvvars", :type => :flag, :desc => "Whether to load environment variables from the shell before running the application." }, { :name => "PassengerBufferUpload", :type => :flag, :context => ["OR_ALL"], :desc => "Whether to buffer file uploads.", :header => nil }, { :name => 'PassengerAppType', :type => :string, :context => ["OR_ALL"], :desc => "Force specific application type.", :header => nil }, { :name => 'PassengerStartupFile', :type => :string, :context => ["OR_ALL"], :desc => "Force specific startup file." }, { :name => 'PassengerStickySessions', :type => :flag, :context => ["OR_ALL"], :desc => "Whether to enable sticky sessions." }, { :name => 'PassengerStickySessionsCookieName', :type => :flag, :context => ["OR_ALL"], :desc => "The cookie name to use for sticky sessions." }, { :name => "PassengerSpawnMethod", :type => :string, :context => ["RSRC_CONF"], :desc => "The spawn method to use.", :function => "cmd_passenger_spawn_method" }, { :name => "PassengerShowVersionInHeader", :type => :flag, :desc => "Whether to show the Phusion Passenger version number in the X-Powered-By header." }, { :name => "PassengerFriendlyErrorPages", :type => :flag, :desc => "Whether to display friendly error pages when something goes wrong." }, { :name => "PassengerRestartDir", :type => :string, :desc => "The directory in which Passenger should look for restart.txt." }, { :name => "PassengerAppGroupName", :type => :string, :desc => "Application process group name." }, { :name => "PassengerForceMaxConcurrentRequestsPerProcess", :type => :integer, :desc => "Force #{SHORT_PROGRAM_NAME} to believe that an application process " \ "can handle the given number of concurrent requests per process" }, { :name => "PassengerLveMinUid", :type => :integer, :min_value => 0, :context => ["RSRC_CONF"], :desc => "Minimum user id starting from which entering LVE and CageFS is allowed." }, ##### Aliases ##### { :name => "RailsEnv", :type => :string, :desc => "The environment under which applications are run.", :alias_for => "PassengerAppEnv" }, { :name => "RackEnv", :type => :string, :desc => "The environment under which applications are run.", :alias_for => "PassengerAppEnv" }, ##### Deprecated options ##### { :name => "RailsSpawnMethod", :type => :string, :context => ["RSRC_CONF"], :desc => "Deprecated option.", :alias_for => "PassengerSpawnMethod" } ] passenger-5.0.30/src/ruby_supportlib/phusion_passenger/admin_tools/instance.rb000644 000765 000024 00000015720 12233035540 030443 0ustar00honglistaff000000 000000 # encoding: utf-8 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/operating_system' PhusionPassenger.require_passenger_lib 'utils/json' module PhusionPassenger module AdminTools class Instance STALE_TIMEOUT = 60 attr_reader :path, :state, :name, :server_software, :properties def initialize(path) @path = path # Possible states: # # * :good - Everything is good. But the Watchdog process that created # it might have died. In that case, the directory should be cleaned up. # Use `locked?` to check. # # * :not_finalized - The instance directory hasn't been finalized yet, # so we can't read any information from it. It is possible that the # process that has created it has died, i.e. that it will never # become finalized. In that case, the directory should be cleaned up. # Use `stale?` to check. # # * :structure_version_unsupported - The properties file advertised a # structure version that we don't support. We can't read any # information from it. # # * :corrupted - The instance directory is corrupted in some way. @state = nil reload_properties end def locked? if PlatformInfo.supports_flock? begin !File.open("#{@path}/lock", "r") do |f| f.flock(File::LOCK_EX | File::LOCK_NB) end rescue Errno::ENOENT false end else # Solaris :-( # Since using fcntl locks in Ruby is a huge pain, # we'll fallback to checking the watchdog PID. watchdog_alive? end end def stale? stat = File.stat(@path) if stat.mtime < Time.now - STALE_TIMEOUT !locked? else false end end def watchdog_alive? process_is_alive?(@watchdog_pid) end def http_request(socket_path, request) sock = Net::BufferedIO.new(UNIXSocket.new("#{@path}/#{socket_path}")) begin request.exec(sock, "1.1", request.path) done = false while !done response = Net::HTTPResponse.read_new(sock) done = !response.kind_of?(Net::HTTPContinue) end response.reading_body(sock, request.response_body_permitted?) do # Nothing end ensure sock.close end return response end def watchdog_pid properties["watchdog_pid"] end def core_pid @core_pid ||= File.read("#{@path}/core.pid").to_i end def web_server_control_process_pid File.read("#{@path}/web_server_control_process.pid").to_i end def full_admin_password @full_admin_password ||= File.read("#{@path}/full_admin_password.txt") end def read_only_admin_password @read_only_admin_password ||= File.read("#{@path}/read_only_admin_password.txt") end def as_json json = properties json["instance_dir"]["path"] = path json["core_pid"] = core_pid json end private class CorruptedError < StandardError end def reload_properties @properties = nil @state = nil begin reload_properties_internal rescue CorruptedError @state = :corrupted end end def reload_properties_internal if !File.exist?("#{@path}/creation_finalized") @state = :unfinalized return end check(File.file?("#{@path}/creation_finalized")) properties = nil begin File.open("#{@path}/properties.json", "r") do |f| properties = Utils::JSON.parse(f.read) end rescue Errno::ENOENT raise CorruptedError rescue RuntimeError => e if e.message =~ /parse error/ raise CorruptedError else raise end end check(!properties.nil?) props = properties["instance_dir"] check(!props.nil?) major_version = props["major_version"] minor_version = props["minor_version"] check(major_version.is_a?(Integer)) check(minor_version.is_a?(Integer)) name = properties["name"] check(name.is_a?(String)) check(!name.empty?) server_software = properties["server_software"] check(server_software.is_a?(String)) check(!server_software.empty?) watchdog_pid = properties["watchdog_pid"] check(watchdog_pid.is_a?(Integer)) version_supported = major_version == PhusionPassenger::SERVER_INSTANCE_DIR_STRUCTURE_MAJOR_VERSION && minor_version >= PhusionPassenger::SERVER_INSTANCE_DIR_STRUCTURE_MIN_SUPPORTED_MINOR_VERSION if !version_supported @state = :structure_version_unsupported return end check(File.file?("#{@path}/lock")) check(File.directory?("#{@path}/agents.s")) check(File.directory?("#{@path}/apps.s")) @properties = properties @name = name @server_software = server_software @watchdog_pid = watchdog_pid @state = :good end def check(val) raise CorruptedError if !val end def process_is_alive?(pid) begin Process.kill(0, pid) return true rescue Errno::ESRCH return false rescue SystemCallError => e return true end end end end # module AdminTools end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/admin_tools/instance_registry.rb000644 000765 000024 00000007651 12233035540 032377 0ustar00honglistaff000000 000000 # encoding: utf-8 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2014-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'fileutils' PhusionPassenger.require_passenger_lib 'admin_tools/instance' module PhusionPassenger module AdminTools class InstanceRegistry def initialize(paths = nil) @paths = [paths || default_paths].flatten end def list(options = {}) options = { :clean_stale_or_corrupted => true }.merge(options) instances = [] @paths.each do |path| Dir["#{path}/passenger.*"].each do |dir| instance = Instance.new(dir) case instance.state when :good if instance.locked? instances << instance elsif options[:clean_stale_or_corrupted] cleanup(dir) end when :structure_version_unsupported next when :corrupted if !instance.locked? && options[:clean_stale_or_corrupted] cleanup(dir) end when :not_finalized if instance.stale? && options[:clean_stale_or_corrupted] cleanup(dir) end end end end instances end def find_by_name(name, options = {}) return list(options).find { |instance| instance.name == name } end def find_by_name_prefix(name, options = {}) prefix = /^#{Regexp.escape name}/ results = list(options).find_all { |instance| instance.name =~ prefix } if results.size <= 1 return results.first else return :ambiguous end end private def default_paths if result = string_env("PASSENGER_INSTANCE_REGISTRY_DIR") return [result] end # On OSX, TMPDIR is set to a different value per-user. But Apache # is launched through Launchctl and runs without TMPDIR (and thus # uses the default /tmp). # # The RPM packages configure Apache and Nginx to use /var/run/passenger-instreg # as the instance registry dir. See https://github.com/phusion/passenger/issues/1475 [string_env("TMPDIR"), "/tmp", "/var/run/passenger-instreg"].compact end def string_env(name) if (result = ENV[name]) && !result.empty? result else nil end end def cleanup(path) puts "*** Cleaning stale instance directory #{path}" begin FileUtils.chmod_R(0700, path) rescue nil FileUtils.remove_entry_secure(path) rescue SystemCallError => e puts " Warning: #{e}" end end end end # module AdminTools end # module PhusionPassenger passenger-5.0.30/src/ruby_supportlib/phusion_passenger/admin_tools/memory_stats.rb000644 000765 000024 00000024602 12233035540 031364 0ustar00honglistaff000000 000000 # encoding: binary # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2013 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'platform_info/apache' PhusionPassenger.require_passenger_lib 'platform_info/operating_system' module PhusionPassenger module AdminTools class MemoryStats # Information about a single process. class Process attr_accessor :pid attr_accessor :ppid attr_accessor :threads attr_accessor :vm_size # in KB attr_accessor :rss # in KB attr_accessor :cpu attr_accessor :name attr_accessor :private_dirty_rss # in KB def vm_size_in_mb return sprintf("%.1f MB", vm_size / 1024.0) end def rss_in_mb return sprintf("%.1f MB", rss / 1024.0) end def private_dirty_rss_in_mb if private_dirty_rss.is_a?(Numeric) return sprintf("%.1f MB", private_dirty_rss / 1024.0) else return "?" end end def to_a return [pid, ppid, vm_size_in_mb, private_dirty_rss_in_mb, rss_in_mb, name] end end # Returns a list of Apache processes, which may be the empty list if Apache is # not running. If the Apache executable name is unknown then nil will be returned. def apache_processes @apache_processes ||= begin if PlatformInfo.httpd processes = list_processes(:exe => PlatformInfo.httpd) if processes.empty? # On some Linux distros, the Apache worker processes # are called "httpd.worker" processes = list_processes(:exe => "#{PlatformInfo.httpd}.worker") end processes else nil end end end # Returns a list of Nginx processes, which may be the empty list if # Nginx is not running. def nginx_processes @nginx_processes ||= list_processes(:exe => "nginx") end # Returns a list of Phusion Passenger processes, which may be the empty list if # Phusion Passenger is not running. def passenger_processes @passenger_processes ||= list_processes(:match => /((^| )Passenger|(^| )Rails:|(^| )Rack:|wsgi-loader.py|(.*)PassengerAgent)/) end # Returns the sum of the memory usages of all given processes. # Returns a pair [usage, accurate]. +usage+ is the summed memory usage in KB, # and +accurate+ indicates whether this sum is accurate. This may be false # if some process's memory usage cannot be determined. def sum_memory_usage(processes) total = 0 if should_show_private_dirty_rss? accurate = true processes.each do |p| if p.private_dirty_rss.is_a?(Numeric) total += p.private_dirty_rss else accurate = true end end return [total, accurate] else processes.each do |p| total += p.rss end return [total, true] end end def platform_provides_private_dirty_rss_information? return os_name_simple == "linux" end # Returns whether root privileges are required in order to measure private dirty RSS. # Only meaningful if #platform_provides_private_dirty_rss_information? returns true. def root_privileges_required_for_private_dirty_rss? all_processes = (apache_processes || []) + nginx_processes + passenger_processes return all_processes.any?{ |p| p.private_dirty_rss.nil? } end def should_show_private_dirty_rss? return platform_provides_private_dirty_rss_information? && (::Process.euid == 0 || root_privileges_required_for_private_dirty_rss?) end # Determine the system's RAM usage, not including swap. # Returns a tuple [total, used] where both numbers are in KB, or nil # if the system's RAM usage cannot be determined. def system_ram_usage @total_system_ram ||= begin case os_name_simple when "linux" free_text = `free -k` free_text =~ %r{Mem:(.+)$} line = $1.strip total = line.split(/ +/).first.to_i free_text =~ %r{buffers/cache:(.+)$} line = $1.strip used = line.split(/ +/).first.to_i [total, used] when "macosx" vm_stat = `vm_stat` vm_stat =~ /page size of (\d+) bytes/ page_size = $1 vm_stat =~ /Pages free: +(\d+)/ free = $1 vm_stat =~ /Pages active: +(\d+)/ active = $1 vm_stat =~ /Pages inactive: +(\d+)/ inactive = $1 vm_stat =~ /Pages wired down: +(\d+)/ wired = $1 if page_size && free && active && inactive && wired page_size = page_size.to_i free = free.to_i * page_size / 1024 active = active.to_i * page_size / 1024 inactive = inactive.to_i * page_size / 1024 wired = wired.to_i * page_size / 1024 used = active + wired [free + inactive + used, used] else nil end else `top` =~ /(\d+)(K|M) Active, (\d+)(K|M) Inact, (\d+)(K|M) Wired,.*?(\d+)(K|M) Free/ if $1 && $2 && $3 && $4 && $5 && $6 && $7 && $8 to_kb = lambda do |number, unit| if unit == 'K' number.to_i else number.to_i * 1024 end end active = to_kb.call($1, $2) inactive = to_kb.call($3, $4) wired = to_kb.call($5, $6) free = to_kb.call($7, $8) used = active + wired [free + inactive + used, used] else nil end end end end private def os_name_simple return PlatformInfo.os_name_simple end # Returns a list of Process objects that match the given search criteria. # # # Search by executable path. # list_processes(:exe => '/usr/sbin/apache2') # # # Search by executable name. # list_processes(:name => 'ruby1.8') # # # Search by process name. # list_processes(:match => 'Passenger FrameworkSpawner') def list_processes(options) if options[:exe] name = options[:exe].sub(/.*\/(.*)/, '\1') if os_name_simple == "linux" ps = "ps -C '#{name}'" else ps = "ps -A" options[:match] = Regexp.new(Regexp.escape(name)) end elsif options[:name] if os_name_simple == "linux" ps = "ps -C '#{options[:name]}'" else ps = "ps -A" options[:match] = Regexp.new(" #{Regexp.escape(options[:name])}") end elsif options[:match] ps = "ps -A" else raise ArgumentError, "Invalid options." end processes = [] case os_name_simple when "solaris" ps_output = `#{ps} -o pid,ppid,nlwp,vsz,rss,pcpu,comm` threads_known = true when "macosx" ps_output = `#{ps} -w -o pid,ppid,vsz,rss,%cpu,command` threads_known = false else ps_output = `#{ps} -w -o pid,ppid,nlwp,vsz,rss,%cpu,command` threads_known = true end list = force_binary(ps_output).split("\n") list.shift list.each do |line| line.gsub!(/^ */, '') line.gsub!(/ *$/, '') p = Process.new if threads_known p.pid, p.ppid, p.threads, p.vm_size, p.rss, p.cpu, p.name = line.split(/ +/, 7) else p.pid, p.ppid, p.vm_size, p.rss, p.cpu, p.name = line.split(/ +/, 6) end p.name.sub!(/\Aruby: /, '') p.name.sub!(/ \(ruby\)\Z/, '') if p.name !~ /^ps/ && (!options[:match] || p.name.match(options[:match])) # Convert some values to integer. [:pid, :ppid, :vm_size, :rss].each do |attr| p.send("#{attr}=", p.send(attr).to_i) end p.threads = p.threads.to_i if threads_known if platform_provides_private_dirty_rss_information? p.private_dirty_rss = determine_private_dirty_rss(p.pid) end processes << p end end return processes end # Returns the private dirty RSS for the given process, in KB. def determine_private_dirty_rss(pid) total = 0 File.read("/proc/#{pid}/smaps").split("\n").each do |line| line =~ /^(Private)_Dirty: +(\d+)/ if $2 total += $2.to_i end end if total == 0 return nil else return total end rescue Errno::EACCES, Errno::ENOENT return nil end if ''.respond_to?(:force_encoding) def force_binary(str) str.force_encoding('binary') end else def force_binary(str) str end end end end # module AdminTools end # module PhusionPassenger passenger-5.0.30/src/ruby_native_extension/extconf.rb000644 000765 000024 00000005022 12233035540 023412 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2013 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require 'mkmf' $LIBS << " -lpthread" if $LIBS !~ /-lpthread/ $CFLAGS << " -g" if RUBY_PLATFORM =~ /solaris/ have_library('xnet') $CFLAGS << " -D_XPG4_2" $CFLAGS << " -D__EXTENSIONS__" if RUBY_PLATFORM =~ /solaris2.9/ $CFLAGS << " -D__SOLARIS9__" end end have_header('alloca.h') have_header('ruby/version.h') have_header('ruby/io.h') have_header('ruby/thread.h') have_var('ruby_version') have_func('rb_thread_io_blocking_region', 'ruby/io.h') have_func('rb_thread_call_without_gvl', 'ruby/thread.h') with_cflags($CFLAGS) do create_makefile('passenger_native_support') if RUBY_PLATFORM =~ /solaris/ # Fix syntax error in Solaris /usr/ccs/bin/make. # https://code.google.com/p/phusion-passenger/issues/detail?id=999 makefile = File.read("Makefile") makefile.sub!(/^ECHO = .*/, "ECHO = echo") File.open("Makefile", "w") do |f| f.write(makefile) end elsif RUBY_PLATFORM =~ /darwin/ # The OS X Clang 503.0.38 update (circa March 15 2014) broke # /usr/bin/ruby's mkmf. mkmf inserts -multiply_definedsuppress # into the Makefile, but that flag is no longer supported by # Clang. We remove this manually. makefile = File.read("Makefile") makefile.sub!(/-multiply_definedsuppress/, "") File.open("Makefile", "w") do |f| f.write(makefile) end end end passenger-5.0.30/src/ruby_native_extension/passenger_native_support.c000644 000765 000024 00000071065 12233035540 026726 0ustar00honglistaff000000 000000 /* * Phusion Passenger - https://www.phusionpassenger.com/ * Copyright (c) 2010-2014 Phusion Holding B.V. * * "Passenger", "Phusion Passenger" and "Union Station" are registered * trademarks of Phusion Holding B.V. * * 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, sublicense, 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 NONINFRINGEMENT. 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. */ #include "ruby.h" #ifdef HAVE_RUBY_IO_H /* Ruby 1.9 */ #include "ruby/intern.h" #include "ruby/io.h" #else /* Ruby 1.8 */ #include "rubysig.h" #include "rubyio.h" #include "version.h" #endif #ifdef HAVE_RUBY_VERSION_H #include "ruby/version.h" #endif #ifdef HAVE_RUBY_THREAD_H #include "ruby/thread.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_ALLOCA_H #include #endif #if defined(__APPLE__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) #define HAVE_KQUEUE #include #include #endif #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #ifndef RARRAY_LEN #define RARRAY_LEN(ary) RARRAY(ary)->len #endif #ifndef RSTRING_PTR #define RSTRING_PTR(str) RSTRING(str)->ptr #endif #ifndef RSTRING_LEN #define RSTRING_LEN(str) RSTRING(str)->len #endif #if !defined(RUBY_UBF_IO) && defined(RB_UBF_DFL) /* MacRuby compatibility */ #define RUBY_UBF_IO RB_UBF_DFL #endif #ifndef IOV_MAX /* Linux doesn't define IOV_MAX in limits.h for some reason. */ #define IOV_MAX sysconf(_SC_IOV_MAX) #endif #ifdef HAVE_RB_THREAD_IO_BLOCKING_REGION /* Ruby doesn't define this function in its headers */ VALUE rb_thread_io_blocking_region(rb_blocking_function_t *func, void *data1, int fd); #endif static VALUE mPassenger; static VALUE mNativeSupport; static VALUE S_ProcessTimes; #ifdef HAVE_KQUEUE static VALUE cFileSystemWatcher; #endif /* * call-seq: disable_stdio_buffering * * Disables any kind of buffering on the C +stdout+ and +stderr+ variables, * so that +fprintf()+ on +stdout+ and +stderr+ have immediate effect. */ static VALUE disable_stdio_buffering(VALUE self) { setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); return Qnil; } /** * Split the given string into an hash. Keys and values are obtained by splitting the * string using the null character as the delimitor. */ static VALUE split_by_null_into_hash(VALUE self, VALUE data) { const char *cdata = RSTRING_PTR(data); unsigned long len = RSTRING_LEN(data); const char *begin = cdata; const char *current = cdata; const char *end = cdata + len; VALUE result, key, value; result = rb_hash_new(); while (current < end) { if (*current == '\0') { key = rb_str_substr(data, begin - cdata, current - begin); begin = current = current + 1; while (current < end) { if (*current == '\0') { value = rb_str_substr(data, begin - cdata, current - begin);; begin = current = current + 1; rb_hash_aset(result, key, value); break; } else { current++; } } } else { current++; } } return result; } typedef struct { /* The IO vectors in this group. */ struct iovec *io_vectors; /* The number of IO vectors in io_vectors. */ unsigned int count; /* The combined size of all IO vectors in this group. */ ssize_t total_size; } IOVectorGroup; /* Given that _bytes_written_ bytes in _group_ had been successfully written, * update the information in _group_ so that the next writev() call doesn't * write the already written bytes. */ static void update_group_written_info(IOVectorGroup *group, ssize_t bytes_written) { unsigned int i; size_t counter; struct iovec *current_vec; /* Find the last vector that contains data that had already been written. */ counter = 0; for (i = 0; i < group->count; i++) { counter += group->io_vectors[i].iov_len; if (counter == (size_t) bytes_written) { /* Found. In fact, all vectors up to this one contain exactly * bytes_written bytes. So discard all these vectors. */ group->io_vectors += i + 1; group->count -= i + 1; group->total_size -= bytes_written; return; } else if (counter > (size_t) bytes_written) { /* Found. Discard all vectors before this one, and * truncate this vector. */ group->io_vectors += i; group->count -= i; group->total_size -= bytes_written; current_vec = &group->io_vectors[0]; current_vec->iov_base = ((char *) current_vec->iov_base) + current_vec->iov_len - (counter - bytes_written); current_vec->iov_len = counter - bytes_written; return; } } rb_raise(rb_eRuntimeError, "writev() returned an unexpected result"); } #ifndef TRAP_BEG typedef struct { int filedes; const struct iovec *iov; int iovcnt; } WritevWrapperData; #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) static void * writev_wrapper(void *ptr) { WritevWrapperData *data = (WritevWrapperData *) ptr; return (void *) writev(data->filedes, data->iov, data->iovcnt); } #else static VALUE writev_wrapper(void *ptr) { WritevWrapperData *data = (WritevWrapperData *) ptr; return (VALUE) writev(data->filedes, data->iov, data->iovcnt); } #endif #endif static VALUE f_generic_writev(VALUE fd, VALUE *array_of_components, unsigned int count) { VALUE components, str; unsigned int total_size, total_components, ngroups; IOVectorGroup *groups; unsigned int i, j, group_offset, vector_offset; unsigned long long ssize_max; ssize_t ret; int done, fd_num, e; #ifndef TRAP_BEG WritevWrapperData writev_wrapper_data; #endif /* First determine the number of components that we have. */ total_components = 0; for (i = 0; i < count; i++) { Check_Type(array_of_components[i], T_ARRAY); total_components += (unsigned int) RARRAY_LEN(array_of_components[i]); } if (total_components == 0) { return NUM2INT(0); } /* A single writev() call can only accept IOV_MAX vectors, so we * may have to split the components into groups and perform * multiple writev() calls, one per group. Determine the number * of groups needed, how big each group should be and allocate * memory for them. */ if (total_components % IOV_MAX == 0) { ngroups = total_components / IOV_MAX; groups = alloca(ngroups * sizeof(IOVectorGroup)); if (groups == NULL) { rb_raise(rb_eNoMemError, "Insufficient stack space."); } memset(groups, 0, ngroups * sizeof(IOVectorGroup)); for (i = 0; i < ngroups; i++) { groups[i].io_vectors = alloca(IOV_MAX * sizeof(struct iovec)); if (groups[i].io_vectors == NULL) { rb_raise(rb_eNoMemError, "Insufficient stack space."); } groups[i].count = IOV_MAX; } } else { ngroups = total_components / IOV_MAX + 1; groups = alloca(ngroups * sizeof(IOVectorGroup)); if (groups == NULL) { rb_raise(rb_eNoMemError, "Insufficient stack space."); } memset(groups, 0, ngroups * sizeof(IOVectorGroup)); for (i = 0; i < ngroups - 1; i++) { groups[i].io_vectors = alloca(IOV_MAX * sizeof(struct iovec)); if (groups[i].io_vectors == NULL) { rb_raise(rb_eNoMemError, "Insufficient stack space."); } groups[i].count = IOV_MAX; } groups[ngroups - 1].io_vectors = alloca((total_components % IOV_MAX) * sizeof(struct iovec)); if (groups[ngroups - 1].io_vectors == NULL) { rb_raise(rb_eNoMemError, "Insufficient stack space."); } groups[ngroups - 1].count = total_components % IOV_MAX; } /* Now distribute the components among the groups, filling the iovec * array in each group. Also calculate the total data size while we're * at it. */ total_size = 0; group_offset = 0; vector_offset = 0; for (i = 0; i < count; i++) { components = array_of_components[i]; for (j = 0; j < (unsigned int) RARRAY_LEN(components); j++) { str = rb_ary_entry(components, j); str = rb_obj_as_string(str); total_size += (unsigned int) RSTRING_LEN(str); /* I know writev() doesn't write to iov_base, but on some * platforms it's still defined as non-const char * * :-( */ groups[group_offset].io_vectors[vector_offset].iov_base = (char *) RSTRING_PTR(str); groups[group_offset].io_vectors[vector_offset].iov_len = RSTRING_LEN(str); groups[group_offset].total_size += RSTRING_LEN(str); vector_offset++; if (vector_offset == groups[group_offset].count) { group_offset++; vector_offset = 0; } } } /* We don't compare to SSIZE_MAX directly in order to shut up a compiler warning on OS X Snow Leopard. */ ssize_max = SSIZE_MAX; if (total_size > ssize_max) { rb_raise(rb_eArgError, "The total size of the components may not be larger than SSIZE_MAX."); } /* Write the data. */ fd_num = NUM2INT(fd); for (i = 0; i < ngroups; i++) { /* Wait until the file descriptor becomes writable before writing things. */ rb_thread_fd_writable(fd_num); done = 0; while (!done) { #ifdef TRAP_BEG TRAP_BEG; ret = writev(fd_num, groups[i].io_vectors, groups[i].count); TRAP_END; #else writev_wrapper_data.filedes = fd_num; writev_wrapper_data.iov = groups[i].io_vectors; writev_wrapper_data.iovcnt = groups[i].count; #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) ret = (ssize_t) rb_thread_call_without_gvl(writev_wrapper, &writev_wrapper_data, RUBY_UBF_IO, NULL); #elif defined(HAVE_RB_THREAD_IO_BLOCKING_REGION) ret = (ssize_t) rb_thread_io_blocking_region(writev_wrapper, &writev_wrapper_data, fd_num); #else ret = (ssize_t) rb_thread_blocking_region(writev_wrapper, &writev_wrapper_data, RUBY_UBF_IO, 0); #endif #endif if (ret == -1) { /* If the error is something like EAGAIN, yield to another * thread until the file descriptor becomes writable again. * In case of other errors, raise an exception. */ if (!rb_io_wait_writable(fd_num)) { rb_sys_fail("writev()"); } } else if (ret < groups[i].total_size) { /* Not everything in this group has been written. Retry without * writing the bytes that been successfully written. */ e = errno; update_group_written_info(&groups[i], ret); errno = e; rb_io_wait_writable(fd_num); } else { done = 1; } } } return INT2NUM(total_size); } /** * Writes all of the strings in the +components+ array into the given file * descriptor using the +writev()+ system call. Unlike IO#write, this method * does not require one to concatenate all those strings into a single buffer * in order to send the data in a single system call. Thus, #writev is a great * way to perform zero-copy I/O. * * Unlike the raw writev() system call, this method ensures that all given * data is written before returning, by performing multiple writev() calls * and whatever else is necessary. * * writev(@socket.fileno, ["hello ", "world", "\n"]) */ static VALUE f_writev(VALUE self, VALUE fd, VALUE components) { return f_generic_writev(fd, &components, 1); } /** * Like #writev, but accepts two arrays. The data is written in the given order. * * writev2(@socket.fileno, ["hello ", "world", "\n"], ["another ", "message\n"]) */ static VALUE f_writev2(VALUE self, VALUE fd, VALUE components1, VALUE components2) { VALUE array_of_components[2] = { components1, components2 }; return f_generic_writev(fd, array_of_components, 2); } /** * Like #writev, but accepts three arrays. The data is written in the given order. * * writev3(@socket.fileno, * ["hello ", "world", "\n"], * ["another ", "message\n"], * ["yet ", "another ", "one", "\n"]) */ static VALUE f_writev3(VALUE self, VALUE fd, VALUE components1, VALUE components2, VALUE components3) { VALUE array_of_components[3] = { components1, components2, components3 }; return f_generic_writev(fd, array_of_components, 3); } static VALUE process_times(VALUE self) { struct rusage usage; unsigned long long utime, stime; if (getrusage(RUSAGE_SELF, &usage) == -1) { rb_sys_fail("getrusage()"); } utime = (unsigned long long) usage.ru_utime.tv_sec * 1000000 + usage.ru_utime.tv_usec; stime = (unsigned long long) usage.ru_stime.tv_sec * 1000000 + usage.ru_stime.tv_usec; return rb_struct_new(S_ProcessTimes, rb_ull2inum(utime), rb_ull2inum(stime)); } static void * detach_process_main(void *arg) { pid_t pid = (pid_t) (long) arg; int ret; do { ret = waitpid(pid, NULL, 0); } while (ret == -1 && errno == EINTR); return NULL; } static VALUE detach_process(VALUE self, VALUE pid) { pthread_t thr; pthread_attr_t attr; size_t stack_size = 96 * 1024; unsigned long min_stack_size; int stack_min_size_defined; int round_stack_size; #ifdef PTHREAD_STACK_MIN // PTHREAD_STACK_MIN may not be a constant macro so we need // to evaluate it dynamically. min_stack_size = PTHREAD_STACK_MIN; stack_min_size_defined = 1; #else // Assume minimum stack size is 128 KB. min_stack_size = 128 * 1024; stack_min_size_defined = 0; #endif if (stack_size != 0 && stack_size < min_stack_size) { stack_size = min_stack_size; round_stack_size = !stack_min_size_defined; } else { round_stack_size = 1; } if (round_stack_size) { // Round stack size up to page boundary. long page_size; #if defined(_SC_PAGESIZE) page_size = sysconf(_SC_PAGESIZE); #elif defined(_SC_PAGE_SIZE) page_size = sysconf(_SC_PAGE_SIZE); #elif defined(PAGESIZE) page_size = sysconf(PAGESIZE); #elif defined(PAGE_SIZE) page_size = sysconf(PAGE_SIZE); #else page_size = getpagesize(); #endif if (stack_size % page_size != 0) { stack_size = stack_size - (stack_size % page_size) + page_size; } } pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, 1); pthread_attr_setstacksize(&attr, stack_size); pthread_create(&thr, &attr, detach_process_main, (void *) NUM2LONG(pid)); pthread_attr_destroy(&attr); return Qnil; } /** * Freeze the current process forever. On Ruby 1.9 this never unlocks the GIL. * Useful for testing purposes. */ static VALUE freeze_process(VALUE self) { while (1) { usleep(60 * 1000000); } return Qnil; } #if defined(HAVE_KQUEUE) || defined(IN_DOXYGEN) typedef struct { VALUE klass; VALUE filenames; VALUE termination_pipe; /* File descriptor of termination_pipe. */ int termination_fd; /* Whether something went wrong during initialization. */ int preparation_error; /* Information for kqueue. */ unsigned int events_len; int *fds; unsigned int fds_len; int kq; /* When the watcher thread is done it'll write to this pipe * to signal the main (Ruby) thread. */ int notification_fd[2]; /* When the main (Ruby) thread is interrupted it'll write to * this pipe to tell the watcher thread to exit. */ int interruption_fd[2]; } FSWatcher; typedef struct { int fd; ssize_t ret; char byte; int error; } FSWatcherReadByteData; static void fs_watcher_real_close(FSWatcher *watcher) { unsigned int i; if (watcher->kq != -1) { close(watcher->kq); watcher->kq = -1; } if (watcher->notification_fd[0] != -1) { close(watcher->notification_fd[0]); watcher->notification_fd[0] = -1; } if (watcher->notification_fd[1] != -1) { close(watcher->notification_fd[1]); watcher->notification_fd[1] = -1; } if (watcher->interruption_fd[0] != -1) { close(watcher->interruption_fd[0]); watcher->interruption_fd[0] = -1; } if (watcher->interruption_fd[1] != -1) { close(watcher->interruption_fd[1]); watcher->interruption_fd[1] = -1; } if (watcher->fds != NULL) { for (i = 0; i < watcher->fds_len; i++) { close(watcher->fds[i]); } free(watcher->fds); watcher->fds = NULL; watcher->fds_len = 0; } } static void fs_watcher_free(void *obj) { FSWatcher *watcher = (FSWatcher *) obj; fs_watcher_real_close(watcher); free(watcher); } static VALUE fs_watcher_init(VALUE arg) { FSWatcher *watcher = (FSWatcher *) arg; struct kevent *events; VALUE filename; unsigned int i; uint32_t fflags; VALUE filenum; struct stat buf; int fd; /* Open each file in the filenames list and add each one to the events array. */ /* +2 for the termination pipe and the interruption pipe. */ events = alloca((RARRAY_LEN(watcher->filenames) + 2) * sizeof(struct kevent)); watcher->fds = malloc(RARRAY_LEN(watcher->filenames) * sizeof(int)); if (watcher->fds == NULL) { rb_raise(rb_eNoMemError, "Cannot allocate memory."); return Qnil; } for (i = 0; i < RARRAY_LEN(watcher->filenames); i++) { filename = rb_ary_entry(watcher->filenames, i); if (TYPE(filename) != T_STRING) { filename = rb_obj_as_string(filename); } if (stat(RSTRING_PTR(filename), &buf) == -1) { watcher->preparation_error = 1; goto end; } #ifdef O_EVTONLY fd = open(RSTRING_PTR(filename), O_EVTONLY); #else fd = open(RSTRING_PTR(filename), O_RDONLY); #endif if (fd == -1) { watcher->preparation_error = 1; goto end; } watcher->fds[i] = fd; watcher->fds_len++; fflags = NOTE_WRITE | NOTE_EXTEND | NOTE_RENAME | NOTE_DELETE | NOTE_REVOKE; EV_SET(&events[i], fd, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_CLEAR, fflags, 0, 0); } watcher->events_len = watcher->fds_len; /* Create pipes for inter-thread communication. */ if (pipe(watcher->notification_fd) == -1) { rb_sys_fail("pipe()"); return Qnil; } if (pipe(watcher->interruption_fd) == -1) { rb_sys_fail("pipe()"); return Qnil; } /* Create a kqueue and register all events. */ watcher->kq = kqueue(); if (watcher->kq == -1) { rb_sys_fail("kqueue()"); return Qnil; } if (watcher->termination_pipe != Qnil) { filenum = rb_funcall(watcher->termination_pipe, rb_intern("fileno"), 0); EV_SET(&events[watcher->events_len], NUM2INT(filenum), EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0); watcher->termination_fd = NUM2INT(filenum); watcher->events_len++; } EV_SET(&events[watcher->events_len], watcher->interruption_fd[0], EVFILT_READ, EV_ADD | EV_ENABLE | EV_CLEAR, 0, 0, 0); watcher->events_len++; if (kevent(watcher->kq, events, watcher->events_len, NULL, 0, NULL) == -1) { rb_sys_fail("kevent()"); return Qnil; } end: if (watcher->preparation_error) { for (i = 0; i < watcher->fds_len; i++) { close(watcher->fds[i]); } free(watcher->fds); watcher->fds = NULL; watcher->fds_len = 0; } return Data_Wrap_Struct(watcher->klass, NULL, fs_watcher_free, watcher); } static VALUE fs_watcher_new(VALUE klass, VALUE filenames, VALUE termination_pipe) { FSWatcher *watcher; VALUE result; int status; Check_Type(filenames, T_ARRAY); watcher = (FSWatcher *) calloc(1, sizeof(FSWatcher)); if (watcher == NULL) { rb_raise(rb_eNoMemError, "Cannot allocate memory."); return Qnil; } watcher->klass = klass; watcher->filenames = filenames; watcher->termination_pipe = termination_pipe; watcher->termination_fd = -1; watcher->kq = -1; watcher->notification_fd[0] = -1; watcher->notification_fd[1] = -1; watcher->interruption_fd[0] = -1; watcher->interruption_fd[1] = -1; result = rb_protect(fs_watcher_init, (VALUE) watcher, &status); if (status) { fs_watcher_free(watcher); rb_jump_tag(status); return Qnil; } else { return result; } } static void * fs_watcher_wait_on_kqueue(void *arg) { FSWatcher *watcher = (FSWatcher *) arg; struct kevent *events; int nevents; ssize_t ret; events = alloca(sizeof(struct kevent) * watcher->events_len); nevents = kevent(watcher->kq, NULL, 0, events, watcher->events_len, NULL); if (nevents == -1) { ret = write(watcher->notification_fd[1], "e", 1); } else if (nevents >= 1 && ( events[0].ident == (uintptr_t) watcher->termination_fd || events[0].ident == (uintptr_t) watcher->interruption_fd[0] )) { ret = write(watcher->notification_fd[1], "t", 1); } else { ret = write(watcher->notification_fd[1], "f", 1); } if (ret == -1) { close(watcher->notification_fd[1]); watcher->notification_fd[1] = -1; } return NULL; } static VALUE fs_watcher_wait_fd(VALUE _fd) { int fd = (int) _fd; rb_thread_wait_fd(fd); return Qnil; } #ifndef TRAP_BEG #if defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) static void * fs_watcher_read_byte_from_fd_wrapper(void *_arg) { FSWatcherReadByteData *data = (FSWatcherReadByteData *) _arg; data->ret = read(data->fd, &data->byte, 1); data->error = errno; return NULL; } #else static VALUE fs_watcher_read_byte_from_fd_wrapper(void *_arg) { FSWatcherReadByteData *data = (FSWatcherReadByteData *) _arg; data->ret = read(data->fd, &data->byte, 1); data->error = errno; return Qnil; } #endif #endif static VALUE fs_watcher_read_byte_from_fd(VALUE _arg) { FSWatcherReadByteData *data = (FSWatcherReadByteData *) _arg; #if defined(TRAP_BEG) TRAP_BEG; data->ret = read(data->fd, &data->byte, 1); TRAP_END; data->error = errno; #elif defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL) rb_thread_call_without_gvl2(fs_watcher_read_byte_from_fd_wrapper, data, RUBY_UBF_IO, NULL); #elif defined(HAVE_RB_THREAD_IO_BLOCKING_REGION) rb_thread_io_blocking_region(fs_watcher_read_byte_from_fd_wrapper, data, data->fd); #else rb_thread_blocking_region(fs_watcher_read_byte_from_fd_wrapper, data, RUBY_UBF_IO, 0); #endif return Qnil; } static VALUE fs_watcher_wait_for_change(VALUE self) { FSWatcher *watcher; pthread_t thr; ssize_t ret; int e, interrupted = 0; FSWatcherReadByteData read_data; Data_Get_Struct(self, FSWatcher, watcher); if (watcher->preparation_error) { return Qfalse; } /* Spawn a thread, and let the thread perform the blocking kqueue * wait. When kevent() returns the thread will write its status to the * notification pipe. In the mean time we let the Ruby interpreter wait * on the other side of the pipe for us so that we don't block Ruby * threads. */ e = pthread_create(&thr, NULL, fs_watcher_wait_on_kqueue, watcher); if (e != 0) { errno = e; rb_sys_fail("pthread_create()"); return Qnil; } /* Note that rb_thread_wait() does not wait for the fd when the app * is single threaded, so we must join the thread after we've read * from the notification fd. */ rb_protect(fs_watcher_wait_fd, (VALUE) watcher->notification_fd[0], &interrupted); if (interrupted) { /* We got interrupted so tell the watcher thread to exit. */ ret = write(watcher->interruption_fd[1], "x", 1); if (ret == -1) { e = errno; fs_watcher_real_close(watcher); errno = e; rb_sys_fail("write() to interruption pipe"); return Qnil; } pthread_join(thr, NULL); /* Now clean up stuff. */ fs_watcher_real_close(watcher); rb_jump_tag(interrupted); return Qnil; } read_data.fd = watcher->notification_fd[0]; rb_protect(fs_watcher_read_byte_from_fd, (VALUE) &read_data, &interrupted); if (interrupted) { /* We got interrupted so tell the watcher thread to exit. */ ret = write(watcher->interruption_fd[1], "x", 1); if (ret == -1) { e = errno; fs_watcher_real_close(watcher); errno = e; rb_sys_fail("write() to interruption pipe"); return Qnil; } pthread_join(thr, NULL); /* Now clean up stuff. */ fs_watcher_real_close(watcher); rb_jump_tag(interrupted); return Qnil; } pthread_join(thr, NULL); if (read_data.ret == -1) { fs_watcher_real_close(watcher); errno = read_data.error; rb_sys_fail("read()"); return Qnil; } else if (read_data.ret == 0) { fs_watcher_real_close(watcher); errno = read_data.error; rb_raise(rb_eRuntimeError, "Unknown error: unexpected EOF"); return Qnil; } else if (read_data.byte == 't') { /* termination_fd or interruption_fd became readable */ return Qnil; } else if (read_data.byte == 'f') { /* a file or directory changed */ return Qtrue; } else { fs_watcher_real_close(watcher); errno = read_data.error; rb_raise(rb_eRuntimeError, "Unknown error: unexpected notification data"); return Qnil; } } static VALUE fs_watcher_close(VALUE self) { FSWatcher *watcher; Data_Get_Struct(self, FSWatcher, watcher); fs_watcher_real_close(watcher); return Qnil; } #endif /***************************/ void Init_passenger_native_support() { struct sockaddr_un addr; /* Only defined on Ruby >= 1.9.3 */ #ifdef RUBY_API_VERSION_CODE if (ruby_api_version[0] != RUBY_API_VERSION_MAJOR || ruby_api_version[1] != RUBY_API_VERSION_MINOR || ruby_api_version[2] != RUBY_API_VERSION_TEENY) { fprintf(stderr, " --> passenger_native_support was compiled for Ruby API version %d.%d.%d, " "but you're currently running a Ruby interpreter with API version %d.%d.%d.\n", RUBY_API_VERSION_MAJOR, RUBY_API_VERSION_MINOR, RUBY_API_VERSION_TEENY, ruby_api_version[0], ruby_api_version[1], ruby_api_version[2]); fprintf(stderr, " Refusing to load existing passenger_native_support.\n"); return; } /* Because native extensions may be linked to libruby, loading * a Ruby 1.9 native extension may not fail on Ruby 1.8 (even though * the extension will crash later on). We detect such a case here and * abort early. */ if (strlen(ruby_version) >= sizeof("1.8.7") - 1 && ruby_version[0] == '1' && ruby_version[1] == '.' && ruby_version[2] == '8') { fprintf(stderr, " --> passenger_native_support was compiled for Ruby %d.%d, " "but you're currently running Ruby %s\n", RUBY_API_VERSION_MAJOR, RUBY_API_VERSION_MINOR, ruby_version); fprintf(stderr, " Refusing to load existing passenger_native_support.\n"); return; } #else /* Ruby 1.8 - 1.9.2 */ /* We may not have included Ruby 1.8's version.h because of compiler * header file search paths, so we can't rely on RUBY_VERSION being * defined. */ #ifdef RUBY_VERSION #define ESTIMATED_RUBY_VERSION RUBY_VERSION #else #ifdef HAVE_RUBY_IO_H #define ESTIMATED_RUBY_VERSION "1.9.1 or 1.9.2" #else #define ESTIMATED_RUBY_VERSION "1.8" #endif #endif #ifdef HAVE_RUBY_IO_H #define ESTIMATED_RUBY_MINOR_VERSION '9' #else #define ESTIMATED_RUBY_MINOR_VERSION '8' #endif #ifdef HAVE_RUBY_VERSION if (strlen(ruby_version) < sizeof("1.8.7") - 1 || ruby_version[0] != '1' || ruby_version[1] != '.' || ruby_version[2] != ESTIMATED_RUBY_MINOR_VERSION) { fprintf(stderr, " --> passenger_native_support was compiled for Ruby %s, " "but you're currently running Ruby %s\n", ESTIMATED_RUBY_VERSION, ruby_version); fprintf(stderr, " Refusing to load existing passenger_native_support.\n"); return; } #endif #endif mPassenger = rb_define_module("PhusionPassenger"); /* * Utility functions for accessing system functionality. */ mNativeSupport = rb_define_module_under(mPassenger, "NativeSupport"); S_ProcessTimes = rb_struct_define("ProcessTimes", "utime", "stime", NULL); rb_define_singleton_method(mNativeSupport, "disable_stdio_buffering", disable_stdio_buffering, 0); rb_define_singleton_method(mNativeSupport, "split_by_null_into_hash", split_by_null_into_hash, 1); rb_define_singleton_method(mNativeSupport, "writev", f_writev, 2); rb_define_singleton_method(mNativeSupport, "writev2", f_writev2, 3); rb_define_singleton_method(mNativeSupport, "writev3", f_writev3, 4); rb_define_singleton_method(mNativeSupport, "process_times", process_times, 0); rb_define_singleton_method(mNativeSupport, "detach_process", detach_process, 1); rb_define_singleton_method(mNativeSupport, "freeze_process", freeze_process, 0); #ifdef HAVE_KQUEUE cFileSystemWatcher = rb_define_class_under(mNativeSupport, "FileSystemWatcher", rb_cObject); rb_define_singleton_method(cFileSystemWatcher, "_new", fs_watcher_new, 2); rb_define_method(cFileSystemWatcher, "wait_for_change", fs_watcher_wait_for_change, 0); rb_define_method(cFileSystemWatcher, "close", fs_watcher_close, 0); #endif /* The maximum length of a Unix socket path, including terminating null. */ rb_define_const(mNativeSupport, "UNIX_PATH_MAX", INT2NUM(sizeof(addr.sun_path))); /* The maximum size of the data that may be passed to #writev. */ rb_define_const(mNativeSupport, "SSIZE_MAX", LL2NUM(SSIZE_MAX)); } passenger-5.0.30/src/nodejs_supportlib/phusion_passenger/000755 000765 000024 00000000000 12233035540 024276 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/000755 000765 000024 00000000000 12233035540 023007 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/codify/000755 000765 000024 00000000000 12233035540 024264 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/continuation-local-storage/000755 000765 000024 00000000000 12233035540 030253 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/network-byte-order/000755 000765 000024 00000000000 12233035540 026552 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/000755 000765 000024 00000000000 12233035540 024510 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/lib/000755 000765 000024 00000000000 12233035540 025256 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/LICENSE000644 000765 000024 00000002042 12233035540 025513 0ustar00honglistaff000000 000000 Copyright (c) 2010 Charlie Robbins 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, sublicense, 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 NONINFRINGEMENT. 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.passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/000755 000765 000024 00000000000 12233035540 027165 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/package.json000644 000765 000024 00000003721 12233035540 027001 0ustar00honglistaff000000 000000 { "name": "winston", "description": "A multi-transport async logging library for Node.js", "version": "1.1.2", "author": { "name": "Charlie Robbins", "email": "charlie.robbins@gmail.com" }, "maintainers": [ { "name": "indexzero", "email": "charlie.robbins@gmail.com" }, { "name": "chjj", "email": "chjjeffrey@gmail.com" }, { "name": "jcrugzz", "email": "jcrugzz@gmail.com" }, { "name": "pose", "email": "albertopose@gmail.com" }, { "name": "v1", "email": "info@3rd-Eden.com" }, { "name": "3rdeden", "email": "npm@3rd-Eden.com" } ], "repository": { "type": "git", "url": "git+https://github.com/winstonjs/winston.git" }, "keywords": [ "logging", "sysadmin", "tools" ], "dependencies": { "async": "~1.0.0", "colors": "1.0.x", "cycle": "1.0.x", "eyes": "0.1.x", "isstream": "0.1.x", "pkginfo": "0.3.x", "stack-trace": "0.0.x" }, "devDependencies": { "hock": "1.x.x", "std-mocks": "~1.0.0", "vows": "0.7.x" }, "main": "./lib/winston", "scripts": { "test": "vows --spec --isolate" }, "engines": { "node": ">= 0.8.0" }, "license": "MIT", "gitHead": "2064bfe3b0f3da77c798d248a1609dbc24ff077e", "bugs": { "url": "https://github.com/winstonjs/winston/issues" }, "homepage": "https://github.com/winstonjs/winston#readme", "_id": "winston@1.1.2", "_shasum": "68edd769ff79d4f9528cf0e5d80021aade67480c", "_from": "winston@^1.1.0", "_npmVersion": "2.14.5", "_nodeVersion": "0.12.7", "_npmUser": { "name": "indexzero", "email": "charlie.robbins@gmail.com" }, "dist": { "shasum": "68edd769ff79d4f9528cf0e5d80021aade67480c", "tarball": "http://registry.npmjs.org/winston/-/winston-1.1.2.tgz" }, "directories": {}, "_resolved": "https://registry.npmjs.org/winston/-/winston-1.1.2.tgz", "readme": "ERROR: No README data found!" } passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/async/000755 000765 000024 00000000000 12233035540 030302 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/000755 000765 000024 00000000000 12233035540 030466 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/cycle/000755 000765 000024 00000000000 12233035540 030264 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/eyes/000755 000765 000024 00000000000 12233035540 030132 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/isstream/000755 000765 000024 00000000000 12233035540 031014 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/pkginfo/000755 000765 000024 00000000000 12233035540 030622 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/stack-trace/000755 000765 000024 00000000000 12233035540 031366 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/stack-trace/lib/000755 000765 000024 00000000000 12233035540 032134 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/stack-trace/License000644 000765 000024 00000002115 12233035540 032672 0ustar00honglistaff000000 000000 Copyright (c) 2011 Felix Geisendörfer (felix@debuggable.com) 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, sublicense, 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 NONINFRINGEMENT. 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. passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/stack-trace/package.json000644 000765 000024 00000011134 12233035540 033654 0ustar00honglistaff000000 000000 { "author": { "name": "Felix Geisendörfer", "email": "felix@debuggable.com", "url": "http://debuggable.com/" }, "name": "stack-trace", "description": "Get v8 stack traces as an array of CallSite objects.", "version": "0.0.9", "homepage": "https://github.com/felixge/node-stack-trace", "repository": { "type": "git", "url": "git://github.com/felixge/node-stack-trace.git" }, "main": "./lib/stack-trace", "engines": { "node": "*" }, "dependencies": {}, "devDependencies": { "far": "0.0.3", "long-stack-traces": "0.1.2" }, "readme": "# stack-trace\n\nGet v8 stack traces as an array of CallSite objects.\n\n## Install\n\n``` bash\nnpm install stack-trace\n```\n\n## Usage\n\nThe stack-trace module makes it easy for you to capture the current stack:\n\n``` javascript\nvar stackTrace = require('stack-trace');\nvar trace = stackTrace.get();\n\nrequire('assert').strictEqual(trace[0].getFileName(), __filename);\n```\n\nHowever, sometimes you have already popped the stack you are interested in,\nand all you have left is an `Error` object. This module can help:\n\n``` javascript\nvar stackTrace = require('stack-trace');\nvar err = new Error('something went wrong');\nvar trace = stackTrace.parse(err);\n\nrequire('assert').strictEqual(trace[0].getFileName(), __filename);\n```\n\nPlease note that parsing the `Error#stack` property is not perfect, only\ncertain properties can be retrieved with it as noted in the API docs below.\n\n## Long stack traces\n\nstack-trace works great with [long-stack-traces][], when parsing an `err.stack`\nthat has crossed the event loop boundary, a `CallSite` object returning\n`'----------------------------------------'` for `getFileName()` is created.\nAll other methods of the event loop boundary call site return `null`.\n\n[long-stack-traces]: https://github.com/tlrobinson/long-stack-traces\n\n## API\n\n### stackTrace.get([belowFn])\n\nReturns an array of `CallSite` objects, where element `0` is the current call\nsite.\n\nWhen passing a function on the current stack as the `belowFn` parameter, the\nreturned array will only include `CallSite` objects below this function.\n\n### stackTrace.parse(err)\n\nParses the `err.stack` property of an `Error` object into an array compatible\nwith those returned by `stackTrace.get()`. However, only the following methods\nare implemented on the returned `CallSite` objects.\n\n* getTypeName\n* getFunctionName\n* getMethodName\n* getFileName\n* getLineNumber\n* getColumnNumber\n* isNative\n\nNote: Except `getFunctionName()`, all of the above methods return exactly the\nsame values as you would get from `stackTrace.get()`. `getFunctionName()`\nis sometimes a little different, but still useful.\n\n### CallSite\n\nThe official v8 CallSite object API can be found [here][v8stackapi]. A quick\nexcerpt:\n\n> A CallSite object defines the following methods:\n>\n> * **getThis**: returns the value of this\n> * **getTypeName**: returns the type of this as a string. This is the name of the function stored in the constructor field of this, if available, otherwise the object's [[Class]] internal property.\n> * **getFunction**: returns the current function\n> * **getFunctionName**: returns the name of the current function, typically its name property. If a name property is not available an attempt will be made to try to infer a name from the function's context.\n> * **getMethodName**: returns the name of the property of this or one of its prototypes that holds the current function\n> * **getFileName**: if this function was defined in a script returns the name of the script\n> * **getLineNumber**: if this function was defined in a script returns the current line number\n> * **getColumnNumber**: if this function was defined in a script returns the current column number\n> * **getEvalOrigin**: if this function was created using a call to eval returns a CallSite object representing the location where eval was called\n> * **isToplevel**: is this a toplevel invocation, that is, is this the global object?\n> * **isEval**: does this call take place in code defined by a call to eval?\n> * **isNative**: is this call in native V8 code?\n> * **isConstructor**: is this a constructor call?\n\n[v8stackapi]: http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi\n\n## License\n\nstack-trace is licensed under the MIT license.\n", "readmeFilename": "Readme.md", "bugs": { "url": "https://github.com/felixge/node-stack-trace/issues" }, "_id": "stack-trace@0.0.9", "_shasum": "a8f6eaeca90674c333e7c43953f275b451510695", "_from": "stack-trace@0.0.x", "_resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.9.tgz" } src/nodejs_supportlib/vendor-copy/winston/node_modules/stack-trace/lib/stack-trace.js000644 000765 000024 00000005233 12233035540 034617 0ustar00honglistaff000000 000000 passenger-5.0.30exports.get = function(belowFn) { var oldLimit = Error.stackTraceLimit; Error.stackTraceLimit = Infinity; var dummyObject = {}; var v8Handler = Error.prepareStackTrace; Error.prepareStackTrace = function(dummyObject, v8StackTrace) { return v8StackTrace; }; Error.captureStackTrace(dummyObject, belowFn || exports.get); var v8StackTrace = dummyObject.stack; Error.prepareStackTrace = v8Handler; Error.stackTraceLimit = oldLimit; return v8StackTrace; }; exports.parse = function(err) { if (!err.stack) { return []; } var self = this; var lines = err.stack.split('\n').slice(1); return lines .map(function(line) { if (line.match(/^\s*[-]{4,}$/)) { return self._createParsedCallSite({ fileName: line, lineNumber: null, functionName: null, typeName: null, methodName: null, columnNumber: null, 'native': null, }); } var lineMatch = line.match(/at (?:(.+)\s+)?\(?(?:(.+?):(\d+):(\d+)|([^)]+))\)?/); if (!lineMatch) { return; } var object = null; var method = null; var functionName = null; var typeName = null; var methodName = null; var isNative = (lineMatch[5] === 'native'); if (lineMatch[1]) { var methodMatch = lineMatch[1].match(/([^\.]+)(?:\.(.+))?/); object = methodMatch[1]; method = methodMatch[2]; functionName = lineMatch[1]; typeName = 'Object'; } if (method) { typeName = object; methodName = method; } if (method === '') { methodName = null; functionName = ''; } var properties = { fileName: lineMatch[2] || null, lineNumber: parseInt(lineMatch[3], 10) || null, functionName: functionName, typeName: typeName, methodName: methodName, columnNumber: parseInt(lineMatch[4], 10) || null, 'native': isNative, }; return self._createParsedCallSite(properties); }) .filter(function(callSite) { return !!callSite; }); }; exports._createParsedCallSite = function(properties) { var methods = {}; for (var property in properties) { var prefix = 'get'; if (property === 'native') { prefix = 'is'; } var method = prefix + property.substr(0, 1).toUpperCase() + property.substr(1); (function(property) { methods[method] = function() { return properties[property]; } })(property); } var callSite = Object.create(methods); for (var property in properties) { callSite[property] = properties[property]; } return callSite; }; passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/pkginfo/lib/000755 000765 000024 00000000000 12233035540 031370 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/pkginfo/LICENSE000644 000765 000024 00000002043 12233035540 031626 0ustar00honglistaff000000 000000 Copyright (c) 2010 Charlie Robbins. 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, sublicense, 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 NONINFRINGEMENT. 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.passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/pkginfo/package.json000644 000765 000024 00000002642 12233035540 033114 0ustar00honglistaff000000 000000 { "name": "pkginfo", "version": "0.3.1", "license": "MIT", "description": "An easy way to expose properties on a module from a package.json", "author": { "name": "Charlie Robbins", "email": "charlie.robbins@gmail.com" }, "repository": { "type": "git", "url": "git+ssh://git@github.com/indexzero/node-pkginfo.git" }, "bugs": { "url": "https://github.com/indexzero/node-pkginfo/issues" }, "keywords": [ "info", "tools", "package.json" ], "devDependencies": { "vows": "0.7.x" }, "main": "./lib/pkginfo.js", "scripts": { "test": "vows test/*-test.js --spec" }, "engines": { "node": ">= 0.4.0" }, "gitHead": "630fcf486543ee48b4c16afc575c0421fe039f26", "homepage": "https://github.com/indexzero/node-pkginfo#readme", "_id": "pkginfo@0.3.1", "_shasum": "5b29f6a81f70717142e09e765bbeab97b4f81e21", "_from": "pkginfo@0.3.x", "_npmVersion": "2.14.1", "_nodeVersion": "0.10.38", "_npmUser": { "name": "indexzero", "email": "charlie.robbins@gmail.com" }, "maintainers": [ { "name": "indexzero", "email": "charlie.robbins@gmail.com" } ], "dist": { "shasum": "5b29f6a81f70717142e09e765bbeab97b4f81e21", "tarball": "http://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz" }, "directories": {}, "_resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", "readme": "ERROR: No README data found!" } passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/pkginfo/lib/pkginfo.js000644 000765 000024 00000007014 12233035540 033365 0ustar00honglistaff000000 000000 /* * pkginfo.js: Top-level include for the pkginfo module * * (C) 2011, Charlie Robbins * */ var fs = require('fs'), path = require('path'); // // ### function pkginfo ([options, 'property', 'property' ..]) // #### @pmodule {Module} Parent module to read from. // #### @options {Object|Array|string} **Optional** Options used when exposing properties. // #### @arguments {string...} **Optional** Specified properties to expose. // Exposes properties from the package.json file for the parent module on // it's exports. Valid usage: // // `require('pkginfo')()` // // `require('pkginfo')('version', 'author');` // // `require('pkginfo')(['version', 'author']);` // // `require('pkginfo')({ include: ['version', 'author'] });` // var pkginfo = module.exports = function (pmodule, options) { var args = [].slice.call(arguments, 2).filter(function (arg) { return typeof arg === 'string'; }); // // **Parse variable arguments** // if (Array.isArray(options)) { // // If the options passed in is an Array assume that // it is the Array of properties to expose from the // on the package.json file on the parent module. // options = { include: options }; } else if (typeof options === 'string') { // // Otherwise if the first argument is a string, then // assume that it is the first property to expose from // the package.json file on the parent module. // options = { include: [options] }; } // // **Setup default options** // options = options || {}; // ensure that includes have been defined options.include = options.include || []; if (args.length > 0) { // // If additional string arguments have been passed in // then add them to the properties to expose on the // parent module. // options.include = options.include.concat(args); } var pkg = pkginfo.read(pmodule, options.dir).package; Object.keys(pkg).forEach(function (key) { if (options.include.length > 0 && !~options.include.indexOf(key)) { return; } if (!pmodule.exports[key]) { pmodule.exports[key] = pkg[key]; } }); return pkginfo; }; // // ### function find (dir) // #### @pmodule {Module} Parent module to read from. // #### @dir {string} **Optional** Directory to start search from. // Searches up the directory tree from `dir` until it finds a directory // which contains a `package.json` file. // pkginfo.find = function (pmodule, dir) { if (! dir) { dir = path.dirname(pmodule.filename); } var files = fs.readdirSync(dir); if (~files.indexOf('package.json')) { return path.join(dir, 'package.json'); } if (dir === '/') { throw new Error('Could not find package.json up from: ' + dir); } else if (!dir || dir === '.') { throw new Error('Cannot find package.json from unspecified directory'); } return pkginfo.find(pmodule, path.dirname(dir)); }; // // ### function read (pmodule, dir) // #### @pmodule {Module} Parent module to read from. // #### @dir {string} **Optional** Directory to start search from. // Searches up the directory tree from `dir` until it finds a directory // which contains a `package.json` file and returns the package information. // pkginfo.read = function (pmodule, dir) { dir = pkginfo.find(pmodule, dir); var data = fs.readFileSync(dir).toString(); return { dir: dir, package: JSON.parse(data) }; }; // // Call `pkginfo` on this module and expose version. // pkginfo(module, { dir: __dirname, include: ['version'], target: pkginfo });passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/isstream/isstream.js000644 000765 000024 00000001114 12233035540 033176 0ustar00honglistaff000000 000000 var stream = require('stream') function isStream (obj) { return obj instanceof stream.Stream } function isReadable (obj) { return isStream(obj) && typeof obj._read == 'function' && typeof obj._readableState == 'object' } function isWritable (obj) { return isStream(obj) && typeof obj._write == 'function' && typeof obj._writableState == 'object' } function isDuplex (obj) { return isReadable(obj) && isWritable(obj) } module.exports = isStream module.exports.isReadable = isReadable module.exports.isWritable = isWritable module.exports.isDuplex = isDuplex passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/isstream/LICENSE.md000644 000765 000024 00000002145 12233035540 032422 0ustar00honglistaff000000 000000 The MIT License (MIT) ===================== Copyright (c) 2015 Rod Vagg --------------------------- 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, sublicense, 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 NONINFRINGEMENT. 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. passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/isstream/package.json000644 000765 000024 00000007114 12233035540 033305 0ustar00honglistaff000000 000000 { "name": "isstream", "version": "0.1.2", "description": "Determine if an object is a Stream", "main": "isstream.js", "scripts": { "test": "tar --xform 's/^package/readable-stream-1.0/' -zxf readable-stream-1.0.*.tgz && tar --xform 's/^package/readable-stream-1.1/' -zxf readable-stream-1.1.*.tgz && node test.js; rm -rf readable-stream-1.?/" }, "repository": { "type": "git", "url": "git+https://github.com/rvagg/isstream.git" }, "keywords": [ "stream", "type", "streams", "readable-stream", "hippo" ], "devDependencies": { "tape": "~2.12.3", "core-util-is": "~1.0.0", "isarray": "0.0.1", "string_decoder": "~0.10.x", "inherits": "~2.0.1" }, "author": { "name": "Rod Vagg", "email": "rod@vagg.org" }, "license": "MIT", "bugs": { "url": "https://github.com/rvagg/isstream/issues" }, "homepage": "https://github.com/rvagg/isstream", "readme": "# isStream\n\n[![Build Status](https://secure.travis-ci.org/rvagg/isstream.png)](http://travis-ci.org/rvagg/isstream)\n\n**Test if an object is a `Stream`**\n\n[![NPM](https://nodei.co/npm/isstream.svg)](https://nodei.co/npm/isstream/)\n\nThe missing `Stream.isStream(obj)`: determine if an object is standard Node.js `Stream`. Works for Node-core `Stream` objects (for 0.8, 0.10, 0.11, and in theory, older and newer versions) and all versions of **[readable-stream](https://github.com/isaacs/readable-stream)**.\n\n## Usage:\n\n```js\nvar isStream = require('isstream')\nvar Stream = require('stream')\n\nisStream(new Stream()) // true\n\nisStream({}) // false\n\nisStream(new Stream.Readable()) // true\nisStream(new Stream.Writable()) // true\nisStream(new Stream.Duplex()) // true\nisStream(new Stream.Transform()) // true\nisStream(new Stream.PassThrough()) // true\n```\n\n## But wait! There's more!\n\nYou can also test for `isReadable(obj)`, `isWritable(obj)` and `isDuplex(obj)` to test for implementations of Streams2 (and Streams3) base classes.\n\n```js\nvar isReadable = require('isstream').isReadable\nvar isWritable = require('isstream').isWritable\nvar isDuplex = require('isstream').isDuplex\nvar Stream = require('stream')\n\nisReadable(new Stream()) // false\nisWritable(new Stream()) // false\nisDuplex(new Stream()) // false\n\nisReadable(new Stream.Readable()) // true\nisReadable(new Stream.Writable()) // false\nisReadable(new Stream.Duplex()) // true\nisReadable(new Stream.Transform()) // true\nisReadable(new Stream.PassThrough()) // true\n\nisWritable(new Stream.Readable()) // false\nisWritable(new Stream.Writable()) // true\nisWritable(new Stream.Duplex()) // true\nisWritable(new Stream.Transform()) // true\nisWritable(new Stream.PassThrough()) // true\n\nisDuplex(new Stream.Readable()) // false\nisDuplex(new Stream.Writable()) // false\nisDuplex(new Stream.Duplex()) // true\nisDuplex(new Stream.Transform()) // true\nisDuplex(new Stream.PassThrough()) // true\n```\n\n*Reminder: when implementing your own streams, please [use **readable-stream** rather than core streams](http://r.va.gg/2014/06/why-i-dont-use-nodes-core-stream-module.html).*\n\n\n## License\n\n**isStream** is Copyright (c) 2015 Rod Vagg [@rvagg](https://twitter.com/rvagg) and licenced under the MIT licence. All rights not explicitly granted in the MIT license are reserved. See the included LICENSE.md file for more details.\n", "readmeFilename": "README.md", "_id": "isstream@0.1.2", "_shasum": "47e63f7af55afa6f92e1500e690eb8b8529c099a", "_from": "isstream@0.1.x", "_resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz" } passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/eyes/lib/000755 000765 000024 00000000000 12233035540 030700 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/eyes/LICENSE000644 000765 000024 00000002035 12233035540 031137 0ustar00honglistaff000000 000000 Copyright (c) 2009 cloudhead 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, sublicense, 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 NONINFRINGEMENT. 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. passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/eyes/package.json000644 000765 000024 00000006263 12233035540 032427 0ustar00honglistaff000000 000000 { "name": "eyes", "description": "a customizable value inspector", "url": "http://github.com/cloudhead/eyes.js", "keywords": [ "inspector", "debug", "inspect", "print" ], "author": { "name": "Alexis Sellier", "email": "self@cloudhead.net" }, "contributors": [ { "name": "Charlie Robbins", "email": "charlie@nodejitsu.com" } ], "licenses": [ "MIT" ], "main": "./lib/eyes", "version": "0.1.8", "scripts": { "test": "node test/*-test.js" }, "directories": { "lib": "./lib", "test": "./test" }, "engines": { "node": "> 0.1.90" }, "readme": "eyes\n====\n\na customizable value inspector for Node.js\n\nsynopsis\n--------\n\nI was tired of looking at cluttered output in the console -- something needed to be done,\n`sys.inspect()` didn't display regexps correctly, and was too verbose, and I had an hour or two to spare. \nSo I decided to have some fun. _eyes_ were born.\n\n![eyes-ss](http://dl.dropbox.com/u/251849/eyes-js-ss.gif)\n\n_example of the output of a user-customized eyes.js inspector_\n\n*eyes* also deals with circular objects in an intelligent way, and can pretty-print object literals.\n\nusage\n-----\n\n var inspect = require('eyes').inspector({styles: {all: 'magenta'}});\n\n inspect(something); // inspect with the settings passed to `inspector`\n\nor\n\n var eyes = require('eyes');\n\n eyes.inspect(something); // inspect with the default settings\n\nyou can pass a _label_ to `inspect()`, to keep track of your inspections:\n\n eyes.inspect(something, \"a random value\");\n\nIf you want to return the output of eyes without printing it, you can set it up this way:\n\n var inspect = require('eyes').inspector({ stream: null });\n\n sys.puts(inspect({ something: 42 }));\n\ncustomization\n-------------\n\nThese are the default styles and settings used by _eyes_.\n\n styles: { // Styles applied to stdout\n all: 'cyan', // Overall style applied to everything\n label: 'underline', // Inspection labels, like 'array' in `array: [1, 2, 3]`\n other: 'inverted', // Objects which don't have a literal representation, such as functions\n key: 'bold', // The keys in object literals, like 'a' in `{a: 1}`\n special: 'grey', // null, undefined...\n string: 'green',\n number: 'magenta',\n bool: 'blue', // true false\n regexp: 'green', // /\\d+/\n },\n \n pretty: true, // Indent object literals\n hideFunctions: false, // Don't output functions at all\n stream: process.stdout, // Stream to write to, or null\n maxLength: 2048 // Truncate output if longer\n\nYou can overwrite them with your own, by passing a similar object to `inspector()` or `inspect()`.\n\n var inspect = require('eyes').inspector({\n styles: {\n all: 'magenta',\n special: 'bold'\n },\n maxLength: 512\n });\n\n", "readmeFilename": "README.md", "_id": "eyes@0.1.8", "_shasum": "62cf120234c683785d902348a800ef3e0cc20bc0", "_from": "eyes@0.1.x", "_resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz" } passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/eyes/lib/eyes.js000644 000765 000024 00000020547 12233035540 032213 0ustar00honglistaff000000 000000 // // Eyes.js - a customizable value inspector for Node.js // // usage: // // var inspect = require('eyes').inspector({styles: {all: 'magenta'}}); // inspect(something); // inspect with the settings passed to `inspector` // // or // // var eyes = require('eyes'); // eyes.inspect(something); // inspect with the default settings // var eyes = exports, stack = []; eyes.defaults = { styles: { // Styles applied to stdout all: 'cyan', // Overall style applied to everything label: 'underline', // Inspection labels, like 'array' in `array: [1, 2, 3]` other: 'inverted', // Objects which don't have a literal representation, such as functions key: 'bold', // The keys in object literals, like 'a' in `{a: 1}` special: 'grey', // null, undefined... string: 'green', number: 'magenta', bool: 'blue', // true false regexp: 'green', // /\d+/ }, pretty: true, // Indent object literals hideFunctions: false, showHidden: false, stream: process.stdout, maxLength: 2048 // Truncate output if longer }; // Return a curried inspect() function, with the `options` argument filled in. eyes.inspector = function (options) { var that = this; return function (obj, label, opts) { return that.inspect.call(that, obj, label, merge(options || {}, opts || {})); }; }; // If we have a `stream` defined, use it to print a styled string, // if not, we just return the stringified object. eyes.inspect = function (obj, label, options) { options = merge(this.defaults, options || {}); if (options.stream) { return this.print(stringify(obj, options), label, options); } else { return stringify(obj, options) + (options.styles ? '\033[39m' : ''); } }; // Output using the 'stream', and an optional label // Loop through `str`, and truncate it after `options.maxLength` has been reached. // Because escape sequences are, at this point embeded within // the output string, we can't measure the length of the string // in a useful way, without separating what is an escape sequence, // versus a printable character (`c`). So we resort to counting the // length manually. eyes.print = function (str, label, options) { for (var c = 0, i = 0; i < str.length; i++) { if (str.charAt(i) === '\033') { i += 4 } // `4` because '\033[25m'.length + 1 == 5 else if (c === options.maxLength) { str = str.slice(0, i - 1) + '…'; break; } else { c++ } } return options.stream.write.call(options.stream, (label ? this.stylize(label, options.styles.label, options.styles) + ': ' : '') + this.stylize(str, options.styles.all, options.styles) + '\033[0m' + "\n"); }; // Apply a style to a string, eventually, // I'd like this to support passing multiple // styles. eyes.stylize = function (str, style, styles) { var codes = { 'bold' : [1, 22], 'underline' : [4, 24], 'inverse' : [7, 27], 'cyan' : [36, 39], 'magenta' : [35, 39], 'blue' : [34, 39], 'yellow' : [33, 39], 'green' : [32, 39], 'red' : [31, 39], 'grey' : [90, 39] }, endCode; if (style && codes[style]) { endCode = (codes[style][1] === 39 && styles.all) ? codes[styles.all][0] : codes[style][1]; return '\033[' + codes[style][0] + 'm' + str + '\033[' + endCode + 'm'; } else { return str } }; // Convert any object to a string, ready for output. // When an 'array' or an 'object' are encountered, they are // passed to specialized functions, which can then recursively call // stringify(). function stringify(obj, options) { var that = this, stylize = function (str, style) { return eyes.stylize(str, options.styles[style], options.styles) }, index, result; if ((index = stack.indexOf(obj)) !== -1) { return stylize(new(Array)(stack.length - index + 1).join('.'), 'special'); } stack.push(obj); result = (function (obj) { switch (typeOf(obj)) { case "string" : obj = stringifyString(obj.indexOf("'") === -1 ? "'" + obj + "'" : '"' + obj + '"'); return stylize(obj, 'string'); case "regexp" : return stylize('/' + obj.source + '/', 'regexp'); case "number" : return stylize(obj + '', 'number'); case "function" : return options.stream ? stylize("Function", 'other') : '[Function]'; case "null" : return stylize("null", 'special'); case "undefined": return stylize("undefined", 'special'); case "boolean" : return stylize(obj + '', 'bool'); case "date" : return stylize(obj.toUTCString()); case "array" : return stringifyArray(obj, options, stack.length); case "object" : return stringifyObject(obj, options, stack.length); } })(obj); stack.pop(); return result; }; // Escape invisible characters in a string function stringifyString (str, options) { return str.replace(/\\/g, '\\\\') .replace(/\n/g, '\\n') .replace(/[\u0001-\u001F]/g, function (match) { return '\\0' + match[0].charCodeAt(0).toString(8); }); } // Convert an array to a string, such as [1, 2, 3]. // This function calls stringify() for each of the elements // in the array. function stringifyArray(ary, options, level) { var out = []; var pretty = options.pretty && (ary.length > 4 || ary.some(function (o) { return (o !== null && typeof(o) === 'object' && Object.keys(o).length > 0) || (Array.isArray(o) && o.length > 0); })); var ws = pretty ? '\n' + new(Array)(level * 4 + 1).join(' ') : ' '; for (var i = 0; i < ary.length; i++) { out.push(stringify(ary[i], options)); } if (out.length === 0) { return '[]'; } else { return '[' + ws + out.join(',' + (pretty ? ws : ' ')) + (pretty ? ws.slice(0, -4) : ws) + ']'; } }; // Convert an object to a string, such as {a: 1}. // This function calls stringify() for each of its values, // and does not output functions or prototype values. function stringifyObject(obj, options, level) { var out = []; var pretty = options.pretty && (Object.keys(obj).length > 2 || Object.keys(obj).some(function (k) { return typeof(obj[k]) === 'object' })); var ws = pretty ? '\n' + new(Array)(level * 4 + 1).join(' ') : ' '; var keys = options.showHidden ? Object.keys(obj) : Object.getOwnPropertyNames(obj); keys.forEach(function (k) { if (Object.prototype.hasOwnProperty.call(obj, k) && !(obj[k] instanceof Function && options.hideFunctions)) { out.push(eyes.stylize(k, options.styles.key, options.styles) + ': ' + stringify(obj[k], options)); } }); if (out.length === 0) { return '{}'; } else { return "{" + ws + out.join(',' + (pretty ? ws : ' ')) + (pretty ? ws.slice(0, -4) : ws) + "}"; } }; // A better `typeof` function typeOf(value) { var s = typeof(value), types = [Object, Array, String, RegExp, Number, Function, Boolean, Date]; if (s === 'object' || s === 'function') { if (value) { types.forEach(function (t) { if (value instanceof t) { s = t.name.toLowerCase() } }); } else { s = 'null' } } return s; } function merge(/* variable args */) { var objs = Array.prototype.slice.call(arguments); var target = {}; objs.forEach(function (o) { Object.keys(o).forEach(function (k) { if (k === 'styles') { if (! o.styles) { target.styles = false; } else { target.styles = {} for (var s in o.styles) { target.styles[s] = o.styles[s]; } } } else { target[k] = o[k]; } }); }); return target; } passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/cycle/cycle.js000644 000765 000024 00000013312 12233035540 031721 0ustar00honglistaff000000 000000 /* cycle.js 2013-02-19 Public Domain. NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. This code should be minified before deployment. See http://javascript.crockford.com/jsmin.html USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO NOT CONTROL. */ /*jslint evil: true, regexp: true */ /*members $ref, apply, call, decycle, hasOwnProperty, length, prototype, push, retrocycle, stringify, test, toString */ var cycle = exports; cycle.decycle = function decycle(object) { 'use strict'; // Make a deep copy of an object or array, assuring that there is at most // one instance of each object or array in the resulting structure. The // duplicate references (which might be forming cycles) are replaced with // an object of the form // {$ref: PATH} // where the PATH is a JSONPath string that locates the first occurance. // So, // var a = []; // a[0] = a; // return JSON.stringify(JSON.decycle(a)); // produces the string '[{"$ref":"$"}]'. // JSONPath is used to locate the unique object. $ indicates the top level of // the object or array. [NUMBER] or [STRING] indicates a child member or // property. var objects = [], // Keep a reference to each unique object or array paths = []; // Keep the path to each unique object or array return (function derez(value, path) { // The derez recurses through the object, producing the deep copy. var i, // The loop counter name, // Property name nu; // The new object or array // typeof null === 'object', so go on if this value is really an object but not // one of the weird builtin objects. if (typeof value === 'object' && value !== null && !(value instanceof Boolean) && !(value instanceof Date) && !(value instanceof Number) && !(value instanceof RegExp) && !(value instanceof String)) { // If the value is an object or array, look to see if we have already // encountered it. If so, return a $ref/path object. This is a hard way, // linear search that will get slower as the number of unique objects grows. for (i = 0; i < objects.length; i += 1) { if (objects[i] === value) { return {$ref: paths[i]}; } } // Otherwise, accumulate the unique value and its path. objects.push(value); paths.push(path); // If it is an array, replicate the array. if (Object.prototype.toString.apply(value) === '[object Array]') { nu = []; for (i = 0; i < value.length; i += 1) { nu[i] = derez(value[i], path + '[' + i + ']'); } } else { // If it is an object, replicate the object. nu = {}; for (name in value) { if (Object.prototype.hasOwnProperty.call(value, name)) { nu[name] = derez(value[name], path + '[' + JSON.stringify(name) + ']'); } } } return nu; } return value; }(object, '$')); }; cycle.retrocycle = function retrocycle($) { 'use strict'; // Restore an object that was reduced by decycle. Members whose values are // objects of the form // {$ref: PATH} // are replaced with references to the value found by the PATH. This will // restore cycles. The object will be mutated. // The eval function is used to locate the values described by a PATH. The // root object is kept in a $ variable. A regular expression is used to // assure that the PATH is extremely well formed. The regexp contains nested // * quantifiers. That has been known to have extremely bad performance // problems on some browsers for very long strings. A PATH is expected to be // reasonably short. A PATH is allowed to belong to a very restricted subset of // Goessner's JSONPath. // So, // var s = '[{"$ref":"$"}]'; // return JSON.retrocycle(JSON.parse(s)); // produces an array containing a single element which is the array itself. var px = /^\$(?:\[(?:\d+|\"(?:[^\\\"\u0000-\u001f]|\\([\\\"\/bfnrt]|u[0-9a-zA-Z]{4}))*\")\])*$/; (function rez(value) { // The rez function walks recursively through the object looking for $ref // properties. When it finds one that has a value that is a path, then it // replaces the $ref object with a reference to the value that is found by // the path. var i, item, name, path; if (value && typeof value === 'object') { if (Object.prototype.toString.apply(value) === '[object Array]') { for (i = 0; i < value.length; i += 1) { item = value[i]; if (item && typeof item === 'object') { path = item.$ref; if (typeof path === 'string' && px.test(path)) { value[i] = eval(path); } else { rez(item); } } } } else { for (name in value) { if (typeof value[name] === 'object') { item = value[name]; if (item) { path = item.$ref; if (typeof path === 'string' && px.test(path)) { value[name] = eval(path); } else { rez(item); } } } } } } }($)); return $; }; passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/cycle/package.json000644 000765 000024 00000004774 12233035540 032566 0ustar00honglistaff000000 000000 { "name": "cycle", "description": "decycle your json", "author": "", "version": "1.0.3", "main": "./cycle.js", "homepage": "https://github.com/douglascrockford/JSON-js", "repository": { "type": "git", "url": "git+ssh://git@github.com/dscape/cycle.git" }, "bugs": { "url": "http://github.com/douglascrockford/JSON-js/issues" }, "keywords": [ "json", "cycle", "stringify", "parse" ], "engines": { "node": ">=0.4.0" }, "readme": "Fork of https://github.com/douglascrockford/JSON-js, maintained in npm as `cycle`.\n\n# Contributors\n\n* Douglas Crockford\n* Nuno Job\n* Justin Warkentin\n\n# JSON in JavaScript\n\nDouglas Crockford\ndouglas@crockford.com\n\n2010-11-18\n\n\nJSON is a light-weight, language independent, data interchange format.\nSee http://www.JSON.org/\n\nThe files in this collection implement JSON encoders/decoders in JavaScript.\n\nJSON became a built-in feature of JavaScript when the ECMAScript Programming\nLanguage Standard - Fifth Edition was adopted by the ECMA General Assembly\nin December 2009. Most of the files in this collection are for applications\nthat are expected to run in obsolete web browsers. For most purposes, json2.js\nis the best choice.\n\n\njson2.js: This file creates a JSON property in the global object, if there\nisn't already one, setting its value to an object containing a stringify\nmethod and a parse method. The parse method uses the eval method to do the\nparsing, guarding it with several regular expressions to defend against\naccidental code execution hazards. On current browsers, this file does nothing,\nprefering the built-in JSON object.\n\njson.js: This file does everything that json2.js does. It also adds a\ntoJSONString method and a parseJSON method to Object.prototype. Use of this\nfile is not recommended.\n\njson_parse.js: This file contains an alternative JSON parse function that\nuses recursive descent instead of eval.\n\njson_parse_state.js: This files contains an alternative JSON parse function that\nuses a state machine instead of eval.\n\ncycle.js: This file contains two functions, JSON.decycle and JSON.retrocycle,\nwhich make it possible to encode cyclical structures and dags in JSON, and to\nthen recover them. JSONPath is used to represent the links.\nhttp://GOESSNER.net/articles/JsonPath/\n", "readmeFilename": "README.md", "_id": "cycle@1.0.3", "_shasum": "21e80b2be8580f98b468f379430662b046c34ad2", "_from": "cycle@1.0.x", "_resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz" } passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/lib/000755 000765 000024 00000000000 12233035540 031234 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/MIT-LICENSE.txt000644 000765 000024 00000002225 12233035540 032741 0ustar00honglistaff000000 000000 Original Library - Copyright (c) Marak Squires Additional Functionality - Copyright (c) Sindre Sorhus (sindresorhus.com) 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, sublicense, 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 NONINFRINGEMENT. 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.passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/package.json000644 000765 000024 00000010152 12233035540 032753 0ustar00honglistaff000000 000000 { "name": "colors", "description": "get colors in your node.js console", "version": "1.0.3", "author": { "name": "Marak Squires" }, "homepage": "https://github.com/Marak/colors.js", "bugs": { "url": "https://github.com/Marak/colors.js/issues" }, "keywords": [ "ansi", "terminal", "colors" ], "repository": { "type": "git", "url": "git+ssh://git@github.com/Marak/colors.js.git" }, "license": "MIT", "scripts": { "test": "node tests/basic-test.js && node tests/safe-test.js" }, "engines": { "node": ">=0.1.90" }, "main": "./lib/index", "readme": "# colors.js\n\n## get color and style in your node.js console\n\n\n\n## Installation\n\n npm install colors\n\n## colors and styles!\n\n### text colors\n\n - black\n - red\n - green\n - yellow\n - blue\n - magenta\n - cyan\n - white\n - gray\n - grey\n\n### background colors\n\n\n\n - bgBlack\n - bgRed\n - bgGreen\n - bgYellow\n - bgBlue\n - bgMagenta\n - bgCyan\n - bgWhite\n\n### styles\n\n - reset\n - bold\n - dim\n - italic\n - underline\n - inverse\n - hidden\n - strikethrough\n\n### extras\n\n - rainbow\n - zebra\n - america\n - trap\n - random\n\n\n## Usage\n\nBy popular demand, `colors` now ships with two types of usages!\n\nThe super nifty way\n\n```js\nvar colors = require('colors');\n\nconsole.log('hello'.green); // outputs green text\nconsole.log('i like cake and pies'.underline.red) // outputs red underlined text\nconsole.log('inverse the color'.inverse); // inverses the color\nconsole.log('OMG Rainbows!'.rainbow); // rainbow\nconsole.log('Run the trap'.trap); // Drops the bass\n\n```\n\nor a slightly less nifty way which doesn't extend `String.prototype`\n\n```js\nvar colors = require('colors/safe');\n\nconsole.log(colors.green('hello')); // outputs green text\nconsole.log(colors.red.underline('i like cake and pies')) // outputs red underlined text\nconsole.log(colors.inverse('inverse the color')); // inverses the color\nconsole.log(colors.rainbow('OMG Rainbows!')); // rainbow\nconsole.log(colors.trap('Run the trap')); // Drops the bass\n\n```\n\nI prefer the first way. Some people seem to be afraid of extending `String.prototype` and prefer the second way. \n\nIf you are writing good code you will never have an issue with the first approach. If you really don't want to touch `String.prototype`, the second usage will not touch `String` native object.\n\n## Disabling Colors\n\nTo disable colors you can pass the following arguments in the command line to your application:\n\n```bash\nnode myapp.js --no-color\n```\n\n## Console.log [string substitution](http://nodejs.org/docs/latest/api/console.html#console_console_log_data)\n\n```js\nvar name = 'Marak';\nconsole.log(colors.green('Hello %s'), name);\n// outputs -> 'Hello Marak'\n```\n\n## Custom themes\n\n### Using standard API\n\n```js\n\nvar colors = require('colors');\n\ncolors.setTheme({\n silly: 'rainbow',\n input: 'grey',\n verbose: 'cyan',\n prompt: 'grey',\n info: 'green',\n data: 'grey',\n help: 'cyan',\n warn: 'yellow',\n debug: 'blue',\n error: 'red'\n});\n\n// outputs red text\nconsole.log(\"this is an error\".error);\n\n// outputs yellow text\nconsole.log(\"this is a warning\".warn);\n```\n\n### Using string safe API\n\n```js\nvar colors = require('colors/safe');\n\n// set single property\nvar error = colors.red;\nerror('this is red');\n\n// set theme\ncolors.setTheme({\n silly: 'rainbow',\n input: 'grey',\n verbose: 'cyan',\n prompt: 'grey',\n info: 'green',\n data: 'grey',\n help: 'cyan',\n warn: 'yellow',\n debug: 'blue',\n error: 'red'\n});\n\n// outputs red text\nconsole.log(colors.error(\"this is an error\"));\n\n// outputs yellow text\nconsole.log(colors.warn(\"this is a warning\"));\n```\n\n*Protip: There is a secret undocumented style in `colors`. If you find the style you can summon him.*", "readmeFilename": "ReadMe.md", "_id": "colors@1.0.3", "_shasum": "0433f44d809680fdeb60ed260f1b0c262e82a40b", "_from": "colors@1.0.x", "_resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz" } passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/safe.js000644 000765 000024 00000000361 12233035540 031742 0ustar00honglistaff000000 000000 // // Remark: Requiring this file will use the "safe" colors API which will not touch String.prototype // // var colors = require('colors/safe); // colors.red("foo") // // var colors = require('./lib/colors'); module['exports'] = colors;passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/themes/000755 000765 000024 00000000000 12233035540 031753 5ustar00honglistaff000000 000000 src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/themes/generic-logging.js000644 000765 000024 00000000305 12233035540 035270 0ustar00honglistaff000000 000000 passenger-5.0.30module['exports'] = { silly: 'rainbow', input: 'grey', verbose: 'cyan', prompt: 'grey', info: 'green', data: 'grey', help: 'cyan', warn: 'yellow', debug: 'blue', error: 'red' };passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/lib/colors.js000644 000765 000024 00000010735 12233035540 033101 0ustar00honglistaff000000 000000 /* The MIT License (MIT) Original Library - Copyright (c) Marak Squires Additional functionality - Copyright (c) Sindre Sorhus (sindresorhus.com) 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, sublicense, 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 NONINFRINGEMENT. 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. */ var colors = {}; module['exports'] = colors; colors.themes = {}; var ansiStyles = colors.styles = require('./styles'); var defineProps = Object.defineProperties; colors.supportsColor = require('./system/supports-colors'); if (typeof colors.enabled === "undefined") { colors.enabled = colors.supportsColor; } colors.stripColors = colors.strip = function(str){ return ("" + str).replace(/\x1B\[\d+m/g, ''); }; var stylize = colors.stylize = function stylize (str, style) { return ansiStyles[style].open + str + ansiStyles[style].close; } var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g; var escapeStringRegexp = function (str) { if (typeof str !== 'string') { throw new TypeError('Expected a string'); } return str.replace(matchOperatorsRe, '\\$&'); } function build(_styles) { var builder = function builder() { return applyStyle.apply(builder, arguments); }; builder._styles = _styles; // __proto__ is used because we must return a function, but there is // no way to create a function with a different prototype. builder.__proto__ = proto; return builder; } var styles = (function () { var ret = {}; ansiStyles.grey = ansiStyles.gray; Object.keys(ansiStyles).forEach(function (key) { ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g'); ret[key] = { get: function () { return build(this._styles.concat(key)); } }; }); return ret; })(); var proto = defineProps(function colors() {}, styles); function applyStyle() { var args = arguments; var argsLen = args.length; var str = argsLen !== 0 && String(arguments[0]); if (argsLen > 1) { for (var a = 1; a < argsLen; a++) { str += ' ' + args[a]; } } if (!colors.enabled || !str) { return str; } var nestedStyles = this._styles; var i = nestedStyles.length; while (i--) { var code = ansiStyles[nestedStyles[i]]; str = code.open + str.replace(code.closeRe, code.open) + code.close; } return str; } function applyTheme (theme) { for (var style in theme) { (function(style){ colors[style] = function(str){ return colors[theme[style]](str); }; })(style) } } colors.setTheme = function (theme) { if (typeof theme === 'string') { try { colors.themes[theme] = require(theme); applyTheme(colors.themes[theme]); return colors.themes[theme]; } catch (err) { console.log(err); return err; } } else { applyTheme(theme); } }; function init() { var ret = {}; Object.keys(styles).forEach(function (name) { ret[name] = { get: function () { return build([name]); } }; }); return ret; } var sequencer = function sequencer (map, str) { var exploded = str.split(""), i = 0; exploded = exploded.map(map); return exploded.join(""); }; // custom formatter methods colors.trap = require('./custom/trap'); colors.zalgo = require('./custom/zalgo'); // maps colors.maps = {}; colors.maps.america = require('./maps/america'); colors.maps.zebra = require('./maps/zebra'); colors.maps.rainbow = require('./maps/rainbow'); colors.maps.random = require('./maps/random') for (var map in colors.maps) { (function(map){ colors[map] = function (str) { return sequencer(colors.maps[map], str); } })(map) } defineProps(colors, init());passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/lib/custom/000755 000765 000024 00000000000 12233035540 032546 5ustar00honglistaff000000 000000 src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/lib/extendStringPrototype.js000644 000765 000024 00000006164 12233035540 036126 0ustar00honglistaff000000 000000 passenger-5.0.30var colors = require('./colors'), styles = require('./styles'); module['exports'] = function () { // // Extends prototype of native string object to allow for "foo".red syntax // var addProperty = function (color, func) { String.prototype.__defineGetter__(color, func); }; var sequencer = function sequencer (map, str) { return function () { var exploded = this.split(""), i = 0; exploded = exploded.map(map); return exploded.join(""); } }; var stylize = function stylize (str, style) { return styles[style].open + str + styles[style].close; } addProperty('strip', function () { return colors.strip(this); }); addProperty('stripColors', function () { return colors.strip(this); }); addProperty("trap", function(){ return colors.trap(this); }); addProperty("zalgo", function(){ return colors.zalgo(this); }); addProperty("zebra", function(){ return colors.zebra(this); }); addProperty("rainbow", function(){ return colors.rainbow(this); }); addProperty("random", function(){ return colors.random(this); }); addProperty("america", function(){ return colors.america(this); }); // // Iterate through all default styles and colors // var x = Object.keys(colors.styles); x.forEach(function (style) { addProperty(style, function () { return stylize(this, style); }); }); function applyTheme(theme) { // // Remark: This is a list of methods that exist // on String that you should not overwrite. // var stringPrototypeBlacklist = [ '__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__', 'charAt', 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf', 'charCodeAt', 'indexOf', 'lastIndexof', 'length', 'localeCompare', 'match', 'replace', 'search', 'slice', 'split', 'substring', 'toLocaleLowerCase', 'toLocaleUpperCase', 'toLowerCase', 'toUpperCase', 'trim', 'trimLeft', 'trimRight' ]; Object.keys(theme).forEach(function (prop) { if (stringPrototypeBlacklist.indexOf(prop) !== -1) { console.log('warn: '.red + ('String.prototype' + prop).magenta + ' is probably something you don\'t want to override. Ignoring style name'); } else { if (typeof(theme[prop]) === 'string') { colors[prop] = colors[theme[prop]]; addProperty(prop, function () { return colors[theme[prop]](this); }); } else { addProperty(prop, function () { var ret = this; for (var t = 0; t < theme[prop].length; t++) { ret = exports[theme[prop][t]](ret); } return ret; }); } } }); } colors.setTheme = function (theme) { if (typeof theme === 'string') { try { colors.themes[theme] = require(theme); applyTheme(colors.themes[theme]); return colors.themes[theme]; } catch (err) { console.log(err); return err; } } else { applyTheme(theme); } }; };passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/lib/index.js000644 000765 000024 00000000606 12233035540 032703 0ustar00honglistaff000000 000000 var colors = require('./colors'); module['exports'] = colors; // Remark: By default, colors will add style properties to String.prototype // // If you don't wish to extend String.prototype you can do this instead and native String will not be touched // // var colors = require('colors/safe); // colors.red("foo") // // var extendStringPrototype = require('./extendStringPrototype')();passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/lib/maps/000755 000765 000024 00000000000 12233035540 032174 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/lib/styles.js000644 000765 000024 00000004063 12233035540 033120 0ustar00honglistaff000000 000000 /* The MIT License (MIT) Copyright (c) Sindre Sorhus (sindresorhus.com) 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, sublicense, 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 NONINFRINGEMENT. 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. */ var styles = {}; module['exports'] = styles; var codes = { reset: [0, 0], bold: [1, 22], dim: [2, 22], italic: [3, 23], underline: [4, 24], inverse: [7, 27], hidden: [8, 28], strikethrough: [9, 29], black: [30, 39], red: [31, 39], green: [32, 39], yellow: [33, 39], blue: [34, 39], magenta: [35, 39], cyan: [36, 39], white: [37, 39], gray: [90, 39], grey: [90, 39], bgBlack: [40, 49], bgRed: [41, 49], bgGreen: [42, 49], bgYellow: [43, 49], bgBlue: [44, 49], bgMagenta: [45, 49], bgCyan: [46, 49], bgWhite: [47, 49], // legacy styles for colors pre v1.0.0 blackBG: [40, 49], redBG: [41, 49], greenBG: [42, 49], yellowBG: [43, 49], blueBG: [44, 49], magentaBG: [45, 49], cyanBG: [46, 49], whiteBG: [47, 49] }; Object.keys(codes).forEach(function (key) { var val = codes[key]; var style = styles[key] = []; style.open = '\u001b[' + val[0] + 'm'; style.close = '\u001b[' + val[1] + 'm'; });passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/lib/system/000755 000765 000024 00000000000 12233035540 032560 5ustar00honglistaff000000 000000 src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/lib/system/supports-colors.js000644 000765 000024 00000003433 12233035540 036240 0ustar00honglistaff000000 000000 passenger-5.0.30/* The MIT License (MIT) Copyright (c) Sindre Sorhus (sindresorhus.com) 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, sublicense, 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 NONINFRINGEMENT. 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. */ var argv = process.argv; module.exports = (function () { if (argv.indexOf('--no-color') !== -1 || argv.indexOf('--color=false') !== -1) { return false; } if (argv.indexOf('--color') !== -1 || argv.indexOf('--color=true') !== -1 || argv.indexOf('--color=always') !== -1) { return true; } if (process.stdout && !process.stdout.isTTY) { return false; } if (process.platform === 'win32') { return true; } if ('COLORTERM' in process.env) { return true; } if (process.env.TERM === 'dumb') { return false; } if (/^screen|^xterm|^vt100|color|ansi|cygwin|linux/i.test(process.env.TERM)) { return true; } return false; })();passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/lib/maps/america.js000644 000765 000024 00000000463 12233035540 034136 0ustar00honglistaff000000 000000 var colors = require('../colors'); module['exports'] = (function() { return function (letter, i, exploded) { if(letter === " ") return letter; switch(i%3) { case 0: return colors.red(letter); case 1: return colors.white(letter) case 2: return colors.blue(letter) } } })();passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/lib/maps/rainbow.js000644 000765 000024 00000000530 12233035540 034171 0ustar00honglistaff000000 000000 var colors = require('../colors'); module['exports'] = (function () { var rainbowColors = ['red', 'yellow', 'green', 'blue', 'magenta']; //RoY G BiV return function (letter, i, exploded) { if (letter === " ") { return letter; } else { return colors[rainbowColors[i++ % rainbowColors.length]](letter); } }; })(); passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/lib/maps/random.js000644 000765 000024 00000000540 12233035540 034011 0ustar00honglistaff000000 000000 var colors = require('../colors'); module['exports'] = (function () { var available = ['underline', 'inverse', 'grey', 'yellow', 'red', 'green', 'blue', 'white', 'cyan', 'magenta']; return function(letter, i, exploded) { return letter === " " ? letter : colors[available[Math.round(Math.random() * (available.length - 1))]](letter); }; })();passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/lib/maps/zebra.js000644 000765 000024 00000000223 12233035540 033632 0ustar00honglistaff000000 000000 var colors = require('../colors'); module['exports'] = function (letter, i, exploded) { return i % 2 === 0 ? letter : colors.inverse(letter); };passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/lib/custom/trap.js000644 000765 000024 00000003200 12233035540 034045 0ustar00honglistaff000000 000000 module['exports'] = function runTheTrap (text, options) { var result = ""; text = text || "Run the trap, drop the bass"; text = text.split(''); var trap = { a: ["\u0040", "\u0104", "\u023a", "\u0245", "\u0394", "\u039b", "\u0414"], b: ["\u00df", "\u0181", "\u0243", "\u026e", "\u03b2", "\u0e3f"], c: ["\u00a9", "\u023b", "\u03fe"], d: ["\u00d0", "\u018a", "\u0500" , "\u0501" ,"\u0502", "\u0503"], e: ["\u00cb", "\u0115", "\u018e", "\u0258", "\u03a3", "\u03be", "\u04bc", "\u0a6c"], f: ["\u04fa"], g: ["\u0262"], h: ["\u0126", "\u0195", "\u04a2", "\u04ba", "\u04c7", "\u050a"], i: ["\u0f0f"], j: ["\u0134"], k: ["\u0138", "\u04a0", "\u04c3", "\u051e"], l: ["\u0139"], m: ["\u028d", "\u04cd", "\u04ce", "\u0520", "\u0521", "\u0d69"], n: ["\u00d1", "\u014b", "\u019d", "\u0376", "\u03a0", "\u048a"], o: ["\u00d8", "\u00f5", "\u00f8", "\u01fe", "\u0298", "\u047a", "\u05dd", "\u06dd", "\u0e4f"], p: ["\u01f7", "\u048e"], q: ["\u09cd"], r: ["\u00ae", "\u01a6", "\u0210", "\u024c", "\u0280", "\u042f"], s: ["\u00a7", "\u03de", "\u03df", "\u03e8"], t: ["\u0141", "\u0166", "\u0373"], u: ["\u01b1", "\u054d"], v: ["\u05d8"], w: ["\u0428", "\u0460", "\u047c", "\u0d70"], x: ["\u04b2", "\u04fe", "\u04fc", "\u04fd"], y: ["\u00a5", "\u04b0", "\u04cb"], z: ["\u01b5", "\u0240"] } text.forEach(function(c){ c = c.toLowerCase(); var chars = trap[c] || [" "]; var rand = Math.floor(Math.random() * chars.length); if (typeof trap[c] !== "undefined") { result += trap[c][rand]; } else { result += c; } }); return result; } passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/colors/lib/custom/zalgo.js000644 000765 000024 00000005156 12233035540 034227 0ustar00honglistaff000000 000000 // please no module['exports'] = function zalgo(text, options) { text = text || " he is here "; var soul = { "up" : [ '̍', '̎', '̄', '̅', '̿', '̑', '̆', '̐', '͒', '͗', '͑', '̇', '̈', '̊', '͂', '̓', '̈', '͊', '͋', '͌', '̃', '̂', '̌', '͐', '̀', '́', '̋', '̏', '̒', '̓', '̔', '̽', '̉', 'ͣ', 'ͤ', 'ͥ', 'ͦ', 'ͧ', 'ͨ', 'ͩ', 'ͪ', 'ͫ', 'ͬ', 'ͭ', 'ͮ', 'ͯ', '̾', '͛', '͆', '̚' ], "down" : [ '̖', '̗', '̘', '̙', '̜', '̝', '̞', '̟', '̠', '̤', '̥', '̦', '̩', '̪', '̫', '̬', '̭', '̮', '̯', '̰', '̱', '̲', '̳', '̹', '̺', '̻', '̼', 'ͅ', '͇', '͈', '͉', '͍', '͎', '͓', '͔', '͕', '͖', '͙', '͚', '̣' ], "mid" : [ '̕', '̛', '̀', '́', '͘', '̡', '̢', '̧', '̨', '̴', '̵', '̶', '͜', '͝', '͞', '͟', '͠', '͢', '̸', '̷', '͡', ' ҉' ] }, all = [].concat(soul.up, soul.down, soul.mid), zalgo = {}; function randomNumber(range) { var r = Math.floor(Math.random() * range); return r; } function is_char(character) { var bool = false; all.filter(function (i) { bool = (i === character); }); return bool; } function heComes(text, options) { var result = '', counts, l; options = options || {}; options["up"] = options["up"] || true; options["mid"] = options["mid"] || true; options["down"] = options["down"] || true; options["size"] = options["size"] || "maxi"; text = text.split(''); for (l in text) { if (is_char(l)) { continue; } result = result + text[l]; counts = {"up" : 0, "down" : 0, "mid" : 0}; switch (options.size) { case 'mini': counts.up = randomNumber(8); counts.min = randomNumber(2); counts.down = randomNumber(8); break; case 'maxi': counts.up = randomNumber(16) + 3; counts.min = randomNumber(4) + 1; counts.down = randomNumber(64) + 3; break; default: counts.up = randomNumber(8) + 1; counts.mid = randomNumber(6) / 2; counts.down = randomNumber(8) + 1; break; } var arr = ["up", "mid", "down"]; for (var d in arr) { var index = arr[d]; for (var i = 0 ; i <= counts[index]; i++) { if (options[index]) { result = result + soul[index][randomNumber(soul[index].length)]; } } } } return result; } // don't summon him return heComes(text); } passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/async/lib/000755 000765 000024 00000000000 12233035540 031050 5ustar00honglistaff000000 000000 passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/async/LICENSE000644 000765 000024 00000002047 12233035540 031312 0ustar00honglistaff000000 000000 Copyright (c) 2010-2014 Caolan McMahon 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, sublicense, 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 NONINFRINGEMENT. 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. passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/async/package.json000644 000765 000024 00000161135 12233035540 032577 0ustar00honglistaff000000 000000 { "name": "async", "description": "Higher-order functions and common patterns for asynchronous code", "main": "lib/async.js", "author": { "name": "Caolan McMahon" }, "version": "1.0.0", "keywords": [ "async", "callback", "utility", "module" ], "repository": { "type": "git", "url": "git+https://github.com/caolan/async.git" }, "bugs": { "url": "https://github.com/caolan/async/issues" }, "license": "MIT", "devDependencies": { "benchmark": "~1.0.0", "jshint": "~2.7.0", "lodash": ">=2.4.1", "mkdirp": "~0.5.1", "nodeunit": ">0.0.0", "uglify-js": "1.2.x" }, "jam": { "main": "lib/async.js", "include": [ "lib/async.js", "README.md", "LICENSE" ], "categories": [ "Utilities" ] }, "scripts": { "test": "npm run-script lint && nodeunit test/test-async.js", "lint": "jshint lib/*.js test/*.js perf/*.js" }, "spm": { "main": "lib/async.js" }, "volo": { "main": "lib/async.js", "ignore": [ "**/.*", "node_modules", "bower_components", "test", "tests" ] }, "readme": "# Async.js\n\n[![Build Status via Travis CI](https://travis-ci.org/caolan/async.svg?branch=master)](https://travis-ci.org/caolan/async)\n[![NPM version](http://img.shields.io/npm/v/async.svg)](https://www.npmjs.org/package/async)\n\n\nAsync is a utility module which provides straight-forward, powerful functions\nfor working with asynchronous JavaScript. Although originally designed for\nuse with [Node.js](http://nodejs.org) and installable via `npm install async`,\nit can also be used directly in the browser.\n\nAsync is also installable via:\n\n- [bower](http://bower.io/): `bower install async`\n- [component](https://github.com/component/component): `component install\n caolan/async`\n- [jam](http://jamjs.org/): `jam install async`\n- [spm](http://spmjs.io/): `spm install async`\n\nAsync provides around 20 functions that include the usual 'functional'\nsuspects (`map`, `reduce`, `filter`, `each`…) as well as some common patterns\nfor asynchronous control flow (`parallel`, `series`, `waterfall`…). All these\nfunctions assume you follow the Node.js convention of providing a single\ncallback as the last argument of your `async` function.\n\n\n## Quick Examples\n\n```javascript\nasync.map(['file1','file2','file3'], fs.stat, function(err, results){\n // results is now an array of stats for each file\n});\n\nasync.filter(['file1','file2','file3'], fs.exists, function(results){\n // results now equals an array of the existing files\n});\n\nasync.parallel([\n function(){ ... },\n function(){ ... }\n], callback);\n\nasync.series([\n function(){ ... },\n function(){ ... }\n]);\n```\n\nThere are many more functions available so take a look at the docs below for a\nfull list. This module aims to be comprehensive, so if you feel anything is\nmissing please create a GitHub issue for it.\n\n## Common Pitfalls\n\n### Binding a context to an iterator\n\nThis section is really about `bind`, not about `async`. If you are wondering how to\nmake `async` execute your iterators in a given context, or are confused as to why\na method of another library isn't working as an iterator, study this example:\n\n```js\n// Here is a simple object with an (unnecessarily roundabout) squaring method\nvar AsyncSquaringLibrary = {\n squareExponent: 2,\n square: function(number, callback){ \n var result = Math.pow(number, this.squareExponent);\n setTimeout(function(){\n callback(null, result);\n }, 200);\n }\n};\n\nasync.map([1, 2, 3], AsyncSquaringLibrary.square, function(err, result){\n // result is [NaN, NaN, NaN]\n // This fails because the `this.squareExponent` expression in the square\n // function is not evaluated in the context of AsyncSquaringLibrary, and is\n // therefore undefined.\n});\n\nasync.map([1, 2, 3], AsyncSquaringLibrary.square.bind(AsyncSquaringLibrary), function(err, result){\n // result is [1, 4, 9]\n // With the help of bind we can attach a context to the iterator before\n // passing it to async. Now the square function will be executed in its \n // 'home' AsyncSquaringLibrary context and the value of `this.squareExponent`\n // will be as expected.\n});\n```\n\n## Download\n\nThe source is available for download from\n[GitHub](https://github.com/caolan/async/blob/master/lib/async.js).\nAlternatively, you can install using Node Package Manager (`npm`):\n\n npm install async\n\nAs well as using Bower: \n\n bower install async\n\n__Development:__ [async.js](https://github.com/caolan/async/raw/master/lib/async.js) - 29.6kb Uncompressed\n\n## In the Browser\n\nSo far it's been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. \n\nUsage:\n\n```html\n\n\n```\n\n## Documentation\n\n### Collections\n\n* [`each`](#each)\n* [`eachSeries`](#eachSeries)\n* [`eachLimit`](#eachLimit)\n* [`forEachOf`](#forEachOf)\n* [`forEachOfSeries`](#forEachOfSeries)\n* [`forEachOfLimit`](#forEachOfLimit)\n* [`map`](#map)\n* [`mapSeries`](#mapSeries)\n* [`mapLimit`](#mapLimit)\n* [`filter`](#filter)\n* [`filterSeries`](#filterSeries)\n* [`reject`](#reject)\n* [`rejectSeries`](#rejectSeries)\n* [`reduce`](#reduce)\n* [`reduceRight`](#reduceRight)\n* [`detect`](#detect)\n* [`detectSeries`](#detectSeries)\n* [`sortBy`](#sortBy)\n* [`some`](#some)\n* [`every`](#every)\n* [`concat`](#concat)\n* [`concatSeries`](#concatSeries)\n\n### Control Flow\n\n* [`series`](#seriestasks-callback)\n* [`parallel`](#parallel)\n* [`parallelLimit`](#parallellimittasks-limit-callback)\n* [`whilst`](#whilst)\n* [`doWhilst`](#doWhilst)\n* [`until`](#until)\n* [`doUntil`](#doUntil)\n* [`forever`](#forever)\n* [`waterfall`](#waterfall)\n* [`compose`](#compose)\n* [`seq`](#seq)\n* [`applyEach`](#applyEach)\n* [`applyEachSeries`](#applyEachSeries)\n* [`queue`](#queue)\n* [`priorityQueue`](#priorityQueue)\n* [`cargo`](#cargo)\n* [`auto`](#auto)\n* [`retry`](#retry)\n* [`iterator`](#iterator)\n* [`apply`](#apply)\n* [`nextTick`](#nextTick)\n* [`times`](#times)\n* [`timesSeries`](#timesSeries)\n\n### Utils\n\n* [`memoize`](#memoize)\n* [`unmemoize`](#unmemoize)\n* [`log`](#log)\n* [`dir`](#dir)\n* [`noConflict`](#noConflict)\n\n\n## Collections\n\n\n\n### each(arr, iterator, callback)\n\nApplies the function `iterator` to each item in `arr`, in parallel.\nThe `iterator` is called with an item from the list, and a callback for when it\nhas finished. If the `iterator` passes an error to its `callback`, the main\n`callback` (for the `each` function) is immediately called with the error.\n\nNote, that since this function applies `iterator` to each item in parallel,\nthere is no guarantee that the iterator functions will complete in order.\n\n__Arguments__\n\n* `arr` - An array to iterate over.\n* `iterator(item, callback)` - A function to apply to each item in `arr`.\n The iterator is passed a `callback(err)` which must be called once it has \n completed. If no error has occurred, the `callback` should be run without \n arguments or with an explicit `null` argument. The array index is not passed\n to the iterator. If you need the index, use [`forEachOf`](#forEachOf).\n* `callback(err)` - A callback which is called when all `iterator` functions\n have finished, or an error occurs.\n\n__Examples__\n\n\n```js\n// assuming openFiles is an array of file names and saveFile is a function\n// to save the modified contents of that file:\n\nasync.each(openFiles, saveFile, function(err){\n // if any of the saves produced an error, err would equal that error\n});\n```\n\n```js\n// assuming openFiles is an array of file names \n\nasync.each(openFiles, function(file, callback) {\n \n // Perform operation on file here.\n console.log('Processing file ' + file);\n \n if( file.length > 32 ) {\n console.log('This file name is too long');\n callback('File name too long');\n } else {\n // Do work to process file here\n console.log('File processed');\n callback();\n }\n}, function(err){\n // if any of the file processing produced an error, err would equal that error\n if( err ) {\n // One of the iterations produced an error.\n // All processing will now stop.\n console.log('A file failed to process');\n } else {\n console.log('All files have been processed successfully');\n }\n});\n```\n\n---------------------------------------\n\n\n\n### eachSeries(arr, iterator, callback)\n\nThe same as [`each`](#each), only `iterator` is applied to each item in `arr` in\nseries. The next `iterator` is only called once the current one has completed. \nThis means the `iterator` functions will complete in order.\n\n\n---------------------------------------\n\n\n\n### eachLimit(arr, limit, iterator, callback)\n\nThe same as [`each`](#each), only no more than `limit` `iterator`s will be simultaneously \nrunning at any time.\n\nNote that the items in `arr` are not processed in batches, so there is no guarantee that \nthe first `limit` `iterator` functions will complete before any others are started.\n\n__Arguments__\n\n* `arr` - An array to iterate over.\n* `limit` - The maximum number of `iterator`s to run at any time.\n* `iterator(item, callback)` - A function to apply to each item in `arr`.\n The iterator is passed a `callback(err)` which must be called once it has \n completed. If no error has occurred, the callback should be run without \n arguments or with an explicit `null` argument.\n* `callback(err)` - A callback which is called when all `iterator` functions\n have finished, or an error occurs.\n\n__Example__\n\n```js\n// Assume documents is an array of JSON objects and requestApi is a\n// function that interacts with a rate-limited REST api.\n\nasync.eachLimit(documents, 20, requestApi, function(err){\n // if any of the saves produced an error, err would equal that error\n});\n```\n\n---------------------------------------\n\n\n\n\n### forEachOf(obj, iterator, callback)\n\nLike `each`, except that it iterates over objects, and passes the key as the second argument to the iterator.\n\n__Arguments__\n\n* `obj` - An object or array to iterate over.\n* `iterator(item, key, callback)` - A function to apply to each item in `obj`. \nThe `key` is the item's key, or index in the case of an array. The iterator is \npassed a `callback(err)` which must be called once it has completed. If no\nerror has occurred, the callback should be run without arguments or with an\nexplicit `null` argument.\n* `callback(err)` - A callback which is called when all `iterator` functions have finished, or an error occurs.\n\n__Example__\n\n```js\nvar obj = {dev: \"/dev.json\", test: \"/test.json\", prod: \"/prod.json\"};\nvar configs = {};\n\nasync.forEachOf(obj, function (value, key, callback) {\n fs.readFile(__dirname + value, \"utf8\", function (err, data) {\n if (err) return callback(err);\n try {\n configs[key] = JSON.parse(data);\n } catch (e) {\n return callback(e);\n }\n callback();\n })\n}, function (err) {\n if (err) console.error(err.message);\n // configs is now a map of JSON data\n doSomethingWith(configs);\n})\n```\n\n---------------------------------------\n\n\n\n\n### forEachOfSeries(obj, iterator, callback)\n\nLike [`forEachOf`](#forEachOf), except only one `iterator` is run at a time. The order of execution is not guaranteed for objects, but it will be guaranteed for arrays.\n\n---------------------------------------\n\n\n\n\n### forEachOfLimit(obj, limit, iterator, callback)\n\nLike [`forEachOf`](#forEachOf), except the number of `iterator`s running at a given time is controlled by `limit`.\n\n\n---------------------------------------\n\n\n### map(arr, iterator, callback)\n\nProduces a new array of values by mapping each value in `arr` through\nthe `iterator` function. The `iterator` is called with an item from `arr` and a\ncallback for when it has finished processing. Each of these callback takes 2 arguments: \nan `error`, and the transformed item from `arr`. If `iterator` passes an error to its \ncallback, the main `callback` (for the `map` function) is immediately called with the error.\n\nNote, that since this function applies the `iterator` to each item in parallel,\nthere is no guarantee that the `iterator` functions will complete in order. \nHowever, the results array will be in the same order as the original `arr`.\n\n__Arguments__\n\n* `arr` - An array to iterate over.\n* `iterator(item, callback)` - A function to apply to each item in `arr`.\n The iterator is passed a `callback(err, transformed)` which must be called once \n it has completed with an error (which can be `null`) and a transformed item.\n* `callback(err, results)` - A callback which is called when all `iterator`\n functions have finished, or an error occurs. Results is an array of the\n transformed items from the `arr`.\n\n__Example__\n\n```js\nasync.map(['file1','file2','file3'], fs.stat, function(err, results){\n // results is now an array of stats for each file\n});\n```\n\n---------------------------------------\n\n\n### mapSeries(arr, iterator, callback)\n\nThe same as [`map`](#map), only the `iterator` is applied to each item in `arr` in\nseries. The next `iterator` is only called once the current one has completed. \nThe results array will be in the same order as the original.\n\n\n---------------------------------------\n\n\n### mapLimit(arr, limit, iterator, callback)\n\nThe same as [`map`](#map), only no more than `limit` `iterator`s will be simultaneously \nrunning at any time.\n\nNote that the items are not processed in batches, so there is no guarantee that \nthe first `limit` `iterator` functions will complete before any others are started.\n\n__Arguments__\n\n* `arr` - An array to iterate over.\n* `limit` - The maximum number of `iterator`s to run at any time.\n* `iterator(item, callback)` - A function to apply to each item in `arr`.\n The iterator is passed a `callback(err, transformed)` which must be called once \n it has completed with an error (which can be `null`) and a transformed item.\n* `callback(err, results)` - A callback which is called when all `iterator`\n calls have finished, or an error occurs. The result is an array of the\n transformed items from the original `arr`.\n\n__Example__\n\n```js\nasync.mapLimit(['file1','file2','file3'], 1, fs.stat, function(err, results){\n // results is now an array of stats for each file\n});\n```\n\n---------------------------------------\n\n\n\n### filter(arr, iterator, callback)\n\n__Alias:__ `select`\n\nReturns a new array of all the values in `arr` which pass an async truth test.\n_The callback for each `iterator` call only accepts a single argument of `true` or\n`false`; it does not accept an error argument first!_ This is in-line with the\nway node libraries work with truth tests like `fs.exists`. This operation is\nperformed in parallel, but the results array will be in the same order as the\noriginal.\n\n__Arguments__\n\n* `arr` - An array to iterate over.\n* `iterator(item, callback)` - A truth test to apply to each item in `arr`.\n The `iterator` is passed a `callback(truthValue)`, which must be called with a \n boolean argument once it has completed.\n* `callback(results)` - A callback which is called after all the `iterator`\n functions have finished.\n\n__Example__\n\n```js\nasync.filter(['file1','file2','file3'], fs.exists, function(results){\n // results now equals an array of the existing files\n});\n```\n\n---------------------------------------\n\n\n\n### filterSeries(arr, iterator, callback)\n\n__Alias:__ `selectSeries`\n\nThe same as [`filter`](#filter) only the `iterator` is applied to each item in `arr` in\nseries. The next `iterator` is only called once the current one has completed. \nThe results array will be in the same order as the original.\n\n---------------------------------------\n\n\n### reject(arr, iterator, callback)\n\nThe opposite of [`filter`](#filter). Removes values that pass an `async` truth test.\n\n---------------------------------------\n\n\n### rejectSeries(arr, iterator, callback)\n\nThe same as [`reject`](#reject), only the `iterator` is applied to each item in `arr`\nin series.\n\n\n---------------------------------------\n\n\n### reduce(arr, memo, iterator, callback)\n\n__Aliases:__ `inject`, `foldl`\n\nReduces `arr` into a single value using an async `iterator` to return\neach successive step. `memo` is the initial state of the reduction. \nThis function only operates in series. \n\nFor performance reasons, it may make sense to split a call to this function into \na parallel map, and then use the normal `Array.prototype.reduce` on the results. \nThis function is for situations where each step in the reduction needs to be async; \nif you can get the data before reducing it, then it's probably a good idea to do so.\n\n__Arguments__\n\n* `arr` - An array to iterate over.\n* `memo` - The initial state of the reduction.\n* `iterator(memo, item, callback)` - A function applied to each item in the\n array to produce the next step in the reduction. The `iterator` is passed a\n `callback(err, reduction)` which accepts an optional error as its first \n argument, and the state of the reduction as the second. If an error is \n passed to the callback, the reduction is stopped and the main `callback` is \n immediately called with the error.\n* `callback(err, result)` - A callback which is called after all the `iterator`\n functions have finished. Result is the reduced value.\n\n__Example__\n\n```js\nasync.reduce([1,2,3], 0, function(memo, item, callback){\n // pointless async:\n process.nextTick(function(){\n callback(null, memo + item)\n });\n}, function(err, result){\n // result is now equal to the last value of memo, which is 6\n});\n```\n\n---------------------------------------\n\n\n### reduceRight(arr, memo, iterator, callback)\n\n__Alias:__ `foldr`\n\nSame as [`reduce`](#reduce), only operates on `arr` in reverse order.\n\n\n---------------------------------------\n\n\n### detect(arr, iterator, callback)\n\nReturns the first value in `arr` that passes an async truth test. The\n`iterator` is applied in parallel, meaning the first iterator to return `true` will\nfire the detect `callback` with that result. That means the result might not be\nthe first item in the original `arr` (in terms of order) that passes the test.\n\nIf order within the original `arr` is important, then look at [`detectSeries`](#detectSeries).\n\n__Arguments__\n\n* `arr` - An array to iterate over.\n* `iterator(item, callback)` - A truth test to apply to each item in `arr`.\n The iterator is passed a `callback(truthValue)` which must be called with a \n boolean argument once it has completed.\n* `callback(result)` - A callback which is called as soon as any iterator returns\n `true`, or after all the `iterator` functions have finished. Result will be\n the first item in the array that passes the truth test (iterator) or the\n value `undefined` if none passed.\n\n__Example__\n\n```js\nasync.detect(['file1','file2','file3'], fs.exists, function(result){\n // result now equals the first file in the list that exists\n});\n```\n\n---------------------------------------\n\n\n### detectSeries(arr, iterator, callback)\n\nThe same as [`detect`](#detect), only the `iterator` is applied to each item in `arr`\nin series. This means the result is always the first in the original `arr` (in\nterms of array order) that passes the truth test.\n\n\n---------------------------------------\n\n\n### sortBy(arr, iterator, callback)\n\nSorts a list by the results of running each `arr` value through an async `iterator`.\n\n__Arguments__\n\n* `arr` - An array to iterate over.\n* `iterator(item, callback)` - A function to apply to each item in `arr`.\n The iterator is passed a `callback(err, sortValue)` which must be called once it\n has completed with an error (which can be `null`) and a value to use as the sort\n criteria.\n* `callback(err, results)` - A callback which is called after all the `iterator`\n functions have finished, or an error occurs. Results is the items from\n the original `arr` sorted by the values returned by the `iterator` calls.\n\n__Example__\n\n```js\nasync.sortBy(['file1','file2','file3'], function(file, callback){\n fs.stat(file, function(err, stats){\n callback(err, stats.mtime);\n });\n}, function(err, results){\n // results is now the original array of files sorted by\n // modified date\n});\n```\n\n__Sort Order__\n\nBy modifying the callback parameter the sorting order can be influenced:\n\n```js\n//ascending order\nasync.sortBy([1,9,3,5], function(x, callback){\n callback(null, x);\n}, function(err,result){\n //result callback\n} );\n\n//descending order\nasync.sortBy([1,9,3,5], function(x, callback){\n callback(null, x*-1); //<- x*-1 instead of x, turns the order around\n}, function(err,result){\n //result callback\n} );\n```\n\n---------------------------------------\n\n\n### some(arr, iterator, callback)\n\n__Alias:__ `any`\n\nReturns `true` if at least one element in the `arr` satisfies an async test.\n_The callback for each iterator call only accepts a single argument of `true` or\n`false`; it does not accept an error argument first!_ This is in-line with the\nway node libraries work with truth tests like `fs.exists`. Once any iterator\ncall returns `true`, the main `callback` is immediately called.\n\n__Arguments__\n\n* `arr` - An array to iterate over.\n* `iterator(item, callback)` - A truth test to apply to each item in the array\n in parallel. The iterator is passed a callback(truthValue) which must be \n called with a boolean argument once it has completed.\n* `callback(result)` - A callback which is called as soon as any iterator returns\n `true`, or after all the iterator functions have finished. Result will be\n either `true` or `false` depending on the values of the async tests.\n\n__Example__\n\n```js\nasync.some(['file1','file2','file3'], fs.exists, function(result){\n // if result is true then at least one of the files exists\n});\n```\n\n---------------------------------------\n\n\n### every(arr, iterator, callback)\n\n__Alias:__ `all`\n\nReturns `true` if every element in `arr` satisfies an async test.\n_The callback for each `iterator` call only accepts a single argument of `true` or\n`false`; it does not accept an error argument first!_ This is in-line with the\nway node libraries work with truth tests like `fs.exists`.\n\n__Arguments__\n\n* `arr` - An array to iterate over.\n* `iterator(item, callback)` - A truth test to apply to each item in the array\n in parallel. The iterator is passed a callback(truthValue) which must be \n called with a boolean argument once it has completed.\n* `callback(result)` - A callback which is called after all the `iterator`\n functions have finished. Result will be either `true` or `false` depending on\n the values of the async tests.\n\n__Example__\n\n```js\nasync.every(['file1','file2','file3'], fs.exists, function(result){\n // if result is true then every file exists\n});\n```\n\n---------------------------------------\n\n\n### concat(arr, iterator, callback)\n\nApplies `iterator` to each item in `arr`, concatenating the results. Returns the\nconcatenated list. The `iterator`s are called in parallel, and the results are\nconcatenated as they return. There is no guarantee that the results array will\nbe returned in the original order of `arr` passed to the `iterator` function.\n\n__Arguments__\n\n* `arr` - An array to iterate over.\n* `iterator(item, callback)` - A function to apply to each item in `arr`.\n The iterator is passed a `callback(err, results)` which must be called once it \n has completed with an error (which can be `null`) and an array of results.\n* `callback(err, results)` - A callback which is called after all the `iterator`\n functions have finished, or an error occurs. Results is an array containing\n the concatenated results of the `iterator` function.\n\n__Example__\n\n```js\nasync.concat(['dir1','dir2','dir3'], fs.readdir, function(err, files){\n // files is now a list of filenames that exist in the 3 directories\n});\n```\n\n---------------------------------------\n\n\n### concatSeries(arr, iterator, callback)\n\nSame as [`concat`](#concat), but executes in series instead of parallel.\n\n\n## Control Flow\n\n\n### series(tasks, [callback])\n\nRun the functions in the `tasks` array in series, each one running once the previous\nfunction has completed. If any functions in the series pass an error to its\ncallback, no more functions are run, and `callback` is immediately called with the value of the error. \nOtherwise, `callback` receives an array of results when `tasks` have completed.\n\nIt is also possible to use an object instead of an array. Each property will be\nrun as a function, and the results will be passed to the final `callback` as an object\ninstead of an array. This can be a more readable way of handling results from\n[`series`](#series).\n\n**Note** that while many implementations preserve the order of object properties, the\n[ECMAScript Language Specifcation](http://www.ecma-international.org/ecma-262/5.1/#sec-8.6) \nexplicitly states that\n\n> The mechanics and order of enumerating the properties is not specified.\n\nSo if you rely on the order in which your series of functions are executed, and want\nthis to work on all platforms, consider using an array. \n\n__Arguments__\n\n* `tasks` - An array or object containing functions to run, each function is passed\n a `callback(err, result)` it must call on completion with an error `err` (which can\n be `null`) and an optional `result` value.\n* `callback(err, results)` - An optional callback to run once all the functions\n have completed. This function gets a results array (or object) containing all \n the result arguments passed to the `task` callbacks.\n\n__Example__\n\n```js\nasync.series([\n function(callback){\n // do some stuff ...\n callback(null, 'one');\n },\n function(callback){\n // do some more stuff ...\n callback(null, 'two');\n }\n],\n// optional callback\nfunction(err, results){\n // results is now equal to ['one', 'two']\n});\n\n\n// an example using an object instead of an array\nasync.series({\n one: function(callback){\n setTimeout(function(){\n callback(null, 1);\n }, 200);\n },\n two: function(callback){\n setTimeout(function(){\n callback(null, 2);\n }, 100);\n }\n},\nfunction(err, results) {\n // results is now equal to: {one: 1, two: 2}\n});\n```\n\n---------------------------------------\n\n\n### parallel(tasks, [callback])\n\nRun the `tasks` array of functions in parallel, without waiting until the previous\nfunction has completed. If any of the functions pass an error to its\ncallback, the main `callback` is immediately called with the value of the error.\nOnce the `tasks` have completed, the results are passed to the final `callback` as an\narray.\n\nIt is also possible to use an object instead of an array. Each property will be\nrun as a function and the results will be passed to the final `callback` as an object\ninstead of an array. This can be a more readable way of handling results from\n[`parallel`](#parallel).\n\n\n__Arguments__\n\n* `tasks` - An array or object containing functions to run. Each function is passed \n a `callback(err, result)` which it must call on completion with an error `err` \n (which can be `null`) and an optional `result` value.\n* `callback(err, results)` - An optional callback to run once all the functions\n have completed. This function gets a results array (or object) containing all \n the result arguments passed to the task callbacks.\n\n__Example__\n\n```js\nasync.parallel([\n function(callback){\n setTimeout(function(){\n callback(null, 'one');\n }, 200);\n },\n function(callback){\n setTimeout(function(){\n callback(null, 'two');\n }, 100);\n }\n],\n// optional callback\nfunction(err, results){\n // the results array will equal ['one','two'] even though\n // the second function had a shorter timeout.\n});\n\n\n// an example using an object instead of an array\nasync.parallel({\n one: function(callback){\n setTimeout(function(){\n callback(null, 1);\n }, 200);\n },\n two: function(callback){\n setTimeout(function(){\n callback(null, 2);\n }, 100);\n }\n},\nfunction(err, results) {\n // results is now equals to: {one: 1, two: 2}\n});\n```\n\n---------------------------------------\n\n\n### parallelLimit(tasks, limit, [callback])\n\nThe same as [`parallel`](#parallel), only `tasks` are executed in parallel \nwith a maximum of `limit` tasks executing at any time.\n\nNote that the `tasks` are not executed in batches, so there is no guarantee that \nthe first `limit` tasks will complete before any others are started.\n\n__Arguments__\n\n* `tasks` - An array or object containing functions to run, each function is passed \n a `callback(err, result)` it must call on completion with an error `err` (which can\n be `null`) and an optional `result` value.\n* `limit` - The maximum number of `tasks` to run at any time.\n* `callback(err, results)` - An optional callback to run once all the functions\n have completed. This function gets a results array (or object) containing all \n the result arguments passed to the `task` callbacks.\n\n---------------------------------------\n\n\n### whilst(test, fn, callback)\n\nRepeatedly call `fn`, while `test` returns `true`. Calls `callback` when stopped,\nor an error occurs.\n\n__Arguments__\n\n* `test()` - synchronous truth test to perform before each execution of `fn`.\n* `fn(callback)` - A function which is called each time `test` passes. The function is\n passed a `callback(err)`, which must be called once it has completed with an \n optional `err` argument.\n* `callback(err)` - A callback which is called after the test fails and repeated\n execution of `fn` has stopped.\n\n__Example__\n\n```js\nvar count = 0;\n\nasync.whilst(\n function () { return count < 5; },\n function (callback) {\n count++;\n setTimeout(callback, 1000);\n },\n function (err) {\n // 5 seconds have passed\n }\n);\n```\n\n---------------------------------------\n\n\n### doWhilst(fn, test, callback)\n\nThe post-check version of [`whilst`](#whilst). To reflect the difference in \nthe order of operations, the arguments `test` and `fn` are switched. \n\n`doWhilst` is to `whilst` as `do while` is to `while` in plain JavaScript.\n\n---------------------------------------\n\n\n### until(test, fn, callback)\n\nRepeatedly call `fn` until `test` returns `true`. Calls `callback` when stopped,\nor an error occurs.\n\nThe inverse of [`whilst`](#whilst).\n\n---------------------------------------\n\n\n### doUntil(fn, test, callback)\n\nLike [`doWhilst`](#doWhilst), except the `test` is inverted. Note the argument ordering differs from `until`.\n\n---------------------------------------\n\n\n### forever(fn, errback)\n\nCalls the asynchronous function `fn` with a callback parameter that allows it to\ncall itself again, in series, indefinitely.\n\nIf an error is passed to the callback then `errback` is called with the\nerror, and execution stops, otherwise it will never be called.\n\n```js\nasync.forever(\n function(next) {\n // next is suitable for passing to things that need a callback(err [, whatever]);\n // it will result in this function being called again.\n },\n function(err) {\n // if next is called with a value in its first parameter, it will appear\n // in here as 'err', and execution will stop.\n }\n);\n```\n\n---------------------------------------\n\n\n### waterfall(tasks, [callback])\n\nRuns the `tasks` array of functions in series, each passing their results to the next in\nthe array. However, if any of the `tasks` pass an error to their own callback, the\nnext function is not executed, and the main `callback` is immediately called with\nthe error.\n\n__Arguments__\n\n* `tasks` - An array of functions to run, each function is passed a \n `callback(err, result1, result2, ...)` it must call on completion. The first\n argument is an error (which can be `null`) and any further arguments will be \n passed as arguments in order to the next task.\n* `callback(err, [results])` - An optional callback to run once all the functions\n have completed. This will be passed the results of the last task's callback.\n\n\n\n__Example__\n\n```js\nasync.waterfall([\n function(callback) {\n callback(null, 'one', 'two');\n },\n function(arg1, arg2, callback) {\n // arg1 now equals 'one' and arg2 now equals 'two'\n callback(null, 'three');\n },\n function(arg1, callback) {\n // arg1 now equals 'three'\n callback(null, 'done');\n }\n], function (err, result) {\n // result now equals 'done' \n});\n```\n\n---------------------------------------\n\n### compose(fn1, fn2...)\n\nCreates a function which is a composition of the passed asynchronous\nfunctions. Each function consumes the return value of the function that\nfollows. Composing functions `f()`, `g()`, and `h()` would produce the result of\n`f(g(h()))`, only this version uses callbacks to obtain the return values.\n\nEach function is executed with the `this` binding of the composed function.\n\n__Arguments__\n\n* `functions...` - the asynchronous functions to compose\n\n\n__Example__\n\n```js\nfunction add1(n, callback) {\n setTimeout(function () {\n callback(null, n + 1);\n }, 10);\n}\n\nfunction mul3(n, callback) {\n setTimeout(function () {\n callback(null, n * 3);\n }, 10);\n}\n\nvar add1mul3 = async.compose(mul3, add1);\n\nadd1mul3(4, function (err, result) {\n // result now equals 15\n});\n```\n\n---------------------------------------\n\n### seq(fn1, fn2...)\n\nVersion of the compose function that is more natural to read.\nEach function consumes the return value of the previous function.\nIt is the equivalent of [`compose`](#compose) with the arguments reversed.\n\nEach function is executed with the `this` binding of the composed function.\n\n__Arguments__\n\n* functions... - the asynchronous functions to compose\n\n\n__Example__\n\n```js\n// Requires lodash (or underscore), express3 and dresende's orm2.\n// Part of an app, that fetches cats of the logged user.\n// This example uses `seq` function to avoid overnesting and error \n// handling clutter.\napp.get('/cats', function(request, response) {\n var User = request.models.User;\n async.seq(\n _.bind(User.get, User), // 'User.get' has signature (id, callback(err, data))\n function(user, fn) {\n user.getCats(fn); // 'getCats' has signature (callback(err, data))\n }\n )(req.session.user_id, function (err, cats) {\n if (err) {\n console.error(err);\n response.json({ status: 'error', message: err.message });\n } else {\n response.json({ status: 'ok', message: 'Cats found', data: cats });\n }\n });\n});\n```\n\n---------------------------------------\n\n### applyEach(fns, args..., callback)\n\nApplies the provided arguments to each function in the array, calling \n`callback` after all functions have completed. If you only provide the first\nargument, then it will return a function which lets you pass in the\narguments as if it were a single function call.\n\n__Arguments__\n\n* `fns` - the asynchronous functions to all call with the same arguments\n* `args...` - any number of separate arguments to pass to the function\n* `callback` - the final argument should be the callback, called when all\n functions have completed processing\n\n\n__Example__\n\n```js\nasync.applyEach([enableSearch, updateSchema], 'bucket', callback);\n\n// partial application example:\nasync.each(\n buckets,\n async.applyEach([enableSearch, updateSchema]),\n callback\n);\n```\n\n---------------------------------------\n\n\n### applyEachSeries(arr, args..., callback)\n\nThe same as [`applyEach`](#applyEach) only the functions are applied in series.\n\n---------------------------------------\n\n\n### queue(worker, concurrency)\n\nCreates a `queue` object with the specified `concurrency`. Tasks added to the\n`queue` are processed in parallel (up to the `concurrency` limit). If all\n`worker`s are in progress, the task is queued until one becomes available. \nOnce a `worker` completes a `task`, that `task`'s callback is called.\n\n__Arguments__\n\n* `worker(task, callback)` - An asynchronous function for processing a queued\n task, which must call its `callback(err)` argument when finished, with an \n optional `error` as an argument.\n* `concurrency` - An `integer` for determining how many `worker` functions should be\n run in parallel.\n\n__Queue objects__\n\nThe `queue` object returned by this function has the following properties and\nmethods:\n\n* `length()` - a function returning the number of items waiting to be processed.\n* `started` - a function returning whether or not any items have been pushed and processed by the queue\n* `running()` - a function returning the number of items currently being processed.\n* `idle()` - a function returning false if there are items waiting or being processed, or true if not.\n* `concurrency` - an integer for determining how many `worker` functions should be\n run in parallel. This property can be changed after a `queue` is created to\n alter the concurrency on-the-fly.\n* `push(task, [callback])` - add a new task to the `queue`. Calls `callback` once \n the `worker` has finished processing the task. Instead of a single task, a `tasks` array\n can be submitted. The respective callback is used for every task in the list.\n* `unshift(task, [callback])` - add a new task to the front of the `queue`.\n* `saturated` - a callback that is called when the `queue` length hits the `concurrency` limit, \n and further tasks will be queued.\n* `empty` - a callback that is called when the last item from the `queue` is given to a `worker`.\n* `drain` - a callback that is called when the last item from the `queue` has returned from the `worker`.\n* `paused` - a boolean for determining whether the queue is in a paused state\n* `pause()` - a function that pauses the processing of tasks until `resume()` is called.\n* `resume()` - a function that resumes the processing of queued tasks when the queue is paused.\n* `kill()` - a function that removes the `drain` callback and empties remaining tasks from the queue forcing it to go idle.\n\n__Example__\n\n```js\n// create a queue object with concurrency 2\n\nvar q = async.queue(function (task, callback) {\n console.log('hello ' + task.name);\n callback();\n}, 2);\n\n\n// assign a callback\nq.drain = function() {\n console.log('all items have been processed');\n}\n\n// add some items to the queue\n\nq.push({name: 'foo'}, function (err) {\n console.log('finished processing foo');\n});\nq.push({name: 'bar'}, function (err) {\n console.log('finished processing bar');\n});\n\n// add some items to the queue (batch-wise)\n\nq.push([{name: 'baz'},{name: 'bay'},{name: 'bax'}], function (err) {\n console.log('finished processing item');\n});\n\n// add some items to the front of the queue\n\nq.unshift({name: 'bar'}, function (err) {\n console.log('finished processing bar');\n});\n```\n\n\n---------------------------------------\n\n\n### priorityQueue(worker, concurrency)\n\nThe same as [`queue`](#queue) only tasks are assigned a priority and completed in ascending priority order. There are two differences between `queue` and `priorityQueue` objects:\n\n* `push(task, priority, [callback])` - `priority` should be a number. If an array of\n `tasks` is given, all tasks will be assigned the same priority.\n* The `unshift` method was removed.\n\n---------------------------------------\n\n\n### cargo(worker, [payload])\n\nCreates a `cargo` object with the specified payload. Tasks added to the\ncargo will be processed altogether (up to the `payload` limit). If the\n`worker` is in progress, the task is queued until it becomes available. Once\nthe `worker` has completed some tasks, each callback of those tasks is called.\nCheck out [this animation](https://camo.githubusercontent.com/6bbd36f4cf5b35a0f11a96dcd2e97711ffc2fb37/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130382f62626330636662302d356632392d313165322d393734662d3333393763363464633835382e676966) for how `cargo` and `queue` work.\n\nWhile [queue](#queue) passes only one task to one of a group of workers\nat a time, cargo passes an array of tasks to a single worker, repeating\nwhen the worker is finished.\n\n__Arguments__\n\n* `worker(tasks, callback)` - An asynchronous function for processing an array of\n queued tasks, which must call its `callback(err)` argument when finished, with \n an optional `err` argument.\n* `payload` - An optional `integer` for determining how many tasks should be\n processed per round; if omitted, the default is unlimited.\n\n__Cargo objects__\n\nThe `cargo` object returned by this function has the following properties and\nmethods:\n\n* `length()` - A function returning the number of items waiting to be processed.\n* `payload` - An `integer` for determining how many tasks should be\n process per round. This property can be changed after a `cargo` is created to\n alter the payload on-the-fly.\n* `push(task, [callback])` - Adds `task` to the `queue`. The callback is called\n once the `worker` has finished processing the task. Instead of a single task, an array of `tasks` \n can be submitted. The respective callback is used for every task in the list.\n* `saturated` - A callback that is called when the `queue.length()` hits the concurrency and further tasks will be queued.\n* `empty` - A callback that is called when the last item from the `queue` is given to a `worker`.\n* `drain` - A callback that is called when the last item from the `queue` has returned from the `worker`.\n\n__Example__\n\n```js\n// create a cargo object with payload 2\n\nvar cargo = async.cargo(function (tasks, callback) {\n for(var i=0; i\n### auto(tasks, [callback])\n\nDetermines the best order for running the functions in `tasks`, based on their \nrequirements. Each function can optionally depend on other functions being completed \nfirst, and each function is run as soon as its requirements are satisfied. \n\nIf any of the functions pass an error to their callback, it will not \ncomplete (so any other functions depending on it will not run), and the main \n`callback` is immediately called with the error. Functions also receive an \nobject containing the results of functions which have completed so far.\n\nNote, all functions are called with a `results` object as a second argument, \nso it is unsafe to pass functions in the `tasks` object which cannot handle the\nextra argument. \n\nFor example, this snippet of code:\n\n```js\nasync.auto({\n readData: async.apply(fs.readFile, 'data.txt', 'utf-8')\n}, callback);\n```\n\nwill have the effect of calling `readFile` with the results object as the last\nargument, which will fail:\n\n```js\nfs.readFile('data.txt', 'utf-8', cb, {});\n```\n\nInstead, wrap the call to `readFile` in a function which does not forward the \n`results` object:\n\n```js\nasync.auto({\n readData: function(cb, results){\n fs.readFile('data.txt', 'utf-8', cb);\n }\n}, callback);\n```\n\n__Arguments__\n\n* `tasks` - An object. Each of its properties is either a function or an array of\n requirements, with the function itself the last item in the array. The object's key\n of a property serves as the name of the task defined by that property,\n i.e. can be used when specifying requirements for other tasks.\n The function receives two arguments: (1) a `callback(err, result)` which must be \n called when finished, passing an `error` (which can be `null`) and the result of \n the function's execution, and (2) a `results` object, containing the results of\n the previously executed functions.\n* `callback(err, results)` - An optional callback which is called when all the\n tasks have been completed. It receives the `err` argument if any `tasks` \n pass an error to their callback. Results are always returned; however, if \n an error occurs, no further `tasks` will be performed, and the results\n object will only contain partial results.\n\n\n__Example__\n\n```js\nasync.auto({\n get_data: function(callback){\n console.log('in get_data');\n // async code to get some data\n callback(null, 'data', 'converted to array');\n },\n make_folder: function(callback){\n console.log('in make_folder');\n // async code to create a directory to store a file in\n // this is run at the same time as getting the data\n callback(null, 'folder');\n },\n write_file: ['get_data', 'make_folder', function(callback, results){\n console.log('in write_file', JSON.stringify(results));\n // once there is some data and the directory exists,\n // write the data to a file in the directory\n callback(null, 'filename');\n }],\n email_link: ['write_file', function(callback, results){\n console.log('in email_link', JSON.stringify(results));\n // once the file is written let's email a link to it...\n // results.write_file contains the filename returned by write_file.\n callback(null, {'file':results.write_file, 'email':'user@example.com'});\n }]\n}, function(err, results) {\n console.log('err = ', err);\n console.log('results = ', results);\n});\n```\n\nThis is a fairly trivial example, but to do this using the basic parallel and\nseries functions would look like this:\n\n```js\nasync.parallel([\n function(callback){\n console.log('in get_data');\n // async code to get some data\n callback(null, 'data', 'converted to array');\n },\n function(callback){\n console.log('in make_folder');\n // async code to create a directory to store a file in\n // this is run at the same time as getting the data\n callback(null, 'folder');\n }\n],\nfunction(err, results){\n async.series([\n function(callback){\n console.log('in write_file', JSON.stringify(results));\n // once there is some data and the directory exists,\n // write the data to a file in the directory\n results.push('filename');\n callback(null);\n },\n function(callback){\n console.log('in email_link', JSON.stringify(results));\n // once the file is written let's email a link to it...\n callback(null, {'file':results.pop(), 'email':'user@example.com'});\n }\n ]);\n});\n```\n\nFor a complicated series of `async` tasks, using the [`auto`](#auto) function makes adding\nnew tasks much easier (and the code more readable).\n\n\n---------------------------------------\n\n\n### retry([times = 5], task, [callback])\n\nAttempts to get a successful response from `task` no more than `times` times before\nreturning an error. If the task is successful, the `callback` will be passed the result\nof the successful task. If all attempts fail, the callback will be passed the error and\nresult (if any) of the final attempt.\n\n__Arguments__\n\n* `times` - An integer indicating how many times to attempt the `task` before giving up. Defaults to 5.\n* `task(callback, results)` - A function which receives two arguments: (1) a `callback(err, result)`\n which must be called when finished, passing `err` (which can be `null`) and the `result` of \n the function's execution, and (2) a `results` object, containing the results of\n the previously executed functions (if nested inside another control flow).\n* `callback(err, results)` - An optional callback which is called when the\n task has succeeded, or after the final failed attempt. It receives the `err` and `result` arguments of the last attempt at completing the `task`.\n\nThe [`retry`](#retry) function can be used as a stand-alone control flow by passing a\ncallback, as shown below:\n\n```js\nasync.retry(3, apiMethod, function(err, result) {\n // do something with the result\n});\n```\n\nIt can also be embeded within other control flow functions to retry individual methods\nthat are not as reliable, like this:\n\n```js\nasync.auto({\n users: api.getUsers.bind(api),\n payments: async.retry(3, api.getPayments.bind(api))\n}, function(err, results) {\n // do something with the results\n});\n```\n\n\n---------------------------------------\n\n\n### iterator(tasks)\n\nCreates an iterator function which calls the next function in the `tasks` array,\nreturning a continuation to call the next one after that. It's also possible to\n“peek” at the next iterator with `iterator.next()`.\n\nThis function is used internally by the `async` module, but can be useful when\nyou want to manually control the flow of functions in series.\n\n__Arguments__\n\n* `tasks` - An array of functions to run.\n\n__Example__\n\n```js\nvar iterator = async.iterator([\n function(){ sys.p('one'); },\n function(){ sys.p('two'); },\n function(){ sys.p('three'); }\n]);\n\nnode> var iterator2 = iterator();\n'one'\nnode> var iterator3 = iterator2();\n'two'\nnode> iterator3();\n'three'\nnode> var nextfn = iterator2.next();\nnode> nextfn();\n'three'\n```\n\n---------------------------------------\n\n\n### apply(function, arguments..)\n\nCreates a continuation function with some arguments already applied. \n\nUseful as a shorthand when combined with other control flow functions. Any arguments\npassed to the returned function are added to the arguments originally passed\nto apply.\n\n__Arguments__\n\n* `function` - The function you want to eventually apply all arguments to.\n* `arguments...` - Any number of arguments to automatically apply when the\n continuation is called.\n\n__Example__\n\n```js\n// using apply\n\nasync.parallel([\n async.apply(fs.writeFile, 'testfile1', 'test1'),\n async.apply(fs.writeFile, 'testfile2', 'test2'),\n]);\n\n\n// the same process without using apply\n\nasync.parallel([\n function(callback){\n fs.writeFile('testfile1', 'test1', callback);\n },\n function(callback){\n fs.writeFile('testfile2', 'test2', callback);\n }\n]);\n```\n\nIt's possible to pass any number of additional arguments when calling the\ncontinuation:\n\n```js\nnode> var fn = async.apply(sys.puts, 'one');\nnode> fn('two', 'three');\none\ntwo\nthree\n```\n\n---------------------------------------\n\n\n### nextTick(callback), setImmediate(callback)\n\nCalls `callback` on a later loop around the event loop. In Node.js this just\ncalls `process.nextTick`; in the browser it falls back to `setImmediate(callback)`\nif available, otherwise `setTimeout(callback, 0)`, which means other higher priority\nevents may precede the execution of `callback`.\n\nThis is used internally for browser-compatibility purposes.\n\n__Arguments__\n\n* `callback` - The function to call on a later loop around the event loop.\n\n__Example__\n\n```js\nvar call_order = [];\nasync.nextTick(function(){\n call_order.push('two');\n // call_order now equals ['one','two']\n});\ncall_order.push('one')\n```\n\n\n### times(n, callback)\n\nCalls the `callback` function `n` times, and accumulates results in the same manner\nyou would use with [`map`](#map).\n\n__Arguments__\n\n* `n` - The number of times to run the function.\n* `iterator` - The function to call `n` times.\n* `callback` - see [`map`](#map)\n\n__Example__\n\n```js\n// Pretend this is some complicated async factory\nvar createUser = function(id, callback) {\n callback(null, {\n id: 'user' + id\n })\n}\n// generate 5 users\nasync.times(5, function(n, next){\n createUser(n, function(err, user) {\n next(err, user)\n })\n}, function(err, users) {\n // we should now have 5 users\n});\n```\n\n\n### timesSeries(n, callback)\n\nThe same as [`times`](#times), only the iterator is applied to each item in `arr` in\nseries. The next `iterator` is only called once the current one has completed. \nThe results array will be in the same order as the original.\n\n\n## Utils\n\n\n### memoize(fn, [hasher])\n\nCaches the results of an `async` function. When creating a hash to store function\nresults against, the callback is omitted from the hash and an optional hash\nfunction can be used.\n\nIf no hash function is specified, the first argument is used as a hash key, which may work reasonably if it is a string or a data type that converts to a distinct string. Note that objects and arrays will not behave reasonably. Neither will cases where the other arguments are significant. In such cases, specify your own hash function.\n\nThe cache of results is exposed as the `memo` property of the function returned\nby `memoize`.\n\n__Arguments__\n\n* `fn` - The function to proxy and cache results from.\n* `hasher` - An optional function for generating a custom hash for storing\n results. It has all the arguments applied to it apart from the callback, and\n must be synchronous.\n\n__Example__\n\n```js\nvar slow_fn = function (name, callback) {\n // do something\n callback(null, result);\n};\nvar fn = async.memoize(slow_fn);\n\n// fn can now be used as if it were slow_fn\nfn('some name', function () {\n // callback\n});\n```\n\n\n### unmemoize(fn)\n\nUndoes a [`memoize`](#memoize)d function, reverting it to the original, unmemoized\nform. Handy for testing.\n\n__Arguments__\n\n* `fn` - the memoized function\n\n\n### log(function, arguments)\n\nLogs the result of an `async` function to the `console`. Only works in Node.js or\nin browsers that support `console.log` and `console.error` (such as FF and Chrome).\nIf multiple arguments are returned from the async function, `console.log` is\ncalled on each argument in order.\n\n__Arguments__\n\n* `function` - The function you want to eventually apply all arguments to.\n* `arguments...` - Any number of arguments to apply to the function.\n\n__Example__\n\n```js\nvar hello = function(name, callback){\n setTimeout(function(){\n callback(null, 'hello ' + name);\n }, 1000);\n};\n```\n```js\nnode> async.log(hello, 'world');\n'hello world'\n```\n\n---------------------------------------\n\n\n### dir(function, arguments)\n\nLogs the result of an `async` function to the `console` using `console.dir` to\ndisplay the properties of the resulting object. Only works in Node.js or\nin browsers that support `console.dir` and `console.error` (such as FF and Chrome).\nIf multiple arguments are returned from the async function, `console.dir` is\ncalled on each argument in order.\n\n__Arguments__\n\n* `function` - The function you want to eventually apply all arguments to.\n* `arguments...` - Any number of arguments to apply to the function.\n\n__Example__\n\n```js\nvar hello = function(name, callback){\n setTimeout(function(){\n callback(null, {hello: name});\n }, 1000);\n};\n```\n```js\nnode> async.dir(hello, 'world');\n{hello: 'world'}\n```\n\n---------------------------------------\n\n\n### noConflict()\n\nChanges the value of `async` back to its original value, returning a reference to the\n`async` object.\n", "readmeFilename": "README.md", "homepage": "https://github.com/caolan/async", "_id": "async@1.0.0", "_shasum": "f8fc04ca3a13784ade9e1641af98578cfbd647a9", "_from": "async@~1.0.0", "_resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz" } passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/async/support/000755 000765 000024 00000000000 12233035540 032016 5ustar00honglistaff000000 000000 src/nodejs_supportlib/vendor-copy/winston/node_modules/async/support/sync-package-managers.js000755 000765 000024 00000003031 12233035540 036435 0ustar00honglistaff000000 000000 passenger-5.0.30#!/usr/bin/env node // This should probably be its own module but complaints about bower/etc. // support keep coming up and I'd rather just enable the workflow here for now // and figure out where this should live later. -- @beaugunderson var fs = require('fs'); var _ = require('lodash'); var packageJson = require('../package.json'); var IGNORES = ['**/.*', 'node_modules', 'bower_components', 'test', 'tests']; var INCLUDES = ['lib/async.js', 'README.md', 'LICENSE']; var REPOSITORY_NAME = 'caolan/async'; packageJson.jam = { main: packageJson.main, include: INCLUDES, categories: ['Utilities'] }; packageJson.spm = { main: packageJson.main }; packageJson.volo = { main: packageJson.main, ignore: IGNORES }; var bowerSpecific = { moduleType: ['amd', 'globals', 'node'], ignore: IGNORES, authors: [packageJson.author] }; var bowerInclude = ['name', 'description', 'version', 'main', 'keywords', 'license', 'homepage', 'repository', 'devDependencies']; var componentSpecific = { repository: REPOSITORY_NAME, scripts: [packageJson.main] }; var componentInclude = ['name', 'description', 'version', 'keywords', 'license', 'main']; var bowerJson = _.merge({}, _.pick(packageJson, bowerInclude), bowerSpecific); var componentJson = _.merge({}, _.pick(packageJson, componentInclude), componentSpecific); fs.writeFileSync('./bower.json', JSON.stringify(bowerJson, null, 2)); fs.writeFileSync('./component.json', JSON.stringify(componentJson, null, 2)); fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, 2)); passenger-5.0.30/src/nodejs_supportlib/vendor-copy/winston/node_modules/async/lib/async.js000644 000765 000024 00000113653 12233035540 032534 0ustar00honglistaff000000 000000 /*! * async * https://github.com/caolan/async * * Copyright 2010-2014 Caolan McMahon * Released under the MIT license */ (function () { var async = {}; var noop = function () {}; // global on the server, window in the browser var root, previous_async; if (typeof window == 'object' && this === window) { root = window; } else if (typeof global == 'object' && this === global) { root = global; } else { root = this; } if (root != null) { previous_async = root.async; } async.noConflict = function () { root.async = previous_async; return async; }; function only_once(fn) { var called = false; return function() { if (called) throw new Error("Callback was already called."); called = true; fn.apply(root, arguments); }; } //// cross-browser compatiblity functions //// var _toString = Object.prototype.toString; var _isArray = Array.isArray || function (obj) { return _toString.call(obj) === '[object Array]'; }; var _each = function (arr, iterator) { var index = -1, length = arr.length; while (++index < length) { iterator(arr[index], index, arr); } }; var _map = function (arr, iterator) { var index = -1, length = arr.length, result = Array(length); while (++index < length) { result[index] = iterator(arr[index], index, arr); } return result; }; var _reduce = function (arr, iterator, memo) { _each(arr, function (x, i, a) { memo = iterator(memo, x, i, a); }); return memo; }; var _forEachOf = function (object, iterator) { _each(_keys(object), function (key) { iterator(object[key], key); }); }; var _keys = Object.keys || function (obj) { var keys = []; for (var k in obj) { if (obj.hasOwnProperty(k)) { keys.push(k); } } return keys; }; var _baseSlice = function (arr, start) { start = start || 0; var index = -1; var length = arr.length; if (start) { length -= start; length = length < 0 ? 0 : length; } var result = Array(length); while (++index < length) { result[index] = arr[index + start]; } return result; }; //// exported async module functions //// //// nextTick implementation with browser-compatible fallback //// // capture the global reference to guard against fakeTimer mocks var _setImmediate; if (typeof setImmediate === 'function') { _setImmediate = setImmediate; } if (typeof process === 'undefined' || !(process.nextTick)) { if (_setImmediate) { async.nextTick = function (fn) { // not a direct alias for IE10 compatibility _setImmediate(fn); }; async.setImmediate = async.nextTick; } else { async.nextTick = function (fn) { setTimeout(fn, 0); }; async.setImmediate = async.nextTick; } } else { async.nextTick = process.nextTick; if (_setImmediate) { async.setImmediate = function (fn) { // not a direct alias for IE10 compatibility _setImmediate(fn); }; } else { async.setImmediate = async.nextTick; } } async.each = function (arr, iterator, callback) { callback = callback || noop; if (!arr.length) { return callback(); } var completed = 0; _each(arr, function (x) { iterator(x, only_once(done) ); }); function done(err) { if (err) { callback(err); callback = noop; } else { completed += 1; if (completed >= arr.length) { callback(); } } } }; async.forEach = async.each; async.eachSeries = function (arr, iterator, callback) { callback = callback || noop; if (!arr.length) { return callback(); } var completed = 0; var iterate = function () { iterator(arr[completed], function (err) { if (err) { callback(err); callback = noop; } else { completed += 1; if (completed >= arr.length) { callback(); } else { iterate(); } } }); }; iterate(); }; async.forEachSeries = async.eachSeries; async.eachLimit = function (arr, limit, iterator, callback) { var fn = _eachLimit(limit); fn.apply(null, [arr, iterator, callback]); }; async.forEachLimit = async.eachLimit; var _eachLimit = function (limit) { return function (arr, iterator, callback) { callback = callback || noop; if (!arr.length || limit <= 0) { return callback(); } var completed = 0; var started = 0; var running = 0; (function replenish () { if (completed >= arr.length) { return callback(); } while (running < limit && started < arr.length) { started += 1; running += 1; iterator(arr[started - 1], function (err) { if (err) { callback(err); callback = noop; } else { completed += 1; running -= 1; if (completed >= arr.length) { callback(); } else { replenish(); } } }); } })(); }; }; async.forEachOf = async.eachOf = function (object, iterator, callback) { callback = callback || function () {}; var size = object.length || _keys(object).length; var completed = 0; if (!size) { return callback(); } _forEachOf(object, function (value, key) { iterator(object[key], key, function (err) { if (err) { callback(err); callback = function () {}; } else { completed += 1; if (completed === size) { callback(null); } } }); }); }; async.forEachOfSeries = async.eachOfSeries = function (obj, iterator, callback) { callback = callback || function () {}; var keys = _keys(obj); var size = keys.length; if (!size) { return callback(); } var completed = 0; var iterate = function () { var sync = true; var key = keys[completed]; iterator(obj[key], key, function (err) { if (err) { callback(err); callback = function () {}; } else { completed += 1; if (completed >= size) { callback(null); } else { if (sync) { async.nextTick(iterate); } else { iterate(); } } } }); sync = false; }; iterate(); }; async.forEachOfLimit = async.eachOfLimit = function (obj, limit, iterator, callback) { _forEachOfLimit(limit)(obj, iterator, callback); }; var _forEachOfLimit = function (limit) { return function (obj, iterator, callback) { callback = callback || function () {}; var keys = _keys(obj); var size = keys.length; if (!size || limit <= 0) { return callback(); } var completed = 0; var started = 0; var running = 0; (function replenish () { if (completed >= size) { return callback(); } while (running < limit && started < size) { started += 1; running += 1; var key = keys[started - 1]; iterator(obj[key], key, function (err) { if (err) { callback(err); callback = function () {}; } else { completed += 1; running -= 1; if (completed >= size) { callback(); } else { replenish(); } } }); } })(); }; }; var doParallel = function (fn) { return function () { var args = _baseSlice(arguments); return fn.apply(null, [async.each].concat(args)); }; }; var doParallelLimit = function(limit, fn) { return function () { var args = _baseSlice(arguments); return fn.apply(null, [_eachLimit(limit)].concat(args)); }; }; var doSeries = function (fn) { return function () { var args = _baseSlice(arguments); return fn.apply(null, [async.eachSeries].concat(args)); }; }; var _asyncMap = function (eachfn, arr, iterator, callback) { arr = _map(arr, function (x, i) { return {index: i, value: x}; }); if (!callback) { eachfn(arr, function (x, callback) { iterator(x.value, function (err) { callback(err); }); }); } else { var results = []; eachfn(arr, function (x, callback) { iterator(x.value, function (err, v) { results[x.index] = v; callback(err); }); }, function (err) { callback(err, results); }); } }; async.map = doParallel(_asyncMap); async.mapSeries = doSeries(_asyncMap); async.mapLimit = function (arr, limit, iterator, callback) { return _mapLimit(limit)(arr, iterator, callback); }; var _mapLimit = function(limit) { return doParallelLimit(limit, _asyncMap); }; // reduce only has a series version, as doing reduce in parallel won't // work in many situations. async.reduce = function (arr, memo, iterator, callback) { async.eachSeries(arr, function (x, callback) { iterator(memo, x, function (err, v) { memo = v; callback(err); }); }, function (err) { callback(err, memo); }); }; // inject alias async.inject = async.reduce; // foldl alias async.foldl = async.reduce; async.reduceRight = function (arr, memo, iterator, callback) { var reversed = _map(arr, function (x) { return x; }).reverse(); async.reduce(reversed, memo, iterator, callback); }; // foldr alias async.foldr = async.reduceRight; var _filter = function (eachfn, arr, iterator, callback) { var results = []; arr = _map(arr, function (x, i) { return {index: i, value: x}; }); eachfn(arr, function (x, callback) { iterator(x.value, function (v) { if (v) { results.push(x); } callback(); }); }, function (err) { callback(_map(results.sort(function (a, b) { return a.index - b.index; }), function (x) { return x.value; })); }); }; async.filter = doParallel(_filter); async.filterSeries = doSeries(_filter); // select alias async.select = async.filter; async.selectSeries = async.filterSeries; var _reject = function (eachfn, arr, iterator, callback) { var results = []; arr = _map(arr, function (x, i) { return {index: i, value: x}; }); eachfn(arr, function (x, callback) { iterator(x.value, function (v) { if (!v) { results.push(x); } callback(); }); }, function (err) { callback(_map(results.sort(function (a, b) { return a.index - b.index; }), function (x) { return x.value; })); }); }; async.reject = doParallel(_reject); async.rejectSeries = doSeries(_reject); var _detect = function (eachfn, arr, iterator, main_callback) { eachfn(arr, function (x, callback) { iterator(x, function (result) { if (result) { main_callback(x); main_callback = noop; } else { callback(); } }); }, function (err) { main_callback(); }); }; async.detect = doParallel(_detect); async.detectSeries = doSeries(_detect); async.some = function (arr, iterator, main_callback) { async.each(arr, function (x, callback) { iterator(x, function (v) { if (v) { main_callback(true); main_callback = noop; } callback(); }); }, function (err) { main_callback(false); }); }; // any alias async.any = async.some; async.every = function (arr, iterator, main_callback) { async.each(arr, function (x, callback) { iterator(x, function (v) { if (!v) { main_callback(false); main_callback = noop; } callback(); }); }, function (err) { main_callback(true); }); }; // all alias async.all = async.every; async.sortBy = function (arr, iterator, callback) { async.map(arr, function (x, callback) { iterator(x, function (err, criteria) { if (err) { callback(err); } else { callback(null, {value: x, criteria: criteria}); } }); }, function (err, results) { if (err) { return callback(err); } else { var fn = function (left, right) { var a = left.criteria, b = right.criteria; return a < b ? -1 : a > b ? 1 : 0; }; callback(null, _map(results.sort(fn), function (x) { return x.value; })); } }); }; async.auto = function (tasks, callback) { callback = callback || noop; var keys = _keys(tasks); var remainingTasks = keys.length; if (!remainingTasks) { return callback(); } var results = {}; var listeners = []; var addListener = function (fn) { listeners.unshift(fn); }; var removeListener = function (fn) { for (var i = 0; i < listeners.length; i += 1) { if (listeners[i] === fn) { listeners.splice(i, 1); return; } } }; var taskComplete = function () { remainingTasks--; _each(listeners.slice(0), function (fn) { fn(); }); }; addListener(function () { if (!remainingTasks) { var theCallback = callback; // prevent final callback from calling itself if it errors callback = noop; theCallback(null, results); } }); _each(keys, function (k) { var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]]; var taskCallback = function (err) { var args = _baseSlice(arguments, 1); if (args.length <= 1) { args = args[0]; } if (err) { var safeResults = {}; _each(_keys(results), function(rkey) { safeResults[rkey] = results[rkey]; }); safeResults[k] = args; callback(err, safeResults); // stop subsequent errors hitting callback multiple times callback = noop; } else { results[k] = args; async.setImmediate(taskComplete); } }; var requires = task.slice(0, Math.abs(task.length - 1)) || []; // prevent dead-locks var len = requires.length; var dep; while (len--) { if (!(dep = tasks[requires[len]])) { throw new Error('Has inexistant dependency'); } if (_isArray(dep) && !!~dep.indexOf(k)) { throw new Error('Has cyclic dependencies'); } } var ready = function () { return _reduce(requires, function (a, x) { return (a && results.hasOwnProperty(x)); }, true) && !results.hasOwnProperty(k); }; if (ready()) { task[task.length - 1](taskCallback, results); } else { var listener = function () { if (ready()) { removeListener(listener); task[task.length - 1](taskCallback, results); } }; addListener(listener); } }); }; async.retry = function(times, task, callback) { var DEFAULT_TIMES = 5; var attempts = []; // Use defaults if times not passed if (typeof times === 'function') { callback = task; task = times; times = DEFAULT_TIMES; } // Make sure times is a number times = parseInt(times, 10) || DEFAULT_TIMES; var wrappedTask = function(wrappedCallback, wrappedResults) { var retryAttempt = function(task, finalAttempt) { return function(seriesCallback) { task(function(err, result){ seriesCallback(!err || finalAttempt, {err: err, result: result}); }, wrappedResults); }; }; while (times) { attempts.push(retryAttempt(task, !(times-=1))); } async.series(attempts, function(done, data){ data = data[data.length - 1]; (wrappedCallback || callback)(data.err, data.result); }); }; // If a callback is passed, run this as a controll flow return callback ? wrappedTask() : wrappedTask; }; async.waterfall = function (tasks, callback) { callback = callback || noop; if (!_isArray(tasks)) { var err = new Error('First argument to waterfall must be an array of functions'); return callback(err); } if (!tasks.length) { return callback(); } var wrapIterator = function (iterator) { return function (err) { if (err) { callback.apply(null, arguments); callback = noop; } else { var args = _baseSlice(arguments, 1); var next = iterator.next(); if (next) { args.push(wrapIterator(next)); } else { args.push(callback); } async.setImmediate(function () { iterator.apply(null, args); }); } }; }; wrapIterator(async.iterator(tasks))(); }; var _parallel = function(eachfn, tasks, callback) { callback = callback || noop; if (_isArray(tasks)) { eachfn.map(tasks, function (fn, callback) { if (fn) { fn(function (err) { var args = _baseSlice(arguments, 1); if (args.length <= 1) { args = args[0]; } callback.call(null, err, args); }); } }, callback); } else { var results = {}; eachfn.each(_keys(tasks), function (k, callback) { tasks[k](function (err) { var args = _baseSlice(arguments, 1); if (args.length <= 1) { args = args[0]; } results[k] = args; callback(err); }); }, function (err) { callback(err, results); }); } }; async.parallel = function (tasks, callback) { _parallel({ map: async.map, each: async.each }, tasks, callback); }; async.parallelLimit = function(tasks, limit, callback) { _parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback); }; async.series = function (tasks, callback) { callback = callback || noop; if (_isArray(tasks)) { async.mapSeries(tasks, function (fn, callback) { if (fn) { fn(function (err) { var args = _baseSlice(arguments, 1); if (args.length <= 1) { args = args[0]; } callback.call(null, err, args); }); } }, callback); } else { var results = {}; async.eachSeries(_keys(tasks), function (k, callback) { tasks[k](function (err) { var args = _baseSlice(arguments, 1); if (args.length <= 1) { args = args[0]; } results[k] = args; callback(err); }); }, function (err) { callback(err, results); }); } }; async.iterator = function (tasks) { var makeCallback = function (index) { var fn = function () { if (tasks.length) { tasks[index].apply(null, arguments); } return fn.next(); }; fn.next = function () { return (index < tasks.length - 1) ? makeCallback(index + 1): null; }; return fn; }; return makeCallback(0); }; async.apply = function (fn) { var args = _baseSlice(arguments, 1); return function () { return fn.apply( null, args.concat(_baseSlice(arguments)) ); }; }; var _concat = function (eachfn, arr, fn, callback) { var r = []; eachfn(arr, function (x, cb) { fn(x, function (err, y) { r = r.concat(y || []); cb(err); }); }, function (err) { callback(err, r); }); }; async.concat = doParallel(_concat); async.concatSeries = doSeries(_concat); async.whilst = function (test, iterator, callback) { if (test()) { iterator(function (err) { if (err) { return callback(err); } async.whilst(test, iterator, callback); }); } else { callback(); } }; async.doWhilst = function (iterator, test, callback) { iterator(function (err) { if (err) { return callback(err); } var args = _baseSlice(arguments, 1); if (test.apply(null, args)) { async.doWhilst(iterator, test, callback); } else { callback(); } }); }; async.until = function (test, iterator, callback) { if (!test()) { iterator(function (err) { if (err) { return callback(err); } async.until(test, iterator, callback); }); } else { callback(); } }; async.doUntil = function (iterator, test, callback) { iterator(function (err) { if (err) { return callback(err); } var args = _baseSlice(arguments, 1); if (!test.apply(null, args)) { async.doUntil(iterator, test, callback); } else { callback(); } }); }; async.queue = function (worker, concurrency) { if (concurrency === undefined) { concurrency = 1; } else if(concurrency === 0) { throw new Error('Concurrency must not be zero'); } function _insert(q, data, pos, callback) { if (!q.started){ q.started = true; } if (!_isArray(data)) { data = [data]; } if(data.length === 0) { // call drain immediately if there are no tasks return async.setImmediate(function() { if (q.drain) { q.drain(); } }); } _each(data, function(task) { var item = { data: task, callback: typeof callback === 'function' ? callback : null }; if (pos) { q.tasks.unshift(item); } else { q.tasks.push(item); } if (q.saturated && q.tasks.length === q.concurrency) { q.saturated(); } async.setImmediate(q.process); }); } var workers = 0; var q = { tasks: [], concurrency: concurrency, saturated: null, empty: null, drain: null, started: false, paused: false, push: function (data, callback) { _insert(q, data, false, callback); }, kill: function () { q.drain = null; q.tasks = []; }, unshift: function (data, callback) { _insert(q, data, true, callback); }, process: function () { if (!q.paused && workers < q.concurrency && q.tasks.length) { var task = q.tasks.shift(); if (q.empty && q.tasks.length === 0) { q.empty(); } workers += 1; var next = function () { workers -= 1; if (task.callback) { task.callback.apply(task, arguments); } if (q.drain && q.tasks.length + workers === 0) { q.drain(); } q.process(); }; var cb = only_once(next); worker(task.data, cb); } }, length: function () { return q.tasks.length; }, running: function () { return workers; }, idle: function() { return q.tasks.length + workers === 0; }, pause: function () { if (q.paused === true) { return; } q.paused = true; }, resume: function () { if (q.paused === false) { return; } q.paused = false; var resumeCount = Math.min(q.concurrency, q.tasks.length); // Need to call q.process once per concurrent // worker to preserve full concurrency after pause for (var w = 1; w <= resumeCount; w++) { async.setImmediate(q.process); } } }; return q; }; async.priorityQueue = function (worker, concurrency) { function _compareTasks(a, b){ return a.priority - b.priority; } function _binarySearch(sequence, item, compare) { var beg = -1, end = sequence.length - 1; while (beg < end) { var mid = beg + ((end - beg + 1) >>> 1); if (compare(item, sequence[mid]) >= 0) { beg = mid; } else { end = mid - 1; } } return beg; } function _insert(q, data, priority, callback) { if (!q.started){ q.started = true; } if (!_isArray(data)) { data = [data]; } if(data.length === 0) { // call drain immediately if there are no tasks return async.setImmediate(function() { if (q.drain) { q.drain(); } }); } _each(data, function(task) { var item = { data: task, priority: priority, callback: typeof callback === 'function' ? callback : null }; q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item); if (q.saturated && q.tasks.length === q.concurrency) { q.saturated(); } async.setImmediate(q.process); }); } // Start with a normal queue var q = async.queue(worker, concurrency); // Override push to accept second parameter representing priority q.push = function (data, priority, callback) { _insert(q, data, priority, callback); }; // Remove unshift function delete q.unshift; return q; }; async.cargo = function (worker, payload) { var working = false, tasks = []; var cargo = { tasks: tasks, payload: payload, saturated: null, empty: null, drain: null, drained: true, push: function (data, callback) { if (!_isArray(data)) { data = [data]; } _each(data, function(task) { tasks.push({ data: task, callback: typeof callback === 'function' ? callback : null }); cargo.drained = false; if (cargo.saturated && tasks.length === payload) { cargo.saturated(); } }); async.setImmediate(cargo.process); }, process: function process() { if (working) return; if (tasks.length === 0) { if(cargo.drain && !cargo.drained) cargo.drain(); cargo.drained = true; return; } var ts = typeof payload === 'number' ? tasks.splice(0, payload) : tasks.splice(0, tasks.length); var ds = _map(ts, function (task) { return task.data; }); if(cargo.empty) cargo.empty(); working = true; worker(ds, function () { working = false; var args = arguments; _each(ts, function (data) { if (data.callback) { data.callback.apply(null, args); } }); process(); }); }, length: function () { return tasks.length; }, running: function () { return working; } }; return cargo; }; var _console_fn = function (name) { return function (fn) { var args = _baseSlice(arguments, 1); fn.apply(null, args.concat([function (err) { var args = _baseSlice(arguments, 1); if (typeof console !== 'undefined') { if (err) { if (console.error) { console.error(err); } } else if (console[name]) { _each(args, function (x) { console[name](x); }); } } }])); }; }; async.log = _console_fn('log'); async.dir = _console_fn('dir'); /*async.info = _console_fn('info'); async.warn = _console_fn('warn'); async.error = _console_fn('error');*/ async.memoize = function (fn, hasher) { var memo = {}; var queues = {}; hasher = hasher || function (x) { return x; }; var memoized = function () { var args = _baseSlice(arguments); var callback = args.pop(); var key = hasher.apply(null, args); if (key in memo) { async.nextTick(function () { callback.apply(null, memo[key]); }); } else if (key in queues) { queues[key].push(callback); } else { queues[key] = [callback]; fn.apply(null, args.concat([function () { memo[key] = _baseSlice(arguments); var q = queues[key]; delete queues[key]; for (var i = 0, l = q.length; i < l; i++) { q[i].apply(null, arguments); } }])); } }; memoized.memo = memo; memoized.unmemoized = fn; return memoized; }; async.unmemoize = function (fn) { return function () { return (fn.unmemoized || fn).apply(null, arguments); }; }; async.times = function (count, iterator, callback) { var counter = []; for (var i = 0; i < count; i++) { counter.push(i); } return async.map(counter, iterator, callback); }; async.timesSeries = function (count, iterator, callback) { var counter = []; for (var i = 0; i < count; i++) { counter.push(i); } return async.mapSeries(counter, iterator, callback); }; async.seq = function (/* functions... */) { var fns = arguments; return function () { var that = this; var args = _baseSlice(arguments); var callback = args.pop(); async.reduce(fns, args, function (newargs, fn, cb) { fn.apply(that, newargs.concat([function () { var err = arguments[0]; var nextargs = _baseSlice(arguments, 1); cb(err, nextargs); }])); }, function (err, results) { callback.apply(that, [err].concat(results)); }); }; }; async.compose = function (/* functions... */) { return async.seq.apply(null, Array.prototype.reverse.call(arguments)); }; var _applyEach = function (eachfn, fns /*args...*/) { var go = function () { var that = this; var args = _baseSlice(arguments); var callback = args.pop(); return eachfn(fns, function (fn, cb) { fn.apply(that, args.concat([cb])); }, callback); }; if (arguments.length > 2) { var args = _baseSlice(arguments, 2); return go.apply(this, args); } else { return go; } }; async.applyEach = doParallel(_applyEach); async.applyEachSeries = doSeries(_applyEach); async.forever = function (fn, callback) { function next(err) { if (err) { if (callback) { return callback(err); } throw err; } fn(next); } next(); }; // Node.js if (typeof module !== 'undefined' && module.exports) { module.exports = async; } // AMD / RequireJS else if (typeof define !== 'undefined' && define.amd) { define([], function () { return async; }); } // included directly via passenger-5.0.30/resources/templates/standalone/cannot_write_to_dir.txt.erb000644 000765 000024 00000000715 12233035540 027715 0ustar00honglistaff000000 000000 Permission problems Phusion Passenger Standalone must be able to write to the following directory: <%= @dir %> But it can't do that, despite the fact that it's running as root. There's probably a permission problem, or (if you're running on a RedHat/CentOS-based Linux distribution) SELinux might be interfering. Please fix the permissions for the aforementioned directory and/or disable SELinux, and re-run Phusion Passenger Standalone. passenger-5.0.30/resources/templates/standalone/config.erb000644 000765 000024 00000006115 12233035540 024310 0ustar00honglistaff000000 000000 ########################################################################## # Passenger Standalone is built on the same technology that powers # Passenger for Nginx, so any configuration option supported by Passenger # for Nginx can be applied to Passenger Standalone as well. You can do # this by direct editing the Nginx configuration template that is used by # Passenger Standalone. # # This file is the original template. DO NOT EDIT THIS FILE DIRECTLY. # Instead, make a copy of this file and pass the `--nginx-config-template` # parameter to Passenger Standalone. # # Learn more about using the Nginx configuration template at: # https://www.phusionpassenger.com/library/config/standalone/intro.html#nginx-configuration-template # # *** NOTE *** # If you customize the template file, make sure you keep an eye on the # original template file and merge any changes. New Phusion Passenger # features may require changes to the template file. ############################################################## <%= include_passenger_internal_template('global.erb') %> worker_processes 1; events { worker_connections 4096; } http { <%= include_passenger_internal_template('http.erb', 4) %> ### BEGIN your own configuration options ### # This is a good place to put your own config # options. Note that your options must not # conflict with the ones Passenger already sets. # Learn more at: # https://www.phusionpassenger.com/library/config/standalone/intro.html#nginx-configuration-template ### END your own configuration options ### default_type application/octet-stream; types_hash_max_size 2048; server_names_hash_bucket_size 64; client_max_body_size 1024m; access_log off; keepalive_timeout 60; underscores_in_headers on; gzip on; gzip_comp_level 3; gzip_min_length 150; gzip_proxied any; gzip_types text/plain text/css text/json text/javascript application/javascript application/x-javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/xml font/opentype image/svg+xml text/xml; <% if @app_finder.multi_mode? %> # Default server entry for mass deployment mode. server { <%= include_passenger_internal_template('mass_deployment_default_server.erb', 12) %> } <% end %> <% for app in @apps %> server { <%= include_passenger_internal_template('server.erb', 8, true, binding) %> <%= include_passenger_internal_template('rails_asset_pipeline.erb', 8, false) %> ### BEGIN your own configuration options ### # This is a good place to put your own config # options. Note that your options must not # conflict with the ones Passenger already sets. # Learn more at: # https://www.phusionpassenger.com/library/config/standalone/intro.html#nginx-configuration-template ### END your own configuration options ### } passenger_pre_start <%= listen_url(app) %>; <% end %> <%= include_passenger_internal_template('footer.erb', 4) %> } passenger-5.0.30/resources/templates/standalone/footer.erb000644 000765 000024 00000000037 12233035540 024336 0ustar00honglistaff000000 000000 <% # Reserved for future use %>passenger-5.0.30/resources/templates/standalone/global.erb000644 000765 000024 00000001120 12233035540 024272 0ustar00honglistaff000000 000000 master_process on; daemon on; error_log '<%= @options[:log_file] %>' <% if @options[:log_level] >= LVL_DEBUG %>info<% end %>; pid '<%= @options[:pid_file] %>'; <% if Process.euid == 0 %> <% if @options[:user] %> <%# Run workers as the given user. The master process will always run as root and will be able to bind to any port. %> user <%= @options[:user] %> <%= default_group_for(@options[:user]) %>; <% else %> <%# Prevent running Nginx workers as nobody. %> user <%= current_user %> <%= default_group_for(current_user) %>; <% end %> <% end %> passenger-5.0.30/resources/templates/standalone/http.erb000644 000765 000024 00000003273 12233035540 024024 0ustar00honglistaff000000 000000 log_format debug '[$time_local] $msec "$request" $status conn=$connection sent=$bytes_sent body_sent=$body_bytes_sent'; include '<%= PhusionPassenger.resources_dir %>/mime.types'; passenger_root '<%= PhusionPassenger.install_spec %>'; passenger_abort_on_startup_error on; passenger_ctl cleanup_pidfiles <%= serialize_strset("#{@working_dir}/temp_dir_toucher.pid") %>; passenger_ctl integration_mode standalone; passenger_ctl standalone_engine nginx; passenger_user_switching off; <% if @options[:ruby] %> passenger_ruby <%= @options[:ruby] %>; <% else %> passenger_ruby <%= PlatformInfo.ruby_command %>; <% end %> <% if @options[:user] %> passenger_user <%= @options[:user] %>; passenger_default_user <%= @options[:user] %>; passenger_analytics_log_user <%= @options[:user] %>; <% else %> passenger_user <%= current_user %>; passenger_default_user <%= current_user %>; passenger_analytics_log_user <%= current_user %>; <% end %> <%= nginx_http_option(:socket_backlog) %> <%= nginx_http_option(:python) %> <%= nginx_http_option(:nodejs) %> <%= nginx_http_option(:log_level) %> <%= nginx_http_option(:max_pool_size) %> <%= nginx_http_option(:pool_idle_time) %> <%= nginx_http_option(:max_preloader_idle_time) %> <%= nginx_http_option(:turbocaching) %> <%= nginx_http_option(:instance_registry_dir) %> <%= nginx_http_option(:data_buffer_dir) %> <%= nginx_http_option(:core_file_descriptor_ulimit) %> <%= nginx_http_option(:union_station_gateway_address) %> <%= nginx_http_option(:union_station_gateway_port) %> <%= nginx_http_option(:union_station_gateway_cert) %> <% @options[:ctls].each do |ctl| %> passenger_ctl '<%= ctl.split("=", 2)[0] %>' '<%= ctl.split("=", 2)[1] %>'; <% end %> passenger-5.0.30/resources/templates/standalone/mass_deployment_default_server.erb000644 000765 000024 00000000563 12233035540 031341 0ustar00honglistaff000000 000000 <% if @options[:ssl] %> <% if @options[:ssl_port] %> listen <%= nginx_listen_address %>; listen <%= nginx_listen_address_with_ssl_port %> ssl; <% else %> listen <%= nginx_listen_address %> ssl; <% end %> <% else %> listen <%= nginx_listen_address %>; <% end %> root '<%= PhusionPassenger.resources_dir %>/standalone_default_root'; passenger-5.0.30/resources/templates/standalone/rails_asset_pipeline.erb000644 000765 000024 00000000761 12233035540 027242 0ustar00honglistaff000000 000000 # Rails asset pipeline support. location ~ "^/assets/.+-([0-9a-f]{32}|[0-9a-f]{64})\..+" { error_page 490 = @static_asset; error_page 491 = @dynamic_request; recursive_error_pages on; if (-f $request_filename) { return 490; } if (!-f $request_filename) { return 491; } } location @static_asset { gzip_static on; expires max; add_header Cache-Control public; add_header ETag ""; } location @dynamic_request { passenger_enabled on; } passenger-5.0.30/resources/templates/standalone/server.erb000644 000765 000024 00000004773 12233035540 024361 0ustar00honglistaff000000 000000 server_name <%= app[:server_names].join(' ') %>; <% if app[:ssl] %> <% if app[:ssl_port] %> listen <%= nginx_listen_address(app) %>; listen <%= nginx_listen_address_with_ssl_port(app) %> ssl; <% else %> listen <%= nginx_listen_address(app) %> ssl; <% end %> ssl_certificate <%= app[:ssl_certificate] %>; ssl_certificate_key <%= app[:ssl_certificate_key] %>; <% else %> listen <%= nginx_listen_address(app) %>; <% end %> <% if app[:static_files_dir] %> root '<%= app[:static_files_dir] %>'; <% else %> root '<%= app[:root] %>/public'; <% end %> passenger_app_root '<%= app[:root] %>'; passenger_enabled on; <% if app[:union_station_key] %> union_station_support on; union_station_key <%= app[:union_station_key] %>; <% end %> <% app[:envvars].each_pair do |name, value| %> passenger_env_var '<%= name %>' '<%= value %>'; <% end %> <% if app[:concurrency_model] && app[:concurrency_model] != DEFAULT_CONCURRENCY_MODEL %> passenger_concurrency_model <%= app[:concurrency_model] %>; <% end %> <% if app[:thread_count] && app[:thread_count] != DEFAULT_APP_THREAD_COUNT %> passenger_thread_count <%= app[:thread_count] %>; <% end %> <%= nginx_option(app, :environment, :passenger_app_env) %> <%= nginx_option(app, :ruby) %> <%= nginx_option(app, :python) %> <%= nginx_option(app, :nodejs) %> <%= nginx_option(app, :spawn_method) %> <%= nginx_option(app, :app_type) %> <%= nginx_option(app, :startup_file) %> <%= nginx_option(app, :min_instances) %> <%= nginx_option(app, :max_request_queue_size) %> <%= nginx_option(app, :restart_dir) %> <%= nginx_option(app, :sticky_sessions) %> <%= nginx_option(app, :sticky_sessions_cookie_name) %> <%= nginx_option(app, :vary_turbocache_by_cookie) %> <%= nginx_option(app, :meteor_app_settings) %> <%= nginx_option(app, :load_shell_envvars) %> <%= nginx_option(app, :app_file_descriptor_ulimit) %> <%= nginx_option(app, :friendly_error_pages) %> <%= nginx_option(app, :abort_websockets_on_process_shutdown) %> <%= nginx_option(app, :force_max_concurrent_requests_per_process) %> <%= nginx_option(app, :rolling_restarts) %> <%= nginx_option(app, :resist_deployment_errors) %> <%= nginx_option(app, :memory_limit) %> <%= nginx_option(app, :max_request_time) %> <%= nginx_option(app, :debugger) %> <% app[:unlimited_concurrency_paths].each do |path| %> location ~ ^<%= path %>(/.*|$) { passenger_app_group_name '<%= app[:root] %>#unlimited_concurrency'; passenger_force_max_concurrent_requests_per_process 0; } <% end %> passenger-5.0.30/resources/templates/nginx/ask_for_extra_configure_flags.txt.erb000644 000765 000024 00000000442 12233035540 030715 0ustar00honglistaff000000 000000 Extra Nginx configure options If you want to pass extra arguments to the Nginx 'configure' script, then please specify them. If not, then specify nothing and press Enter. If you specify nothing then the 'configure' script will be run as follows: <%= @command %>passenger-5.0.30/resources/templates/nginx/cannot_write_to_dir.txt.erb000644 000765 000024 00000000660 12233035540 026707 0ustar00honglistaff000000 000000 Permission problems This installer must be able to write to the following directory: <%= @dir %> But it can't do that, despite the fact that it's running as root. There's probably a permission problem, or (if you're running on a RedHat/CentOS-based Linux distribution) SELinux might be interfering. Please fix the permissions for the aforementioned directory and/or disable SELinux, and re-run this installer.passenger-5.0.30/resources/templates/nginx/config_snippets.txt.erb000644 000765 000024 00000001052 12233035540 026041 0ustar00honglistaff000000 000000 Nginx with Passenger support was successfully installed. Please edit your Nginx configuration file<% if @config_file %> (probably <%= @config_file %>)<% end %>, and set the passenger_root and passenger_ruby configuration options in the 'http' block, like this: http { ... passenger_root <%= @passenger_root %>; passenger_ruby <%= @ruby %>; ... } After you (re)start Nginx, you are ready to deploy any number of web applications on Nginx. Press ENTER to continue. passenger-5.0.30/resources/templates/nginx/config_snippets_inserted.txt.erb000644 000765 000024 00000001155 12233035540 027742 0ustar00honglistaff000000 000000 Nginx with Passenger support was successfully installed. The Nginx configuration file (<%= @config_file %>) must contain the correct configuration options in order for Phusion Passenger to function correctly. This installer has already modified the configuration file for you! The following configuration snippet was inserted: http { ... passenger_root <%= @passenger_root %>; passenger_ruby <%= @ruby %>; ... } After you start Nginx, you are ready to deploy any number of Ruby on Rails applications on Nginx. Press ENTER to continue. passenger-5.0.30/resources/templates/nginx/confirm_extra_configure_flags.txt.erb000644 000765 000024 00000000166 12233035540 030731 0ustar00honglistaff000000 000000 Confirm configure flags The Nginx configure script will be run as follows: <%= @command %>passenger-5.0.30/resources/templates/nginx/deployment_example.txt.erb000644 000765 000024 00000000556 12233035540 026552 0ustar00honglistaff000000 000000 Deploying a web application To learn how to deploy a web app on Passenger, please follow the deployment guide: <%= @deployment_guide_url %> Enjoy Phusion Passenger, a product of Phusion (<%= @phusion_website %>) :-) <%= @passenger_website %> Phusion Passenger is a registered trademark of Hongli Lai & Ninh Bui. passenger-5.0.30/resources/templates/nginx/nginx_module_sources_not_available.txt.erb000644 000765 000024 00000000701 12233035540 031762 0ustar00honglistaff000000 000000 The necessary source files for compiling Nginx are not installed. <% if PhusionPassenger.packaging_method == "deb" %> Please install them with: sudo apt-get install <%= DEB_DEV_PACKAGE %> <% elsif PhusionPassenger.packaging_method == "rpm" %> Please install them with: sudo yum install <%= RPM_DEV_PACKAGE %>-<%= VERSION_STRING %> <% else %> Please ask your operating system vendor how to solve this problem. <% end %>passenger-5.0.30/resources/templates/nginx/other_nginx_installations_exist.txt.erb000644 000765 000024 00000001367 12233035540 031364 0ustar00honglistaff000000 000000 Wait! You are about to install a NEW Nginx installation! Your system already has an Nginx installation at: <%= @existing_binary %> If you continue using this installer, then it will install an an entirely new Nginx installation, at <%= @prefix %>/sbin/nginx. Are you sure this is what you want? "Wait, what? Do you mean you're not going to extend my existing Nginx with <%= PROGRAM_NAME %> support?" We're sorry, but no. Please read the following for more information, and for advise on how to deal with this situation: https://github.com/phusion/passenger/wiki/Why-can't-Phusion-Passenger-extend-my-existing-Nginx%3F Press Enter to continue installing, or Ctrl-C to abort.passenger-5.0.30/resources/templates/nginx/pcre_checksum_could_not_be_verified.txt.erb000644 000765 000024 00000000542 12233035540 032056 0ustar00honglistaff000000 000000 The PCRE checksum could not be verified Nginx requires PCRE for its rewrite module, so this installer will attempt to install Nginx without the rewrite module. If you want to make use of Nginx's rewrite module, please install PCRE manually by downloading it from: http://www.pcre.org/ Press ENTER to continue, or Ctrl-C to abort. passenger-5.0.30/resources/templates/nginx/pcre_could_not_be_downloaded.txt.erb000644 000765 000024 00000000526 12233035540 030521 0ustar00honglistaff000000 000000 PCRE could not be downloaded Nginx requires PCRE for its rewrite module, so this installer will attempt to install Nginx without the rewrite module. If you want to make use of Nginx's rewrite module, please install PCRE manually by downloading it from: http://www.pcre.org/ Press ENTER to continue, or Ctrl-C to abort.passenger-5.0.30/resources/templates/nginx/pcre_could_not_be_extracted.txt.erb000644 000765 000024 00000000550 12233035540 030361 0ustar00honglistaff000000 000000 The PCRE source tarball could not be extracted Nginx requires PCRE for its rewrite module, so this installer will attempt to install Nginx without the rewrite module. If you want to make use of Nginx's rewrite module, please install PCRE manually by downloading it from: http://www.pcre.org/ Press ENTER to continue, or Ctrl-C to abort.resources/templates/nginx/possible_solutions_for_compilation_and_installation_problems.txt.erb000644 000765 000024 00000000561 12233035540 037305 0ustar00honglistaff000000 000000 passenger-5.0.30It looks like something went wrong Please read our documentation for troubleshooting tips: https://www.phusionpassenger.com/library/install/nginx/ https://www.phusionpassenger.com/library/admin/nginx/troubleshooting/ If that doesn't help, please use our support facilities. We'll do our best to help you. <%= @support_url %>resources/templates/nginx/possible_solutions_for_download_and_extraction_problems.txt.erb000644 000765 000024 00000001623 12233035540 036255 0ustar00honglistaff000000 000000 passenger-5.0.30Unable to download or extract Nginx source tarball Possible reasons: * You are not connected to the Internet. If this is the case, then please connect to the Internet and try again. * The URL that this installer tried to download from no longer exists. If this is the case, then please download and extract the Nginx source manually. Then restart this installer, and choose option 2 ("No: I want to customize my Nginx installation"). * The download was corrupted. Please re-run this installer; it will re-download Nginx. If this still fails, then please download and extract the Nginx source manually. Then restart this installer, and choose option 2 ("No: I want to customize my Nginx installation"). * This installer is not able to write to /tmp. Please fix the permissions for this directory, or run this installer as root.passenger-5.0.30/resources/templates/nginx/query_download_and_install.txt.erb000644 000765 000024 00000002166 12233035540 030262 0ustar00honglistaff000000 000000 Automatically download and install Nginx? Nginx doesn't support loadable modules such as some other web servers do, so in order to install Nginx with Passenger support, it must be recompiled. Do you want this installer to download, compile and install Nginx for you? 1. Yes: download, compile and install Nginx for me. (recommended) The easiest way to get started. A stock Nginx <%= @nginx_version %> with Passenger support, but with no other additional third party modules, will be installed for you to a directory of your choice. 2. No: I want to customize my Nginx installation. (for advanced users) Choose this if you want to compile Nginx with more third party modules besides Passenger, or if you need to pass additional options to Nginx's 'configure' script. This installer will 1) ask you for the location of the Nginx source code, 2) run the 'configure' script according to your instructions, and 3) run 'make install'. Whichever you choose, if you already have an existing Nginx configuration file, then it will be preserved.passenger-5.0.30/resources/templates/nginx/welcome.txt.erb000644 000765 000024 00000001170 12233035540 024303 0ustar00honglistaff000000 000000 Welcome to the Phusion Passenger Nginx module installer, v<%= @version %>. This installer will guide you through the entire installation process. It shouldn't take more than 5 minutes in total. Here's what you can expect from the installation process: 1. This installer will compile and install Nginx with Passenger support. 2. You'll learn how to configure Passenger in Nginx. 3. You'll learn how to deploy a Ruby on Rails application. Don't worry if anything goes wrong. This installer will advise you on how to solve any problems. Press Enter to continue, or Ctrl-C to abort. passenger-5.0.30/resources/templates/installer_common/cannot_access_files_as_root.txt.erb000644 000765 000024 00000001330 12233035540 032603 0ustar00honglistaff000000 000000 Cannot access <%= @type || "files" %> This installer must be able to <%= @access || "write to" %> the following <%= @type || "files" %>: <%= @files.join("\n ") %> But it can't do that, despite the fact that it's running as root. There's probably a permission problem, an SELinux problem or some kind of operating system security problem. This installer tried its best to find out the exact reason for this failure, but unfortunately it couldn't figure it out, so it's up to you now. Please find out what's wrong with your system, fix the problems, and re-run this installer. If you don't know it either, please contact your operating system vendor for support, or consult your operating system's manual.passenger-5.0.30/resources/templates/installer_common/gem_install_permission_problems.txt.erb000644 000765 000024 00000001343 12233035540 033545 0ustar00honglistaff000000 000000 Permission problems in your gem directory detected Your gem directory is <%= Gem.dir %>, and the files within are supposed to be owned by the '<%= `whoami`.strip %>' user. However, it looks like you installed the Phusion Passenger gem with 'sudo', causing some files within to be owned by the 'root' user. This will cause permission problems later on. Please run the following command to fix the permissions: sudo chown -R <%= `whoami`.strip %> "<%= Gem.dir %>" Also, please remember to run 'gem install' commands without sudo in the future. Sudo is only necessary if the gem directory is located outside the home directory. After having fixed the permissions, please re-run this installer.passenger-5.0.30/resources/templates/installer_common/low_amount_of_memory_warning.txt.erb000644 000765 000024 00000001750 12233035540 033063 0ustar00honglistaff000000 000000 Your system does not have a lot of virtual memory Compiling <%= PROGRAM_NAME %> works best when you have at least <%= @required %> MB of virtual memory. However your system only has <%= @current %> MB of total virtual memory (<%= @ram %> MB RAM, <%= @swap %> MB swap). It is recommended that you temporarily add more swap space before proceeding. You can do it as follows: sudo dd if=/dev/zero of=/swap bs=1M count=1024 sudo mkswap /swap sudo swapon /swap See also https://wiki.archlinux.org/index.php/Swap for more information about the swap file on Linux. If you cannot activate a swap file (e.g. because you're on OpenVZ, or if you don't have root privileges) then you should install <%= PROGRAM_NAME %> through DEB/RPM packages. For more information, please refer to our installation documentation: <%= @install_doc_url %> Press Ctrl-C to abort this installer (recommended). Press Enter if you want to continue with installation anyway. passenger-5.0.30/resources/templates/installer_common/run_installer_as_root.txt.erb000644 000765 000024 00000001050 12233035540 031476 0ustar00honglistaff000000 000000 Permission problems <% if @desc %> This installer must be able to <%= @access || "write to" %> <%= @desc %>. <% else %> This installer must be able to <%= @access || "write to" %> the following directory: <%= @dir %> <% end -%> But it can't do that, because you're running the installer as <%= `whoami`.strip %>. Please give this installer root privileges, by re-running it with <%= @sudo %>: export ORIG_PATH="$PATH" <%= @sudo_s_e %> export PATH="$ORIG_PATH" <%= @ruby %> <%= @installer %>passenger-5.0.30/resources/templates/installer_common/world_inaccessible_directories.txt.erb000644 000765 000024 00000001227 12233035540 033324 0ustar00honglistaff000000 000000 Warning: some directories may be inaccessible by the web server! The web server typically runs under a separate user account for security reasons. That user must be able to access the <%= PROGRAM_NAME %> files. However, it appears that some directories have too strict permissions. This may prevent the web server user from accessing <%= PROGRAM_NAME %> files. It is recommended that you relax permissions as follows: <% for dir in @directories -%> sudo chmod o+x "<%= dir %>" <% end %> Press Ctrl-C to return to the shell. (Recommended) After relaxing permissions, re-run this installer. -OR- Press Enter to continue anyway.passenger-5.0.30/resources/templates/config/agent_compiler/000755 000765 000024 00000000000 12233035540 024453 5ustar00honglistaff000000 000000 passenger-5.0.30/resources/templates/config/installation_utils/000755 000765 000024 00000000000 12233035540 025404 5ustar00honglistaff000000 000000 passenger-5.0.30/resources/templates/config/nginx_engine_compiler/000755 000765 000024 00000000000 12233035540 026025 5ustar00honglistaff000000 000000 config/nginx_engine_compiler/possible_solutions_for_download_and_extraction_problems.txt.erb000644 000765 000024 00000001727 12233035540 042746 0ustar00honglistaff000000 000000 passenger-5.0.30/resources/templatesUnable to download or extract Nginx source tarball Possible reasons: * Your Internet connection is unstable. If this is the case, try re-running this installer with higher connection timeouts: passenger-config compile-nginx-engine --connect-timeout 60 --idle-timeout 60 * You are not connected to the Internet. If this is the case, then please connect to the Internet and try again. * The URL that this installer tried to download from no longer exists. Please file a bug report if this is the case: https://github.com/phusion/passenger/issues * The download was corrupted. Please try to re-run this installer: passenger-config compile-nginx-engine * Phusion Passenger Standalone is not able to write to /tmp. Please fix the permissions for this directory, or run this installer with root privileges: <%= PlatformInfo.ruby_sudo_command %> passenger-config compile-nginx-engine resources/templates/config/installation_utils/cannot_create_user_support_binaries_dir.txt.erb000644 000765 000024 00000001320 12233035540 037002 0ustar00honglistaff000000 000000 passenger-5.0.30This program must be able to create this directory: <%= @dir %> However, it was unable to do that because of incorrect permissions on parent directories. The parent directories must allow the <%= @myself %>b> user (which this program is running as) to create the directory that we want. Please fix the permissions on all parent directories, then re-run this program. If you don't know how to fix permissions, please search the Internet and learn about how Unix permissions works. These are good tutorials: * http://www.tutorialspoint.com/unix/unix-file-permission.htm * http://www.perlfect.com/articles/chmod.shtml * http://www.linux.com/learn/tutorials/309527-understanding-linux-file-permissionspassenger-5.0.30/resources/templates/config/installation_utils/download_tool_missing.txt.erb000644 000765 000024 00000000366 12233035540 033316 0ustar00honglistaff000000 000000 No download tool found This program requires a download tool, but none can be found on your system. Please install one as follows: <% @runner.missing_dependencies.each do |dep| %> - <%= dep.install_instructions %> <% end -%>resources/templates/config/installation_utils/passenger_not_installed_as_root.txt.erb000644 000765 000024 00000001131 12233035540 035265 0ustar00honglistaff000000 000000 passenger-5.0.30You should not run this program as root You are currently running this program as the root user. However, <%= PROGRAM_NAME %> was not installed by the root user. Instead, it was installed by the <%= @owner %> user. For this reason, this program has stopped itself, because proceeding as root can mess up all kinds of file permissions. Please re-run this program as the <%= @owner %> user, not as root. But if you are sure that you want to run this program as the root user (which this program doesn't recommend), then re-run this program using the --force parameter.config/installation_utils/support_binaries_dir_not_writable_despite_running_as_root.txt.erb000644 000765 000024 00000001161 12233035540 042636 0ustar00honglistaff000000 000000 passenger-5.0.30/resources/templatesThis program must be able to write to the following directory: <%= @dir %> But it can't do that, despite the fact that it's running as root. There's probably a permission problem, an SELinux problem or some kind of operating system security problem. This installer tried its best to find out the exact reason for this failure, but unfortunately it couldn't figure it out, so it's up to you now. Please find out what's wrong with your system, fix the problems, and re-run this program. If you don't know it either, please contact your operating system vendor for support, or consult your operating system's manual.passenger-5.0.30/resources/templates/config/installation_utils/unexpected_filesystem_problem.txt.erb000644 000765 000024 00000001073 12233035540 035045 0ustar00honglistaff000000 000000 An unexpected problem occurred This program must be able to create this directory: <%= @dir %> However, some completely unexpected filesystem error occurred. Unfortunately, this program can't figure out what's wrong, so it's up to you now. You can find the system error message below. Please find out what's wrong, fix the problems, and re-run this program. If you don't know it either, please contact your operating system vendor for support, or consult your operating system's manual. The error message is as follows: <%= @exception %>resources/templates/config/installation_utils/user_support_binaries_dir_not_writable.txt.erb000644 000765 000024 00000000000 12233035540 036660 0ustar00honglistaff000000 000000 passenger-5.0.30passenger-5.0.30/resources/templates/config/agent_compiler/confirm_enable_optimizations.txt.erb000644 000765 000024 00000000434 12233035540 033720 0ustar00honglistaff000000 000000 Compile the agent with optimizations? Compiling the agent with optimizations will make <%= PROGRAM_NAME %> faster, but it will take longer to compile and it requires at least 2 GB of memory. <% if @total_ram %>(You have <%= @total_ram %> GB memory.)<% end -%>passenger-5.0.30/resources/templates/apache2/apache_install_broken.txt.erb000644 000765 000024 00000001427 12233035540 027344 0ustar00honglistaff000000 000000 Your Apache installation might be broken You are about to install <%= PhusionPassenger::PROGRAM_NAME %> against the following Apache installation: apxs2: <%= @apxs2 %> However, this Apache installation appears to be broken, so this installer cannot continue. To find out why this installer thinks the above Apache installation is broken, run: export ORIG_PATH="$PATH:<%= @extra_paths %>" export APXS2="<%= @apxs2 %>" <%= @sudo_s_e %> export PATH="$ORIG_PATH" <%= @ruby %> <%= @passenger_config %> --detect-apache2 It is also possible that your system has multiple Apache installations, and that you are simply compiling <%= PROGRAM_NAME %> against the wrong Apache install. If this is the case, then the above command will also advise you about what to do.passenger-5.0.30/resources/templates/apache2/apache_must_be_compiled_with_compatible_mpm.txt.erb000644 000765 000024 00000001043 12233035540 033745 0ustar00honglistaff000000 000000 WARNING: Apache doesn't seem to be compiled with the 'prefork', 'worker' or 'event' MPM Phusion Passenger has only been tested on Apache with the 'prefork', the 'worker' and the 'event' MPM. Your Apache installation is compiled with the '<%= @current_mpm %>' MPM. We recommend you to abort this installer and to recompile Apache with either the 'prefork', the 'worker' or the 'event' MPM. Press Ctrl-C to abort this installer (recommended). Press Enter if you want to continue with installation anyway. passenger-5.0.30/resources/templates/apache2/config_snippets.txt.erb000644 000765 000024 00000000512 12233035540 026221 0ustar00honglistaff000000 000000 Almost there! Please edit your Apache configuration file, and add these lines: <%= @snippet.split("\n").join("\n ") %> After you restart Apache, you are ready to deploy any number of web applications on Apache, with a minimum amount of configuration! Press ENTER when you are done editing. passenger-5.0.30/resources/templates/apache2/deployment_example.txt.erb000644 000765 000024 00000000556 12233035540 026732 0ustar00honglistaff000000 000000 Deploying a web application To learn how to deploy a web app on Passenger, please follow the deployment guide: <%= @deployment_guide_url %> Enjoy Phusion Passenger, a product of Phusion (<%= @phusion_website %>) :-) <%= @passenger_website %> Phusion Passenger is a registered trademark of Hongli Lai & Ninh Bui. passenger-5.0.30/resources/templates/apache2/installing_against_a_different_apache.txt.erb000644 000765 000024 00000001030 12233035540 032524 0ustar00honglistaff000000 000000 <% @other_installs.each do |result| %> * To compile against Apache <%= result.version %> (<%= result.apxs2 %>): Re-run this installer with: --apxs2-path "<%= result.apxs2 %>" <% end %> You may also want to read the installation guide and the troubleshooting guide: https://www.phusionpassenger.com/library/install/apache/ https://www.phusionpassenger.com/library/admin/apache/troubleshooting/ If you keep having problems installing, please visit the following website for support: <%= SUPPORT_URL %>passenger-5.0.30/resources/templates/apache2/mpm_unknown.txt.erb000644 000765 000024 00000002056 12233035540 025404 0ustar00honglistaff000000 000000 WARNING: unable to autodetect Apache's MPM <%= PROGRAM_NAME %> has only been tested on Apache with the 'prefork', the 'worker' and the 'event' MPM. However, this installer was not able to automatically figure out the MPM that your Apache was compiled with. This installer tried to autodetect the MPM by running <%= @control_command %> -V, but that command failed for some reason. This may happen if your Apache installation is broken, or if your Apache configuration file has errors. Because at this point this installer is not sure whether your Apache installation is compatible with <%= SHORT_PROGRAM_NAME %>, we recommend that you abort this installer, run the above command and diagnose the problem before continuing. If you cannot figure out what is wrong, please contact the vendor that supplied your Apache installation (e.g. your operating system vendor) for support. Press Ctrl-C to abort this installer (so that you can double-check on things). Press Enter if you want to continue with installation anyway. passenger-5.0.30/resources/templates/apache2/multiple_apache_installations_detected.txt.erb000644 000765 000024 00000001021 12233035540 032764 0ustar00honglistaff000000 000000 Multiple Apache installations detected! You are about to install <%= PhusionPassenger::PROGRAM_NAME %> against the following Apache installation: Apache <%= @current.version %> apxs2 : <%= @current.apxs2 %> Executable: <%= @current.httpd %> However, <%= @other_installs.size %> other Apache installation(s) have been found on your system: <% @other_installs.each do |result| %> * Apache <%= result.version %> apxs2 : <%= result.apxs2 %> Executable: <%= result.httpd %> <% end %>passenger-5.0.30/resources/templates/apache2/notify_apache_module_installed.txt.erb000644 000765 000024 00000000210 12233035540 031237 0ustar00honglistaff000000 000000 The <%= PROGRAM_NAME %> Apache module is correctly installed :-) Press Enter to learn how to deploy a web app on Apache.resources/templates/apache2/possible_solutions_for_compilation_and_installation_problems.txt.erb000644 000765 000024 00000000563 12233035540 037467 0ustar00honglistaff000000 000000 passenger-5.0.30It looks like something went wrong Please read our documentation for troubleshooting tips: https://www.phusionpassenger.com/library/install/apache/ https://www.phusionpassenger.com/library/admin/apache/troubleshooting/ If that doesn't help, please use our support facilities. We'll do our best to help you. <%= @support_url %>passenger-5.0.30/resources/templates/apache2/present_choice_for_no_update_config.txt.erb000644 000765 000024 00000000546 12233035540 032261 0ustar00honglistaff000000 000000 (Tip: you can also re-run this installer with --no-update-config, to prevent it from automatically updating your Apache configuration files. This way, the installer will not need as many privileges. The installer will then tell you how you must update the config files. You will then have to edit the config files manually.)passenger-5.0.30/resources/templates/apache2/rpm_installation_recommended.txt.erb000644 000765 000024 00000001665 12233035540 030762 0ustar00honglistaff000000 000000 Installation through RPMs recommended It looks like you are on a Red Hat or CentOS operating system, with SELinux enabled. SELinux is a security mechanism for which special <%= SHORT_PROGRAM_NAME %>-specific configuration is required. We supply this configuration as part of our <%= SHORT_PROGRAM_NAME %> RPMs. However, <%= SHORT_PROGRAM_NAME %> is currently installed through <%= PhusionPassenger.packaging_method_description %> and does not include any SELinux configuration. Therefore, we recommend that you: 1. Uninstall your current <%= SHORT_PROGRAM_NAME %> install. 2. Reinstall <%= SHORT_PROGRAM_NAME %> through the RPMs that we provide: https://www.phusionpassenger.com/library/install/apache/yum_repo/ What would you like to do? Press Ctrl-C to exit this installer so that you can install RPMs (recommended) -OR- Press Enter to continue using this installer anywaypassenger-5.0.30/resources/templates/apache2/run_installer_as_root_for_apache_analysis.txt.erb000644 000765 000024 00000000617 12233035540 033516 0ustar00honglistaff000000 000000 Permission problems This installer must be able to analyze your Apache installation. But it can't do that, because you're running the installer as <%= `whoami`.strip %>. Please give this installer root privileges, by re-running it with <%= @sudo %>: export ORIG_PATH="$PATH" <%= @sudo_s_e %> export PATH="$ORIG_PATH" <%= @ruby %> <%= @installer %> passenger-5.0.30/resources/templates/apache2/welcome.txt.erb000644 000765 000024 00000001130 12233035540 024457 0ustar00honglistaff000000 000000 Welcome to the Phusion Passenger Apache 2 module installer, v<%= @version %>. This installer will guide you through the entire installation process. It shouldn't take more than 3 minutes in total. Here's what you can expect from the installation process: 1. The Apache 2 module will be installed for you. 2. You'll learn how to configure Apache. 3. You'll learn how to deploy a Ruby on Rails application. Don't worry if anything goes wrong. This installer will advise you on how to solve any problems. Press Enter to continue, or Ctrl-C to abort. passenger-5.0.30/resources/standalone_default_root/index.html000644 000765 000024 00000000060 12233035540 025110 0ustar00honglistaff000000 000000 There's no website configured on this host name.passenger-5.0.30/man/passenger-config.1000644 000765 000024 00000001641 12233035540 020277 0ustar00honglistaff000000 000000 .TH "passenger-config" "1" "2.0" "Phusion Passenger" "User Commands" .SH "NAME" .LP passenger\-config \- Show configuration settings for Phusion Passenger .SH "SYNOPSIS" .LP \fBpassenger\-config\fR \fI\-\-root\fR | \fI\-\-version\fR .SH "DESCRIPTION" .LP \fBpassenger\-config\fR shows the current configuration settings for Phusion Passenger. You need to select one of the following options. .SH "OPTIONS" .TP \fB\-\-root\fR Shows the Phusion Passenger root directory .TP \fB\-\-version\fR Shows the current Phusion Passenger version number .SH "SEE ALSO" .LP passenger\-memory\-stats(1), passenger\-stress\-test(1), passenger\-status(8) .LP User guide at https://www.phusionpassenger.com/documentation_and_support .SH "AUTHOR" .LP Phusion Passenger is written by Phusion (http://www.phusion.nl) .LP This manual page was written by Neil Wilson for the Ubuntu project (but may be used by others). passenger-5.0.30/man/passenger-memory-stats.8000644 000765 000024 00000003164 12233035540 021507 0ustar00honglistaff000000 000000 .TH "passenger-memory-stats" "8" "2.0" "Phusion Passenger" "Administration Commands" .SH "NAME" .LP passenger\-memory\-stats \- reports a snapshot of the Apache and Phusion Passenger memory statistcs .SH "SYNOPSIS" .LP \fBpassenger\-memory\-stats\fR .SH "DESCRIPTION" .LP \fBpassenger\-memory\-stats\fR allows you to easily analyze the real memory usage of Phusion Passenger and Apache. .LP Process inspection tools such as \fIps\fR and \fItop\fR are useful, but they rarely show the correct memory usage. The real memory usage is usually lower than what \fIps\fR and \fItop\fR report. .LP There are many technical reasons why this is so, but an explanation is beyond the scope of this page. We refer the interested reader to operating systems literature about virtual memory and copy\-on\-write. .LP When you run this tool the \fIPrivate\fR or \fIprivate dirty RSS\fR field shows the real memory usage of processes. .SH "ENVIRONMENT VARIABLES" .LP .TP \fBHTTPD\fR The full filename to the Apache executable. By default, Passenger will attempt to autodetect the Apache executable. If autodetection fails for whatever reason, then Apache processes will not be shown in the memory statistics. In that case, you may manually specify the location to the Apache executable using this environment variable. .SH "SEE ALSO" .LP passenger\-status(8), ps(1), top(1), .LP User guide at https://www.phusionpassenger.com/documentation_and_support .SH "AUTHOR" .LP Phusion Passenger is written by Phusion (http://www.phusion.nl) .LP This manual page was written by Neil Wilson for the Ubuntu project (but may be used by others). passenger-5.0.30/man/passenger-status.8000644 000765 000024 00000004013 12233035540 020360 0ustar00honglistaff000000 000000 .TH "passenger-status" "8" "2.0" "Phusion Passenger" "Adminstration Commands" .SH "NAME" .LP passenger\-status \- inspect Phusion Passenger's internal status .SH "SYNOPSIS" .LP \fBpassenger\-status\fR [\fIpid\fR] .SH "DESCRIPTION" .LP \fBpassenger\-status\fR looks at the current status of a Phusion Passenger installation. It will locate Phusion Passenger automatically if it is running and will give you a list of instances if there is more than one Phusion Passenger instance running. You can then select the instance by specifying the relevant PID on the command line. .LP The \fIgeneral information\fR section shows the following details: .TP max The maximum number of application instances that Phusion Passenger will spawn. This equals the value given for \fIPassengerMaxPoolSize\fR in the configuration. .TP count The number of application instances that are currently alive. This value is always less than or equal to \fImax\fR. .TP active The number of application instances that are currently processing requests. This value is always less than or equal to \fIcount\fR. .TP inactive The number of application instances that are currently not processing requests, i.e. are idle. Idle application instances will be shutdown after a while, as can be specified with \fIPassengerPoolIdleTime\fR in the configuration. The value of \fIinactive\fR equals \fIcount\fR \- \fIactive\fR. .LP The \fIapplications\fR section shows each application instance, which directory it belongs to. The \fIsessions\fR field shows how many HTTP client are currently being processed by that application instance. .SH "OPTIONS" .LP .TP \fBpid\fR The process ID of the Phusion Passenger instance you want to look at .SH "SEE ALSO" .LP passenger\-memory\-stats(8), ps(1), top(1) .LP User guide at https://www.phusionpassenger.com/documentation_and_support .SH "AUTHOR" .LP Phusion Passenger is written by Phusion (http://www.phusion.nl) .LP This manual page was written by Neil Wilson for the Ubuntu project (but may be used by others). passenger-5.0.30/doc/CloudLicensingConfiguration.html000644 000765 000024 00000110370 12233035540 023275 0ustar00honglistaff000000 000000 Cloud Licensing Configuration
Cloud Licensing Configuration

Phusion Passenger Enterprise cloud licensing configuration

This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/cloudlicensingconfiguration/

© Phusion

passenger-5.0.30/doc/CloudLicensingConfiguration.txt.md000644 000765 000024 00000000412 12233035540 023542 0ustar00honglistaff000000 000000 # Phusion Passenger Enterprise cloud licensing configuration This documentation has moved. Please visit [https://www.phusionpassenger.com/library/config/cloud_licensing_configuration/](https://www.phusionpassenger.com/library/config/cloud_licensing_configuration/) passenger-5.0.30/doc/CodingTipsAndPitfalls.md000644 000765 000024 00000005665 12233035540 021476 0ustar00honglistaff000000 000000 # Coding tips and common coding pitfalls ## Prefer shared_ptrs You should prefer `shared_ptr`s over raw pointers because they make memory leaks and memory errors less likely. There are only very limited cases in which raw pointers are justified, e.g. optimizations in very hot code paths. ### Unexpected shared_ptr invalidation Suppose you have a function which takes a reference to a shared_ptr, like this: void foo(const shared_ptr &client) { performNetworking(); log(client->getAddress()); } You might think that `client` is never going to be destroyed during the scope of the function, right? Wrong. If `client` came from a non-local variable, and your function calls another function (perhaps indirectly) which resets that variable, then `client` is going to be invalidated. Consider a contrived example like this: static shared_ptr client; void performNetworking() { if (write(...) == -1) { // An error occurred, disconnect client. client.reset(); } } More realistically, this kind of bug is likely to occur if `foo()` calls some function which invokes a user-defined callback, which unreferences the original object under some conditions. Thus, if your code might call an arbitrary user-defined function, you should increase the reference count locally: void foo(const shared_ptr &client) { shared_ptr extraReference = client; performNetworking(); log(client->getAddress()); } A variant of this pitfall is documented under "Event loop callbacks". ## Event loop callbacks ### Obtain extra shared_ptr reference on `this` If your event loop callback ever calls user-defined functions, either explicitly or implicitly, you should obtain a `shared_ptr` to your `this` object. This is because the user-defined function could call something that would free your object. The problem is documented in detail in "Unexpected shared_ptr invalidation". Your class should derive from `boost::enable_shared_from_this` to make it easy for you to obtain a `shared_ptr` to yourself. void callback(ev::io &io, int revents) { shared_ptr extraReference = shared_from_this(); ... } ### Exceptions Event loop callbacks should catch expected exceptions. Letting an exception pass will crash the program. When system call failure simulation is turned on, the code can throw arbitrary SystemExceptions, so beware of those. ## Thread interruption and RAII destructors When using thread interruption, make sure that RAII destructors are non-interruptable. If your code is interrupted and then a `thread_interrupted` is thrown, make sure that RAII destructors don't check for the interruption flag and then throw `thread_interrupted` again. This not only fails to clean things up properly, but also confuses the exception system, resulting in strange errors such as "terminate called without an active exception". passenger-5.0.30/doc/DebuggingAndStressTesting.md000644 000765 000024 00000011721 12233035540 022357 0ustar00honglistaff000000 000000 # Debugging and Stress Testing Phusion Passenger This guide tells you: * How to debug Phusion Passenger in case of any serious problems, e.g. crashes and mysterious connection problems. * How to stress test Phusion Passenger. ## Process output All PassengerAgent processes as well as all spawned application processes have their stdout and stderr redirected to the _global web server error log_ (that is, _not_ the per-virtual host error log). This is usually '/var/log/apache2/error.log' or '/var/log/nginx/error.log'. Note that in case of Nginx, Phusion Passenger prints to the error log specified in the server context, not the "http" context. If the server context does not contain an `error_log` directive then the default error log location will be used. The default location depends on how Nginx is configured during compilation, but it is usually either '$PREFIX/logs/error.log' or '/var/log/nginx/error.log'. For example, if your Nginx configuration looks like this: worker_processes 2; http { error_log /home/nginx/error.log; ... } then Phusion Passenger will print to the default error log location, *not* '/home/nginx/error.log'! ## Crash behavior Whenever a Phusion Passenger agent process crashes because of a signal (SIGABRT, SIGBUS, SIGSEGV and similar signals), its default behavior is to attempt to write a crash report to its stderr. This crash report contains: * A simple libc-level backtrace of the current thread. This backtrace may or may not correspond to the thread that caused the crash. * A detailed backtrace report, covering all threads. This report even contains the values of variables on the stack. The report is obtained through the [crash-watch](https://github.com/FooBarWidget/crash-watch) tool so you must have it installed. Crash-watch in turn requires gdb, which must also be installed. * Agent-specific diagnostics information. For example the Passenger core will report the status of its process pool and its connected clients. You can change the crash behavior with the following environment variables: * `PASSENGER_ABORT_HANDLER` (default: true) - Whether agent processes should install their crash handlers. When disabled, crashes will be handled by the default signal handler, meaning that they will likely just crash without dumping any crash report. * `PASSENGER_DUMP_WITH_CRASH_WATCH` (default: true) - Whether [crash-watch](https://github.com/FooBarWidget/crash-watch) should be used to obtain detailed backtraces. * `PASSENGER_BEEP_ON_ABORT` (default: false) - Whether agent processes should beep when they crash. This is useful during development, e.g. when you're stress testing the system and want to be notified when a crash occurs. On OS X, it will execute `osascript -e "beep 2"` to trigger the beep. On Linux it will execute the `beep` command. * `PASSENGER_STOP_ON_ABORT` (default: false) - When enabled, causes agent processes to stop themselves on crash, by raising SIGSTOP. This gives you the opportunity to attach gdb on them. ## Behavior logging Increase PassengerLogLevel to print more debugging messages. ## Debugging with AddressSanitizer [AddressSanitizer](http://code.google.com/p/address-sanitizer/) is an excellent tool created by Google to detect memory problems in C and C++ programs. It is for example used for detecting memory errors in Google Chrome. Unlike [Valgrind](http://www.valgrind.org/), which is an x86 emulator and makes everything 100 times slower, AddressSanitizer's performance penalty is only about 10%. Recompile Phusion Passenger with the environment variable `USE_ASAN=1` to enable support for AddressSanitizer. ## Simulating system call failures Error conditions are sometimes hard to test. Things like network errors are usually hard to simulate using real equipment. In order to facilitate with error testing, we've developed a system call failure simulation framework, inspired by sqlite's failure test suite. You specify which system call errors should be simulated, and with what probability they should occur. By running normal tests multiple times you can see how Phusion Passenger behaves under these simulated error conditions. To enable, set the environment variable `PASSENGER_SIMULATE_SYSCALL_FAILURES`. The format is: program_name1=error1:probability1,error2:probability2,...;program_name2=... `program_nameN` specifies the name of the Phusion Passenger process for which system call failure simulation should be enabled. This is followed by a list of system call `errno` names and the respective probabilities (between 0 and 1). For example: export PASSENGER_SIMULATE_SYSCALL_FAILURES='PassengerAgent watchdog=ENOSPC:0.01;PassengerAgent core=EMFILE:0.001,ECONNREFUSED:0.02' This will enable system call failure simulation only for the watchdog and the core, but not for the UstRouter. All system calls in the watchdog will have a 1% probability of throwing ENOSPC. All system calls in the HTTP server will have a 0.1% probability of throwing EMFILE, and a 2% probability of throwing ECONNREFUSED. passenger-5.0.30/doc/Design and Architecture.html000644 000765 000024 00000610627 12233035540 022214 0ustar00honglistaff000000 000000 Phusion Passenger Design and Architecture

1. Introduction

1.1. Web application models and the role of the application server

Before we describe Phusion Passenger, it is important to understand how typical web applications work from the viewpoint of someone who wants to connect a web application to a web server.

A typical, isolated, web application accepts an HTTP request from some I/O channel, processes it internally, and outputs an HTTP response, which is sent back to the client. This is done in a loop, until the application is commanded to exit. This does not necessarily mean that the web application speaks HTTP directly: it just means that the web application accepts some kind of representation of an HTTP request.

Architecture of a typical web application in isolation

Some web applications are directly accessible through the HTTP protocol, while others are not. It depends on the language and framework that the web application is built on. For example, Ruby (Rack/Rails) and Python (WSGI) web applications are typically not directly accessible through the HTTP protocol. On the other hand, Node.js web applications do tend to be accessible through the HTTP protocol. The reasons for this are historical, but they’re outside the scope of this guide.

1.1.1. Common models

Here are some common models that are in use:

  1. The web application is contained in an application server. This application server may or may not be able to contain multiple web applications. The administrator then connects the application server to a web server through some kind of protocol. This protocol may be HTTP, FastCGI, SCGI, AJP or whatever. The web server dispatches (forwards) requests to the application server, which in turn dispatches requests to the correct web application, in a format that the web application understands. Conversely, HTTP responses outputted by the web application are sent to the application server, which in turn sends them to the web server, and eventually to the HTTP client.

    Typical examples of such a model:

    • A J2EE application, contained in the Tomcat application server, reverse proxied behind the Apache web server. Tomcat can contain multiple web applications in a single Tomcat instance.

    • Most Ruby application servers besides Phusion Passenger (Thin, Unicorn, Goliath, etc). These application servers can only contain a single Ruby web application per instance. They load the web application into their own process and are put behind a web server (Apache, Nginx) in a reverse proxy setup.

    • Green Unicorn, the Python (WSGI) application server, behind a reverse proxy setup.

    • PHP web applications spawned by the FastCGI Process Manager (FPM), behind an Nginx reverse proxy setup.

  2. The web application is contained directly in a web server. In this case, the web server acts like an application server. Typical examples include:

    • PHP web applications running on Apache through mod_php.

    • Python (WSGI) web applications running on Apache through mod_uwsgi or mod_python.

    Note that this does not necessarily mean that the web application is run inside the same process as the web server: it just means that the web server manages applications. In case of mod_php, PHP runs directly inside the Apache worker processes, but in case of mod_uwsgi the Python processes can be configured to run out-of-process.

    Phusion Passenger for Apache and Phusion Passenger for Nginx implement this model, and run applications outside the web server process.

  3. The web application is a web server, and can accept HTTP requests directly. Examples of this model:

    • Almost all Node.js and Meteor JS web applications.

    • The Trac bug tracking software, running in its standalone server.

    In most setups, the administrator puts them in a reverse proxy configuration, behind a real web server such as Apache or Nginx, instead of letting them accept HTTP requests directly.

    Phusion Passenger Standalone implements this model. However, you can expose Phusion Passenger Standalone directly to the Internet because it uses Nginx internally.

  4. The web application does not speak HTTP directly, but is connected directly to the web server through some communication adapter. CGI, FastCGI and SCGI are good examples of this.

The above models cover how nearly all web applications work, whether they’re based on PHP, Django, J2EE, ASP.NET, Ruby on Rails, or whatever. Note that all of these models provide the same functionality, i.e. no model can do something that a different model can’t. The critical reader will notice that all of these models are identical to the one described in the first diagram, if the combination of web servers, application servers, web applications etc. are considered to be a single entity; a black box if you will.

It should also be noted that these models do not enforce any particular I/O processing implementation. The web servers, application servers, web applications, etc. could process I/O serially (i.e. one request at a time), could multiplex I/O with a single thread (e.g. by using select(2) or poll(2)) or it could process I/O with multiple threads and/or multiple processes. It depends on the implementation.

Of course, there are many variations possible. For example, load balancers could be used. But that is outside the scope of this document.

1.1.2. The rationale behind reverse proxying

As you’ve seen, administrators often put the web application or its application server behind a real web server in a reverse proxy setup, even when the web app/app server already speaks HTTP. This is because implementing HTTP in a proper, secure way involves more than just speaking the protocol. The public Internet is a hostile environment where clients can send any arbitrary data and can exhibit any arbitrary I/O patterns. If you don’t properly implement I/O handling, then you could open yourself either to parser vulnerabilities, or denial-of-service attacks.

Web servers like Apache and Nginx have already implemented world-class I/O and connection handling code and it would be a waste to reinvent their wheel. In the end, putting the application in a reverse proxying setup often makes the whole system more robust and and more secure. This is the reason why it’s considered good practice.

A typical problem involves dealing with slow clients. These clients may send HTTP requests slowly and read HTTP responses slowly, perhaps taking many seconds to complete their work. A naive single-threaded HTTP server implementation that reads an HTTP requests, processes, and sends the HTTP response in a loop may end up spending so much time waiting for I/O that spends very little time doing actual work. Worse: suppose that the client is malicious, just leaves the socket open and never reads the HTTP response, then the server will spend forever waiting for the client, not being able to handle any more requests. A real-world attack based on this principle is Slowloris.

An example of a naive HTTP server implementation
while true
    client = accept_next_client()
    request = read_http_request(client)
    response = process_request(request)
    send_http_response(client, response)
end

There are many ways to solve this problem. One could use one thread per client, one could implement I/O timeouts, one could use an evented I/O architecture, one could have a dedicated I/O thread or process buffer requests and responses. The point is, implementing all this properly is non-trivial. Instead of reimplementing these over and over in each application server, it’s better to let a real web server deal with all the details and let the application server and the web application do what they’re best at: their own core business logic.

1.2. Phusion Passenger architecture overview

An overview of Phusion Passenger’s architecture

Phusion Passenger is not a single, monolithic entity. Instead, it consists of multiple components and processes that work together. Part of the reason why Phusion Passenger is split like this, is because it’s technically necessary (no other way to implement it). But another part of the reason is stability and robustness. Individual components can crash and can be restarted independently from each other. If we were to put everything inside a single process, then a crash will take down all of Phusion Passenger.

Thus, if the Passenger core crashes, or if an application process crashes, they can both be restarted without affecting the web server’s stability.

1.2.1. Web server module

When an HTTP client sends a request, it is received by the web server (Nginx or Apache). Both Apache and Nginx can be extended with modules. Phusion Passenger provides such a module. The module is loaded into Nginx/Apache. It checks whether the request should be handled by a Phusion Passenger-served web application, and if so, forwards the request to the Passenger core. The internal wire protocol used during this forwarding, is a modified version of SCGI.

The Nginx module and Apache module have an entirely different code base. Their code bases are in src/nginx_module and src/apache2_module, respectively. Both modules are relatively small because they outsource most logic to the Passenger core, and because they utilize a common library (src/cxx_supportlib). This allows us to support both Nginx and Apache without having to write a lot of things twice.

1.2.2. Passenger core

The Passenger core is where most of the processing is done. The core keeps track of which application processes currently exist, and using load balancing rules, determines which process a request should be forwarded to. The core also takes care of application spawning: if it determines that having more application processes is necessary or beneficial, then it will make that happen. Process spawning is subject to user-configured limits: the core will never spawn more processes than a user-configured maximum.

The core also has monitoring and statistics gathering capabilities. It constantly keeps track of applications' memory usage, how many requests they’ve handled, etc. This information can later be queried from administration tools. And if an application process crashes, the core restarts it.

The core is by far the largest and most complex part of the system, but it is itself composed of several smaller subsystems. Most of the core architecture chapter is devoted to describing the core.

1.2.3. UstRouter

The core cooperates with the UstRouter. This latter is responsible for sending data to Union Station, a monitoring web service. If you didn’t explicitly tell Phusion Passenger to send data to Union Station, then the UstRouter sits idle and does not consume resources.

1.2.4. Watchdog

The core and the UstRouter contain complex logic, so they could contain bugs which could crash them. So as a safety measure, they are both monitored by the Watchdog. If either of them crash, they are restarted by the Watchdog. This setup seeks to ensure that the system stays up, no matter what.

You might now wonder: what happens if the Watchdog crashes? Shouldn’t the Watchdog be monitored by another Watchdog? We’ve contemplated this possibility, but the Watchdog is very simple, and since 2012 we haven’t seen a single report of the Watchdog crashing, nor have we been able to make it crash since that time. So, for the sake of keeping the codebase as simple as possible, we’ve chosen not to introduce multiple Watchdogs.

1.2.5. Command line tools

Finally, there is an array of command line tools which support Phusion Passenger. The installers — passenger-install-*-module — are responsible for installing Phusion Passenger. There are administrative tools such as passenger-status and passenger-memory-stats. And many more. Some of these tools may communicate with one of the agents. For example, the passenger-status queries the core for information that the core has collected. How this communication is done, is described in Instance state and communication.

1.2.6. Passenger Standalone

You might have noticed that Phusion Passenger Standalone is not part of the diagram. So how does it fit into the architecture? Well, Phusion Passenger Standalone is actually just Phusion Passenger for Nginx. The passenger start command simply sets up a modified and stripped-down Nginx web server (which we call the WebHelper) with the Phusion Passenger Nginx module loaded.

1.3. Build system and source tree

Phusion Passenger is written mostly in C++ and Ruby. The web server modules, core, UstRouter and Watchdog are written in C++. Most command line tools are written in Ruby. You can find each component here:

  • The web server modules can be found in src/apache2_module and src/nginx_module.

  • The Passenger core, UstRouter and Watchdog can be found in src/agent.

  • The command line tools can be found in bin, with some parts of their code in lib.

More information can be found in the Contributors Guide. This guide also teaches you how to compile Phusion Passenger.

2. Initialization

Startup sequence

Phusion Passenger initializes as follows.

  1. First, the user begins with starting the web server. This for example be done by running sudo service apache2 start or sudo service nginx start. Or perhaps the web server is configured to be automatically started by the OS, in which case the user doesn’t have to do anything. In case of Phusion Passenger Standalone, the user runs passenger start which in turn starts Nginx.

  2. The Phusion Passenger module inside Nginx/Apache proceeds with starting the Watchdog. This is implemented in:

    • src/nginx_module/ngx_http_passenger_module.c, function start_watchdog().

    • src/apache2_module/Hooks.cpp, in the constructor for the Hooks class.

    • src/cxx_supportlib/WatchdogLauncher.h and WatchdogLauncher.cpp. Most of the logic pertaining starting the Watchdog is in this file.

  3. The Watchdog first initializes a "instance directory", which is a temporary directory containing files that will be used during the life time of this Phusion Passenger instance. For example, the directory contains Unix domain socket files, so that the different Phusion Passenger processes can communicate with each other. The Watchdog is implemented in src/agent/Watchdog/Main.cpp.

  4. The Watchdog starts the Passenger core and the UstRouter simultaneously. Each performs its own initialization.

  5. When the core is done initializing, it will send a message back to the Watchdog saying that it’s done. The UstRouter does something similar. When the Watchdog has received both acknowledgment messages, it finishes initialization. If the Watchdog notices that one of the agents have exited without sending an acknowledgment message, then it enters an error state.

  6. The Watchdog reports successful startup back to the Phusion Passenger module that’s running inside Nginx/Apache. Or, if initialization didn’t success, the Watchdog reports back an error. The Phusion Passenger module inside Nginx/Apache then logs the error.

After initialization, Phusion Passenger is ready to receive and to process requests.

3. Passenger core architecture

Passenger core architecture

The Passenger core consists of two subsystems. One is the request handling subsystem. The other is the ApplicationPool subsystem, which performs the bulk of process management. The core also uses a number of support libraries. The largest third-party support libraries are shown in the diagram. Many more — internal — support libraries are used, but they’re omitted from the diagram. You can find these internal support libraries in the directory src/cxx_supportlib/Utils.

3.1. Request handling

Recall that requests are first received from the web server. The web server serializes the request into a slightly modified version of the SCGI format, and sends it to the core’s RequestHandler. The RequestHandler performs some work, and eventually sends back a regular HTTP response. The web server parses the RequestHandler response, and sends a response to the original HTTP client.

The RequestHandler listens on a Unix domain socket file. This Unix domain socket file is called request, and is located in the instance directory.

3.1.1. One client per request

The web server creates a new connection to the core on every request. Thus, from the viewpoint of the RequestHandler, its client is the web server. Every time a client connects (i.e. a new request is forwarded), the RequestHandler creates a new Client object which represents that request. All request-specific state is stored inside the Client. After the RequestHandler is done processing a request, it closes the client socket.

Note that in the diagram, a Client has a 0..1 association with RequestHandler. That’s because when a Client is disconnected, the pointer to the associated RequestHandler is set to NULL. There might be background operations left which still have a pointer to the Client. As soon as those background operations finish, they check whether the Client has a valid pointer to the RequestHandler. If so, they commit their work; if not, they discard their work. The Client is destroyed when all its associated background operations have finished.

3.1.2. Forwarding to the application

The RequestHandler asynchronously asks the ApplicationPool subsystem to select an appropriate application process to handle this request. The ApplicationPool checks whether there is an appropriate process, and if not, tries to spawn one. Maybe spawning is not possible right now because of configured resource limits, and we have to wait. In any case, the ApplicationPool takes care of all the nasty details and book keeping, and eventually replies back to the RequestHandler with either a Session object, or an exception.

A Session object represents a single request/response cycle with a particular application process. The RequestHandler uses the information in this Session object to establish a connection with that process and forwards the request, using a protocol that the application prefers and that the RequestHandler supports. The process performs work, and replies back with an HTTP response. The RequestHandler parses and postprocesses the response, and sends a response back to the web server.

If the ApplicationPool replied with an exception, the RequestHandler sends back an error response.

3.1.3. I/O model

The RequestHandler uses the evented I/O model. This means that the RequestHandler handles many clients (requests) at the same time, using a single thread, inside a single process. This is possible through the use of I/O event multiplexing mechanisms, which are provided by the OS. Examples of such mechanisms include the select(), poll(), epoll() and kqueue() system calls. But those mechanisms are very low-level and OS-specific, so the RequestHandler uses two libraries which abstract away the differences and provide a higher-level API: libev and libuv.

The evented I/O model is also used in Nginx. It is in contrast to the single-threaded multi-process model which handles 1 client per process (used by Apache with the prefork MPM), or the multi-threaded model which handles 1 client per thread (used by Apache with the worker MPM). You can learn more about evented I/O and the different I/O models through these resources:

3.2. The ApplicationPool subsystem

The ApplicationPool subsystem is responsible for:

  • Keeping track of which application processes exist.

  • Spawning processes.

  • Routing requests to an appropriate process. This also implies that it load balances requests between processes.

  • Monitoring processes (CPU usage, memory usage, etc).

  • Enforcing resource limits. Ensuring that not too many processes are spawned, ensuring that processes that use too much memory are shut down, etc.

  • Restarting processes on demand (e.g. when the timestamp of restart.txt has changed).

  • Restarting processes that have crashed.

  • Queuing requests and limiting concurrency. Each process tells the ApplicationPool how many concurrent requests it can handle. If more concurrent requests come in than the processes says it can handle, then the excess requests are queued within the ApplicationPool subsystem. Similarly, if requests come in while a process is being spawned, then those requests are queued until the process is done spawning.

The main interface into the subsystem is the Pool class, with its asyncGet() method. The RequestHandler calls something like pool->asyncGet(options, callback) inside its checkoutSession() method. asyncGet() replies with a Session, or an exception.

The Pool class is the core of the subsystem. It contains high-level process management logic but not low-level details, such as the details of spawning processes. The code is further divided into the following classes, each of which contain the core code managing its respective domain:

Group

Represents an application. It can contain multiple processes, all belonging to the same application.

Process

Represents an OS process; an instance of a certain application. A process may have multiple server sockets on which it listens for requests. The Process class contains various book keeping information, such as the number of sessions that are currently open. It also contains the communication channel with the underlying OS process. Process objects are created through the ApplicationPool Spawner sub-subsystem.

Socket

Represents a single server socket, on which a process listens for requests. Session objects are created through Socket. Socket maintains book keeping information about how many sessions are currently open for that particular socket.

Session

Represents a single request/response cycle with a particular process. Upon creation and destruction, various book keeping information is updated.

Options (not shown in diagram)

A configuration object for the Pool::asyncGet() method.

If you look at the diagram, then you see that Group and Process all have 0..1 associations with their containing classes. An object that has a NULL association with its containing object, is considered invalid and should not be used. The fact that the association can be NULL is a detail of the memory management scheme that we employ.

3.3. The Spawner subsystem

The Spawner subsystem is a sub-subsystem within ApplicationPool. It is responsible for actually spawning application processes, and then creating Process objects with the correct information in it.

The Spawner interface encapsulates all low-level process spawning logic. Pool calls Spawner whenever it needs to spawn another application process.

Recall that Phusion Passenger supports multiple spawn methods. For example, the smart spawn method spawns processes through an intermediate preloader process, and can utilize copy-on-write. This is explained in detail in Spawn methods explained in the Phusion Passenger manual. Each spawn method corresponds to a different implementation of the Spawner interface. The following implementations are available:

  • DirectSpawner — implements the direct spawn method.

  • SmartSpawner — implements the smart spawn method.

  • DummySpawner (not shown in diagram) — only used in unit tests.

The spawn method is user-configurable through the spawnMethod field in the Options object. To avoid convoluting the Pool code with spawner implementation selection logic, we also have a SpawnerFactory class, which the Pool uses.

The details of the spawning process is described in Application spawning and loading.

4. Application spawning and loading

Application processes are spawned from the Passenger core process. Spawning a process involves a lot of preparation work, such as setting up communication channels, setting up the current working directory, environment variables, etc. This preparation work is done by a Spawner object, together with various support executables.

When preparation is done, your application’s entry point has to be loaded somehow. That loading is done through a language-specific loader program. The loader program communicates with the Spawner through the communication channel that was set up earlier, initializes the language-specific environment, sets up a server, and reports back to the Spawner. This communication is done through a certain protocol.

4.1. Preparation work

Spawning preparation work

4.1.1. Basic setup and forking

Spawning begins when the spawn() method is called on a Spawner object. The Spawner determines which user the process should run as, and sets up some communication channels (anonymous Unix domain socket pairs), and forks a process. The parent waits until the child exits, or replies with something over the communication channel.

The communication channel in question is — from the viewpoint of the (pre)loader — actually just stdin, stdout and stderr! The anonymous Unix domain socket pairs that the Spawner creates, is mapped to the child process’s stdin, stdout and stderr file descriptors. Thus, Spawner sends data to the (pre)loader by writing stuff to its stdin, and the (pre)loader sends data back to the Spawner by writing stuff to stdout or stderr.

4.1.2. Loading SpawnPreparer, possibly through bash

Because the Passenger core is heavily multi-threaded, the child process has been forked by the Spawner may only perform async-signal-safe operations:

"A process shall be created with a single thread. If a multi-threaded process calls fork(), the new process shall contain a replica of the calling thread and its entire address space, possibly including the states of mutexes and other resources. Consequently, to avoid errors, the child process may only execute async-signal-safe operations until such time as one of the exec functions is called. Fork handlers may be established by means of the pthread_atfork() function in order to maintain application invariants across fork() calls."
fork() man page
— The Open Group's POSIX specification

Don’t worry if you don’t know what this means. The point is, there’s almost nothing the forked process can safely do at that stage. So it outsources most of the remaining preparation work to an external executable, the SpawnPreparer. The SpawnPreparer starts with a clean environment where it can safely execute code.

To execute the SpawnPreparer, the child process executes one of the following commands:

  • If the target user’s shell is bash, and the passenger_load_shell_envvars option is turned on:

    bash -l -c '/path-to/SpawnPreparer /path-to-loader-or-preloader'

    This causes bash to load its startup files, e.g. bashrc, profile, etc, after which it executes the SpawnPreparer with the given parameters. The reason why we do this is because a lot of users try to set environment variables in their bashrc, and they expect these environment variables to be picked up by applications spawned by Phusion Passenger. Unfortunately environment variables don’t work that way, but we support it anyway because it is good for usability.

  • Otherwise, the SpawnPreparer is executed directly, without bash:

    /path-to/SpawnPreparer /path-to-loader-or-preloader

How path-to-loader-or-preloader is determined, is described in The AppTypes registry.

4.1.3. SpawnPreparer further sets up the environment

The SpawnPreparer is responsible for setting up certain environment variables, current working directory, and other process environmental conditions. When SpawnPreparer is done, it executes the loader or the preloader.

4.1.4. Executing the loader or preloader

If passenger_spawn_method is set to smart (the default), and there is a preloader available for the application’s programming language, then this step executes the language-specific preloader. If either of the previous conditions are not met (and thus the passenger_spawn_method is automatically forced to direct), then this step executes the language-specific loader.

All (pre)loaders are located in the helper-scripts directory in the source tree. Here are some of the (pre)loaders that are used:

Language/Framework Loader Preloader

Ruby Rack and Rails

rack-loader.rb

rack-preloader.rb

Python

wsgi-loader.py

-

Node.js and bundled Meteor

node-loader.js

-

Unbundled Meteor

meteor-loader.rb

-

The AppTypes registry keeps a list of available (pre)loaders, and which languages they belong to.

What the loader does is described in Loaders. Likewise, preloaders are described in Preloaders.

4.2. Loaders

A loader initializes in 4 stages:

  1. It first goes through a handshake, where it reads the parameters that the Spawner has sent over the communication channel.

  2. It loads the application. The behavior of this stage may be customized by the received parameters.

  3. It sets up a server, on which this application process listens for requests.

  4. It sends a response back to the Spawner, in which it tells Spawner whether initialization was successful, and if so, where the socket is on which this application process listens for requests.

Once initialized, the loader enters a main loop, in which it keeps handling requests until a signal has been received that says it should terminate.

As explained in Basic setup and forking, the communication channels that the loader uses are just plain old stdin and stdout. Every programming language supports reading and writing from these channels. This also means that you can easily test a loader by simply executing it and entering messages in the terminal.

But stdout can also be used for printing normal output. How does the Spawner distinguish between control messages, and normal messages that should be displayed? The answer is that control messages must start with `!> ` (including the trailing whitespace), and must end with a newline. The Spawner reads messages line-by-line, processes lines that start with `!> `, and prints lines that don’t start with that marker.

4.2.1. Handshake

The handshaking process begins with a protocol version handshake. The loader printing the line !> I have control 1.0. The Spawner then sends "You have control 1.0", which the loader checks. If the loader observes that the version handshake does not match the expectation, then it aborts with an error.

The Spawner also sends a list of key-value pairs, which is terminated by an empty newline. Upon receiving the empty newline, the Spawner proceeds with loading the application.

Example:

Loader                      Spawner

!> I have control 1.0
                            You have control 1.0
                            passenger_root: ...
                            passenger_version: 4.0.45
                            ruby_libdir: /Users/hongli/Projects/passenger/lib
                            generation_dir: /tmp/passenger.1.0.2082/generation-0
                            gupid: 1647ad4-ovJJMiPkAAt
                            connect_password: jXGaSzo8vRX5oGe2uuSv5tJsf1uX7ZgIeEH2x0nfOEa
                            app_root: /Users/hongli/Sites/rack.test
                            startup_file: config.ru
                            process_title: Passenger RackApp
                            log_level: 3
                            environment: development
                            base_uri: /
                            ...
                            (empty newline)

4.2.2. Application loading

How the application is loaded, depends on the programming language. Here are some examples:

  • The Ruby Rack loader does it by load()-ing the startup file, which by default is config.ru.

  • The Python loader does it by calling imp.load_source('passenger_wsgi', 'passenger_wsgi.py').

  • The Node.js (and bundled Meteor) loader does it by require()-ing the startup file, which by default is app.js.

  • The unbundled Meteor loader does it by executing the meteor run command.

If no errors occur, the loader proceeds with setting up a server. Otherwise, it reports an error.

4.2.3. Setting up a server

The loader sets up a server on which the application listens for requests. The Spawner doesn’t care how this is done, how this server works, or even what its concurrency is. It only cares about how it can contact the server. So the loader has full freedom in this step.

As explained in section Request handling and subsection Forwarding to the application, the RequestHandler can talk with the application process in a protocol that the application prefers. The RequestHandler supports two protocols:

  • A Phusion Passenger internal protocol which we call the session protocol. This protocol is used by the Ruby loaders and the Python loader. A description of this protocol is outside the scope of this document, but if you’re interested in how it looks like and how it behaves, you can study the source code of the Ruby and Python loaders, as well as src/cxx_supportlib/Utils/MessageIO.h.

  • The HTTP protocol. This protocol is used by the Node.js and Meteor loaders. If you’re writing a new loader then, it’s probably easiest to use this protocol, together with whatever HTTP library is available for the loader’s target language.

Typically, the server is setup to listen on a Unix domain socket file, inside the backends subdirectory of the generation directory. The path to the generation directory was passed during handshake. However the server may also listen on a TCP socket.

Once a server has been setup, the loader can report readiness.

4.2.4. Reporting readiness

Once the server is set up, the loader sends back a !> Ready response, followed by information about where the server socket listens on, and what protocol it expects. The response is terminated with a `!> ` line (notice the trailing whitespace, which is required).

The information about where the server socket listens on, is a 4-tuple:

  • The name. This must always be main.

  • The address. For Unix domain sockets, it has the form unix:/path-to-socket. For TCP socket, it has the form tcp://127.0.0.1:PORT.

  • The protocol. This must be either session or http_session.

  • The maximum number of concurrent connections the server supports. The ApplicationPool will ensure that the process never receives more concurrent requests than this number. A value of 0 means that the concurrency is unlimited.

Here’s an example of what the Node.js loader sends as response:

!> Ready
!> socket: main;unix:/path-to-generation-dir/backends/node.1234-5677;session;0")
!>

After reporting readiness, the loader can enter a main loop and wait for termination.

4.2.5. Error reporting

If something goes wrong in any of the stages, the loader can report an error in two ways:

  1. Just write the error message to stdout as you normally do, and abort without printing the !> Ready message. The Passenger core will read everything that the loader has written to stdout, and use it as the error message. This error message is considered to be plain text.

  2. Abort after printing a special !> Error message. The loader can signal that the message is HTML. The RequestHandler will format the error message as HTML.

4.2.6. Main loop and termination

The loader’s main loop’s job is to wait until a single byte has been received on stdin. As long as the byte has not been received, the loader should not exit, and should keep processing requests. When the byte has been received, the following conditions are guaranteed to be true:

  • All clients for this particular process have disconnected.

  • No more clients will be routed to this particular process.

This guarantee is enforced by the RequestHandler and the ApplicationPool. Thus, the loader doesn’t have to perform any kind of complicated shutdown. It can just exit the process.

If the server was listening on a Unix domain socket file, then the loader doesn’t even have to remove the file. The ApplicationPool already takes care of that.

4.2.7. Stdout and stderr forwarding

All lines that the loader writes to stdout, and that are not prefixed with `!> `, are forwarded by the Passenger core to its own stdout. Similarly, everything that the loader writes to stderr, whether prefixed with `!> ` or not, is forwarded by the Passenger core to its own stdout.

While the Spawner is still doing its work, it takes care of this forwarding by itself. Once the Spawner is done, it outsources this work to two PipeWatcher objects, each which spawns a background thread for this purpose.

4.3. Preloaders

Preloaders are a special kind of loaders, used for reducing spawn time and leveraging copy-on-write. You can learn more about this at Spawning methods explained.

Preloaders look a lot like loaders, but behave slightly differently. They also use stdin, stderr and stdout to communicate with the Spawner. The protocols are very similar.

A preloader initializes in 4 stages:

  1. It first goes through a handshake, which is the same as the loader handshake.

  2. It loads the application just like the loader does.

  3. It sets up a server on which it listens for spawn commands.

  4. It sends a response back to the Spawner. This is similar to how the loader does it, but instead of telling the Spawner where the application listens for requests, it tells the Spawner where the preloader process listens for spawn commands.

Once initialized, the preloader enters a main loop, in which it keeps handling spawn commands until a signal has been received that says it should terminate.

When a spawn command is received, the preloader forks off a child process (which already has the application loaded) and reports the child process’s PID to the Spawner. It also sets up a communication channel between the Spawner and the child process.

4.4. The AppTypes registry

When the web server receives a request, the Phusion Passenger module inside it autodetects the type of application that the request belongs to. It does that by examening the filesystem and checking which one of the startup files exist. For example, if config.ru exists, then it assumes that it’s a Ruby app. Or if app.js exists, then it assumes that it’s a Node.js app. The Phusion Passenger module forwards the inferred application type to the Passenger core.

Given an application type, the associated loader and preloader can be looked up.

Information about the supported application types, startup files, loaders and preloaders are defined in the following places:

  • The constant appTypeDefinitions in the file src/cxx_supportlib/AppTypes.cpp keeps a list of supported languages. It also specifies the default startup file name belonging to each language.

  • The method getStartCommand() in the file src/agent/Core/ApplicationPool2/Options.h defines the loaders that should be used for each language.

  • The method tryCreateSmartSpawner() in the file src/agent/Core/SpawningKit/Factory.h defines the preloaders that should be used for each language.

  • The method looks_like_app_directory? in the file src/ruby_supportlib/phusion_passenger/standalone/app_finder.rb keeps a list of supported startup files. This is only used within Passenger Standalone.

5. Instance state and communication

Every time you start Phusion Passenger, you’ve created a new instance. Every instance consists of multiple processes that work together (Watchdog, Passenger core, UstRouter, application processes). All those processes have to be able to communicate with each other. Those processes must also not communicate with the processes belonging to other instances. For example, if you start Apache+Passenger and Nginx+Passenger, then we don’t want the Passenger core that’s started from Apache to use UstRouter that’s started from Nginx.

Clearly, the processes can’t listen on a specific TCP port for communication. Nor can they listen on a fixed Unix domain socket filename.

That is where the instance directory comes in. Every Phusion Passenger instance has its own, unique temporary directory. That directory is removed when the instance halts. The directory contains Unix domain socket files that the processes listen on. Every Phusion Passenger related process knows where its own instance directory is, and thus, knows how to communicate with other processes belonging to the same instance. The instance directory is implemented in src/cxx_supportlib/InstanceDirectory.h.

Administration tools such as passenger-status query information using instance directories. First, they check which instance directories exist on the system. If they find only one, then they query the sockets inside that sole instance directory. Otherwise, they abort with an error and ask the user to specifically select the instance to query.

6. Appendix A: About Rack

The de-facto standard interface for Ruby web applications is Rack. Rack specifies an programming interface for web application developers to implement. This interface covers HTTP request and response handling, and is not dependent on any particular application server. The idea is that any Rack-compliant application server can implement the Rack specification and work with all Rack-compliant web applications.

images/rack.png

In the distant past, each Ruby web framework had its own interface, so application servers needed to explicitly add support for each web framework. Nowadays application servers just support Rack.

images/many_web_framework_protocols.png

Ruby on Rails has been fully Rack compliant since version 3.0. Rails 2.3 was partially Rack-compliant while earlier versions were not Rack-compliant at all. Phusion Passenger supports Rack as well as all Rails 1.x and 2.x versions.

7. Appendix B: About Apache

The Apache web server has a dynamic module system and a pluggable I/O multiprocessing (the ability to handle more than 1 concurrent HTTP client at the same time) architecture. An Apache module which implements a particular multiprocessing strategy, is called a Multi-Processing Module (MPM). The single-threaded multi-process prefork MPM had been the default and the most popular one for a long time, but in recent times the hybrid multi-threaded/multi-process worker MPM is becoming increasingly popular because of its better performance and scalability. Furthermore, Apache 2.4 introduced the event MPM which is a hybrid evented/multi-threaded/multi-process MPM and offers even more scalability benefits.

The prefork MPM remains in wide use today because it’s the only MPM that works well with mod_php.

The prefork MPM spawns multiple worker child processes. HTTP requests are first accepted by a so-called control process, and then forwarded to one of the worker processes. The next section contains a diagram which shows the prefork MPM’s architecture.

8. Appendix C: About Nginx

Nginx is a lightweight web server that is becoming increasingly popular. It is known to be smaller, lighter weight and more scalable than Apache thanks to its evented I/O architecture. That said, Nginx is less flexible than Apache. For example it has no dynamic module system: all modules must be statically compiled into Nginx.


passenger-5.0.30/doc/Design and Architecture.txt000644 000765 000024 00000134302 12233035540 022056 0ustar00honglistaff000000 000000 = Phusion Passenger Design and Architecture = image:images/phusion_banner.png[link="http://www.phusion.nl/"] This guide describes link:https://www.phusionpassenger.com/[Phusion Passenger]'s design and architecture in detail. With this guide, we hope that contributors can quickly find their way around the Phusion Passenger codebase. The guide assumes that you're familiar with using Phusion Passenger and with Nginx or Apache, and that you've read the link:https://github.com/phusion/passenger/blob/master/CONTRIBUTING.md[Contributors Guide] and the link:https://github.com/phusion/passenger/blob/master/doc/DeveloperQuickstart.md[Developer QuickStart]. image:images/code_walkthrough.jpg[link="http://vimeo.com/phusionnl/review/98027409/03ba678684"] + 'You should also watch the complementary video: link:http://vimeo.com/phusionnl/review/98027409/03ba678684[Phusion Passenger Code Walkthrough]' == Introduction [[web_app_models]] === Web application models and the role of the application server Before we describe Phusion Passenger, it is important to understand how typical web applications work from the viewpoint of someone who wants to connect a web application to a web server. A typical, isolated, web application accepts an HTTP request from some I/O channel, processes it internally, and outputs an HTTP response, which is sent back to the client. This is done in a loop, until the application is commanded to exit. This does not necessarily mean that the web application speaks HTTP directly: it just means that the web application accepts some kind of representation of an HTTP request. image:images/typical_isolated_web_application.png[Architecture of a typical web application in isolation] Some web applications are directly accessible through the HTTP protocol, while others are not. It depends on the language and framework that the web application is built on. For example, Ruby (Rack/Rails) and Python (WSGI) web applications are typically not directly accessible through the HTTP protocol. On the other hand, Node.js web applications *do* tend to be accessible through the HTTP protocol. The reasons for this are historical, but they're outside the scope of this guide. ==== Common models Here are some common models that are in use: 1. The web application is contained in an application server. This application server may or may not be able to contain multiple web applications. The administrator then connects the application server to a web server through some kind of protocol. This protocol may be HTTP, FastCGI, SCGI, AJP or whatever. The web server dispatches (forwards) requests to the application server, which in turn dispatches requests to the correct web application, in a format that the web application understands. Conversely, HTTP responses outputted by the web application are sent to the application server, which in turn sends them to the web server, and eventually to the HTTP client. + Typical examples of such a model: + * A J2EE application, contained in the Tomcat application server, reverse proxied behind the Apache web server. Tomcat can contain multiple web applications in a single Tomcat instance. * Most Ruby application servers besides Phusion Passenger (Thin, Unicorn, Goliath, etc). These application servers can only contain a single Ruby web application per instance. They load the web application into their own process and are put behind a web server (Apache, Nginx) in a reverse proxy setup. * Green Unicorn, the Python (WSGI) application server, behind a reverse proxy setup. * PHP web applications spawned by the FastCGI Process Manager (FPM), behind an Nginx reverse proxy setup. 2. The web application is contained directly in a web server. In this case, the web server acts like an application server. Typical examples include: + -- * PHP web applications running on Apache through mod_php. * Python (WSGI) web applications running on Apache through mod_uwsgi or mod_python. -- + Note that this does not necessarily mean that the web application is run inside the same process as the web server: it just means that the web server manages applications. In case of mod_php, PHP runs directly inside the Apache worker processes, but in case of mod_uwsgi the Python processes can be configured to run out-of-process. + Phusion Passenger for Apache and Phusion Passenger for Nginx implement this model, and run applications outside the web server process. 3. The web application *is* a web server, and can accept HTTP requests directly. Examples of this model: + -- * Almost all Node.js and Meteor JS web applications. * The Trac bug tracking software, running in its standalone server. -- + In most setups, the administrator puts them in a reverse proxy configuration, behind a real web server such as Apache or Nginx, instead of letting them accept HTTP requests directly. + Phusion Passenger Standalone implements this model. However, you can expose Phusion Passenger Standalone directly to the Internet because it uses Nginx internally. 4. The web application does not speak HTTP directly, but is connected directly to the web server through some communication adapter. CGI, FastCGI and SCGI are good examples of this. The above models cover how nearly all web applications work, whether they're based on PHP, Django, J2EE, ASP.NET, Ruby on Rails, or whatever. Note that all of these models provide the same functionality, i.e. no model can do something that a different model can't. The critical reader will notice that all of these models are identical to the one described in the first diagram, if the combination of web servers, application servers, web applications etc. are considered to be a single entity; a black box if you will. It should also be noted that these models do not enforce any particular I/O processing implementation. The web servers, application servers, web applications, etc. could process I/O serially (i.e. one request at a time), could multiplex I/O with a single thread (e.g. by using `select(2)` or `poll(2)`) or it could process I/O with multiple threads and/or multiple processes. It depends on the implementation. Of course, there are many variations possible. For example, load balancers could be used. But that is outside the scope of this document. ==== The rationale behind reverse proxying As you've seen, administrators often put the web application or its application server behind a real web server in a reverse proxy setup, even when the web app/app server already speaks HTTP. This is because implementing HTTP in a proper, secure way involves more than just speaking the protocol. The public Internet is a hostile environment where clients can send any arbitrary data and can exhibit any arbitrary I/O patterns. If you don't properly implement I/O handling, then you could open yourself either to parser vulnerabilities, or denial-of-service attacks. Web servers like Apache and Nginx have already implemented world-class I/O and connection handling code and it would be a waste to reinvent their wheel. In the end, putting the application in a reverse proxying setup often makes the whole system more robust and and more secure. This is the reason why it's considered good practice. A typical problem involves dealing with *slow clients*. These clients may send HTTP requests slowly and read HTTP responses slowly, perhaps taking many seconds to complete their work. A naive single-threaded HTTP server implementation that reads an HTTP requests, processes, and sends the HTTP response in a loop may end up spending so much time waiting for I/O that spends very little time doing actual work. Worse: suppose that the client is malicious, just leaves the socket open and never reads the HTTP response, then the server will spend forever waiting for the client, not being able to handle any more requests. A real-world attack based on this principle is link:http://en.wikipedia.org/wiki/Slowloris[Slowloris]. .An example of a naive HTTP server implementation ------------------- while true client = accept_next_client() request = read_http_request(client) response = process_request(request) send_http_response(client, response) end ------------------- There are many ways to solve this problem. One could use one thread per client, one could implement I/O timeouts, one could use an evented I/O architecture, one could have a dedicated I/O thread or process buffer requests and responses. The point is, implementing all this properly is non-trivial. Instead of reimplementing these over and over in each application server, it's better to let a real web server deal with all the details and let the application server and the web application do what they're best at: their own core business logic. === Phusion Passenger architecture overview image:images/passenger_architecture_overview.png[An overview of Phusion Passenger's architecture] Phusion Passenger is not a single, monolithic entity. Instead, it consists of multiple components and processes that work together. Part of the reason why Phusion Passenger is split like this, is because it's technically necessary (no other way to implement it). But another part of the reason is stability and robustness. Individual components can crash and can be restarted independently from each other. If we were to put everything inside a single process, then a crash will take down all of Phusion Passenger. Thus, if the Passenger core crashes, or if an application process crashes, they can both be restarted without affecting the web server's stability. ==== Web server module When an HTTP client sends a request, it is received by the web server (Nginx or Apache). Both Apache and Nginx can be extended with **modules**. Phusion Passenger provides such a module. The module is loaded into Nginx/Apache. It checks whether the request should be handled by a Phusion Passenger-served web application, and if so, forwards the request to the Passenger core. The internal wire protocol used during this forwarding, is a modified version of link:http://en.wikipedia.org/wiki/SCGI[SCGI]. The Nginx module and Apache module have an entirely different code base. Their code bases are in `src/nginx_module` and `src/apache2_module`, respectively. Both modules are relatively small because they outsource most logic to the Passenger core, and because they utilize a common library (`src/cxx_supportlib`). This allows us to support both Nginx and Apache without having to write a lot of things twice. ==== Passenger core The **Passenger core** is where most of the processing is done. The core keeps track of which application processes currently exist, and using load balancing rules, determines which process a request should be forwarded to. The core also takes care of **application spawning**: if it determines that having more application processes is necessary or beneficial, then it will make that happen. Process spawning is subject to user-configured limits: the core will never spawn more processes than a user-configured maximum. The core also has monitoring and statistics gathering capabilities. It constantly keeps track of applications' memory usage, how many requests they've handled, etc. This information can later be queried from administration tools. And if an application process crashes, the core restarts it. The core is by far the largest and most complex part of the system, but it is itself composed of several smaller subsystems. Most of the <> chapter is devoted to describing the core. ==== UstRouter The core cooperates with the **UstRouter**. This latter is responsible for sending data to link:https://www.unionstationapp.com[Union Station], a monitoring web service. If you didn't explicitly tell Phusion Passenger to send data to Union Station, then the UstRouter sits idle and does not consume resources. ==== Watchdog The core and the UstRouter contain complex logic, so they could contain bugs which could crash them. So as a safety measure, they are both monitored by the **Watchdog**. If either of them crash, they are restarted by the Watchdog. This setup seeks to ensure that the system stays up, no matter what. You might now wonder: what happens if the Watchdog crashes? Shouldn't the Watchdog be monitored by another Watchdog? We've contemplated this possibility, but the Watchdog is very simple, and since 2012 we haven't seen a single report of the Watchdog crashing, nor have we been able to make it crash since that time. So, for the sake of keeping the codebase as simple as possible, we've chosen not to introduce multiple Watchdogs. ==== Command line tools Finally, there is an array of **command line tools** which support Phusion Passenger. The installers -- `passenger-install-*-module` -- are responsible for installing Phusion Passenger. There are administrative tools such as `passenger-status` and `passenger-memory-stats`. And many more. Some of these tools may communicate with one of the agents. For example, the `passenger-status` queries the core for information that the core has collected. How this communication is done, is described in <>. ==== Passenger Standalone You might have noticed that Phusion Passenger Standalone is not part of the diagram. So how does it fit into the architecture? Well, Phusion Passenger Standalone is actually just Phusion Passenger for Nginx. The `passenger start` command simply sets up a modified and stripped-down Nginx web server (which we call the `WebHelper`) with the Phusion Passenger Nginx module loaded. === Build system and source tree Phusion Passenger is written mostly in C\++ and Ruby. The web server modules, core, UstRouter and Watchdog are written in C++. Most command line tools are written in Ruby. You can find each component here: * The web server modules can be found in `src/apache2_module` and `src/nginx_module`. * The Passenger core, UstRouter and Watchdog can be found in `src/agent`. * The command line tools can be found in `bin`, with some parts of their code in `lib`. More information can be found in the link:https://github.com/phusion/passenger/blob/master/CONTRIBUTING.md[Contributors Guide]. This guide also teaches you how to compile Phusion Passenger. == Initialization image:images/startup_sequence.png[Startup sequence] Phusion Passenger initializes as follows. 1. First, the user begins with starting the web server. This for example be done by running `sudo service apache2 start` or `sudo service nginx start`. Or perhaps the web server is configured to be automatically started by the OS, in which case the user doesn't have to do anything. In case of Phusion Passenger Standalone, the user runs `passenger start` which in turn starts Nginx. 2. The Phusion Passenger module inside Nginx/Apache proceeds with starting the Watchdog. This is implemented in: + * `src/nginx_module/ngx_http_passenger_module.c`, function `start_watchdog()`. * `src/apache2_module/Hooks.cpp`, in the constructor for the `Hooks` class. * `src/cxx_supportlib/WatchdogLauncher.h` and `WatchdogLauncher.cpp`. Most of the logic pertaining starting the Watchdog is in this file. 3. The Watchdog first initializes a <>, which is a temporary directory containing files that will be used during the life time of this Phusion Passenger instance. For example, the directory contains Unix domain socket files, so that the different Phusion Passenger processes can communicate with each other. The Watchdog is implemented in `src/agent/Watchdog/Main.cpp`. 4. The Watchdog starts the Passenger core and the UstRouter simultaneously. Each performs its own initialization. 5. When the core is done initializing, it will send a message back to the Watchdog saying that it's done. The UstRouter does something similar. When the Watchdog has received both acknowledgment messages, it finishes initialization. If the Watchdog notices that one of the agents have exited without sending an acknowledgment message, then it enters an error state. 6. The Watchdog reports successful startup back to the Phusion Passenger module that's running inside Nginx/Apache. Or, if initialization didn't success, the Watchdog reports back an error. The Phusion Passenger module inside Nginx/Apache then logs the error. After initialization, Phusion Passenger is ready to receive and to process requests. [[passenger_core_architecture]] == Passenger core architecture image:images/passenger_core_architecture.png[Passenger core architecture] The Passenger core consists of two subsystems. One is the *request handling subsystem*. The other is *the ApplicationPool subsystem*, which performs the bulk of process management. The core also uses a number of support libraries. The largest third-party support libraries are shown in the diagram. Many more -- internal -- support libraries are used, but they're omitted from the diagram. You can find these internal support libraries in the directory `src/cxx_supportlib/Utils`. === Request handling Recall that requests are first received from the web server. The web server serializes the request into a slightly modified version of link:http://en.wikipedia.org/wiki/SCGI[the SCGI format], and sends it to the core's RequestHandler. The RequestHandler performs some work, and eventually sends back a regular HTTP response. The web server parses the RequestHandler response, and sends a response to the original HTTP client. The RequestHandler listens on a Unix domain socket file. This Unix domain socket file is called `request`, and is located in <>. ==== One client per request The web server creates a new connection to the core on every request. Thus, from the viewpoint of the RequestHandler, its client is the web server. Every time a client connects (i.e. a new request is forwarded), the RequestHandler creates a new Client object which represents that request. All request-specific state is stored inside the Client. After the RequestHandler is done processing a request, it closes the client socket. Note that in the diagram, a Client has a 0..1 association with RequestHandler. That's because when a Client is disconnected, the pointer to the associated RequestHandler is set to NULL. There might be background operations left which still have a pointer to the Client. As soon as those background operations finish, they check whether the Client has a valid pointer to the RequestHandler. If so, they commit their work; if not, they discard their work. The Client is destroyed when all its associated background operations have finished. [[request_handler_forwarding_to_app]] ==== Forwarding to the application The RequestHandler asynchronously asks the ApplicationPool subsystem to select an appropriate application process to handle this request. The ApplicationPool checks whether there is an appropriate process, and if not, tries to spawn one. Maybe spawning is not possible right now because of configured resource limits, and we have to wait. In any case, the ApplicationPool takes care of all the nasty details and book keeping, and eventually replies back to the RequestHandler with either a Session object, or an exception. A Session object represents a single request/response cycle with a particular application process. The RequestHandler uses the information in this Session object to establish a connection with that process and forwards the request, using <> and that the RequestHandler supports. The process performs work, and replies back with an HTTP response. The RequestHandler parses and postprocesses the response, and sends a response back to the web server. If the ApplicationPool replied with an exception, the RequestHandler sends back an error response. ==== I/O model The RequestHandler uses the **evented I/O model**. This means that the RequestHandler handles many clients (requests) at the same time, using a single thread, inside a single process. This is possible through the use of I/O event multiplexing mechanisms, which are provided by the OS. Examples of such mechanisms include the `select()`, `poll()`, `epoll()` and `kqueue()` system calls. But those mechanisms are very low-level and OS-specific, so the RequestHandler uses two libraries which abstract away the differences and provide a higher-level API: link:http://software.schmorp.de/pkg/libev.html[libev] and link:https://github.com/libuv/libuv[libuv]. The evented I/O model is also used in Nginx. It is in contrast to the single-threaded multi-process model which handles 1 client per process (used by Apache with the prefork MPM), or the multi-threaded model which handles 1 client per thread (used by Apache with the worker MPM). You can learn more about evented I/O and the different I/O models through these resources: * link:http://www.slideshare.net/marc.seeger/seeger-aysnc-io[Event-Driven I/O: A hands-on introduction] -- Marc Seeger, 2010 * link:http://stackoverflow.com/questions/5807246/event-driven-io-and-blocking-vs-nonblocking[Event Driven IO And Blocking vs NonBlocking] -- Stack Overflow * link:http://stackoverflow.com/questions/3231018/how-does-event-driven-i-o-allow-multiprocessing[How does event driven I/O allow multiprocessing?] -- Stack Overflow * link:http://www.kegel.com/c10k.html[The C10K problem] -- an overview of the different I/O models used in different servers; Dan Kegel === The ApplicationPool subsystem The ApplicationPool subsystem is responsible for: * Keeping track of which application processes exist. * Spawning processes. * Routing requests to an appropriate process. This also implies that it load balances requests between processes. * Monitoring processes (CPU usage, memory usage, etc). * Enforcing resource limits. Ensuring that not too many processes are spawned, ensuring that processes that use too much memory are shut down, etc. * Restarting processes on demand (e.g. when the timestamp of `restart.txt` has changed). * Restarting processes that have crashed. * Queuing requests and limiting concurrency. Each process tells the ApplicationPool how many concurrent requests it can handle. If more concurrent requests come in than the processes says it can handle, then the excess requests are queued within the ApplicationPool subsystem. Similarly, if requests come in while a process is being spawned, then those requests are queued until the process is done spawning. The main interface into the subsystem is the Pool class, with its `asyncGet()` method. The RequestHandler calls something like `pool->asyncGet(options, callback)` inside its `checkoutSession()` method. `asyncGet()` replies with a Session, or an exception. The Pool class is the core of the subsystem. It contains high-level process management logic but not low-level details, such as the details of spawning processes. The code is further divided into the following classes, each of which contain the core code managing its respective domain: **Group**:: Represents an application. It can contain multiple processes, all belonging to the same application. **Process**:: Represents an OS process; an instance of a certain application. A process may have multiple server sockets on which it listens for requests. The Process class contains various book keeping information, such as the number of sessions that are currently open. It also contains the communication channel with the underlying OS process. Process objects are created through <>. **Socket**:: Represents a single server socket, on which a process listens for requests. Session objects are created through Socket. Socket maintains book keeping information about how many sessions are currently open for that particular socket. **Session**:: Represents a single request/response cycle with a particular process. Upon creation and destruction, various book keeping information is updated. **Options (not shown in diagram)**:: A configuration object for the `Pool::asyncGet()` method. If you look at the diagram, then you see that Group and Process all have 0..1 associations with their containing classes. An object that has a NULL association with its containing object, is considered invalid and should not be used. The fact that the association can be NULL is a detail of the memory management scheme that we employ. [[spawner_subsystem]] === The Spawner subsystem The Spawner subsystem is a sub-subsystem within ApplicationPool. It is responsible for actually spawning application processes, and then creating Process objects with the correct information in it. The `Spawner` interface encapsulates all low-level process spawning logic. Pool calls Spawner whenever it needs to spawn another application process. Recall that Phusion Passenger supports multiple spawn methods. For example, the `smart` spawn method spawns processes through an intermediate preloader process, and can utilize copy-on-write. This is explained in detail in link:https://www.phusionpassenger.com/library/indepth/spawn_methods/[Spawn methods explained] in the Phusion Passenger manual. Each spawn method corresponds to a different implementation of the Spawner interface. The following implementations are available: * DirectSpawner -- implements the `direct` spawn method. * SmartSpawner -- implements the `smart` spawn method. * DummySpawner (not shown in diagram) -- only used in unit tests. The spawn method is user-configurable through the `spawnMethod` field in the `Options` object. To avoid convoluting the Pool code with spawner implementation selection logic, we also have a SpawnerFactory class, which the Pool uses. The details of the spawning process is described in <>. [[app_spawning_and_loading]] == Application spawning and loading Application processes are spawned from the Passenger core process. Spawning a process involves a lot of **preparation work**, such as setting up communication channels, setting up the current working directory, environment variables, etc. This preparation work is done by <>, together with various support executables. When preparation is done, your application's entry point has to be loaded somehow. That loading is done through a language-specific **loader program**. The loader program communicates with the Spawner through the communication channel that was set up earlier, initializes the language-specific environment, sets up a server, and reports back to the Spawner. This communication is done through a certain **protocol**. === Preparation work image:images/spawning_preparation_work.png[Spawning preparation work] [[basic_setup_and_forking]] ==== Basic setup and forking Spawning begins when the `spawn()` method is called on a <>. The Spawner determines link:https://www.phusionpassenger.com/library/deploy/nginx/user_sandboxing.html[which user the process should run as], and sets up some communication channels (anonymous Unix domain socket pairs), and forks a process. The parent waits until the child exits, or replies with something over the communication channel. The communication channel in question is -- from the viewpoint of the (pre)loader -- actually just stdin, stdout and stderr! The anonymous Unix domain socket pairs that the Spawner creates, is mapped to the child process's stdin, stdout and stderr file descriptors. Thus, Spawner sends data to the (pre)loader by writing stuff to its stdin, and the (pre)loader sends data back to the Spawner by writing stuff to stdout or stderr. ==== Loading SpawnPreparer, possibly through bash Because the Passenger core is heavily multi-threaded, the child process has been forked by the Spawner link:http://pubs.opengroup.org/onlinepubs/009695399/functions/fork.html[may only perform async-signal-safe operations]: [quote, The Open Group's POSIX specification, fork() man page] "A process shall be created with a single thread. If a multi-threaded process calls fork(), the new process shall contain a replica of the calling thread and its entire address space, possibly including the states of mutexes and other resources. Consequently, to avoid errors, the child process may only execute async-signal-safe operations until such time as one of the exec functions is called. Fork handlers may be established by means of the pthread_atfork() function in order to maintain application invariants across fork() calls." Don't worry if you don't know what this means. The point is, there's almost nothing the forked process can safely do at that stage. So it outsources most of the remaining preparation work to an external executable, the SpawnPreparer. The SpawnPreparer starts with a clean environment where it can safely execute code. To execute the SpawnPreparer, the child process executes one of the following commands: * If the target user's shell is bash, and the `passenger_load_shell_envvars` option is turned on: + `bash -l -c '/path-to/SpawnPreparer /path-to-loader-or-preloader'` + This causes bash to load its startup files, e.g. bashrc, profile, etc, after which it executes the SpawnPreparer with the given parameters. The reason why we do this is because a lot of users try to set environment variables in their bashrc, and they expect these environment variables to be picked up by applications spawned by Phusion Passenger. Unfortunately environment variables link:https://www.phusionpassenger.com/library/indepth/environment_variables.html[don't work that way], but we support it anyway because it is good for usability. * Otherwise, the SpawnPreparer is executed directly, without bash: + `/path-to/SpawnPreparer /path-to-loader-or-preloader` How `path-to-loader-or-preloader` is determined, is described in <>. ==== SpawnPreparer further sets up the environment The SpawnPreparer is responsible for setting up certain environment variables, current working directory, and other process environmental conditions. When SpawnPreparer is done, it executes the loader or the preloader. ==== Executing the loader or preloader If `passenger_spawn_method` is set to `smart` (the default), and there is a preloader available for the application's programming language, then this step executes the language-specific **preloader**. If either of the previous conditions are not met (and thus the `passenger_spawn_method` is automatically forced to `direct`), then this step executes the language-specific **loader**. All (pre)loaders are located in the `helper-scripts` directory in the source tree. Here are some of the (pre)loaders that are used: [options="header"] |================================================================================ | Language/Framework | Loader | Preloader | Ruby Rack and Rails | rack-loader.rb | rack-preloader.rb | Python | wsgi-loader.py | - | Node.js and bundled Meteor | node-loader.js | - | Unbundled Meteor | meteor-loader.rb | - |================================================================================ <> keeps a list of available (pre)loaders, and which languages they belong to. What the loader does is described in <>. Likewise, preloaders are described in <>. [[loaders]] === Loaders A loader initializes in 4 stages: 1. It first goes through a <>, where it reads the parameters that the Spawner has sent over the communication channel. 2. It <>. The behavior of this stage may be customized by the received parameters. 3. It <>, on which this application process listens for requests. 4. It <> to the Spawner, in which it tells Spawner whether initialization was successful, and if so, where the socket is on which this application process listens for requests. Once initialized, the loader enters a main loop, in which it keeps handling requests until a signal has been received that says it should terminate. As explained in <>, the communication channels that the loader uses are just plain old stdin and stdout. Every programming language supports reading and writing from these channels. This also means that you can easily test a loader by simply executing it and entering messages in the terminal. But stdout can also be used for printing normal output. How does the Spawner distinguish between control messages, and normal messages that should be displayed? The answer is that control messages must start with `!> ` (including the trailing whitespace), and must end with a newline. The Spawner reads messages line-by-line, processes lines that start with `!> `, and prints lines that don't start with that marker. [[loader_handshake]] ==== Handshake The handshaking process begins with a protocol version handshake. The loader printing the line `!> I have control 1.0`. The Spawner then sends "You have control 1.0", which the loader checks. If the loader observes that the version handshake does not match the expectation, then it aborts with an error. The Spawner also sends a list of key-value pairs, which is terminated by an empty newline. Upon receiving the empty newline, the Spawner proceeds with <>. Example: -------------------------------------------------------- Loader Spawner !> I have control 1.0 You have control 1.0 passenger_root: ... passenger_version: 4.0.45 ruby_libdir: /Users/hongli/Projects/passenger/lib generation_dir: /tmp/passenger.1.0.2082/generation-0 gupid: 1647ad4-ovJJMiPkAAt connect_password: jXGaSzo8vRX5oGe2uuSv5tJsf1uX7ZgIeEH2x0nfOEa app_root: /Users/hongli/Sites/rack.test startup_file: config.ru process_title: Passenger RackApp log_level: 3 environment: development base_uri: / ... (empty newline) -------------------------------------------------------- [[application_loading]] ==== Application loading How the application is loaded, depends on the programming language. Here are some examples: * The Ruby Rack loader does it by `load()`-ing the startup file, which by default is `config.ru`. * The Python loader does it by calling `imp.load_source('passenger_wsgi', 'passenger_wsgi.py')`. * The Node.js (and bundled Meteor) loader does it by `require()`-ing the startup file, which by default is `app.js`. * The unbundled Meteor loader does it by executing the `meteor run` command. If no errors occur, the loader proceeds with <>. Otherwise, it <>. [[loader_setting_up_server]] ==== Setting up a server The loader sets up a server on which the application listens for requests. The Spawner doesn't care how this is done, how this server works, or even what its concurrency is. It only cares about how it can contact the server. So the loader has full freedom in this step. As explained in <>, the RequestHandler can talk with the application process in a protocol that the application prefers. The RequestHandler supports two protocols: * A Phusion Passenger internal protocol which we call the 'session' protocol. This protocol is used by the Ruby loaders and the Python loader. A description of this protocol is outside the scope of this document, but if you're interested in how it looks like and how it behaves, you can study the source code of the Ruby and Python loaders, as well as `src/cxx_supportlib/Utils/MessageIO.h`. * The HTTP protocol. This protocol is used by the Node.js and Meteor loaders. If you're writing a new loader then, it's probably easiest to use this protocol, together with whatever HTTP library is available for the loader's target language. Typically, the server is setup to listen on a Unix domain socket file, inside the `backends` subdirectory of the 'generation directory'. The path to the generation directory was passed during handshake. However the server may also listen on a TCP socket. Once a server has been setup, the loader can <>. [[loader_report_readiness]] ==== Reporting readiness Once the server is set up, the loader sends back a `!> Ready` response, followed by information about where the server socket listens on, and what protocol it expects. The response is terminated with a `!> ` line (notice the trailing whitespace, which is required). The information about where the server socket listens on, is a 4-tuple: * The **name**. This must always be `main`. * The **address**. For Unix domain sockets, it has the form `unix:/path-to-socket`. For TCP socket, it has the form `tcp://127.0.0.1:PORT`. * The **protocol**. This must be either `session` or `http_session`. * The maximum number of **concurrent connections** the server supports. The ApplicationPool will ensure that the process never receives more concurrent requests than this number. A value of 0 means that the concurrency is unlimited. Here's an example of what the Node.js loader sends as response: -------------------------------------------------------- !> Ready !> socket: main;unix:/path-to-generation-dir/backends/node.1234-5677;session;0") !> -------------------------------------------------------- After reporting readiness, the loader can <>. [[loader_error_reporting]] ==== Error reporting If something goes wrong in any of the stages, the loader can report an error in two ways: 1. Just write the error message to stdout as you normally do, and abort without printing the `!> Ready` message. The Passenger core will read everything that the loader has written to stdout, and use it as the error message. This error message is considered to be plain text. 2. Abort after printing a special `!> Error` message. The loader can signal that the message is HTML. The RequestHandler will format the error message as HTML. [[loader_main_loop]] ==== Main loop and termination The loader's main loop's job is to wait until a single byte has been received on stdin. As long as the byte has not been received, the loader should not exit, and should keep processing requests. When the byte has been received, the following conditions are guaranteed to be true: * All clients for this particular process have disconnected. * No more clients will be routed to this particular process. This guarantee is enforced by the RequestHandler and the ApplicationPool. Thus, the loader doesn't have to perform any kind of complicated shutdown. It can just exit the process. If the server was listening on a Unix domain socket file, then the loader doesn't even have to remove the file. The ApplicationPool already takes care of that. ==== Stdout and stderr forwarding All lines that the loader writes to stdout, and that are not prefixed with `!> `, are forwarded by the Passenger core to its own stdout. Similarly, everything that the loader writes to stderr, whether prefixed with `!> ` or not, is forwarded by the Passenger core to its own stdout. While the Spawner is still doing its work, it takes care of this forwarding by itself. Once the Spawner is done, it outsources this work to two PipeWatcher objects, each which spawns a background thread for this purpose. [[preloaders]] === Preloaders Preloaders are a special kind of loaders, used for reducing spawn time and leveraging copy-on-write. You can learn more about this at link:https://www.phusionpassenger.com/library/indepth/spawn_methods/[Spawning methods explained]. Preloaders look a lot like loaders, but behave slightly differently. They also use stdin, stderr and stdout to communicate with the Spawner. The protocols are very similar. A preloader initializes in 4 stages: 1. It first goes through a handshake, which is the same as <>. 2. It <> just like the loader does. 3. It sets up a server on which it listens for spawn commands. 4. It sends a response back to the Spawner. This is similar to how the loader does it, but instead of telling the Spawner where the application listens for requests, it tells the Spawner where the preloader process listens for spawn commands. Once initialized, the preloader enters a main loop, in which it keeps handling spawn commands until a signal has been received that says it should terminate. When a spawn command is received, the preloader forks off a child process (which already has the application loaded) and reports the child process's PID to the Spawner. It also sets up a communication channel between the Spawner and the child process. [[app_types_registry]] === The AppTypes registry When the web server receives a request, the Phusion Passenger module inside it autodetects the type of application that the request belongs to. It does that by examening the filesystem and checking which one of the startup files exist. For example, if `config.ru` exists, then it assumes that it's a Ruby app. Or if `app.js` exists, then it assumes that it's a Node.js app. The Phusion Passenger module forwards the inferred application type to the Passenger core. Given an application type, the associated loader and preloader can be looked up. Information about the supported application types, startup files, loaders and preloaders are defined in the following places: * The constant `appTypeDefinitions` in the file `src/cxx_supportlib/AppTypes.cpp` keeps a list of supported languages. It also specifies the default startup file name belonging to each language. * The method `getStartCommand()` in the file `src/agent/Core/ApplicationPool2/Options.h` defines the loaders that should be used for each language. * The method `tryCreateSmartSpawner()` in the file `src/agent/Core/SpawningKit/Factory.h` defines the preloaders that should be used for each language. * The method `looks_like_app_directory?` in the file `src/ruby_supportlib/phusion_passenger/standalone/app_finder.rb` keeps a list of supported startup files. This is only used within Passenger Standalone. [[instance_state_and_communication]] == Instance state and communication Every time you start Phusion Passenger, you've created a new *instance*. Every instance consists of multiple processes that work together (Watchdog, Passenger core, UstRouter, application processes). All those processes have to be able to communicate with each other. Those processes must also *not* communicate with the processes belonging to other instances. For example, if you start Apache+Passenger *and* Nginx+Passenger, then we don't want the Passenger core that's started from Apache to use UstRouter that's started from Nginx. Clearly, the processes can't listen on a specific TCP port for communication. Nor can they listen on a fixed Unix domain socket filename. That is where the 'instance directory' comes in. Every Phusion Passenger instance has its own, unique temporary directory. That directory is removed when the instance halts. The directory contains Unix domain socket files that the processes listen on. Every Phusion Passenger related process knows where its own instance directory is, and thus, knows how to communicate with other processes belonging to the same instance. The instance directory is implemented in `src/cxx_supportlib/InstanceDirectory.h`. Administration tools such as `passenger-status` query information using instance directories. First, they check which instance directories exist on the system. If they find only one, then they query the sockets inside that sole instance directory. Otherwise, they abort with an error and ask the user to specifically select the instance to query. [appendix] == About Rack The de-facto standard interface for Ruby web applications is link:http://rack.rubyforge.org/[Rack]. Rack specifies an programming interface for web application developers to implement. This interface covers HTTP request and response handling, and is not dependent on any particular application server. The idea is that any Rack-compliant application server can implement the Rack specification and work with all Rack-compliant web applications. image:images/rack.png[] In the distant past, each Ruby web framework had its own interface, so application servers needed to explicitly add support for each web framework. Nowadays application servers just support Rack. image:images/many_web_framework_protocols.png[] Ruby on Rails has been fully Rack compliant since version 3.0. Rails 2.3 was partially Rack-compliant while earlier versions were not Rack-compliant at all. Phusion Passenger supports Rack as well as all Rails 1.x and 2.x versions. [appendix] == About Apache The Apache web server has a dynamic module system and a pluggable I/O multiprocessing (the ability to handle more than 1 concurrent HTTP client at the same time) architecture. An Apache module which implements a particular multiprocessing strategy, is called a Multi-Processing Module (MPM). The single-threaded multi-process link:http://httpd.apache.org/docs/2.4/mod/prefork.html[prefork MPM] had been the default and the most popular one for a long time, but in recent times the hybrid multi-threaded/multi-process link:http://httpd.apache.org/docs/2.4/mod/worker.html[worker MPM] is becoming increasingly popular because of its better performance and scalability. Furthermore, Apache 2.4 introduced the link:http://httpd.apache.org/docs/2.4/mod/event.html[event MPM] which is a hybrid evented/multi-threaded/multi-process MPM and offers even more scalability benefits. The prefork MPM remains in wide use today because it's the only MPM that works well with mod_php. The prefork MPM spawns multiple worker child processes. HTTP requests are first accepted by a so-called control process, and then forwarded to one of the worker processes. The next section contains a diagram which shows the prefork MPM's architecture. [appendix] == About Nginx Nginx is a lightweight web server that is becoming increasingly popular. It is known to be smaller, lighter weight and more scalable than Apache thanks to its evented I/O architecture. That said, Nginx is less flexible than Apache. For example it has no dynamic module system: all modules must be statically compiled into Nginx. passenger-5.0.30/doc/DeveloperQuickstart.md000644 000765 000024 00000007005 12233035540 021277 0ustar00honglistaff000000 000000 # Phusion Passenger Developer QuickStart _Watch the Developer QuickStart screencast_ Phusion Passenger provides an easy and convenient development environment that contributors can use. The development environment is a 64-bit Ubuntu 14.04 virtual machine and contains everything that you need. The build toolchain is already setup, all dependencies are installed and web servers are preconfigured. You can start coding almost immediately. And because it's a virtual machine, it doesn't matter which host operating system you're using. You use this development environment through [Vagrant](http://www.vagrantup.com/) and [VirtualBox](https://www.virtualbox.org/). Vagrant is an extremely useful tool for managing virtual machines, while VirtualBox is virtualization software. Both tools are free and open source. Vagrant allows you to easily share a directory between the host OS and the VM. This means that you can use the editor on your host OS to edit source files, and compile inside the VM. ## Getting started 1. Install [Vagrant](http://www.vagrantup.com/). 2. Install [VirtualBox](http://www.virtualbox.org/). 3. Inside the Phusion Passenger source tree, run: `vagrant up`. This will spin up the VM and will set it up. This can take a while so feel free to grab a cup of coffee. 4. Once the VM has been setup, login to it by running: `vagrant ssh`. ## Workflow The workflow is to: * Edit code on the host. * Use git commands on the host. * Compile in the VM. * Run tests in the VM. The Phusion Passenger source code is located in /vagrant. When you're done developing Phusion Passenger, you can shut down the VM by running `vagrant halt`. Next time you want to spin it up again, run `vagrant up`. ## Starting and accessing Apache Apache is installed, but it's not set up with Phusion Passenger by default. So you must first compile the Phusion Passenger Apache module: cd /vagrant rake apache2 Next, enable the Phusion Passenger module and restart Apache: sudo a2enmod passenger sudo service apache2 restart You can now access Apache from the host on http://127.0.0.1:8000 through :8005. The VM has a sample Ruby Rack application configured on http://127.0.0.1:8001/. Visit that URL and see it in action. Its source code is located in `dev/rack.test` in the Phusion Passenger source tree. ## Starting and accessing Nginx The Nginx source code and binaries are located in /home/vagrant/nginx. If this is the first time you use Nginx in this VM, you must install it. Run: cd /home/vagrant/nginx rake bootstrap Next, start Nginx by using a script that the development environment provides: ./start You can now access Nginx from the host on http://127.0.0.1:8100 through :8105. The VM has a sample Ruby Rack application configured on http://127.0.0.1:8101/. Visit that URL and see it in action. Its source code is located in `dev/rack.test` in the Phusion Passenger source tree. ## Running tests Tests can be run immediately without any setup. rake test:cxx rake test:integration:apache2 rake test:integration:nginx ## Further reading * [Contributors Guide](https://github.com/phusion/passenger/blob/master/CONTRIBUTING.md) * [Design and Architecture](https://www.phusionpassenger.com/documentation/Design%20and%20Architecture.html) * [Coding Tips and Pitfalls](https://github.com/phusion/passenger/blob/master/doc/CodingTipsAndPitfalls.md) passenger-5.0.30/doc/images/000755 000765 000024 00000000000 12233035540 016220 5ustar00honglistaff000000 000000 passenger-5.0.30/doc/Packaging.html000644 000765 000024 00000143664 12233035540 017543 0ustar00honglistaff000000 000000 Packaging
Packaging

Introduction

This document describes how packagers can package Phusion Passenger binaries for their operating system.

Phusion Passenger can be configured in 2 ways, the "originally packaged" configuration where everything is in the same directory, and the "natively packaged" configuration where files are scattered across the filesystem, e.g. in a FHS-compliant configuration. This document describes how you can configure Phusion Passenger to locate its own files when they're scattered across the filesystem.

Phusion Passenger files are also called assets in this document.

Originally packaged

This is the configuration you get when you checkout Phusion Passenger from git, when you install Phusion Passenger from a gem or when you extract it from a tarball. All the original files are stored in a single directory tree, which we call the source root.

The git repository, gems and tarballs do not come with any binaries; they have to be compiled by the user. Phusion Passenger looks for binaries in, and (if the user initiates the compilation process) stores binaries in, the following directories:

  • Normally, binaries are to be located in the buildout subdirectory under the source root.
  • Phusion Passenger Standalone does things a little differently. Binaries are to be located in one of the following directories, whichever it finds first:

    • ~/.passenger/standalone/<VERSION>/<TYPE-AND-ARCH> (a)
    • /var/lib/passenger-standalone/<VERSION-AND-ARCH> (b)

    If neither directories exist, then Passenger Standalone compiles the binaries and stores them in (b) (when running as root) or in (a). It still looks for everything else (like the .rb files) in the source root.

Natively packaged

Phusion Passenger is packaged, usually (but not necessarily) through a DEB or RPM package. This configuration comes not only with all necessary binaries, but also with some (but not all) source files. This is because when you run Phusion Passenger with a different Ruby interpreter than the packager intended, Phusion Passenger must be able to compile a new Ruby extension for that Ruby interpreter. This configuration does not however allow compiling against a different Apache version than the packager intended (but does allow compiling against a different Nginx version).

In this configuration, files can be scattered anywhere throughout the filesystem. This way Phusion Passenger can be packaged in an FHS-compliant way. The exact locations of the different types of files can be specified through a location configuration file. The existance and usage of a location configuration file does not automatically imply that Phusion Passenger is natively packaged.

If Phusion Passenger needs to have a new Ruby extension compiled, then it will store that in ~/.passenger/native_support/<VERSION>/<ARCH>.

The location configuration file

The Phusion Passenger administration tools, such as passenger-status, look for a location configuration file in the following places, in the given order:

  • The environment variable $PASSENGER_LOCATION_CONFIGURATION_FILE.
  • <RUBYLIBDIR>/phusion_passenger/locations.ini, where is the Ruby library directory that contains phusion_passenger.rb. For example, /usr/lib/ruby/1.9.0/phusion_passenger/locations.ini.
  • ~/.passenger/locations.ini
  • /etc/phusion-passenger/locations.ini

If it cannot find a location configuration file, then it assumes that Phusion Passenger is originally packaged. If a location configuration file is found then the configuration is determined by the natively_packaged option in the location configuration file, which can be either "true" or "false".

The Apache module and the Nginx module expect PassengerRoot/passenger_root to refer to either a directory or a file. If the value refers to a directory, then it assumes that Phusion Passenger is originally packaged, where the source root is the specified directory. If the value refers to a file, then it will use it as the location configuration file, and the configuration depends on the natively_packaged setting.

The location configuration file is an ini file that looks as follows:

[locations]
natively_packaged=true
bin_dir=/usr/bin
support_binaries_dir=/usr/lib/phusion-passenger/support-binaries
lib_dir=/usr/lib/phusion-passenger
helper_scripts_dir=/usr/share/phusion-passenger/helper-scripts
resources_dir=/usr/share/phusion-passenger
include_dir=/usr/share/phusion-passenger/include
doc_dir=/usr/share/doc/phusion-passenger
ruby_libdir=/usr/lib/ruby/vendor_ruby
apache2_module_path=/usr/lib/apache2/modules/mod_passenger.so
ruby_extension_source_dir=/usr/share/phusion-passenger/ruby_extension_source
nginx_module_source_dir=/usr/share/phusion-passenger/ngx_http_passenger_module

All keys except fo natively_packaged specify the locations of assets and asset directories. The "Asset types" section provides a description of all asset types.

Thus, if you're packaging Phusion Passenger, then we recommend the following:

  • Put a locations.ini file in <RUBYLIBDIR>/phusion_passenger/locations.ini and set PassengerRoot/passenger_root to that filename. We don't recommend using ~/.passenger or /etc/phusion-passenger because if the user wants to install a different Phusion Passenger version alongside the one that you've packaged, then that other version will incorrectly locate your packaged files instead of its own files.
  • Always set natively_packaged to "true". The "false" value is used internally for implementing Phusion Passenger Standalone and should never be used by packagers.

The Phusion Passenger Ruby libraries

phusion_passenger.rb

The Phusion Passenger administration tools are written in Ruby. So the first thing they do is trying to load phusion_passenger.rb, which is the source file responsible for figuring out where all the other Phusion Passenger files are. It tries to look for phusionpassenger.rb in <OWN_DIRECTORY>/../src/ruby_supportlib where <OWN_DIRECTORY> is the directory that the tool is located in. If phusionpassenger.rb is not there, then it tries to load it from the normal Ruby load path.

Ruby extension

The Phusion Passenger loader scripts try to load the Phusion Passenger Ruby extension (passenger_native_support.so) from the following places, in the given order:

  • If Phusion Passenger is originally packaged, it will look for the Ruby extension in <SOURCE_ROOT>/libout/ruby/<ARCH>. Otherwise, this step is skipped.
  • The Ruby library load path.
  • ~/.passenger/native_support/<VERSION>/<ARCH>

If it cannot find the Ruby extension in any of the above places, then it will attempt to compile the Ruby extension and store it in ~/.passenger/native_support/<VERSION>/<ARCH>.

Conclusion for packagers

If you're packaging Phusion Passenger then you should put both phusion_passenger.rb and passenger_native_support.so somewhere in the Ruby load path, or make sure that that directory is included in the $RUBYLIB environment variable. You cannot specify a custom directory though the location configuration file.

Asset types

Throughout the Phusion Passenger codebase, we refer to all kinds of assets. Here's a list of all possible assets and asset directories.

  • source_root

    When Phusion Passenger is originally packaged, this refers to the directory that contains the entire Phusion passenger source tree. Not available when natively packaged.

  • bin_dir

    A directory containing administration binaries and scripts and like passenger-status; tools that the user may directly invoke on the command line.

    Value when originally packaged: <SOURCE_ROOT>/bin

  • support_binaries_dir

    A directory that contains (platform-dependent) binaries that Phusion Passenger uses, but that should not be directly invoked from the command line. Things like PassengerAgent are located here.

    Value when originally packaged:

    • Normally: <SOURCE_ROOT>/buildout/support-binaries
    • Passenger Standalone: ~/.passenger/standalone/<VERSION>/support-<ARCH>
  • helper_scripts_dir

    A directory that contains non-binary scripts that Phusion Passenger uses, but that should not be directly invoked from the command line. Things like rack-loader.rb are located here.

    Value when originally packaged: <SOURCE_ROOT>/helper-scripts

  • resources_dir

    A directory that contains non-executable, platform-independent resource files that the user should not directly access, like error page templates and configuration file templates.

    Value when originally packaged: <SOURCE_ROOT>/resources.

  • doc_dir

    A directory that contains documentation.

    Value when originally packaged: <SOURCE_ROOT>/doc.

  • include_dir

    A directory that contains the Phusion Passenger header files that are necessary for compiling Nginx.

    Value when originally packaged: <SOURCE_ROOT>/src

  • lib_dir

    A directory that contains the Phusion Passenger library files, e.g. libboost_oxt.a and various .o files.

    Value when originally packaged: <SOURCE_ROOT>/buildout

  • ruby_libdir

    A directory that contains the Phusion Passenger Ruby library files. Note that the Phusion Passenger administration tools still locate phusion_passenger.rb as described in the section "The Phusion Passenger Ruby libraries", irregardless of the value of this key in the location configuration file. The value is only useful to non-Ruby Phusion Passenger code.

    Value when originally packaged: <SOURCE_ROOT>/src/ruby_supportlib.

  • apache2_module_path

    The filename of the Apache 2 module, or the filename that the Apache 2 module will be stored after it's compiled. Used by passenger-install-module to print an example configuration snippet.

    Value when originally packaged: <SOURCE_ROOT>/buildout/apache2/mod_passenger.so.

  • ruby_extension_source_dir

    The directory that contains the source code for the Phusion Passenger Ruby extension. Phusion Passenger uses these sources to build a Ruby extension, when it detects that the user is using a new Ruby interpreter for which no Ruby extension has been compiled.

    Value when originally packaged: <SOURCE_ROOT>/src/ruby_native_extension.

  • nginx_module_source_dir

    The directory that contains the source code for the Phusion Passenger Nginx module. passenger-install-nginx-module uses these sources to build Nginx with Phusion Passenger support.

    Value when originally packaged: <SOURCE_ROOT>/src/nginx_module.

Optional fields:

  • build_system_dir

    The directory that contains the Phusion Passenger main Rakefile, used for compiling the Apache module and the agent executable. This field is only present if Phusion Passenger is compilable. Native packages usually do not have this field because they ship a precompiled Apache module and a precompiled agent executable.

    Value when originally packaged: <SOURCE_ROOT>

  • download_cache_dir

    The directory that contains cached downloaded agent executables. Its main use case is to speed up agent executable downloading when Phusion Passenger is installed from a Ruby gem. When the user installs the gem, a script is invoked which downloads agent executables from the Phusion Passenger websites. The downloaded files are stored in this directory. Then later, when the user runs passenger start or any other command which requires the agent executable, the executable will be copied from this cache directory instead of downloaded.

    Native packages ship precompiled executables, so they can omit this field.

    Value when originally packaged: <SOURCE_ROOT>/download_cache

Vendoring of libraries

Phusion Passenger vendors libev and libuv in order to make installation easier for users on operating systems without proper package management, like OS X. If you want Phusion Passenger to compile against the system-provided libev and/or libuv instead, then set the following environment variables before compiling:

  • export USE_VENDORED_LIBEV=no
  • export USE_VENDORED_LIBUV=no

Note that we require at least libev 4.11 and libuv 1.4.2.

Generating gem and tarball

Use the following commands to generate a gem and tarball, in which Phusion Passenger is originally packaged and without any binaries:

rake package:gem
rake package:tarball

The files will be stored in pkg/.

Fakeroot

You can generate a fakeroot with the command rake fakeroot. This will generate an FHS-compliant directory tree in pkg/fakeroot, which you can directly package or with minor modifications. The fakeroot even contains a location configuration file.

© Phusion

passenger-5.0.30/doc/Packaging.txt.md000644 000765 000024 00000031033 12233035540 017777 0ustar00honglistaff000000 000000 # Introduction This document describes how packagers can package Phusion Passenger binaries for their operating system. Phusion Passenger can be configured in 2 ways, the "originally packaged" configuration where everything is in the same directory, and the "natively packaged" configuration where files are scattered across the filesystem, e.g. in a FHS-compliant configuration. This document describes how you can configure Phusion Passenger to locate its own files when they're scattered across the filesystem. Phusion Passenger files are also called _assets_ in this document. ## Originally packaged This is the configuration you get when you checkout Phusion Passenger from git, when you install Phusion Passenger from a gem or when you extract it from a tarball. All the original files are stored in a single directory tree, which we call the _source root_. The git repository, gems and tarballs do not come with any binaries; they have to be compiled by the user. Phusion Passenger looks for binaries in, and (if the user initiates the compilation process) stores binaries in, the following directories: * Normally, binaries are to be located in the `buildout` subdirectory under the source root. * Phusion Passenger Standalone does things a little differently. Binaries are to be located in one of the following directories, whichever it finds first: - `~/.passenger/standalone//` (a) - `/var/lib/passenger-standalone/` (b) If neither directories exist, then Passenger Standalone compiles the binaries and stores them in (b) (when running as root) or in (a). It still looks for everything else (like the .rb files) in the source root. ## Natively packaged Phusion Passenger is packaged, usually (but not necessarily) through a DEB or RPM package. This configuration comes not only with all necessary binaries, but also with some (but not all) source files. This is because when you run Phusion Passenger with a different Ruby interpreter than the packager intended, Phusion Passenger must be able to compile a new Ruby extension for that Ruby interpreter. This configuration does not however allow compiling against a different Apache version than the packager intended (but does allow compiling against a different Nginx version). In this configuration, files can be scattered anywhere throughout the filesystem. This way Phusion Passenger can be packaged in an FHS-compliant way. The exact locations of the different types of files can be specified through a _location configuration file_. The existance and usage of a location configuration file does not automatically imply that Phusion Passenger is natively packaged. If Phusion Passenger needs to have a new Ruby extension compiled, then it will store that in `~/.passenger/native_support//`. # The location configuration file The Phusion Passenger administration tools, such as `passenger-status`, look for a location configuration file in the following places, in the given order: * The environment variable `$PASSENGER_LOCATION_CONFIGURATION_FILE`. * `/phusion_passenger/locations.ini`, where is the Ruby library directory that contains phusion_passenger.rb. For example, `/usr/lib/ruby/1.9.0/phusion_passenger/locations.ini`. * `~/.passenger/locations.ini` * `/etc/phusion-passenger/locations.ini` If it cannot find a location configuration file, then it assumes that Phusion Passenger is originally packaged. If a location configuration file is found then the configuration is determined by the `natively_packaged` option in the location configuration file, which can be either "true" or "false". The Apache module and the Nginx module expect `PassengerRoot`/`passenger_root` to refer to either a directory or a file. If the value refers to a directory, then it assumes that Phusion Passenger is originally packaged, where the source root is the specified directory. If the value refers to a file, then it will use it as the location configuration file, and the configuration depends on the `natively_packaged` setting. The location configuration file is an ini file that looks as follows: [locations] natively_packaged=true bin_dir=/usr/bin support_binaries_dir=/usr/lib/phusion-passenger/support-binaries lib_dir=/usr/lib/phusion-passenger helper_scripts_dir=/usr/share/phusion-passenger/helper-scripts resources_dir=/usr/share/phusion-passenger include_dir=/usr/share/phusion-passenger/include doc_dir=/usr/share/doc/phusion-passenger ruby_libdir=/usr/lib/ruby/vendor_ruby apache2_module_path=/usr/lib/apache2/modules/mod_passenger.so ruby_extension_source_dir=/usr/share/phusion-passenger/ruby_extension_source nginx_module_source_dir=/usr/share/phusion-passenger/ngx_http_passenger_module All keys except fo `natively_packaged` specify the locations of assets and asset directories. The "Asset types" section provides a description of all asset types. Thus, if you're packaging Phusion Passenger, then we recommend the following: * Put a locations.ini file in `/phusion_passenger/locations.ini` and set `PassengerRoot`/`passenger_root` to that filename. We don't recommend using `~/.passenger` or `/etc/phusion-passenger` because if the user wants to install a different Phusion Passenger version alongside the one that you've packaged, then that other version will incorrectly locate your packaged files instead of its own files. * Always set `natively_packaged` to "true". The "false" value is used internally for implementing Phusion Passenger Standalone and should never be used by packagers. # The Phusion Passenger Ruby libraries ## phusion_passenger.rb The Phusion Passenger administration tools are written in Ruby. So the first thing they do is trying to load `phusion_passenger.rb`, which is the source file responsible for figuring out where all the other Phusion Passenger files are. It tries to look for phusion_passenger.rb in `/../src/ruby_supportlib` where `` is the directory that the tool is located in. If phusion_passenger.rb is not there, then it tries to load it from the normal Ruby load path. ## Ruby extension The Phusion Passenger loader scripts try to load the Phusion Passenger Ruby extension (`passenger_native_support.so`) from the following places, in the given order: * If Phusion Passenger is originally packaged, it will look for the Ruby extension in `/libout/ruby/`. Otherwise, this step is skipped. * The Ruby library load path. * `~/.passenger/native_support//` If it cannot find the Ruby extension in any of the above places, then it will attempt to compile the Ruby extension and store it in `~/.passenger/native_support//`. ## Conclusion for packagers If you're packaging Phusion Passenger then you should put both phusion_passenger.rb and `passenger_native_support.so` somewhere in the Ruby load path, or make sure that that directory is included in the `$RUBYLIB` environment variable. You cannot specify a custom directory though the location configuration file. # Asset types Throughout the Phusion Passenger codebase, we refer to all kinds of assets. Here's a list of all possible assets and asset directories. * `source_root` When Phusion Passenger is originally packaged, this refers to the directory that contains the entire Phusion passenger source tree. Not available when natively packaged. * `bin_dir` A directory containing administration binaries and scripts and like `passenger-status`; tools that the user may directly invoke on the command line. Value when originally packaged: `/bin` * `support_binaries_dir` A directory that contains (platform-dependent) binaries that Phusion Passenger uses, but that should not be directly invoked from the command line. Things like PassengerAgent are located here. Value when originally packaged: - Normally: `/buildout/support-binaries` - Passenger Standalone: `~/.passenger/standalone//support-` * `helper_scripts_dir` A directory that contains non-binary scripts that Phusion Passenger uses, but that should not be directly invoked from the command line. Things like rack-loader.rb are located here. Value when originally packaged: `/helper-scripts` * `resources_dir` A directory that contains non-executable, platform-independent resource files that the user should not directly access, like error page templates and configuration file templates. Value when originally packaged: `/resources`. * `doc_dir` A directory that contains documentation. Value when originally packaged: `/doc`. * `include_dir` A directory that contains the Phusion Passenger header files that are necessary for compiling Nginx. Value when originally packaged: `/src` * `lib_dir` A directory that contains the Phusion Passenger library files, e.g. libboost_oxt.a and various .o files. Value when originally packaged: `/buildout` * `ruby_libdir` A directory that contains the Phusion Passenger Ruby library files. Note that the Phusion Passenger administration tools still locate phusion_passenger.rb as described in the section "The Phusion Passenger Ruby libraries", irregardless of the value of this key in the location configuration file. The value is only useful to non-Ruby Phusion Passenger code. Value when originally packaged: `/src/ruby_supportlib`. * `apache2_module_path` The filename of the Apache 2 module, or the filename that the Apache 2 module will be stored after it's compiled. Used by `passenger-install-module` to print an example configuration snippet. Value when originally packaged: `/buildout/apache2/mod_passenger.so`. * `ruby_extension_source_dir` The directory that contains the source code for the Phusion Passenger Ruby extension. Phusion Passenger uses these sources to build a Ruby extension, when it detects that the user is using a new Ruby interpreter for which no Ruby extension has been compiled. Value when originally packaged: `/src/ruby_native_extension`. * `nginx_module_source_dir` The directory that contains the source code for the Phusion Passenger Nginx module. passenger-install-nginx-module uses these sources to build Nginx with Phusion Passenger support. Value when originally packaged: `/src/nginx_module`. Optional fields: * `build_system_dir` The directory that contains the Phusion Passenger main Rakefile, used for compiling the Apache module and the agent executable. This field is only present if Phusion Passenger is compilable. Native packages usually do not have this field because they ship a precompiled Apache module and a precompiled agent executable. Value when originally packaged: `` * `download_cache_dir` The directory that contains cached downloaded agent executables. Its main use case is to speed up agent executable downloading when Phusion Passenger is installed from a Ruby gem. When the user installs the gem, a script is invoked which downloads agent executables from the Phusion Passenger websites. The downloaded files are stored in this directory. Then later, when the user runs `passenger start` or any other command which requires the agent executable, the executable will be copied from this cache directory instead of downloaded. Native packages ship precompiled executables, so they can omit this field. Value when originally packaged: `/download_cache` # Vendoring of libraries Phusion Passenger vendors libev and libuv in order to make installation easier for users on operating systems without proper package management, like OS X. If you want Phusion Passenger to compile against the system-provided libev and/or libuv instead, then set the following environment variables before compiling: * `export USE_VENDORED_LIBEV=no` * `export USE_VENDORED_LIBUV=no` Note that we require at least libev 4.11 and libuv 1.4.2. # Generating gem and tarball Use the following commands to generate a gem and tarball, in which Phusion Passenger is originally packaged and without any binaries: rake package:gem rake package:tarball The files will be stored in `pkg/`. # Fakeroot You can generate a fakeroot with the command `rake fakeroot`. This will generate an FHS-compliant directory tree in `pkg/fakeroot`, which you can directly package or with minor modifications. The fakeroot even contains a location configuration file. passenger-5.0.30/doc/Security of user switching support.html000644 000765 000024 00000434205 12233035540 024461 0ustar00honglistaff000000 000000 Security of user switching support in Passenger

1. Problem description

Tip It is strongly recommended that you first read our Architectural Overview.

A straightforward implementation of Passenger will spawn Rails applications in the same user context as Apache itself. On server machines which host multiple websites for multiple users, this may not be desired. All Rails applications spawned by Passenger will be able to read and write to all directories that the web server can. So for example, Joe’s Rails applications could read Jane’s Rails application’s database.yml or delete her application files. This is also a problem that typically plagues PHP web hosts.

There are multiple ways to solve this problem. The goal of this document is to inform the reader about the solutions have we have analyzed, so that Passenger’s security may be peer reviewed.

2. Analysis of possible solutions

It seems that the only way to solve this problem on Unix, is to run each Rails application server as its owner’s user and group. Passenger can make use of one of the following methods to implement this:

  1. Apache (and thus Passenger) must already be running as root.

  2. Using Apache’s suEXEC.

  3. A setuid root wrapper application must exist, to allow non-root processes to obtain root privileges (or at least, the privilege to switch user).

  4. For each user $X that Passenger will need to switch to, there must exist a setuid $X wrapper application.

  5. Using su.

  6. Using sudo.

Let us take a look at each method in detail.

2.1. Apache must already be running as root

First, let us take a look at the typical Apache setup, in which Apache is bound to port 80, and uses the prefork MPM. Binding to any port lower than 1024 requires root privileges, so Apache is typically run as root. This poses an unacceptable security risk, so Apache’s prefork MPM will, upon receiving an HTTP request, spawn a child process with the privileges of a normal user, typically www-data or nobody. See the documentation for the prefork MPM - in particular the “User” and “Group” directives - for details. The process which is responsible for spawning child processes (also called the control process) is run as root. This is also true for the worker MPM.

Since Passenger has access to the control process, in the typical Apache setup, Passenger can already launch Rails applications as a different user. But now we have to ask this question:

If Apache is not running as root, are there still any Passenger users who want to run Rails applications as different users?

If the answer is yes, then we cannot use this method.

The advantage of this method is that setting up Apache to run as root is incredibly easy, and requires no new framework to be written. However, testing this method in automated unit tests will require running the unit test suite as root.

2.2. Using Apache’s suEXEC

Apache’s suEXEC allows one to run CGI processes as different users. But it seems that suEXEC can only be used for CGI, and is not a general-purpose mechanism. The PHP-suEXEC software allows one to run PHP applications via suEXEC, but it requires patching suEXEC. If Passenger is to use suEXEC, then it is likely that we’ll have to patch suEXEC. The suEXEC website strongly discourages patching.

2.3. Using a setuid root wrapper application

If we use this method, we must be extremely careful. It must not be possible for arbitrary processes to gain root privileges. We want Passenger, and only Passenger, to be able to gain root privileges.

There are multiple ways to implement this security. The first one is to use a password file, which only Apache and the wrapper can read, through the use of proper file permissions. The password file must never be world readable or writable.

It works as follows:

  1. Passenger runs the wrapper.

  2. Passenger passes the content of the password file to the wrapper, via an anonymous pipe (or some other anonymous channel, that no other processes can access).

  3. The wrapper checks whether the passed content is the same as what is in the password file. If it is, then it is proven that whatever application ran the wrapper has read access to the password file, and thus is authorized to use the wrapper.

An obvious problem that arises is: how does the wrapper locate its own password file? We obviously do not want to be able to specify the password filename as an argument to the wrapper: that would defeat the point of the password file. The solution is that the filename is to be hardcoded into the binary during compile time.

Another way to implement security is to use a whitelist of users that are allowed to use the wrapper. The wrapper can then check whether the calling process’s user is in the whitelist.

Writing a wrapper is not too hard. Furthermore, unit tests do not have to be run as root, in contrast to the run-Apache-as-root method.

2.4. Using a setuid $X wrapper application

A setuid $X wrapper will work in a fashion similar to the setuid root wrapper, i.e. it will use a password file for authorization.

Passenger does not spawn Rails applications itself, but does so via the spawn server. This spawn server is also responsible for preloading the Rails framework and the Rails application code, in order to speed up the spawning of Rails applications. See the design document of the spawn server for details. The spawn server never calls exec(): doing so will make preloading useless. If Passenger is to use a setuid $X wrapper, then it must start the spawn server via the wrapper. The spawn server itself cannot use the wrapper.

However, doing so will make preloading less efficient. Passenger will be forced to run a spawn server for each user. The different spawn servers do not share memory with each other, so a lot of memory is wasted compared to the other methods.

Implementing this will also take more work. One has to create a different wrapper for each user, and to install it.

2.5. Using su

The standard Unix su tool asks for the root password. It’s a bad idea for Apache to know the root password, so using su is not a viable alternative.

2.6. Using sudo

It might be possible to use the sudo utility. sudo can be configured in such a way that the user Apache runs as can use sudo without having to enter a password.

However, Passenger uses an anonymous communication channel (an unnamed Unix socket) to communicate with the spawn server. sudo seems to close all file descriptors before executing an application, so Passenger will have to communicate with the spawn server via a non-anonymous channel, such as a named Unix socket. Because other processes can access this channel, it can introduce potential security problems. Note that passing information via program arguments is not secure: it is possible to view that information with tools like ps, or (on Linux) by reading the file /proc/$PID/cmdline.

So it seems sudo is not a viable alternative.

2.7. Common security issues

Whatever method Passenger will use, the following security principles must be honored:

  • Rails applications must never be run as root.

It might also be worthy to look into suEXEC’s security model for inspiration.

Also, the following questions remain:

  • Is there a need for a user whitelist/blacklist? That is, is there a need for the ability to restrict the set of users that Passenger can switch to?

3. Chosen solution

Running Apache as root and writing a setuid root wrapper are the main contestants. The former is preferred, because it’s easier to implement.

We have had some conversations with people on the IRC channel #rubyonrails. Among those people, nobody has ever run Apache as non-root. Because of this we have chosen to implement the Running Apache as root solution, until a significant number of users request us to implement the setuid root wrapper solution.

Please read the Ruby API documentation — in particular that of the ApplicationSpawner class — for implementation details. But to make a long story short: it will switch to the owner of the file config/environment.rb. User whitelisting/blacklisting is currently not implemented. We rely on the system administrator to set the correct owner on that file.

We have also not implemented suEXEC’s security model. suEXEC’s model is quite paranoid, and although paranoia is good to a certain extent, it can be in the way of usability while proving little extra security. We are not entirely convinced that implementing suEXEC’s full security model will provide significant benefits, but if you have good reasons to think otherwise, please feel free to discuss it with us.


passenger-5.0.30/doc/Security of user switching support.idmap.txt000644 000765 000024 00000002571 12233035540 025422 0ustar00honglistaff000000 000000 ###### Autogenerated by Mizuho, DO NOT EDIT ###### # This file maps section names to IDs so that the commenting system knows which # comments belong to which section. Section names may be changed at will but # IDs always stay the same, allowing one to retain old comments even if you # rename a section. # # This file is autogenerated but is not a cache; you MUST NOT DELETE this # file and you must check it into your version control system. If you lose # this file you may lose the ability to identity old comments. # # Entries marked with "fuzzy" indicate that the section title has changed # and that Mizuho has found an ID which appears to be associated with that # section. You should check whether it is correct, and if not, fix it. 1. Problem description => problem-description-lvxgz1 2. Analysis of possible solutions => analysis-of-possible-solutions-1ca8rx2 2.1. Apache must already be running as root => apache-must-already-be-running-as-root-17kidbt 2.2. Using Apache’s suEXEC => using-apache-s-suexec-1smlb2z 2.3. Using a setuid root wrapper application => using-a-setuid-root-wrapper-application-1g15zaq 2.4. Using a setuid $X wrapper application => using-a-setuid-x-wrapper-application-1mycuib 2.5. Using su => using-su-13r1zdf 2.6. Using sudo => using-sudo-ry6q7h 2.7. Common security issues => common-security-issues-25g9co 3. Chosen solution => chosen-solution-1wovtue passenger-5.0.30/doc/Security of user switching support.txt000644 000765 000024 00000022111 12233035540 024321 0ustar00honglistaff000000 000000 Security of user switching support in Passenger =============================================== Problem description ------------------- TIP: It is strongly recommended that you first read our link:Architectural%20overview.html[Architectural Overview]. A straightforward implementation of Passenger will spawn Rails applications in the same user context as Apache itself. On server machines which host multiple websites for multiple users, this may not be desired. All Rails applications spawned by Passenger will be able to read and write to all directories that the web server can. So for example, Joe's Rails applications could read Jane's Rails application's 'database.yml' or delete her application files. This is also a problem that typically plagues PHP web hosts. There are multiple ways to solve this problem. The goal of this document is to inform the reader about the solutions have we have analyzed, so that Passenger's security may be peer reviewed. Analysis of possible solutions ------------------------------ It seems that the only way to solve this problem on Unix, is to run each Rails application server as its owner's user and group. Passenger can make use of one of the following methods to implement this: 1. Apache (and thus Passenger) must already be running as root. 2. Using Apache's suEXEC. 3. A setuid root wrapper application must exist, to allow non-root processes to obtain root privileges (or at least, the privilege to switch user). 4. For each user $X that Passenger will need to switch to, there must exist a setuid $X wrapper application. 5. Using 'su'. 6. Using 'sudo'. Let us take a look at each method in detail. [[apache_root]] Apache must already be running as root ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ First, let us take a look at the typical Apache setup, in which Apache is bound to port 80, and uses the prefork MPM. Binding to any port lower than 1024 requires root privileges, so Apache is typically run as root. This poses an unacceptable security risk, so Apache's prefork MPM will, upon receiving an HTTP request, spawn a child process with the privileges of a normal user, typically 'www-data' or 'nobody'. See http://httpd.apache.org/docs/2.2/mod/prefork.html[the documentation for the prefork MPM] - in particular the ``User'' and ``Group'' directives - for details. The process which is responsible for spawning child processes (also called the control process) is run as root. This is also true for http://httpd.apache.org/docs/2.2/mod/worker.html[the worker MPM]. Since Passenger has access to the control process, in the typical Apache setup, Passenger can already launch Rails applications as a different user. But now we have to ask this question: ================================= If Apache is not running as root, are there still any Passenger users who want to run Rails applications as different users? ================================= If the answer is yes, then we cannot use this method. The advantage of this method is that setting up Apache to run as root is incredibly easy, and requires no new framework to be written. However, testing this method in automated unit tests will require running the unit test suite as root. Using Apache's suEXEC ~~~~~~~~~~~~~~~~~~~~~ Apache's http://httpd.apache.org/docs/2.0/suexec.html[suEXEC] allows one to run CGI processes as different users. But it seems that suEXEC can only be used for CGI, and is not a general-purpose mechanism. The http://alain.knaff.lu/howto/PhpSuexec/[PHP-suEXEC] software allows one to run PHP applications via suEXEC, but it requires patching suEXEC. If Passenger is to use suEXEC, then it is likely that we'll have to patch suEXEC. The suEXEC website strongly discourages patching. Using a setuid root wrapper application ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If we use this method, we must be extremely careful. It must not be possible for arbitrary processes to gain root privileges. We want Passenger, and only Passenger, to be able to gain root privileges. There are multiple ways to implement this security. The first one is to use a password file, which only Apache and the wrapper can read, through the use of proper file permissions. The password file must never be world readable or writable. It works as follows: 1. Passenger runs the wrapper. 2. Passenger passes the content of the password file to the wrapper, via an anonymous pipe (or some other anonymous channel, that no other processes can access). 3. The wrapper checks whether the passed content is the same as what is in the password file. If it is, then it is proven that whatever application ran the wrapper has read access to the password file, and thus is authorized to use the wrapper. An obvious problem that arises is: how does the wrapper locate its own password file? We obviously do not want to be able to specify the password filename as an argument to the wrapper: that would defeat the point of the password file. The solution is that the filename is to be hardcoded into the binary during compile time. Another way to implement security is to use a whitelist of users that are allowed to use the wrapper. The wrapper can then check whether the calling process's user is in the whitelist. Writing a wrapper is not too hard. Furthermore, unit tests do not have to be run as root, in contrast to the run-Apache-as-root method. [[setuid_root]] Using a setuid $X wrapper application ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A setuid $X wrapper will work in a fashion similar to the setuid root wrapper, i.e. it will use a password file for authorization. Passenger does not spawn Rails applications itself, but does so via the spawn server. This spawn server is also responsible for preloading the Rails framework and the Rails application code, in order to speed up the spawning of Rails applications. See the design document of the spawn server for details. The spawn server never calls `exec()`: doing so will make preloading useless. If Passenger is to use a setuid $X wrapper, then it must start the spawn server via the wrapper. The spawn server itself cannot use the wrapper. However, doing so will make preloading less efficient. Passenger will be forced to run a spawn server for each user. The different spawn servers do not share memory with each other, so a lot of memory is wasted compared to the other methods. Implementing this will also take more work. One has to create a different wrapper for each user, and to install it. Using 'su' ~~~~~~~~~~ The standard Unix 'su' tool asks for the root password. It's a bad idea for Apache to know the root password, so using 'su' is not a viable alternative. Using 'sudo' ~~~~~~~~~~~~ It might be possible to use the 'sudo' utility. sudo can be configured in such a way that the user Apache runs as can use sudo without having to enter a password. However, Passenger uses an anonymous communication channel (an unnamed Unix socket) to communicate with the spawn server. sudo seems to close all file descriptors before executing an application, so Passenger will have to communicate with the spawn server via a non-anonymous channel, such as a named Unix socket. Because other processes can access this channel, it can introduce potential security problems. Note that passing information via program arguments is not secure: it is possible to view that information with tools like 'ps', or (on Linux) by reading the file `/proc/$PID/cmdline`. So it seems 'sudo' is not a viable alternative. Common security issues ~~~~~~~~~~~~~~~~~~~~~~ Whatever method Passenger will use, the following security principles must be honored: - Rails applications must never be run as root. It might also be worthy to look into suEXEC's security model for inspiration. Also, the following questions remain: - Is there a need for a user whitelist/blacklist? That is, is there a need for the ability to restrict the set of users that Passenger can switch to? Chosen solution --------------- Running Apache as root and writing a setuid root wrapper are the main contestants. The former is preferred, because it's easier to implement. We have had some conversations with people on the IRC channel #rubyonrails. Among those people, nobody has ever run Apache as non-root. Because of this we have chosen to implement the <> solution, until a significant number of users request us to implement the <> solution. Please read link:rdoc/index.html[the Ruby API documentation] -- in particular that of the 'ApplicationSpawner' class -- for implementation details. But to make a long story short: it will switch to the owner of the file 'config/environment.rb'. User whitelisting/blacklisting is currently not implemented. We rely on the system administrator to set the correct owner on that file. We have also not implemented suEXEC's security model. suEXEC's model is quite paranoid, and although paranoia is good to a certain extent, it can be in the way of usability while proving little extra security. We are not entirely convinced that implementing suEXEC's full security model will provide significant benefits, but if you have good reasons to think otherwise, please feel free to discuss it with us. passenger-5.0.30/doc/ServerOptimizationGuide.html000644 000765 000024 00000110246 12233035540 022500 0ustar00honglistaff000000 000000 Server Optimization Guide
Server Optimization Guide

Server optimization guide

This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/optimization/

© Phusion

passenger-5.0.30/doc/ServerOptimizationGuide.txt.md000644 000765 000024 00000000307 12233035540 022746 0ustar00honglistaff000000 000000 # Server optimization guide This documentation has moved. Please visit [https://www.phusionpassenger.com/library/config/optimization/](https://www.phusionpassenger.com/library/config/optimization/) passenger-5.0.30/doc/templates/000755 000765 000024 00000000000 12233035540 016751 5ustar00honglistaff000000 000000 passenger-5.0.30/doc/Users guide Apache.html000644 000765 000024 00001110013 12233035540 021157 0ustar00honglistaff000000 000000 Phusion Passenger users guide, Apache version

1. Support information

1.1. Supported operating systems and languages

1.2. Where to get support

  • Community discussion forum - post a message here if you’re experiencing problems. Support on this forum is provided by the community on a best-effort basis, so a (timely) response is not guaranteed.

  • Issue tracker - report bugs here.

  • Email support@phusion.nl if you are a Phusion Passenger Enterprise customer. Please mention your order reference. If you are not an Enterprise customer, we kindly redirect you to the community discussion forum instead.

  • Commercial support contracts are also available.

  • Report security vulnerabilities to security@phusion.nl. We will do our best to respond to you as quickly as we can, so please do not disclose the vulnerability until then.

Please consult the Phusion Passenger website for a full list of support resources.

2. Installation

2.1. Synopsis

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/

2.2. Installing or upgrading on Mac OS X with Homebrew

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/

2.3. Installing or upgrading on Debian or Ubuntu

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apt_repo/

2.3.1. Adding our APT repository

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apt_repo/

2.3.2. Installing packages

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apt_repo/

2.4. Installing or upgrading on Red Hat or CentOS

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/yum_repo/

2.4.1. Adding our YUM repository

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/yum_repo/

2.4.2. Installing packages

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/yum_repo/

2.5. Installing or upgrading on Heroku

Please refer to our Heroku Ruby demo for installation and upgrade instructions for Heroku.

2.6. Generic installation, upgrade and downgrade method: via RubyGems

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/

2.7. Generic installation, upgrade and downgrade method: via tarball

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/

2.8. Upgrading from open source to Enterprise

2.9. Cryptographic verification of installation files

2.9.1. Synopsis

2.9.2. Importing the Phusion Software Signing key

2.9.3. Verifying the Phusion Software Signing key

2.9.4. Verifying the gem and tarball

2.9.5. Verifying Git signatures

2.9.6. Verifying Debian packages

2.9.7. Verifying RPM packages

2.9.8. Revocation

2.10. Non-interactive, automatic, headless installs or upgrades

2.11. Customizing the compilation process

2.11.1. Setting the compiler

2.11.2. Adding additional compiler or linker flags

2.11.3. Forcing location of command line tools and dependencies

2.12. Dealing with multiple Apache installations

2.13. Working with the Apache configuration file

2.14. Disabling without uninstalling

2.15. Uninstalling

2.16. Moving to a different directory

3. Deploying a Rack-based Ruby application

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/

3.1. Tutorial/example: writing and deploying a Hello World Rack application

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/

3.2. Deploying to a virtual host’s root

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/

3.3. Deploying to a sub URI

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/

3.4. Redeploying (restarting the Rack application)

3.5. Rackup specifications for various web frameworks

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/config_ru.html

4. Deploying a WSGI (Python) application

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/

4.1. Tutorial/example: writing and deploying a Hello World WSGI application

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/

4.2. Deploying to a virtual host’s root

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/

4.3. Deploying to a sub URI

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/

4.4. Redeploying (restarting the WSGI application)

4.5. Sample passenger_wsgi.py for Django

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/wsgi_spec.html

5. Deploying a Node.js application

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/

6. Deploying a Meteor application

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/

7. Configuring Phusion Passenger

This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/

7.1. PassengerRoot <directory>

7.2. PassengerDefaultRuby <filename>

7.3. Deployment options

7.3.1. PassengerEnabled <on|off>

7.3.2. PassengerBaseURI <uri>

7.4. Application loading options

7.4.1. PassengerRuby <filename>

7.4.2. PassengerPython <filename>

7.4.3. PassengerNodejs <filename>

7.4.4. PassengerMeteorAppSettings <filename>

7.4.5. PassengerAppEnv <string>

7.4.6. RailsEnv <string>

7.4.7. RackEnv <string>

7.4.8. PassengerAppRoot <path/to/root>

7.4.9. PassengerAppGroupName <name>

7.4.10. PassengerAppType <name>

7.4.11. PassengerStartupFile <filename>

7.4.12. PassengerRestartDir <directory>

7.4.13. PassengerSpawnMethod <string>

7.4.14. PassengerLoadShellEnvvars <on|off>

7.4.15. PassengerRollingRestarts <on|off>

7.4.16. PassengerResistDeploymentErrors <on|off>

7.5. Security options

7.5.1. PassengerUserSwitching <on|off>

7.5.2. PassengerUser <username>

7.5.3. PassengerGroup <group name>

7.5.4. PassengerDefaultUser <username>

7.5.5. PassengerDefaultGroup <group name>

7.5.6. PassengerFriendlyErrorPages <on|off>

7.6. Resource control and optimization options

7.6.1. PassengerMaxPoolSize <integer>

7.6.2. PassengerMinInstances <integer>

7.6.3. PassengerMaxInstances <integer>

7.6.4. PassengerMaxInstancesPerApp <integer>

7.6.5. PassengerPoolIdleTime <integer>

7.6.6. PassengerMaxPreloaderIdleTime <integer>

7.6.7. PassengerStartTimeout <seconds>

7.6.8. PassengerConcurrencyModel <process|thread>

7.6.9. PassengerThreadCount <number>

7.6.10. PassengerMaxRequests <integer>

7.6.11. PassengerMaxRequestTime <seconds>

7.6.12. PassengerMemoryLimit <integer>

7.6.13. PassengerStatThrottleRate <integer>

7.6.14. PassengerPreStart <url>

7.6.15. PassengerHighPerformance <on|off>

7.7. Connection handling options

7.7.1. PassengerBufferUpload <on|off>

7.7.2. PassengerBufferResponse <on|off>

7.7.3. PassengerResponseBufferHighWatermark <bytes>

7.7.4. PassengerErrorOverride <on|off>

7.7.5. PassengerMaxRequestQueueSize <number>

7.7.6. PassengerStickySessions <on|off>

7.7.7. PassengerStickySessionsCookieName

7.8. Compatibility options

7.8.1. PassengerResolveSymlinksInDocumentRoot <on|off>

7.8.2. PassengerAllowEncodedSlashes <on|off>

7.9. Logging and debugging options

7.9.1. PassengerLogLevel <integer>

7.9.2. PassengerLogFile <filename>

7.9.3. PassengerFileDescriptorLogFile <filename>

7.9.4. PassengerDebugger <on|off>

7.10. Advanced options

7.10.1. PassengerInstanceRegistryDir <directory>

7.10.2. PassengerDataBufferDir <directory>

7.11. Deprecated or removed options

7.11.1. RailsRuby

7.11.2. RailsBaseURI and RackBaseURI

7.11.3. RailsUserSwitching

7.11.4. RailsDefaultUser

7.11.5. RailsAllowModRewrite

7.11.6. RailsSpawnMethod

7.11.7. RailsAutoDetect, RackAutoDetect and WsgiAutoDetect

7.11.8. RailsAppSpawnerIdleTime

7.11.9. RailsFrameworkSpawnerIdleTime

7.11.10. PassengerDebugLogFile

8. Troubleshooting

8.1. Generic troubleshooting tips

8.2. Why does the first request take a long time?

8.3. I get "command not found" when running a Phusion Passenger command through sudo

8.4. OS X: The installer cannot locate MAMP’s Apache

8.6. Static assets such as images and stylesheets aren’t being displayed

8.7. Apache cannot access my app’s files because of SELinux errors

8.8. The application thinks its not on SSL even though it is

8.9. Ruby on Rails-specific troubleshooting

8.9.1. The "About your application’s environment" link does not work

8.9.2. The Rails application reports that it’s unable to start because of a permission error

8.9.3. The Rails application’s log file is not being written to

There are a couple things that you should be aware of:

  • By default, Phusion Passenger runs Rails applications in production mode, so please be sure to check production.log instead of development.log.

    See RailsEnv for configuration. - By default, Phusion Passenger runs Rails applications as the owner of config.ru. So the log file can only be written to if that user has write permission to the log file. Please chmod or chown your log file accordingly.

    See User switching (security) for details.

If you’re using a RedHat-derived Linux distribution (such as Fedora or CentOS) then it is possible that SELinux is interfering. RedHat’s SELinux policy only allows Apache to read/write directories that have the httpd_sys_content_t security context. Please run the following command to give your Rails application folder that context:

chcon -R -h -t httpd_sys_content_t /path/to/your/rails/app

8.10. Conflicting Apache modules

8.10.2. MultiViews (mod_negotiation)

8.10.3. VirtualDocumentRoot

9. Analysis and system maintenance

9.1. Inspecting memory usage

9.2. Inspecting Phusion Passenger’s internal status

9.3. Debugging frozen applications

If one of your application processes is frozen (stopped responding), then you can figure out where it is frozen by killing it with SIGABRT. This will cause the processs to print a backtrace, after which it aborts. The backtrace information is logged into the web server error log file.

In case of Ruby applications, you can also send the SIGQUIT signal to have it print a backtrace without aborting.

9.4. Accessing individual application processes

9.5. Attaching an IRB console to an application process

10. Tips

10.1. User Switching (security feature)

10.1.1. Requirements

10.1.2. Effects

10.1.3. Caveats & troubleshooting

10.1.4. Red Hat and CentOS caveats

10.1.5. Finding out what user an application is running as

10.2. Copy-on-write memory support (reducing memory consumption of Ruby applications)

Phusion Passenger automatically leverages operating system virtual memory copy-on-write features in order to reduce the memory usage of Ruby applications. Experience has shown that this reduces memory usage by 33% on average. For this mechanism to work, a Ruby interpreter with a copy-on-write friendly garbage collector is required. The following Ruby interpreters have copy-on-write friendly garbage collectors:

  • MRI Ruby >= 2.0. Versions prior to 2.0 did not have a copy-on-write friendly garbage collector.

  • Ruby Enterprise Edition, which was Phusion’s branch of MRI Ruby 1.8 with a copy-on-write friendly garbage collector and other enhancement. It has reached End-Of-Life as of 2012, but remains available for legacy systems.

10.3. Tuning for Server Sent Events and WebSockets

10.4. Bundler support

10.4.1. Does Phusion Passenger itself need to be added to the Gemfile?

10.5. Installing multiple Ruby on Rails versions

Each Ruby on Rails applications that are going to be deployed may require a specific Ruby on Rails version. You can install a specific version with this command:

gem install rails -v X.X.X

where X.X.X is the version number of Ruby on Rails.

All of these versions will exist in parallel, and will not conflict with each other. Phusion Passenger will automatically make use of the correct version.

10.6. Making the application restart after each request

In some situations it might be desirable to restart the web application after each request, for example when developing a non-Rails application that doesn’t support code reloading, or when developing a web framework.

To achieve this, simply create the file tmp/always_restart.txt in your application’s root folder. Unlike restart.txt, Phusion Passenger does not check for this file’s timestamp: Phusion Passenger will always restart the application, as long as always_restart.txt exists.

Note If you’re just developing a Rails application then you probably don’t need this feature. If you set RailsEnv development in your web server configuration, then Rails will automatically reload your application code after each request. always_restart.txt is mostly useful when you’re using a web framework that doesn’t support code reloading by itself, of when you’re working on a web framework yourself.

10.7. How to fix broken images/CSS/JavaScript URIs in sub-URI deployments

Some people experience broken images and other broken static assets when they deploy their application to a sub-URI (i.e. http://mysite.com/railsapp/). The reason for this usually is that you used a static URI for your image in the views. This means your img source probably refers to something like /images/foo.jpg. The leading slash means that it’s an absolute URI: you’re telling the browser to always load http://mysite.com/images/foo.jpg no matter what. The problem is that the image is actually at http://mysite.com/railsapp/images/foo.jpg. There are two ways to fix this.

The first way (not recommended) is to change your view templates to refer to images/foo.jpg. This is a relative URI: note the lack of a leading slash). What this does is making the path relative to the current URI. The problem is that if you use restful URIs, then your images will probably break again when you add a level to the URI. For example, when you’re at http://mysite.com/railsapp the browser will look for http://mysite.com/railsapp/images/foo.jpg. But when you’re at http://mysite.com/railsapp/controller. the browser will look for http://mysite.com/railsapp/controller/images/foo.jpg. So relative URIs usually don’t work well with layout templates.

The second and highly recommended way is to always use Rails helper methods to output tags for static assets. These helper methods automatically take care of prepending the base URI that you’ve deployed the application to. For images there is image_tag, for JavaScript there is javascript_include_tag and for CSS there is stylesheet_link_tag. In the above example you would simply remove the <img> HTML tag and replace it with inline Ruby like this:

<%= image_tag("foo.jpg") %>

This will generate the proper image tag to $RAILS_ROOT/public/images/foo.jpg so that your images will always work no matter what sub-URI you’ve deployed to.

These helper methods are more valuable than you may think. For example they also append a timestamp to the URI to better facilitate HTTP caching. For more information, please refer to the Rails API docs.

10.8. Out-of-Band Work and Out-of-Band Garbage Collection

10.9. Hooks

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/hooks.html

10.9.1. Example

10.9.2. Environment

10.9.3. Blocking and concurrency

10.9.4. Error handling

10.9.5. Compatibility

10.9.6. Available hooks

10.10. Flying Passenger

10.10.1. Requirements

10.10.2. Basic usage

10.10.3. Configuring Flying Passenger

10.10.4. Managing the Flying Passenger daemon

10.10.5. Using Flying Passenger with MRI 1.8 or JRuby

10.10.6. Caveats and limitations

10.11. X-Sendfile support

Phusion Passenger does not provide X-Sendfile support by itself. Please install mod_xsendfile for X-Sendfile support.

10.12. Upload progress

Phusion Passenger does not provide upload progress support by itself. Please try drogus’s Apache upload progress module instead.

11. Under the hood

Phusion Passenger hides a lot of complexity for the end user (i.e. the web server system administrator), but sometimes it is desirable to know what is going on. This section describes a few things that Phusion Passenger does under the hood.

11.1. Page caching support

For each HTTP request, Phusion Passenger will automatically look for a corresponding page cache file, and serve that if it exists. It does this by appending ".html" to the filename that the URI normally maps to, and checking whether that file exists. This check occurs after checking whether the original mapped filename exists (as part of static asset serving). All this is done without the need for special mod_rewrite rules.

For example, suppose that the browser requests /foo/bar.

  1. Phusion Passenger will first check whether this URI maps to a static file, i.e. whether the file foo/bar exists in the web application’s public directory. If it does then Phusion Passenger will serve this file through the web server immediately.

  2. If that doesn’t exist, then Phusion Passenger will check whether the file foo/bar.html exists. If it does then Phusion Passenger will serve this file through the web server immediately.

  3. If foo/bar.html doesn’t exist either, then Phusion Passenger will forward the request to the underlying web application.

Note that Phusion Passenger’s page caching support doesn’t work if your web application uses a non-standard page cache directory, i.e. if it doesn’t cache to the public directory. In that case you’ll need to use mod_rewrite to serve such page cache files.

11.2. Phusion Passenger and its relationship with Ruby

11.2.1. How Ruby is used

11.2.2. When the system has multiple Ruby interpreters

11.3. Static assets serving

Phusion Passenger accelerates serving of static files. This means that, if an URI maps to a file that exists, then Phusion Passenger will let Apache serve that file directly, without hitting the web application.

Phusion Passenger does all this without the need for any mod_rewrite rules. People who are switching from an old Mongrel-based setup might have mod_rewrite rules such as these:

# Check whether this request has a corresponding file; if that
# exists, let Apache serve it, otherwise forward the request to
# Mongrel.
RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ balancer://mongrel%{REQUEST_URI} [P,QSA,L]

These kind of mod_rewrite rules are no longer required, and you can safely remove them.

11.4. How Phusion Passenger detects whether a virtual host is a web application

12. Appendix A: About this document

The text of this document is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License.

Phusion Passenger is brought to you by Phusion.

"Passenger", "Phusion Passenger" and "Union Station" are registered trademarks of Phusion Holding B.V.

13. Appendix B: Terminology

13.1. Application root

The root directory of an application that’s served by Phusion Passenger.

In case of Ruby on Rails applications, this is the directory that contains Rakefile, app/, config/, public/, etc. In other words, the directory pointed to by RAILS_ROOT. For example, take the following directory structure:

/apps/foo/       <------ This is the Rails application's application root!
   |
   +- app/
   |   |
   |   +- controllers/
   |   |
   |   +- models/
   |   |
   |   +- views/
   |
   +- config/
   |   |
   |   +- environment.rb
   |   |
   |   +- ...
   |
   +- public/
   |   |
   |   +- ...
   |
   +- ...

In case of Rack applications, this is the directory that contains config.ru. For example, take the following directory structure:

/apps/bar/      <----- This is the Rack application's application root!
   |
   +- public/
   |    |
   |    +- ...
   |
   +- config.ru
   |
   +- ...

In case of Python (WSGI) applications, this is the directory that contains passenger_wsgi.py. For example, take the following directory structure:

/apps/baz/      <----- This is the WSGI application's application root!
   |
   +- public/
   |    |
   |    +- ...
   |
   +- passenger_wsgi.py
   |
   +- ...

13.2. Idle process

An "idle process" refers to a process that hasn’t processed any requests for a while.

13.3. Inactive process

An "inactive process" refers to a process that’s current not processing any requests. An idle process is always inactive, but an inactive process is not always considered idle.

14. Appendix C: Spawning methods explained

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/

14.1. The most straightforward and traditional way: direct spawning

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/

14.2. The smart spawning method

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/

14.2.1. How it works

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/

14.2.2. Summary of benefits

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/

14.3. Smart spawning caveat #1: unintentional file descriptor sharing

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/

14.3.1. Example 1: Memcached connection sharing (harmful)

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/

14.3.2. Example 2: Log file sharing (not harmful)

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/

14.4. Smart spawning caveat #2: the need to revive threads

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/

15. Appendix D: About environment variables

15.1. Working with environment variables

15.2. The PATH environment variable

15.2.1. Adding Phusion Passenger’s administration tools to PATH

15.3. Making environment variables permanent

15.3.1. bash

15.3.2. Apache

15.3.3. Nginx

15.3.4. cron

15.3.5. Phusion Passenger-served apps

15.4. Environment variables and sudo


passenger-5.0.30/doc/Users guide Apache.idmap.txt000644 000765 000024 00000044026 12233035540 022134 0ustar00honglistaff000000 000000 ###### Autogenerated by Mizuho, DO NOT EDIT ###### # This file maps section names to IDs so that the commenting system knows which # comments belong to which section. Section names may be changed at will but # IDs always stay the same, allowing one to retain old comments even if you # rename a section. # # This file is autogenerated but is not a cache; you MUST NOT DELETE this # file and you must check it into your version control system. If you lose # this file you may lose the ability to identity old comments. # # Entries marked with "fuzzy" indicate that the section title has changed # and that Mizuho has found an ID which appears to be associated with that # section. You should check whether it is correct, and if not, fix it. 1. Support information => support-information-zkewk3 1.1. Supported operating systems and languages => supported-operating-systems-rhbg35 1.2. Where to get support => where-to-get-support-f3pbrb 2. Installation => installing-upgrading-and-uninstalling-phusion-passenger-laryvs 2.1. Synopsis => synopsis-l0om5d 2.2. Installing or upgrading on Mac OS X with Homebrew => installing-or-upgrading-on-mac-os-x-with-homebrew-1pia1cx 2.3. Installing or upgrading on Debian or Ubuntu => installing-or-upgrading-on-ubuntu-1xg22xs 2.3.1. Adding our APT repository => adding-our-apt-repository-1l8plxk 2.3.2. Installing packages => installing-packages-1jxn722 2.4. Installing or upgrading on Red Hat or CentOS => installing-or-upgrading-on-red-hat-fedora-centos-or-scientificlinux-1y5ie8b 2.4.1. Adding our YUM repository => adding-our-yum-repository-1fb671i 2.4.2. Installing packages => installing-packages-12a3uty 2.5. Installing or upgrading on Heroku => installing-or-upgrading-on-heroku-o0o2mk 2.6. Generic installation, upgrade and downgrade method: via RubyGems => installing-via-the-gem-39jw1u 2.7. Generic installation, upgrade and downgrade method: via tarball => installing-via-the-source-tarball-1cgxrqc 2.8. Upgrading from open source to Enterprise => upgrading-from-open-source-to-enterprise-b17h8g 2.9. Cryptographic verification of installation files => cryptographic-verification-of-installation-files-ed3r9v 2.9.1. Synopsis => synopsis-uwnz2o 2.9.2. Importing the Phusion Software Signing key => importing-the-phusion-software-signing-key-1x0rhv 2.9.3. Verifying the Phusion Software Signing key => verifying-the-phusion-software-signing-key-10430lg 2.9.4. Verifying the gem and tarball => verifying-the-gem-and-tarball-1nf0uov 2.9.5. Verifying Git signatures => verifying-git-signatures-oaafwd 2.9.6. Verifying Debian packages => verifying-deb-and-rpm-packages-vgtv04 2.9.7. Verifying RPM packages => verifying-rpm-packages-1nlx51r 2.9.8. Revocation => revocation-4o01n2 2.10. Non-interactive, automatic, headless installs or upgrades => non-interactive-automatic-headless-installs-or-upgrades-1arnxii 2.11. Customizing the compilation process => customizing-the-compilation-process-1sds3od 2.11.1. Setting the compiler => setting-the-compiler-1symq7h 2.11.2. Adding additional compiler or linker flags => adding-additional-compiler-or-linker-flags-wzu0ey 2.11.3. Forcing location of command line tools and dependencies => forcing-location-of-certain-command-line-tools-1hym30u 2.12. Dealing with multiple Apache installations => dealing-with-multiple-apache-installations-ks5nkz 2.13. Working with the Apache configuration file => locating-the-apache-configuration-file-1fterqv 2.14. Disabling without uninstalling => unloading-disabling-phusion-passenger-from-apache-without-uninstalling-it-s1axnx 2.15. Uninstalling => uninstalling-phusion-passenger-1qb4ssq 2.16. Moving to a different directory => moving-to-a-different-directory-1n0gkwh 3. Deploying a Rack-based Ruby application => deploying-a-rack-based-ruby-application-including-rails-3--hcs66f 3.1. Tutorial/example: writing and deploying a Hello World Rack application => tutorial-example-writing-and-deploying-a-hello-world-rack-application-axp5z5 3.2. Deploying to a virtual host’s root => deploying-to-a-virtual-host-s-root-ab4zj6 3.3. Deploying to a sub URI => deploying-to-a-sub-uri-13rfygg 3.4. Redeploying (restarting the Rack application) => redeploying-restarting-the-rack-application--18m41m1 3.5. Rackup specifications for various web frameworks => rackup-specifications-for-various-web-frameworks-ndsqc2 4. Deploying a WSGI (Python) application => deploying-a-wsgi-python-application-7aygdl 4.1. Tutorial/example: writing and deploying a Hello World WSGI application => tutorial-example-writing-and-deploying-a-hello-world-wsgi-application-9ziqy8 4.2. Deploying to a virtual host’s root => deploying-to-a-virtual-host-s-root-u9bfax 4.3. Deploying to a sub URI => deploying-to-a-sub-uri-zekba6 4.4. Redeploying (restarting the WSGI application) => redeploying-restarting-the-wsgi-application--na7pmf 4.5. Sample passenger_wsgi.py for Django => sample-passenger-wsgi-py-for-django-slb9m8 5. Deploying a Node.js application => deploying-a-node-js-application-882ecy 6. Deploying a Meteor application => deploying-a-meteor-application-mxfmir 7. Configuring Phusion Passenger => configuring-phusion-passenger-11oztoe 7.1. PassengerRoot => passengerroot-directory--sere8l 7.2. PassengerDefaultRuby => passengerdefaultruby-and-passengerruby-7zblp8 7.3. Deployment options => deployment-options-1unzrll 7.3.1. PassengerEnabled => passengerenabled-on-off--74rzth 7.3.2. PassengerBaseURI => passengerbaseuri-uri--97i9mm 7.4. Application loading options => application-loading-options-1uedd4o 7.4.1. PassengerRuby => passengerruby-filename--1r3fv73 7.4.2. PassengerPython => passengerpython-filename--1ssesv3 7.4.3. PassengerNodejs => passengernodejs-filename--2mjb1j 7.4.4. PassengerMeteorAppSettings => passengermeteorappsettings-filename--inl1bd 7.4.5. PassengerAppEnv => passengerappenv-string--s3ojlc 7.4.6. RailsEnv => railsenv-string--1b0xxvu 7.4.7. RackEnv => rackenv-string--vve9py 7.4.8. PassengerAppRoot => passengerapproot-path-to-root--uf24yd 7.4.9. PassengerAppGroupName => passenger-app-group-name-name--6unn5c 7.4.10. PassengerAppType => passengerapptype-name--62jzqf 7.4.11. PassengerStartupFile => passengerstartupfile-filename--14vhvn9 7.4.12. PassengerRestartDir => passengerrestartdir-directory--1fmhmv0 7.4.13. PassengerSpawnMethod => passengerspawnmethod-string--sodg2y 7.4.14. PassengerLoadShellEnvvars => passengerloadshellenvvars-on-off--1290yz1 7.4.15. PassengerRollingRestarts => passengerrollingrestarts 7.4.16. PassengerResistDeploymentErrors => passengerresistdeploymenterrors 7.5. Security options => security-options-1pb75ho 7.5.1. PassengerUserSwitching => passengeruserswitching 7.5.2. PassengerUser => passengeruser 7.5.3. PassengerGroup => passengergroup 7.5.4. PassengerDefaultUser => passengerdefaultuser 7.5.5. PassengerDefaultGroup => passengerdefaultgroup 7.5.6. PassengerFriendlyErrorPages => passengerfriendlyerrorpages 7.6. Resource control and optimization options => resource-control-and-optimization-options-zu2f7u 7.6.1. PassengerMaxPoolSize => passengermaxpoolsize-integer--am64q8 7.6.2. PassengerMinInstances => passengermininstances-integer--wegq6b 7.6.3. PassengerMaxInstances => passengermaxinstances 7.6.4. PassengerMaxInstancesPerApp => passengermaxinstancesperapp 7.6.5. PassengerPoolIdleTime => passengerpoolidletime-integer--a3gunq 7.6.6. PassengerMaxPreloaderIdleTime => railsappspawneridletime-integer--1awgog1 7.6.7. PassengerStartTimeout => passengerstarttimeout-seconds--ihnfku 7.6.8. PassengerConcurrencyModel => passengerconcurrencymodel-process-thread--1eipofj 7.6.9. PassengerThreadCount => passengerthreadcount-number--10jl64a 7.6.10. PassengerMaxRequests => passengermaxrequests-integer--17qkw9n 7.6.11. PassengerMaxRequestTime => passengermaxrequesttime-seconds--127v1i2 7.6.12. PassengerMemoryLimit => passengermemorylimit-integer--18irza1 7.6.13. PassengerStatThrottleRate => passengerstatthrottlerate-integer--1dcfda3 7.6.14. PassengerPreStart => passengerprestart-url--1utb57k 7.6.15. PassengerHighPerformance => passengerhighperformance-on-off--nvfa86 7.7. Connection handling options => connection-handling-options-1k3zd1a 7.7.1. PassengerBufferUpload => passengerbufferupload-on-off--1lsjr93 7.7.2. PassengerBufferResponse => passengerbufferresponse-on-off--1y7ilka 7.7.3. PassengerResponseBufferHighWatermark => passengerresponsebufferhighwatermark-bytes--1kj32df 7.7.4. PassengerErrorOverride => passengererroroverride-on-off--1pq9nez 7.7.5. PassengerMaxRequestQueueSize => passenger-max-request-queue-size-number--1f1uocd 7.7.6. PassengerStickySessions => passengerstickysessions-on-off--fx1jkt 7.7.7. PassengerStickySessionsCookieName => passenger-sticky-sessions-cookie-name-stktkx 7.8. Compatibility options => compatibility-options-8jve5a 7.8.1. PassengerResolveSymlinksInDocumentRoot => passengerresolvesymlinksindocumentroot-on-off--1r0qcp8 7.8.2. PassengerAllowEncodedSlashes => passengerallowencodedslashes-on-off--1y3s1ww 7.9. Logging and debugging options => logging-and-debugging-options-el2cuc 7.9.1. PassengerLogLevel => passengerloglevel-integer--s3kbil 7.9.2. PassengerLogFile => passengerdebuglogfile-filename--1wjm2j1 7.9.3. PassengerFileDescriptorLogFile => passengerfiledescriptorlogfile-filename--1su5dcn 7.9.4. PassengerDebugger => passengerdebugger-on-off--19you7e 7.10. Advanced options => advanced-options-1ab1jkq 7.10.1. PassengerInstanceRegistryDir => passengerinstanceregistrydir-directory--7r1ks8 7.10.2. PassengerDataBufferDir => passengerdatabufferdir-directory--170fjqg 7.11. Deprecated or removed options => deprecated-options-pm9m57 7.11.1. RailsRuby => railsruby-ht09ei 7.11.2. RailsBaseURI and RackBaseURI => railsbaseuri-uri--1txrw3k 7.11.3. RailsUserSwitching => railsuserswitching-1npx8y4 7.11.4. RailsDefaultUser => railsdefaultuser-19j7n3m 7.11.5. RailsAllowModRewrite => railsallowmodrewrite-1vkziro 7.11.6. RailsSpawnMethod => railsspawnmethod-1aljgpa 7.11.7. RailsAutoDetect, RackAutoDetect and WsgiAutoDetect => railsautodetect-rackautodetect-and-wsgiautodetect-1qgakzs 7.11.8. RailsAppSpawnerIdleTime => railsappspawneridletime-heh41r 7.11.9. RailsFrameworkSpawnerIdleTime => railsframeworkspawneridletime-adcf9k 7.11.10. PassengerDebugLogFile => passengerdebuglogfile-1mb9h6h 8. Troubleshooting => troubleshooting-2ihihi 8.1. Generic troubleshooting tips => generic-troubleshooting-tips-130iuhe 8.2. Why does the first request take a long time? => why-does-the-first-request-take-a-long-time--12mg452 8.3. I get "command not found" when running a Phusion Passenger command through sudo => i-get-command-not-found-when-running-a-phusion-passenger-command-through-sudo-v9z223 8.4. OS X: The installer cannot locate MAMP’s Apache => macos-x-the-installer-cannot-locate-mamp-s-apache-or908n 8.5. Apache reports a "403 Forbidden" error => apache-reports-a-403-forbidden-error-140tb4p 8.6. Static assets such as images and stylesheets aren’t being displayed => static-assets-such-as-images-and-stylesheets-aren-t-being-displayed-uaptpi 8.7. Apache cannot access my app’s files because of SELinux errors => apache-cannot-access-my-app-s-files-because-of-selinux-errors-6febhd 8.8. The application thinks its not on SSL even though it is => the-application-thinks-its-not-on-ssl-even-though-it-is-u9fcp1 8.9. Ruby on Rails-specific troubleshooting => ruby-on-rails-specific-problems-6umss5 8.9.1. The "About your application’s environment" link does not work => the-about-your-application-s-environment-link-does-not-work-7k4tlm 8.9.2. The Rails application reports that it’s unable to start because of a permission error => the-rails-application-reports-that-it-s-unable-to-start-because-of-a-permission-error-v53i6s 8.9.3. The Rails application’s log file is not being written to => my-rails-application-s-log-file-is-not-being-written-to-3i747l 8.10. Conflicting Apache modules => conflicting-apache-modules-1uwpixk 8.10.1. mod_userdir => mod-userdir-x5e2te 8.10.2. MultiViews (mod_negotiation) => multiviews-mod-negotiation--zchfg0 8.10.3. VirtualDocumentRoot => virtualdocumentroot-14cwd7l 9. Analysis and system maintenance => analysis-and-system-maintenance-qvkwzr 9.1. Inspecting memory usage => inspecting-memory-usage-1bkis6i 9.2. Inspecting Phusion Passenger’s internal status => inspecting-phusion-passenger-s-internal-status-ukekf7 9.3. Debugging frozen applications => debugging-frozen-applications-1qsjqq7 9.4. Accessing individual application processes => accessing-individual-application-processes-1p0j4jb 9.5. Attaching an IRB console to an application process => attaching-an-irb-console-to-an-application-process-1cma32j 10. Tips => tips-19cwwf7 10.1. User Switching (security feature) => user-switching-security--8njx1x 10.1.1. Requirements => requirements-yxvtxo 10.1.2. Effects => effects-1lko7vo 10.1.3. Caveats & troubleshooting => caveats-troubleshooting-161hgge 10.1.4. Red Hat and CentOS caveats => red-hat-and-centos-caveats-1335bpz 10.1.5. Finding out what user an application is running as => finding-out-what-user-an-application-is-running-as-7uwrol 10.2. Copy-on-write memory support (reducing memory consumption of Ruby applications) => reducing-memory-consumption-of-ruby-on-rails-applications-by-33--1ubxnq0 10.3. Tuning for Server Sent Events and WebSockets => tuning-for-server-sent-events-and-websockets-1hy6qa9 10.4. Bundler support => bundler-support-cf72ih 10.4.1. Does Phusion Passenger itself need to be added to the Gemfile? => does-phusion-passenger-itself-need-to-be-added-to-the-gemfile--17whimp 10.5. Installing multiple Ruby on Rails versions => installing-multiple-ruby-on-rails-versions-mi5j14 10.6. Making the application restart after each request => making-the-application-restart-after-each-request-183bezx 10.7. How to fix broken images/CSS/JavaScript URIs in sub-URI deployments => how-to-fix-broken-images-css-javascript-uris-in-sub-uri-deployments-a63b6r 10.8. Out-of-Band Work and Out-of-Band Garbage Collection => out-of-band-work-and-out-of-band-garbage-collection-kav2p8 10.9. Hooks => hooks-bcltgp 10.9.1. Example => example-icjf0z 10.9.2. Environment => environment-1amls4v 10.9.3. Blocking and concurrency => blocking-and-concurrency-cxpbyu 10.9.4. Error handling => error-handling-1a1uvod 10.9.5. Compatibility => compatibility-18tl6g3 10.9.6. Available hooks => available-hooks-159anc3 10.10. Flying Passenger => flying-passenger-q916f7 10.10.1. Requirements => requirements-1ntkin3 10.10.2. Basic usage => basic-usage-joifsk 10.10.3. Configuring Flying Passenger => configuring-flying-passenger-1v35vgg 10.10.4. Managing the Flying Passenger daemon => managing-the-flying-passenger-daemon-1bng2bu 10.10.5. Using Flying Passenger with MRI 1.8 or JRuby => using-flying-passenger-with-mri-1-8-or-jruby-f5tbvs 10.10.6. Caveats and limitations => caveats-and-limitations-lz3rbb 10.11. X-Sendfile support => x-sendfile-support-1cgyykw 10.12. Upload progress => upload-progress-71cyl7 11. Under the hood => under-the-hood-21ue5t 11.1. Page caching support => page-caching-support-kwq9b9 11.2. Phusion Passenger and its relationship with Ruby => phusion-passenger-and-its-relationship-with-ruby-1eq08ff 11.2.1. How Ruby is used => how-ruby-is-used-14r83nr 11.2.2. When the system has multiple Ruby interpreters => when-the-system-has-multiple-ruby-interpreters-10tm1fq 11.3. Static assets serving => static-assets-serving-wo2d9v 11.4. How Phusion Passenger detects whether a virtual host is a web application => how-phusion-passenger-detects-whether-a-virtual-host-is-a-web-application-179mp8m 12. Appendix A: About this document => appendix-a-about-this-document-103toqs 13. Appendix B: Terminology => appendix-b-terminology-h4eaf4 13.1. Application root => application-root-otx6xf 13.2. Idle process => idle-process-potb6g 13.3. Inactive process => inactive-process-16gjv2e 14. Appendix C: Spawning methods explained => appendix-c-spawning-methods-explained-owghi9 14.1. The most straightforward and traditional way: direct spawning => the-most-straightforward-and-traditional-way-conservative-spawning-1ybbli2 14.2. The smart spawning method => the-smart-spawning-method-1cvu9dd 14.2.1. How it works => how-it-works-672zja 14.2.2. Summary of benefits => summary-of-benefits-1yrihfb 14.3. Smart spawning caveat #1: unintentional file descriptor sharing => smart-spawning-gotcha-1-unintentional-file-descriptor-sharing-z1y55l 14.3.1. Example 1: Memcached connection sharing (harmful) => example-1-memcached-connection-sharing-harmful--c71wqw 14.3.2. Example 2: Log file sharing (not harmful) => example-2-log-file-sharing-not-harmful--1p2yuol 14.4. Smart spawning caveat #2: the need to revive threads => smart-spawning-gotcha-2-the-need-to-revive-threads-1k6cj7d 15. Appendix D: About environment variables => appendix-d-about-environment-variables-1lebv1u 15.1. Working with environment variables => working-with-environment-variables-85e6aa 15.2. The PATH environment variable => the-path-environment-variable-p8e32r 15.2.1. Adding Phusion Passenger’s administration tools to PATH => adding-phusion-passenger-s-administration-tools-to-path-xwppud 15.3. Making environment variables permanent => making-environment-variables-permanent-13x0l4h 15.3.1. bash => bash-1pktn63 15.3.2. Apache => apache-15zqjvi 15.3.3. Nginx => nginx-l7ztbb 15.3.4. cron => cron-1kjp7ck 15.3.5. Phusion Passenger-served apps => phusion-passenger-served-apps-478vyt 15.4. Environment variables and sudo => environment-variables-and-sudo-1odzcpz ### These sections appear to have been removed. Please check. 3.5.1. Camping => camping-1kxexk0 3.5.2. Halcyon => halcyon-1ghnpmz 3.5.3. Mack => mack-miht6b 3.5.4. Merb => merb-iyj7qy 3.5.5. Ramaze => ramaze-boddtj 3.5.6. Sinatra => sinatra-1hubto4 passenger-5.0.30/doc/Users guide Apache.txt000644 000765 000024 00000047262 12233035540 021050 0ustar00honglistaff000000 000000 Phusion Passenger users guide, Apache version ============================================= image:images/phusion_banner.png[link="http://www.phusion.nl/"] This is the old, deprecated Passenger for Apache documentation. Please visit https://www.phusionpassenger.com/library/ for the new documentation. == Support information include::users_guide_snippets/support_information.txt[] [[installation]] == Installation include::users_guide_snippets/installation.txt[] == Deploying a Rack-based Ruby application == This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/ === Tutorial/example: writing and deploying a Hello World Rack application === This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/ === Deploying to a virtual host's root === This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/ [[deploying_rack_to_sub_uri]] === Deploying to a sub URI === This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/ === Redeploying (restarting the Rack application) === This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/restart_app.html === Rackup specifications for various web frameworks === include::users_guide_snippets/rackup_specifications.txt[] == Deploying a WSGI (Python) application This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/ === Tutorial/example: writing and deploying a Hello World WSGI application === This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/ === Deploying to a virtual host's root === This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/ [[deploying_python_to_sub_uri]] === Deploying to a sub URI === This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/ === Redeploying (restarting the WSGI application) === This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/restart_app.html === Sample `passenger_wsgi.py` for Django This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/wsgi_spec.html == Deploying a Node.js application This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/ == Deploying a Meteor application This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/ == Configuring Phusion Passenger == This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/ [[PassengerRoot]] === PassengerRoot === This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerroot [[PassengerDefaultRuby]] === PassengerDefaultRuby === This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerdefaultruby === Deployment options [[PassengerEnabled]] ==== PassengerEnabled This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerenabled [[PassengerBaseURI]] ==== PassengerBaseURI ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerbaseuri === Application loading options [[PassengerRuby]] ==== PassengerRuby This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerruby ==== PassengerPython This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerpython ==== PassengerNodejs This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengernodejs ==== PassengerMeteorAppSettings This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengermeteorappsettings [[PassengerAppEnv]] ==== PassengerAppEnv ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerappenv [[rails_env]] ==== RailsEnv ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#railsenv-rackenv [[rack_env]] ==== RackEnv ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#railsenv-rackenv [[PassengerAppRoot]] ==== PassengerAppRoot This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerapproot [[PassengerAppGroupName]] ==== PassengerAppGroupName This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerappgroupname [[PassengerAppType]] ==== PassengerAppType This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerapptype [[PassengerStartupFile]] ==== PassengerStartupFile This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerstartupfile ==== PassengerRestartDir This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerrestartdir [[PassengerSpawnMethod]] ==== PassengerSpawnMethod This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerspawnmethod [[PassengerLoadShellEnvvars]] ==== PassengerLoadShellEnvvars This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerloadshellenvvars [[PassengerRollingRestarts]] ==== PassengerRollingRestarts This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerrollingrestarts [[PassengerResistDeploymentErrors]] ==== PassengerResistDeploymentErrors This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerresistdeploymenterrors === Security options === [[PassengerUserSwitching]] ==== PassengerUserSwitching ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengeruserswitching [[PassengerUser]] ==== PassengerUser ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengeruser [[PassengerGroup]] ==== PassengerGroup ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengergroup [[PassengerDefaultUser]] ==== PassengerDefaultUser ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerdefaultuser [[PassengerDefaultGroup]] ==== PassengerDefaultGroup ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerdefaultgroup [[PassengerFriendlyErrorPages]] ==== PassengerFriendlyErrorPages ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerfriendlyerrorpages === Resource control and optimization options === [[PassengerMaxPoolSize]] ==== PassengerMaxPoolSize ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengermaxpoolsize [[PassengerMinInstances]] ==== PassengerMinInstances ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengermininstances [[PassengerMaxInstances]] ==== PassengerMaxInstances ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengermaxinstances ==== PassengerMaxInstancesPerApp ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengermaxinstancesperapp [[PassengerPoolIdleTime]] ==== PassengerPoolIdleTime ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerpoolidletime [[PassengerMaxPreloaderIdleTime]] ==== PassengerMaxPreloaderIdleTime ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengermaxpreloaderidletime ==== PassengerStartTimeout ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerstarttimeout [[PassengerConcurrencyModel]] ==== PassengerConcurrencyModel ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerconcurrencymodel [[PassengerThreadCount]] ==== PassengerThreadCount ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerthreadcount [[PassengerMaxRequests]] ==== PassengerMaxRequests ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengermaxrequests [[PassengerMaxRequestTime]] ==== PassengerMaxRequestTime ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengermaxrequesttime [[PassengerMemoryLimit]] ==== PassengerMemoryLimit ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengermemorylimit ==== PassengerStatThrottleRate ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerstatthrottlerate [[PassengerPreStart]] ==== PassengerPreStart ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerprestart [[PassengerHighPerformance]] ==== PassengerHighPerformance ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerhighperformance ///////////////////////////////////////// ///////////////////////////////////////// === Connection handling options === [[PassengerBufferUpload]] ==== PassengerBufferUpload ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerbufferupload [[PassengerBufferResponse]] ==== PassengerBufferResponse ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerbufferresponse [[PassengerResponseBufferHighWatermark]] ==== PassengerResponseBufferHighWatermark This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerresponsebufferhighwatermark [[PassengerErrorOverride]] ==== PassengerErrorOverride ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengererroroverride [[PassengerMaxRequestQueueSize]] ==== PassengerMaxRequestQueueSize ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengermaxrequestqueuesize [[PassengerStickySessions]] ==== PassengerStickySessions This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerstickysessions [[PassengerStickySessionsCookieName]] ==== PassengerStickySessionsCookieName This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerstickysessionscookiename === Compatibility options === [[PassengerResolveSymlinksInDocumentRoot]] ==== PassengerResolveSymlinksInDocumentRoot ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerresolvesymlinksindocumentroot ==== PassengerAllowEncodedSlashes ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerallowencodedslashes === Logging and debugging options === [[PassengerLogLevel]] ==== PassengerLogLevel ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerloglevel [[PassengerLogFile]] ==== PassengerLogFile ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerlogfile ==== PassengerFileDescriptorLogFile This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerfiledescriptorlogfile ==== PassengerDebugger ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerdebugger === Advanced options [[PassengerInstanceRegistryDir]] ==== PassengerInstanceRegistryDir This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerinstanceregistrydir [[PassengerDataBufferDir]] ==== PassengerDataBufferDir This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerdatabufferdir === Deprecated or removed options === ==== RailsRuby ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#railsruby ==== RailsBaseURI and RackBaseURI ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#railsbaseuri-rackbaseuri ==== RailsUserSwitching ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#railsuserswitching ==== RailsDefaultUser ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#railsdefaultuser ==== RailsAllowModRewrite ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#railsallowmodrewrite ==== RailsSpawnMethod ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#railsspawnmethod ==== RailsAutoDetect, RackAutoDetect and WsgiAutoDetect ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#railsautodetect-rackautodetect-and-wsgiautodetect ==== RailsAppSpawnerIdleTime ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#railsappspawneridletime ==== RailsFrameworkSpawnerIdleTime ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#railsframeworkspawneridletime ==== PassengerDebugLogFile ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/reference/#passengerdebuglogfile [[troubleshooting]] == Troubleshooting include::users_guide_snippets/troubleshooting/default.txt[] === OS X: The installer cannot locate MAMP's Apache This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/troubleshooting/?a=os-x-the-installer-cannot-locate-mamp-s-apache === Apache reports a "403 Forbidden" error This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/troubleshooting/?a=static-assets-such-as-images-and-stylesheets-aren-t-being-displayed === Static assets such as images and stylesheets aren't being displayed This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/troubleshooting/?a=static-assets-such-as-images-and-stylesheets-aren-t-being-displayed [[apache_selinux_permissions]] === Apache cannot access my app's files because of SELinux errors This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/troubleshooting/?a=apache-cannot-access-my-app-s-files-because-of-selinux-errors === The application thinks its not on SSL even though it is This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/troubleshooting/?a=the-application-thinks-its-not-on-ssl-even-though-it-is include::users_guide_snippets/troubleshooting/rails.txt[] [[conflicting_apache_modules]] === Conflicting Apache modules === ==== mod_userdir ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/troubleshooting/?a=conflicting-apache-modules ==== MultiViews (mod_negotiation) ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/troubleshooting/?a=conflicting-apache-modules ==== VirtualDocumentRoot ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/troubleshooting/?a=conflicting-apache-modules == Analysis and system maintenance == include::users_guide_snippets/analysis_and_system_maintenance.txt[] == Tips == include::users_guide_snippets/tips.txt[] === X-Sendfile support === Phusion Passenger does not provide X-Sendfile support by itself. Please install link:http://tn123.ath.cx/mod_xsendfile/[mod_xsendfile] for X-Sendfile support. === Upload progress === Phusion Passenger does not provide upload progress support by itself. Please try drogus's link:http://github.com/drogus/apache-upload-progress-module/tree/master[ Apache upload progress module] instead. == Under the hood == Phusion Passenger hides a lot of complexity for the end user (i.e. the web server system administrator), but sometimes it is desirable to know what is going on. This section describes a few things that Phusion Passenger does under the hood. include::users_guide_snippets/under_the_hood/page_caching_support.txt[] include::users_guide_snippets/under_the_hood/relationship_with_ruby.txt[] === Static assets serving === Phusion Passenger accelerates serving of static files. This means that, if an URI maps to a file that exists, then Phusion Passenger will let Apache serve that file directly, without hitting the web application. Phusion Passenger does all this without the need for any mod_rewrite rules. People who are switching from an old Mongrel-based setup might have mod_rewrite rules such as these: ------------------------------------------------------------ # Check whether this request has a corresponding file; if that # exists, let Apache serve it, otherwise forward the request to # Mongrel. RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f RewriteRule ^(.*)$ balancer://mongrel%{REQUEST_URI} [P,QSA,L] ------------------------------------------------------------ These kind of mod_rewrite rules are no longer required, and you can safely remove them. [[application_detection]] === How Phusion Passenger detects whether a virtual host is a web application === This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/app_autodetection/apache/ include::users_guide_snippets/appendix_a_about.txt[] include::users_guide_snippets/appendix_b_terminology.txt[] include::users_guide_snippets/appendix_c_spawning_methods.txt[] [[about_environment_variables]] == Appendix D: About environment variables include::users_guide_snippets/environment_variables.txt[] passenger-5.0.30/doc/Users guide Nginx.html000644 000765 000024 00001071744 12233035540 021102 0ustar00honglistaff000000 000000 Phusion Passenger users guide, Nginx version

1. Support information

1.1. Supported operating systems and languages

1.2. Where to get support

  • Community discussion forum - post a message here if you’re experiencing problems. Support on this forum is provided by the community on a best-effort basis, so a (timely) response is not guaranteed.

  • Issue tracker - report bugs here.

  • Email support@phusion.nl if you are a Phusion Passenger Enterprise customer. Please mention your order reference. If you are not an Enterprise customer, we kindly redirect you to the community discussion forum instead.

  • Commercial support contracts are also available.

  • Report security vulnerabilities to security@phusion.nl. We will do our best to respond to you as quickly as we can, so please do not disclose the vulnerability until then.

Please consult the Phusion Passenger website for a full list of support resources.

2. Installation

2.1. Synopsis

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/

2.2. Installing or upgrading on Mac OS X with Homebrew

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/

2.3. Installing or upgrading on Debian or Ubuntu

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apt_repo/

2.3.1. Adding our APT repository

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apt_repo/

2.3.2. Installing packages

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apt_repo/

2.3.3. Inserting passenger_root into nginx.conf

2.4. Installing or upgrading on Red Hat or CentOS

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/yum_repo/

2.4.1. Adding our YUM repository

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/yum_repo/

2.4.2. Installing packages

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/yum_repo/

2.5. Installing or upgrading on Heroku

Please refer to our Heroku Ruby demo for installation and upgrade instructions for Heroku.

2.6. Generic installation, upgrade and downgrade method: via RubyGems

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/

2.7. Generic installation, upgrade and downgrade method: via tarball

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/

2.8. Upgrading from open source to Enterprise

2.9. Cryptographic verification of installation files

2.9.1. Synopsis

2.9.2. Importing the Phusion Software Signing key

2.9.3. Verifying the Phusion Software Signing key

2.9.4. Verifying the gem and tarball

2.9.5. Verifying Git signatures

2.9.6. Verifying Debian packages

2.9.7. Verifying RPM packages

2.9.8. Revocation

2.10. Non-interactive, automatic, headless installs or upgrades

2.11. Customizing the compilation process

2.11.1. Setting the compiler

2.11.2. Adding additional compiler or linker flags

2.11.3. Forcing location of command line tools and dependencies

2.12. Installing as a normal Nginx module without using the installer

2.13. Creating an Nginx init script

2.14. Disabling without uninstalling

2.15. Uninstalling

2.16. Moving to a different directory

3. Deploying a Rack-based Ruby application

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/

3.1. Tutorial/example: writing and deploying a Hello World Rack application

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/

3.2. Deploying to a virtual host’s root

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/

3.3. Deploying to a sub URI

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/

3.4. Redeploying (restarting the Rack application)

3.5. Rackup specifications for various web frameworks

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/config_ru.html

4. Deploying a WSGI (Python) application

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/

4.1. Tutorial/example: writing and deploying a Hello World WSGI application

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/

4.2. Deploying to a virtual host’s root

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/

4.3. Deploying to a sub URI

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/

4.4. Redeploying (restarting the WSGI application)

4.5. Sample passenger_wsgi.py for Django

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/wsgi_spec.html

5. Deploying a Node.js application

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/

6. Deploying a Meteor application

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/

7. Configuring Phusion Passenger

This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/

7.1. passenger_root <directory>

7.2. Deployment options

7.2.1. passenger_enabled <on|off>

7.2.2. passenger_base_uri <uri>

7.2.3. passenger_document_root <path>

7.3. Application loading options

7.3.1. passenger_ruby <filename>

7.3.2. passenger_python <filename>

7.3.3. passenger_nodejs <filename>

7.3.4. passenger_meteor_app_settings <filename>

7.3.5. passenger_app_env <string>

7.3.6. rails_env <string>

7.3.7. rack_env <string>

7.3.8. passenger_app_root <path/to/root>

7.3.9. passenger_app_group_name <name>

7.3.10. passenger_app_type <name>

7.3.11. passenger_startup_file <filename>

7.3.12. passenger_spawn_method <string>

7.3.13. passenger_env_var <name> <value>

7.3.14. passenger_load_shell_envvars <on|off>

7.3.15. passenger_rolling_restarts <on|off>

7.3.16. passenger_resist_deployment_errors <on|off>

7.4. Security options

7.4.1. passenger_user_switching <on|off>

7.4.2. passenger_user <username>

7.4.3. passenger_group <group name>

7.4.4. passenger_default_user <username>

7.4.5. Passenger_default_group <group name>

7.4.6. passenger_show_version_in_header <on|off>

7.4.7. passenger_friendly_error_pages <on|off>

7.5. Resource control and optimization options

7.5.1. passenger_max_pool_size <integer>

7.5.2. passenger_min_instances <integer>

7.5.3. passenger_max_instances <integer>

7.5.4. passenger_max_instances_per_app <integer>

7.5.5. passenger_pool_idle_time <integer>

7.5.6. passenger_max_preloader_idle_time <integer>

7.5.7. passenger_start_timeout <seconds>

7.5.8. passenger_concurrency_model <process|thread>

7.5.9. passenger_thread_count <number>

7.5.10. passenger_max_requests <integer>

7.5.11. passenger_max_request_time <seconds>

7.5.12. passenger_memory_limit <integer>

7.5.13. passenger_stat_throttle_rate <integer>

7.5.14. passenger_pre_start <url>

7.6. Connection handling options

7.6.1. passenger_set_header <HTTP header name> <value>

7.6.2. passenger_max_request_queue_size <number>

7.6.3. passenger_request_queue_overflow_status_code <code>

7.6.4. passenger_sticky_sessions <on|off>

7.6.5. passenger_sticky_sessions_cookie_name

7.6.6. passenger_ignore_client_abort <on|off>

7.6.7. passenger_intercept_errors <on|off>

7.6.8. passenger_pass_header <header name>

7.6.9. passenger_ignore_headers <header names…>

7.6.10. passenger_headers_hash_bucket_size <size>

7.6.11. passenger_headers_hash_max_size <size>

7.6.12. passenger_buffer_response <on|off>

7.6.13. passenger_response_buffer_high_watermark <bytes>

7.6.14. passenger_buffer_size

7.6.15. passenger_buffers

7.7. Logging and debugging options

7.7.1. passenger_log_level <integer>

7.7.2. passenger_log_file <filename>

7.7.3. PassengerFileDescriptorLogFile <filename>

7.7.4. passenger_debugger <on|off>

7.8. Advanced options

7.8.1. passenger_instance_registry_dir <directory>

7.8.2. passenger_data_buffer_dir <directory>

7.8.3. passenger_fly_with <socket filename>

7.9. Deprecated or removed options

The following options have been deprecated or removed. Some are still supported for backwards compatibility reasons.

7.9.1. rails_spawn_method

Deprecated in favor of passenger_spawn_method.

7.9.2. passenger_debug_log_file

This option has been renamed in version 5.0.5 to passenger_log_file.

8. Troubleshooting

8.1. Generic troubleshooting tips

8.2. Why does the first request take a long time?

8.3. Upon accessing the web app, Nginx reports a "Permission denied" error

8.4. I get "command not found" when running a Phusion Passenger command through sudo

8.5. The application thinks its not on SSL even though it is

8.6. Ruby on Rails-specific troubleshooting

8.6.1. The "About your application’s environment" link does not work

8.6.2. The Rails application reports that it’s unable to start because of a permission error

8.6.3. The Rails application’s log file is not being written to

There are a couple things that you should be aware of:

  • By default, Phusion Passenger runs Rails applications in production mode, so please be sure to check production.log instead of development.log.

    See rails_env for configuration. - By default, Phusion Passenger runs Rails applications as the owner of config.ru. So the log file can only be written to if that user has write permission to the log file. Please chmod or chown your log file accordingly.

    See User switching (security) for details.

If you’re using a RedHat-derived Linux distribution (such as Fedora or CentOS) then it is possible that SELinux is interfering. RedHat’s SELinux policy only allows Apache to read/write directories that have the httpd_sys_content_t security context. Please run the following command to give your Rails application folder that context:

chcon -R -h -t httpd_sys_content_t /path/to/your/rails/app

9. Analysis and system maintenance

9.1. Inspecting memory usage

9.2. Inspecting Phusion Passenger’s internal status

9.3. Debugging frozen applications

If one of your application processes is frozen (stopped responding), then you can figure out where it is frozen by killing it with SIGABRT. This will cause the processs to print a backtrace, after which it aborts. The backtrace information is logged into the web server error log file.

In case of Ruby applications, you can also send the SIGQUIT signal to have it print a backtrace without aborting.

9.4. Accessing individual application processes

9.5. Attaching an IRB console to an application process

10. Tips

10.1. User Switching (security feature)

10.1.1. Requirements

10.1.2. Effects

10.1.3. Caveats & troubleshooting

10.1.4. Red Hat and CentOS caveats

10.1.5. Finding out what user an application is running as

10.2. Copy-on-write memory support (reducing memory consumption of Ruby applications)

Phusion Passenger automatically leverages operating system virtual memory copy-on-write features in order to reduce the memory usage of Ruby applications. Experience has shown that this reduces memory usage by 33% on average. For this mechanism to work, a Ruby interpreter with a copy-on-write friendly garbage collector is required. The following Ruby interpreters have copy-on-write friendly garbage collectors:

  • MRI Ruby >= 2.0. Versions prior to 2.0 did not have a copy-on-write friendly garbage collector.

  • Ruby Enterprise Edition, which was Phusion’s branch of MRI Ruby 1.8 with a copy-on-write friendly garbage collector and other enhancement. It has reached End-Of-Life as of 2012, but remains available for legacy systems.

10.3. Tuning for Server Sent Events and WebSockets

10.4. Bundler support

10.4.1. Does Phusion Passenger itself need to be added to the Gemfile?

10.5. Installing multiple Ruby on Rails versions

Each Ruby on Rails applications that are going to be deployed may require a specific Ruby on Rails version. You can install a specific version with this command:

gem install rails -v X.X.X

where X.X.X is the version number of Ruby on Rails.

All of these versions will exist in parallel, and will not conflict with each other. Phusion Passenger will automatically make use of the correct version.

10.6. Making the application restart after each request

In some situations it might be desirable to restart the web application after each request, for example when developing a non-Rails application that doesn’t support code reloading, or when developing a web framework.

To achieve this, simply create the file tmp/always_restart.txt in your application’s root folder. Unlike restart.txt, Phusion Passenger does not check for this file’s timestamp: Phusion Passenger will always restart the application, as long as always_restart.txt exists.

Note If you’re just developing a Rails application then you probably don’t need this feature. If you set rails_env development in your web server configuration, then Rails will automatically reload your application code after each request. always_restart.txt is mostly useful when you’re using a web framework that doesn’t support code reloading by itself, of when you’re working on a web framework yourself.

10.7. How to fix broken images/CSS/JavaScript URIs in sub-URI deployments

Some people experience broken images and other broken static assets when they deploy their application to a sub-URI (i.e. http://mysite.com/railsapp/). The reason for this usually is that you used a static URI for your image in the views. This means your img source probably refers to something like /images/foo.jpg. The leading slash means that it’s an absolute URI: you’re telling the browser to always load http://mysite.com/images/foo.jpg no matter what. The problem is that the image is actually at http://mysite.com/railsapp/images/foo.jpg. There are two ways to fix this.

The first way (not recommended) is to change your view templates to refer to images/foo.jpg. This is a relative URI: note the lack of a leading slash). What this does is making the path relative to the current URI. The problem is that if you use restful URIs, then your images will probably break again when you add a level to the URI. For example, when you’re at http://mysite.com/railsapp the browser will look for http://mysite.com/railsapp/images/foo.jpg. But when you’re at http://mysite.com/railsapp/controller. the browser will look for http://mysite.com/railsapp/controller/images/foo.jpg. So relative URIs usually don’t work well with layout templates.

The second and highly recommended way is to always use Rails helper methods to output tags for static assets. These helper methods automatically take care of prepending the base URI that you’ve deployed the application to. For images there is image_tag, for JavaScript there is javascript_include_tag and for CSS there is stylesheet_link_tag. In the above example you would simply remove the <img> HTML tag and replace it with inline Ruby like this:

<%= image_tag("foo.jpg") %>

This will generate the proper image tag to $RAILS_ROOT/public/images/foo.jpg so that your images will always work no matter what sub-URI you’ve deployed to.

These helper methods are more valuable than you may think. For example they also append a timestamp to the URI to better facilitate HTTP caching. For more information, please refer to the Rails API docs.

10.8. Out-of-Band Work and Out-of-Band Garbage Collection

10.9. Hooks

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/hooks.html

10.9.1. Example

10.9.2. Environment

10.9.3. Blocking and concurrency

10.9.4. Error handling

10.9.5. Compatibility

10.9.6. Available hooks

10.10. Flying Passenger

10.10.1. Requirements

10.10.2. Basic usage

10.10.3. Configuring Flying Passenger

10.10.4. Managing the Flying Passenger daemon

10.10.5. Using Flying Passenger with MRI 1.8 or JRuby

10.10.6. Caveats and limitations

11. Under the hood

11.1. Page caching support

For each HTTP request, Phusion Passenger will automatically look for a corresponding page cache file, and serve that if it exists. It does this by appending ".html" to the filename that the URI normally maps to, and checking whether that file exists. This check occurs after checking whether the original mapped filename exists (as part of static asset serving). All this is done without the need for special mod_rewrite rules.

For example, suppose that the browser requests /foo/bar.

  1. Phusion Passenger will first check whether this URI maps to a static file, i.e. whether the file foo/bar exists in the web application’s public directory. If it does then Phusion Passenger will serve this file through the web server immediately.

  2. If that doesn’t exist, then Phusion Passenger will check whether the file foo/bar.html exists. If it does then Phusion Passenger will serve this file through the web server immediately.

  3. If foo/bar.html doesn’t exist either, then Phusion Passenger will forward the request to the underlying web application.

Note that Phusion Passenger’s page caching support doesn’t work if your web application uses a non-standard page cache directory, i.e. if it doesn’t cache to the public directory. In that case you’ll need to use mod_rewrite to serve such page cache files.

11.2. Phusion Passenger and its relationship with Ruby

11.2.1. How Ruby is used

11.2.2. When the system has multiple Ruby interpreters

11.3. How Phusion Passenger detects whether a virtual host is a web application

12. Appendix A: About this document

The text of this document is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License.

Phusion Passenger is brought to you by Phusion.

"Passenger", "Phusion Passenger" and "Union Station" are registered trademarks of Phusion Holding B.V.

13. Appendix B: Terminology

13.1. Application root

The root directory of an application that’s served by Phusion Passenger.

In case of Ruby on Rails applications, this is the directory that contains Rakefile, app/, config/, public/, etc. In other words, the directory pointed to by RAILS_ROOT. For example, take the following directory structure:

/apps/foo/       <------ This is the Rails application's application root!
   |
   +- app/
   |   |
   |   +- controllers/
   |   |
   |   +- models/
   |   |
   |   +- views/
   |
   +- config/
   |   |
   |   +- environment.rb
   |   |
   |   +- ...
   |
   +- public/
   |   |
   |   +- ...
   |
   +- ...

In case of Rack applications, this is the directory that contains config.ru. For example, take the following directory structure:

/apps/bar/      <----- This is the Rack application's application root!
   |
   +- public/
   |    |
   |    +- ...
   |
   +- config.ru
   |
   +- ...

In case of Python (WSGI) applications, this is the directory that contains passenger_wsgi.py. For example, take the following directory structure:

/apps/baz/      <----- This is the WSGI application's application root!
   |
   +- public/
   |    |
   |    +- ...
   |
   +- passenger_wsgi.py
   |
   +- ...

13.2. Idle process

An "idle process" refers to a process that hasn’t processed any requests for a while.

13.3. Inactive process

An "inactive process" refers to a process that’s current not processing any requests. An idle process is always inactive, but an inactive process is not always considered idle.

14. Appendix C: Spawning methods explained

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/

14.1. The most straightforward and traditional way: direct spawning

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/

14.2. The smart spawning method

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/

14.2.1. How it works

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/

14.2.2. Summary of benefits

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/

14.3. Smart spawning caveat #1: unintentional file descriptor sharing

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/

14.3.1. Example 1: Memcached connection sharing (harmful)

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/

14.3.2. Example 2: Log file sharing (not harmful)

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/

14.4. Smart spawning caveat #2: the need to revive threads

This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/

15. Appendix D: About environment variables

15.1. Working with environment variables

15.2. The PATH environment variable

15.2.1. Adding Phusion Passenger’s administration tools to PATH

15.3. Making environment variables permanent

15.3.1. bash

15.3.2. Apache

15.3.3. Nginx

15.3.4. cron

15.3.5. Phusion Passenger-served apps

15.4. Environment variables and sudo


passenger-5.0.30/doc/Users guide Nginx.idmap.txt000644 000765 000024 00000043503 12233035540 022035 0ustar00honglistaff000000 000000 ###### Autogenerated by Mizuho, DO NOT EDIT ###### # This file maps section names to IDs so that the commenting system knows which # comments belong to which section. Section names may be changed at will but # IDs always stay the same, allowing one to retain old comments even if you # rename a section. # # This file is autogenerated but is not a cache; you MUST NOT DELETE this # file and you must check it into your version control system. If you lose # this file you may lose the ability to identity old comments. # # Entries marked with "fuzzy" indicate that the section title has changed # and that Mizuho has found an ID which appears to be associated with that # section. You should check whether it is correct, and if not, fix it. 1. Support information => support-information-nl5gdn 1.1. Supported operating systems and languages => supported-operating-systems-a5n2x4 1.2. Where to get support => where-to-get-support-2s9na5 2. Installation => installing-phusion-passenger-hn03ac 2.1. Synopsis => synopsis-1uu3sqp 2.2. Installing or upgrading on Mac OS X with Homebrew => installing-or-upgrading-on-mac-os-x-with-homebrew-13ovvy9 2.3. Installing or upgrading on Debian or Ubuntu => installing-or-upgrading-on-ubuntu-fw5fvp 2.3.1. Adding our APT repository => adding-our-apt-repository-p60cki 2.3.2. Installing packages => installing-packages-j9glez 2.3.3. Inserting passenger_root into nginx.conf => inserting-passenger-root-into-nginx-conf--1pmj19o 2.4. Installing or upgrading on Red Hat or CentOS => installing-or-upgrading-on-red-hat-fedora-centos-or-scientificlinux-1uus5a1 2.4.1. Adding our YUM repository => adding-our-yum-repository-2i9z9o 2.4.2. Installing packages => installing-packages-1actlet 2.5. Installing or upgrading on Heroku => installing-or-upgrading-on-heroku-jh07kr 2.6. Generic installation, upgrade and downgrade method: via RubyGems => generic-installation-upgrade-and-downgrade-method-via-rubygems-76uol7 2.7. Generic installation, upgrade and downgrade method: via tarball => generic-installation-upgrade-and-downgrade-method-via-tarball-2gkx43 2.8. Upgrading from open source to Enterprise => upgrading-from-open-source-to-enterprise-1a58c2b 2.9. Cryptographic verification of installation files => cryptographic-verification-of-installation-files-2goray 2.9.1. Synopsis => synopsis-4fv6zw 2.9.2. Importing the Phusion Software Signing key => importing-the-phusion-software-signing-key-1qfpaj4 2.9.3. Verifying the Phusion Software Signing key => verifying-the-phusion-software-signing-key-i7f1vj 2.9.4. Verifying the gem and tarball => verifying-the-gem-and-tarball-dr9466 2.9.5. Verifying Git signatures => verifying-git-signatures-dyo4fk 2.9.6. Verifying Debian packages => verifying-deb-and-rpm-packages-1ed36d5 2.9.7. Verifying RPM packages => verifying-rpm-packages-1wwblzx 2.9.8. Revocation => revocation-xwvhea 2.10. Non-interactive, automatic, headless installs or upgrades => non-interactive-automatic-headless-installs-or-upgrades-834ymv 2.11. Customizing the compilation process => customizing-the-compilation-process-u4cdcf 2.11.1. Setting the compiler => setting-the-compiler-1l6dpe1 2.11.2. Adding additional compiler or linker flags => adding-additional-compiler-or-linker-flags-1jehjon 2.11.3. Forcing location of command line tools and dependencies => forcing-location-of-certain-command-line-tools-1j93cki 2.12. Installing as a normal Nginx module without using the installer => installing-as-a-normal-nginx-module-without-using-the-installer-1kkpes5 2.13. Creating an Nginx init script => creating-an-nginx-init-script-1kd8zg5 2.14. Disabling without uninstalling => disabling-without-uninstalling-1t5tqan 2.15. Uninstalling => uninstalling-phusion-passenger-wuycvb 2.16. Moving to a different directory => moving-to-a-different-directory-gif3wo 3. Deploying a Rack-based Ruby application => deploying-a-rack-based-ruby-application-including-rails-3--12benx3 3.1. Tutorial/example: writing and deploying a Hello World Rack application => tutorial-example-writing-and-deploying-a-hello-world-rack-application-1wstx99 3.2. Deploying to a virtual host’s root => deploying-to-a-virtual-host-s-root-1mh24z5 3.3. Deploying to a sub URI => deploying-to-a-sub-uri-1il2qj7 3.4. Redeploying (restarting the Rack application) => redeploying-restarting-the-rack-application--xnbfam 3.5. Rackup specifications for various web frameworks => rackup-specifications-for-various-web-frameworks-1a2cs41 4. Deploying a WSGI (Python) application => deploying-a-wsgi-python-application-1or2efo 4.1. Tutorial/example: writing and deploying a Hello World WSGI application => tutorial-example-writing-and-deploying-a-hello-world-wsgi-application-k5ron2 4.2. Deploying to a virtual host’s root => deploying-to-a-virtual-host-s-root-f02erj 4.3. Deploying to a sub URI => deploying-to-a-sub-uri-37q0ou 4.4. Redeploying (restarting the WSGI application) => redeploying-restarting-the-wsgi-application--10zdh2k 4.5. Sample passenger_wsgi.py for Django => sample-passenger-wsgi-py-for-django-1cvndls 5. Deploying a Node.js application => deploying-a-node-js-application-15wbczd 6. Deploying a Meteor application => deploying-a-meteor-application-1b51wl9 7. Configuring Phusion Passenger => configuring-phusion-passenger-1g1svey 7.1. passenger_root => passenger-root-directory--bqvhhz 7.2. Deployment options => deployment-options-1a1vxsp 7.2.1. passenger_enabled => passenger-enabled-on-off--1rpb2t7 7.2.2. passenger_base_uri => passenger-base-uri-uri--1xtuo50 7.2.3. passenger_document_root => passenger-document-root-path--1pge8kd 7.3. Application loading options => application-loading-options-f3skts 7.3.1. passenger_ruby => passenger-ruby-filename--1gnok5k 7.3.2. passenger_python => passenger-python-filename--14p554 7.3.3. passenger_nodejs => passenger-nodejs-filename--16hzjsv 7.3.4. passenger_meteor_app_settings => passenger-meteor-app-settings-filename--1toqnaz 7.3.5. passenger_app_env => passenger-app-env-string--qjeimp 7.3.6. rails_env => rails-env-string--jlh7v9 7.3.7. rack_env => rack-env-string--tqmrt0 7.3.8. passenger_app_root => passenger-app-root-path-to-root--1dbudc6 7.3.9. passenger_app_group_name => passenger-app-group-name-name--11jrx8u 7.3.10. passenger_app_type => passenger-app-type-name--g9zccv 7.3.11. passenger_startup_file => passenger-startup-file-filename--y4gy1m 7.3.12. passenger_spawn_method => passenger-spawn-method-string--1sc6njl 7.3.13. passenger_env_var => passenger-env-var-name-value--y8e7wh 7.3.14. passenger_load_shell_envvars => passenger-load-shell-envvars-on-off--fw5u4l 7.3.15. passenger_rolling_restarts => passenger-rolling-restarts 7.3.16. passenger_resist_deployment_errors => passenger-resist-deployment-errors-on-off--k9yf1 7.4. Security options => security-options-1bv93g4 7.4.1. passenger_user_switching => passenger-user-switching-on-off--1p37u3l 7.4.2. passenger_user => passenger-user-username--b06ur7 7.4.3. passenger_group => passenger-user-group-name--1fco4j7 7.4.4. passenger_default_user => passenger-default-user-username--1h6cdmf 7.4.5. Passenger_default_group => passenger-default-group-group-name--1qxn2qa 7.4.6. passenger_show_version_in_header => passenger-show-version-in-header-on-off--2h49av 7.4.7. passenger_friendly_error_pages => passenger-friendly-error-pages-on-off--1ti1a0e 7.5. Resource control and optimization options => resource-control-and-optimization-options-xd7evs 7.5.1. passenger_max_pool_size => passenger-max-pool-size-integer--3jzefs 7.5.2. passenger_min_instances => passenger-min-instances-integer--uclykt 7.5.3. passenger_max_instances => passenger-max-instances 7.5.4. passenger_max_instances_per_app => passenger-max-instances-per-app-integer--1xhbbne 7.5.5. passenger_pool_idle_time => passenger-pool-idle-time-integer--xcw65o 7.5.6. passenger_max_preloader_idle_time => rails-app-spawner-idle-time-integer--1xjqe4b 7.5.7. passenger_start_timeout => passenger-start-timeout-seconds--8xn504 7.5.8. passenger_concurrency_model => passenger-concurrency-model-process-thread--brcvkk 7.5.9. passenger_thread_count => passenger-thread-count-number--1kd6ffy 7.5.10. passenger_max_requests => passenger-max-requests-integer--sgzint 7.5.11. passenger_max_request_time => passenger-max-request-time-seconds--1htog2g 7.5.12. passenger_memory_limit => passenger-memory-limit-integer--1ry7dwx 7.5.13. passenger_stat_throttle_rate => passenger-stat-throttle-rate-integer--xzjbry 7.5.14. passenger_pre_start => passenger-pre-start-url--npldeb 7.6. Connection handling options => connection-handling-options-8jgq90 7.6.1. passenger_set_header => passenger-set-cgi-param-cgi-environment-name-value--rx9gc0 7.6.2. passenger_max_request_queue_size => passenger-max-request-queue-size-number--i0te1b 7.6.3. passenger_request_queue_overflow_status_code => passenger-request-queue-overflow-status-code-code--1wcwuxl 7.6.4. passenger_sticky_sessions => passenger-sticky-sessions-on-off--lwvbxs 7.6.5. passenger_sticky_sessions_cookie_name => passenger-sticky-sessions-cookie-name-8hrox9 7.6.6. passenger_ignore_client_abort => passenger-ignore-client-abort 7.6.7. passenger_intercept_errors => passenger-intercept-errors-1uvcb9x 7.6.8. passenger_pass_header
=> passenger-pass-header-header-name--1cg31je 7.6.9. passenger_ignore_headers
=> passenger-ignore-headers-header-names--12zg5oh 7.6.10. passenger_headers_hash_bucket_size => passenger-headers-hash-bucket-size-size--zx1rwf 7.6.11. passenger_headers_hash_max_size => passenger-headers-hash-max-size-size--1vl0i9u 7.6.12. passenger_buffer_response => passenger-buffer-response 7.6.13. passenger_response_buffer_high_watermark => passenger-response-buffer-high-watermark-bytes--ranajv 7.6.14. passenger_buffer_size => passenger-buffer-size-1jfkq87 7.6.15. passenger_buffers => passenger-busy-buffers 7.6.16. passenger_busy_buffers_size => passenger-busy-buffer-size-124sj61 7.7. Logging and debugging options => logging-and-debugging-options-14e91ni 7.7.1. passenger_log_level => passenger-log-level-integer--17snhon 7.7.2. passenger_log_file => passenger-debug-log-file-filename--21ubaj 7.7.3. PassengerFileDescriptorLogFile => passengerfiledescriptorlogfile-filename--bqbga8 7.7.4. passenger_debugger => passenger-debugger-on-off--1wkuq85 7.8. Advanced options => advanced-options-hnuhqz 7.8.1. passenger_instance_registry_dir => passenger-instance-registry-dir-directory--1jl6zij 7.8.2. passenger_data_buffer_dir => passenger-data-buffer-dir-directory--1isg9cm 7.8.3. passenger_fly_with => passenger-fly-with-socket-filename--1amd1xn 7.9. Deprecated or removed options => deprecated-options-1dtzo0g 7.9.1. rails_spawn_method => rails-spawn-method-17vdnpt 7.9.2. passenger_debug_log_file => passenger-debug-log-file-1aqru34 8. Troubleshooting => troubleshooting-1pt0c76 8.1. Generic troubleshooting tips => generic-troubleshooting-tips-xhe4nu 8.2. Why does the first request take a long time? => why-does-the-first-request-take-a-long-time--1knj9fp 8.3. Upon accessing the web app, Nginx reports a "Permission denied" error => upon-accessing-the-web-app-nginx-reports-a-permission-denied-error-1wgatlk 8.4. I get "command not found" when running a Phusion Passenger command through sudo => i-get-command-not-found-when-running-a-phusion-passenger-command-through-sudo-10fzwno 8.5. The application thinks its not on SSL even though it is => the-application-thinks-its-not-on-ssl-even-though-it-is-1e2m21h 8.6. Ruby on Rails-specific troubleshooting => ruby-on-rails-specific-troubleshooting-n8u5u1 8.6.1. The "About your application’s environment" link does not work => the-about-your-application-s-environment-link-does-not-work-9p7b2g 8.6.2. The Rails application reports that it’s unable to start because of a permission error => the-rails-application-reports-that-it-s-unable-to-start-because-of-a-permission-error-58ww8s 8.6.3. The Rails application’s log file is not being written to => the-rails-application-s-log-file-is-not-being-written-to-9m2i5h 9. Analysis and system maintenance => analysis-and-system-maintenance-1nnlnj8 9.1. Inspecting memory usage => inspecting-memory-usage-1k6y8v0 9.2. Inspecting Phusion Passenger’s internal status => inspecting-phusion-passenger-s-internal-status-v36wbc 9.3. Debugging frozen applications => debugging-frozen-applications-qoctl8 9.4. Accessing individual application processes => accessing-individual-application-processes-1qe4fqk 9.5. Attaching an IRB console to an application process => attaching-an-irb-console-to-an-application-process-d36enw 10. Tips => tips-n4c22d 10.1. User Switching (security feature) => user-switching-security--zmsy9o 10.1.1. Requirements => requirements-15ozqdj 10.1.2. Effects => effects-nd2m44 10.1.3. Caveats & troubleshooting => caveats-troubleshooting-mbw582 10.1.4. Red Hat and CentOS caveats => red-hat-and-centos-caveats-ilqxyl 10.1.5. Finding out what user an application is running as => finding-out-what-user-an-application-is-running-as-1ni7zk6 10.2. Copy-on-write memory support (reducing memory consumption of Ruby applications) => reducing-memory-consumption-of-ruby-on-rails-applications-by-33--1o3z66q 10.3. Tuning for Server Sent Events and WebSockets => tuning-for-server-sent-events-and-websockets-8ec9td 10.4. Bundler support => bundler-support-19v1h43 10.4.1. Does Phusion Passenger itself need to be added to the Gemfile? => does-phusion-passenger-itself-need-to-be-added-to-the-gemfile--xn1a11 10.5. Installing multiple Ruby on Rails versions => installing-multiple-ruby-on-rails-versions-1bp1fff 10.6. Making the application restart after each request => making-the-application-restart-after-each-request-vimy48 10.7. How to fix broken images/CSS/JavaScript URIs in sub-URI deployments => how-to-fix-broken-images-css-javascript-uris-in-sub-uri-deployments-11mzwt6 10.8. Out-of-Band Work and Out-of-Band Garbage Collection => out-of-band-work-and-out-of-band-garbage-collection-v89lu2 10.9. Hooks => hooks-qrxle1 10.9.1. Example => example-1orara9 10.9.2. Environment => environment-10uhg8m 10.9.3. Blocking and concurrency => blocking-and-concurrency-g5njp1 10.9.4. Error handling => error-handling-m78oxs 10.9.5. Compatibility => compatibility-132b3ns 10.9.6. Available hooks => available-hooks-11w1prq 10.10. Flying Passenger => flying-passenger-137qg5e 10.10.1. Requirements => requirements-194ysj6 10.10.2. Basic usage => basic-usage-1qtgvwx 10.10.3. Configuring Flying Passenger => configuring-flying-passenger-n558np 10.10.4. Managing the Flying Passenger daemon => managing-the-flying-passenger-daemon-vjmzdh 10.10.5. Using Flying Passenger with MRI 1.8 or JRuby => using-flying-passenger-with-mri-1-8-or-jruby-pxho35 10.10.6. Caveats and limitations => caveats-and-limitations-15wakf 11. Under the hood => under-the-hood-8uney 11.1. Page caching support => page-caching-support-nafhf6 11.2. Phusion Passenger and its relationship with Ruby => phusion-passenger-and-its-relationship-with-ruby-1hub1pa 11.2.1. How Ruby is used => how-ruby-is-used-j7g2a4 11.2.2. When the system has multiple Ruby interpreters => when-the-system-has-multiple-ruby-interpreters-fwn3t 11.3. How Phusion Passenger detects whether a virtual host is a web application => how-phusion-passenger-detects-whether-a-virtual-host-is-a-web-application-13qbmhn 12. Appendix A: About this document => appendix-a-about-this-document-zfvixm 13. Appendix B: Terminology => appendix-b-terminology-wzv5ro 13.1. Application root => application-root-1fd6bqv 13.2. Idle process => idle-process-13byfw9 13.3. Inactive process => inactive-process-1d2h0po 14. Appendix C: Spawning methods explained => appendix-c-spawning-methods-explained-tcp8e6 14.1. The most straightforward and traditional way: direct spawning => the-most-straightforward-and-traditional-way-conservative-spawning-civ29z 14.2. The smart spawning method => the-smart-spawning-method-7nhgtj 14.2.1. How it works => how-it-works-f9umga 14.2.2. Summary of benefits => summary-of-benefits-qovyvk 14.3. Smart spawning caveat #1: unintentional file descriptor sharing => smart-spawning-gotcha-1-unintentional-file-descriptor-sharing-cebw6q 14.3.1. Example 1: Memcached connection sharing (harmful) => example-1-memcached-connection-sharing-harmful--1wfs3ad 14.3.2. Example 2: Log file sharing (not harmful) => example-2-log-file-sharing-not-harmful--ox4yfy 14.4. Smart spawning caveat #2: the need to revive threads => smart-spawning-gotcha-2-the-need-to-revive-threads-1ey176o 15. Appendix D: About environment variables => appendix-d-about-environment-variables-1t2cuff 15.1. Working with environment variables => working-with-environment-variables-1kmvq8w 15.2. The PATH environment variable => the-path-environment-variable-vlp05e 15.2.1. Adding Phusion Passenger’s administration tools to PATH => adding-phusion-passenger-s-administration-tools-to-path-1flz2tu 15.3. Making environment variables permanent => making-environment-variables-permanent-1wjyhzt 15.3.1. bash => bash-19xsxec 15.3.2. Apache => apache-9hqtyj 15.3.3. Nginx => nginx-157dpwy 15.3.4. cron => cron-1nuc9cz 15.3.5. Phusion Passenger-served apps => phusion-passenger-served-apps-uiewl5 15.4. Environment variables and sudo => environment-variables-and-sudo-10lphxn ### These sections appear to have been removed. Please check. 3.5.1. Camping => camping-16vz2yb 3.5.2. Halcyon => halcyon-1benlfl 3.5.3. Mack => mack-1ezijq6 3.5.4. Merb => merb-ddsh55 3.5.5. Ramaze => ramaze-1p2zod 3.5.6. Sinatra => sinatra-a7u9ag passenger-5.0.30/doc/Users guide Nginx.txt000644 000765 000024 00000041074 12233035540 020745 0ustar00honglistaff000000 000000 Phusion Passenger users guide, Nginx version ============================================ image:images/phusion_banner.png[link="http://www.phusion.nl/"] This is the old, deprecated Passenger for Nginx documentation. Please visit https://www.phusionpassenger.com/library/ for the new documentation. == Support information include::users_guide_snippets/support_information.txt[] [[installation]] == Installation include::users_guide_snippets/installation.txt[] [[deploying_a_rack_app]] == Deploying a Rack-based Ruby application == This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/ === Tutorial/example: writing and deploying a Hello World Rack application === This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/ === Deploying to a virtual host's root === This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/ [[deploying_rack_to_sub_uri]] === Deploying to a sub URI === This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/ === Redeploying (restarting the Rack application) === This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/nginx/restart_app.html === Rackup specifications for various web frameworks === This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/config_ru.html [[deploying_a_wsgi_app]] == Deploying a WSGI (Python) application This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/ === Tutorial/example: writing and deploying a Hello World WSGI application === This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/ === Deploying to a virtual host's root === This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/ [[deploying_wsgi_to_sub_uri]] === Deploying to a sub URI === This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/ === Redeploying (restarting the WSGI application) === This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/nginx/restart_app.html === Sample `passenger_wsgi.py` for Django This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/wsgi_spec.html == Deploying a Node.js application This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/ == Deploying a Meteor application This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/ == Configuring Phusion Passenger == This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/ [[PassengerRoot]] === passenger_root === This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_root === Deployment options ==== passenger_enabled This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_enabled [[PassengerBaseURI]] ==== passenger_base_uri This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_base_uri [[PassengerDocumentRoot]] ==== passenger_document_root This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_document_root === Application loading options [[PassengerRuby]] ==== passenger_ruby This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_ruby ==== passenger_python This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_python ==== passenger_nodejs This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_nodejs ==== passenger_meteor_app_settings This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_meteor_app_settings-filename [[PassengerAppEnv]] ==== passenger_app_env This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_app_env [[RailsEnv]] ==== rails_env This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_app_env [[RackEnv]] ==== rack_env This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_app_env [[PassengerAppRoot]] ==== passenger_app_root This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_app_root [[PassengerAppGroupName]] ==== passenger_app_group_name This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_app_group_name [[PassengerAppType]] ==== passenger_app_type This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_app_type [[PassengerStartupFile]] ==== passenger_startup_file This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_startup_file [[PassengerSpawnMethod]] ==== passenger_spawn_method This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_spawn_method [[PassengerEnvVar]] ==== passenger_env_var This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_env_var [[PassengerLoadShellEnvvars]] ==== passenger_load_shell_envvars This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_load_shell_envvars [[PassengerRollingRestarts]] ==== passenger_rolling_restarts This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_rolling_restarts [[PassengerResistDeploymentErrors]] ==== passenger_resist_deployment_errors This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_resist_deployment_errors === Security options === [[PassengerUserSwitching]] ==== passenger_user_switching ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_user_switching [[PassengerUser]] ==== passenger_user ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_user [[PassengerGroup]] ==== passenger_group ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_group [[PassengerDefaultUser]] ==== passenger_default_user ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_default_user [[PassengerDefaultGroup]] ==== Passenger_default_group ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_default_group ==== passenger_show_version_in_header ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_show_version_in_header [[PassengerFriendlyErrorPages]] ==== passenger_friendly_error_pages ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_friendly_error_pages === Resource control and optimization options === [[PassengerMaxPoolSize]] ==== passenger_max_pool_size ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_max_pool_size [[PassengerMinInstances]] ==== passenger_min_instances ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_min_instances [[PassengerMaxInstances]] ==== passenger_max_instances ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_max_instances ==== passenger_max_instances_per_app ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_max_instances_per_app [[PassengerPoolIdleTime]] ==== passenger_pool_idle_time ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_pool_idle_time ==== passenger_max_preloader_idle_time ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_max_preloader_idle_time ==== passenger_start_timeout ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_start_timeout [[PassengerConcurrencyModel]] ==== passenger_concurrency_model ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_concurrency_model [[PassengerThreadCount]] ==== passenger_thread_count ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_thread_count [[PassengerMaxRequests]] ==== passenger_max_requests ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_max_requests [[PassengerMaxRequestTime]] ==== passenger_max_request_time ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_max_request_time [[PassengerMemoryLimit]] ==== passenger_memory_limit ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_memory_limit ==== passenger_stat_throttle_rate ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_stat_throttle_rate [[PassengerPreStart]] ==== passenger_pre_start ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_pre_start === Connection handling options === [[PassengerSetHeader]] ==== passenger_set_header ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_set_header [[passenger_max_request_queue_size]] ==== passenger_max_request_queue_size ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_max_request_queue_size [[passenger_request_queue_overflow_status_code]] ==== passenger_request_queue_overflow_status_code ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_request_queue_overflow_status_code [[PassengerStickySessions]] ==== passenger_sticky_sessions This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_sticky_sessions [[PassengerStickySessionsCookieName]] ==== passenger_sticky_sessions_cookie_name This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_sticky_sessions_cookie_name ==== passenger_ignore_client_abort ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_ignore_client_abort [[passenger_intercept_errors]] ==== passenger_intercept_errors ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_intercept_errors ==== passenger_pass_header
==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_pass_header-header-name ==== passenger_ignore_headers
==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_ignore_headers ==== passenger_headers_hash_bucket_size ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_headers_hash_bucket_size ==== passenger_headers_hash_max_size ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_headers_hash_max_size [[passenger_buffer_response]] ==== passenger_buffer_response ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_buffer_response [[PassengerResponseBufferHighWatermark]] ==== passenger_response_buffer_high_watermark This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_response_buffer_high_watermark ==== passenger_buffer_size ==== ==== passenger_buffers ==== ==== passenger_busy_buffers_size ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_buffer_size-passenger_buffers-passenger_busy_buffers_size === Logging and debugging options === [[PassengerLogLevel]] ==== passenger_log_level ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_log_level [[PassengerLogFile]] ==== passenger_log_file ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_log_file ==== PassengerFileDescriptorLogFile This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_file_descriptor_log_file ==== passenger_debugger ==== This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_debugger === Advanced options [[PassengerInstanceRegistryDir]] ==== passenger_instance_registry_dir This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_instance_registry_dir [[PassengerDataBufferDir]] ==== passenger_data_buffer_dir This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_data_buffer_dir ==== passenger_fly_with This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_fly_with === Deprecated or removed options === The following options have been deprecated or removed. Some are still supported for backwards compatibility reasons. ==== rails_spawn_method ==== Deprecated in favor of <>. ==== passenger_debug_log_file ==== This option has been renamed in version 5.0.5 to <>. [[troubleshooting]] == Troubleshooting == include::users_guide_snippets/troubleshooting/default.txt[] === The application thinks its not on SSL even though it is This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/nginx/troubleshooting/ include::users_guide_snippets/troubleshooting/rails.txt[] == Analysis and system maintenance == include::users_guide_snippets/analysis_and_system_maintenance.txt[] == Tips == include::users_guide_snippets/tips.txt[] == Under the hood == include::users_guide_snippets/under_the_hood/page_caching_support.txt[] include::users_guide_snippets/under_the_hood/relationship_with_ruby.txt[] [[application_detection]] === How Phusion Passenger detects whether a virtual host is a web application === This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/app_autodetection/nginx/ include::users_guide_snippets/appendix_a_about.txt[] include::users_guide_snippets/appendix_b_terminology.txt[] include::users_guide_snippets/appendix_c_spawning_methods.txt[] [[about_environment_variables]] == Appendix D: About environment variables include::users_guide_snippets/environment_variables.txt[] passenger-5.0.30/doc/Users guide Standalone.html000644 000765 000024 00000531067 12233035540 022105 0ustar00honglistaff000000 000000 Phusion Passenger Standalone users guide

1. Support information

1.1. Supported operating systems and languages

1.2. Where to get support

  • Community discussion forum - post a message here if you’re experiencing problems. Support on this forum is provided by the community on a best-effort basis, so a (timely) response is not guaranteed.

  • Issue tracker - report bugs here.

  • Email support@phusion.nl if you are a Phusion Passenger Enterprise customer. Please mention your order reference. If you are not an Enterprise customer, we kindly redirect you to the community discussion forum instead.

  • Commercial support contracts are also available.

  • Report security vulnerabilities to security@phusion.nl. We will do our best to respond to you as quickly as we can, so please do not disclose the vulnerability until then.

Please consult the Phusion Passenger website for a full list of support resources.

2. Installation

2.1. Synopsis

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/

2.2. Installing or upgrading on Mac OS X with Homebrew

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/

2.3. Installing or upgrading on Debian or Ubuntu

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apt_repo/

2.3.1. Adding our APT repository

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apt_repo/

2.3.2. Installing packages

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apt_repo/

2.4. Installing or upgrading on Red Hat or CentOS

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/yum_repo/

2.4.1. Adding our YUM repository

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/yum_repo/

2.4.2. Installing packages

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/yum_repo/

2.5. Installing or upgrading on Heroku

Please refer to our Heroku Ruby demo for installation and upgrade instructions for Heroku.

2.6. Generic installation, upgrade and downgrade method: via RubyGems

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/

2.7. Generic installation, upgrade and downgrade method: via tarball

This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/

2.8. Upgrading from open source to Enterprise

2.9. Cryptographic verification of installation files

2.9.1. Synopsis

2.9.2. Importing the Phusion Software Signing key

2.9.3. Verifying the Phusion Software Signing key

2.9.4. Verifying the gem and tarball

2.9.5. Verifying Git signatures

2.9.6. Verifying Debian packages

2.9.7. Verifying RPM packages

2.9.8. Revocation

2.10. Customizing the compilation process

2.10.1. Setting the compiler

2.10.2. Adding additional compiler or linker flags

2.10.3. Forcing location of command line tools and dependencies

2.11. Uninstalling

2.12. Moving to a different directory

3. Usage

This documentation has moved. Please visit the walkthroughs at https://www.phusionpassenger.com/library/ to learn how to use Passenger Standalone. Or visit the new deployment documentation.

4. Configuration

This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/standalone/

4.1. Command line options

4.2. Configuration file

4.3. Advanced configuration

5. Using Passenger Standalone in production

This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/standalone/

5.1. Starting Passenger Standalone at system boot

5.2. Sharing the same port between multiple Passenger Standalone instances

5.3. Installing Passenger Standalone behind Nginx

6. Mass deployment

7. Troubleshooting

This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/standalone/

8. Under the hood

8.1. Phusion Passenger and its relationship with Ruby

8.1.1. How Ruby is used

8.1.2. When the system has multiple Ruby interpreters

9. Appendix: About environment variables

9.1. Working with environment variables

9.2. The PATH environment variable

9.2.1. Adding Phusion Passenger’s administration tools to PATH

9.3. Making environment variables permanent

9.3.1. bash

9.3.2. Apache

9.3.3. Nginx

9.3.4. cron

9.3.5. Phusion Passenger-served apps

9.4. Environment variables and sudo


passenger-5.0.30/doc/Users guide Standalone.idmap.txt000644 000765 000024 00000013143 12233035540 023037 0ustar00honglistaff000000 000000 ###### Autogenerated by Mizuho, DO NOT EDIT ###### # This file maps section names to IDs so that the commenting system knows which # comments belong to which section. Section names may be changed at will but # IDs always stay the same, allowing one to retain old comments even if you # rename a section. # # This file is autogenerated but is not a cache; you MUST NOT DELETE this # file and you must check it into your version control system. If you lose # this file you may lose the ability to identity old comments. # # Entries marked with "fuzzy" indicate that the section title has changed # and that Mizuho has found an ID which appears to be associated with that # section. You should check whether it is correct, and if not, fix it. 1. Support information => support-information-1x8e9ee 1.1. Supported operating systems and languages => supported-operating-systems-1387080 1.2. Where to get support => where-to-get-support-xkx7rx 2. Installation => installation-2vrmef 2.1. Synopsis => synopsis-92sr34 2.2. Installing or upgrading on Mac OS X with Homebrew => installing-or-upgrading-on-mac-os-x-with-homebrew-fxjdi1 2.3. Installing or upgrading on Debian or Ubuntu => installing-or-upgrading-on-debian-or-ubuntu-gme2a9 2.3.1. Adding our APT repository => adding-our-apt-repository-1qu2se8 2.3.2. Installing packages => installing-packages-k1zxbg 2.4. Installing or upgrading on Red Hat or CentOS => installing-or-upgrading-on-red-hat-fedora-centos-or-scientificlinux-16cek45 2.4.1. Adding our YUM repository => adding-our-yum-repository-7c3y3z 2.4.2. Installing packages => installing-packages-1i5z60y 2.5. Installing or upgrading on Heroku => installing-or-upgrading-on-heroku-rspclc 2.6. Generic installation, upgrade and downgrade method: via RubyGems => generic-installation-upgrade-and-downgrade-method-via-rubygems-1229ugi 2.7. Generic installation, upgrade and downgrade method: via tarball => generic-installation-upgrade-and-downgrade-method-via-tarball-1rwvasy 2.8. Upgrading from open source to Enterprise => upgrading-from-open-source-to-enterprise-1kezors 2.9. Cryptographic verification of installation files => cryptographic-verification-of-installation-files-85nwoi 2.9.1. Synopsis => synopsis-1ij8j9r 2.9.2. Importing the Phusion Software Signing key => importing-the-phusion-software-signing-key-1u5hkcr 2.9.3. Verifying the Phusion Software Signing key => verifying-the-phusion-software-signing-key-qor1n1 2.9.4. Verifying the gem and tarball => verifying-the-gem-and-tarball-i7tj7a 2.9.5. Verifying Git signatures => verifying-git-signatures-dltpan 2.9.6. Verifying Debian packages => verifying-deb-and-rpm-packages-uga0ho 2.9.7. Verifying RPM packages => verifying-rpm-packages-1m745i5 2.9.8. Revocation => revocation-ukzeg9 2.10. Customizing the compilation process => customizing-the-compilation-process-j4xj2t 2.10.1. Setting the compiler => setting-the-compiler-1fxfulc 2.10.2. Adding additional compiler or linker flags => adding-additional-compiler-or-linker-flags-nxfour 2.10.3. Forcing location of command line tools and dependencies => forcing-location-of-command-line-tools-and-dependencies-xajpg1 2.11. Uninstalling => uninstalling-3hpprb 2.12. Moving to a different directory => moving-to-a-different-directory-b3lpy1 3. Usage => usage-1eyt33o 4. Configuration => configuration-10trfau 4.1. Command line options => command-line-options-1njv6kt 4.2. Configuration file => configuration-file-xng7yp 4.3. Advanced configuration => advanced-configuration-e3v4pk 5. Using Passenger Standalone in production => using-passenger-standalone-in-production-1i7yjcz 5.1. Starting Passenger Standalone at system boot => starting-passenger-standalone-at-system-boot-jpem2b 5.2. Sharing the same port between multiple Passenger Standalone instances => sharing-the-same-port-between-multiple-passenger-standalone-instances-3lcmc5 5.3. Installing Passenger Standalone behind Nginx => installing-passenger-standalone-behind-nginx-1xylsfk 6. Mass deployment => mass-deployment-1xqriy4 7. Troubleshooting => troubleshooting-o7g75o 8. Under the hood => under-the-hood-ub7j3q 8.1. Phusion Passenger and its relationship with Ruby => phusion-passenger-and-its-relationship-with-ruby-1usvd0o 8.1.1. How Ruby is used => how-ruby-is-used-1saha86 8.1.2. When the system has multiple Ruby interpreters => when-the-system-has-multiple-ruby-interpreters-acf6d 9. Appendix: About environment variables => appendix-about-environment-variables-1ct91x3 9.1. Working with environment variables => working-with-environment-variables-11cmwlv 9.2. The PATH environment variable => the-path-environment-variable-yzfn3k 9.2.1. Adding Phusion Passenger’s administration tools to PATH => adding-phusion-passenger-s-administration-tools-to-path-d7k5mh 9.3. Making environment variables permanent => making-environment-variables-permanent-mkq46d 9.3.1. bash => bash-hmcscc 9.3.2. Apache => apache-15y3bm0 9.3.3. Nginx => nginx-uarf27 9.3.4. cron => cron-6272ar 9.3.5. Phusion Passenger-served apps => phusion-passenger-served-apps-127wbl2 9.4. Environment variables and sudo => environment-variables-and-sudo-znzmif ### These sections appear to have been removed. Please check. 7.1. Generic troubleshooting tips => generic-troubleshooting-tips-2vokcr 7.2. Upon uploading a file, Phusion Passenger reports "client_body_temp/00000000xx failed (2: No such file or directory)" => upon-uploading-a-file-phusion-passenger-reports-client-body-temp-00000000xx-failed-2-no-such-file-or-directory--d01goe 7.3. I get "command not found" when running a Phusion Passenger command through sudo => i-get-command-not-found-when-running-a-phusion-passenger-command-through-sudo-14k8kde passenger-5.0.30/doc/Users guide Standalone.txt000644 000765 000024 00000005325 12233035540 021751 0ustar00honglistaff000000 000000 = Phusion Passenger Standalone users guide image:images/phusion_banner.png[link="http://www.phusion.nl/"] This is the old, deprecated Passenger Standalone documentation. Please visit https://www.phusionpassenger.com/library/ for the new documentation. == Support information include::users_guide_snippets/support_information.txt[] [[installation]] == Installation include::users_guide_snippets/installation.txt[] == Usage This documentation has moved. Please visit the walkthroughs at https://www.phusionpassenger.com/library/ to learn how to use Passenger Standalone. Or visit link:https://www.phusionpassenger.com/library/deploy/standalone/deploy/[the new deployment documentation]. == Configuration This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/standalone/ === Command line options This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/standalone/reference/ [[config_file]] === Configuration file This documentation has moved. Please visit link:https://www.phusionpassenger.com/library/config/standalone/reference/[the new configuration reference] or link:https://www.phusionpassenger.com/library/config/standalone/intro.html[the new configuration introduction document]. [[advanced_configuration]] === Advanced configuration This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/standalone/intro.html#nginx-configuration-template == Using Passenger Standalone in production This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/standalone/ === Starting Passenger Standalone at system boot This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/standalone/deploy/ruby/#step-4-make-sure-passenger-standalone-starts-on-system-boot [[sharing_port]] === Sharing the same port between multiple Passenger Standalone instances This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/standalone/reverse_proxy.html === Installing Passenger Standalone behind Nginx This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/standalone/reverse_proxy.html [[mass_deployment]] == Mass deployment This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/standalone/mass_deployment.html [[troubleshooting]] == Troubleshooting This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/standalone/ == Under the hood include::users_guide_snippets/under_the_hood/relationship_with_ruby.txt[] [[about_environment_variables]] == Appendix: About environment variables include::users_guide_snippets/environment_variables.txt[] passenger-5.0.30/doc/Users guide.html000644 000765 000024 00000403304 12233035540 020024 0ustar00honglistaff000000 000000 Phusion Passenger users guide index

1. The documentation has moved

This is the old, deprecated Passenger documentation. Please visit https://www.phusionpassenger.com/library/ for the new documentation.


passenger-5.0.30/doc/Users guide.txt000644 000765 000024 00000000460 12233035540 017673 0ustar00honglistaff000000 000000 Phusion Passenger users guide index =================================== image:images/phusion_banner.png[link="http://www.phusion.nl/"] == The documentation has moved This is the old, deprecated Passenger documentation. Please visit https://www.phusionpassenger.com/library/ for the new documentation. passenger-5.0.30/doc/users_guide_snippets/000755 000765 000024 00000000000 12233035540 021216 5ustar00honglistaff000000 000000 passenger-5.0.30/doc/users_guide_snippets/alternative_for_flying_passenger.txt000644 000765 000024 00000000310 12233035540 030554 0ustar00honglistaff000000 000000 NOTE: This option has no effect when you are using <>. Instead, you should configure this by passing the {option} command line option to the Flying Passenger daemon.passenger-5.0.30/doc/users_guide_snippets/analysis_and_system_maintenance.txt000644 000765 000024 00000004676 12233035540 030407 0ustar00honglistaff000000 000000 === Inspecting memory usage === ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/overall_status_report.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/nginx/overall_status_report.html endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/standalone/overall_status_report.html endif::[] === Inspecting Phusion Passenger's internal status === ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/overall_status_report.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/nginx/overall_status_report.html endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/standalone/overall_status_report.html endif::[] [[debugging_frozen]] === Debugging frozen applications === If one of your application processes is frozen (stopped responding), then you can figure out where it is frozen by killing it with 'SIGABRT'. This will cause the processs to print a backtrace, after which it aborts. The backtrace information is logged into the web server error log file. In case of Ruby applications, you can also send the 'SIGQUIT' signal to have it print a backtrace without aborting. === Accessing individual application processes === ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/request_individual_processes.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/nginx/request_individual_processes.html endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/standalone/request_individual_processes.html endif::[] === Attaching an IRB console to an application process === ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/debugging_console/ endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/nginx/debugging_console/ endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/standalone/debugging_console/ endif::[] passenger-5.0.30/doc/users_guide_snippets/appendix_a_about.txt000644 000765 000024 00000001020 12233035540 025252 0ustar00honglistaff000000 000000 == Appendix A: About this document == The text of this document is licensed under the link:http://creativecommons.org/licenses/by-sa/3.0/[Creative Commons Attribution-Share Alike 3.0 Unported License]. image:images/by_sa.png[link="http://creativecommons.org/licenses/by-sa/3.0/"] Phusion Passenger is brought to you by link:http://www.phusion.nl/[Phusion]. image:images/phusion_banner.png[link="http://www.phusion.nl/"] "Passenger", "Phusion Passenger" and "Union Station" are registered trademarks of Phusion Holding B.V. passenger-5.0.30/doc/users_guide_snippets/appendix_b_terminology.txt000644 000765 000024 00000003544 12233035540 026526 0ustar00honglistaff000000 000000 == Appendix B: Terminology == [[application_root]] === Application root === The root directory of an application that's served by Phusion Passenger. In case of Ruby on Rails applications, this is the directory that contains 'Rakefile', 'app/', 'config/', 'public/', etc. In other words, the directory pointed to by `RAILS_ROOT`. For example, take the following directory structure: ----------------------------------------- /apps/foo/ <------ This is the Rails application's application root! | +- app/ | | | +- controllers/ | | | +- models/ | | | +- views/ | +- config/ | | | +- environment.rb | | | +- ... | +- public/ | | | +- ... | +- ... ----------------------------------------- In case of Rack applications, this is the directory that contains 'config.ru'. For example, take the following directory structure: ----------------------------------------- /apps/bar/ <----- This is the Rack application's application root! | +- public/ | | | +- ... | +- config.ru | +- ... ----------------------------------------- In case of Python (WSGI) applications, this is the directory that contains 'passenger_wsgi.py'. For example, take the following directory structure: ----------------------------------------- /apps/baz/ <----- This is the WSGI application's application root! | +- public/ | | | +- ... | +- passenger_wsgi.py | +- ... ----------------------------------------- [[idle_process]] === Idle process === An "idle process" refers to a process that hasn't processed any requests for a while. [[inactive_process]] === Inactive process === An "inactive process" refers to a process that's current not processing any requests. An idle process is always inactive, but an inactive process is not always considered idle. passenger-5.0.30/doc/users_guide_snippets/appendix_c_spawning_methods.txt000644 000765 000024 00000002613 12233035540 027524 0ustar00honglistaff000000 000000 [[spawning_methods_explained]] == Appendix C: Spawning methods explained This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/ === The most straightforward and traditional way: direct spawning This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/ === The smart spawning method This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/ ==== How it works This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/ ==== Summary of benefits This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/ === Smart spawning caveat #1: unintentional file descriptor sharing This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/ ==== Example 1: Memcached connection sharing (harmful) This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/ ==== Example 2: Log file sharing (not harmful) This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/ === Smart spawning caveat #2: the need to revive threads This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/spawn_methods/ passenger-5.0.30/doc/users_guide_snippets/deployment_basics.txt000644 000765 000024 00000001745 12233035540 025472 0ustar00honglistaff000000 000000 == Deploying a web application: the basics === Anatomy of a web application ==== Ruby ==== Python ===== Django For Django >= 1.4, `passenger_wsgi.py` should contain the following. Replace "mysite" with your application's actual module name. -------------------------------------------------------------------- from mysite.wsgi import application -------------------------------------------------------------------- For earlier Django versions, it should contain: -------------------------------------------------------------------- import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") from django.core.wsgi import get_wsgi_application application = get_wsgi_application() -------------------------------------------------------------------- ==== Node.js === Attaching to a virtual host's root URI === Attaching to a sub-URI ==== How to fix broken images/CSS/JavaScript URIs in sub-URI deployments === Restarting the web application === Inspecting the status passenger-5.0.30/doc/users_guide_snippets/enterprise_only.txt000644 000765 000024 00000000364 12233035540 025203 0ustar00honglistaff000000 000000 **This feature is only available in link:https://www.phusionpassenger.com/enterprise[Phusion Passenger Enterprise]. It was introduced in version {version}. link:https://www.phusionpassenger.com/download[Buy Phusion Passenger Enterprise here.]**passenger-5.0.30/doc/users_guide_snippets/environment_variables.txt000644 000765 000024 00000003576 12233035540 026366 0ustar00honglistaff000000 000000 This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/environment_variables.html === Working with environment variables This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/environment_variables.html#working-with-environment-variables [[the_path_env_var]] === The PATH environment variable This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/environment_variables.html#the-path-environment-variable ==== Adding Phusion Passenger's administration tools to PATH This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/environment_variables.html#adding-passenger-s-administration-tools-to-path === Making environment variables permanent This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/environment_variables.html#making-environment-variables-permanent ==== bash This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/environment_variables.html#bash ==== Apache This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/environment_variables.html#apache ==== Nginx This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/environment_variables.html#nginx ==== cron This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/environment_variables.html#cron [[env_vars_passenger_apps]] ==== Phusion Passenger-served apps This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/environment_variables.html#passenger-served-apps [[env_vars_and_sudo]] === Environment variables and sudo This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/environment_variables.html#environment-variables-and-sudo passenger-5.0.30/doc/users_guide_snippets/global_queueing_explained.txt000644 000765 000024 00000006622 12233035540 027160 0ustar00honglistaff000000 000000 .What does this option do? Recall that Phusion Passenger spawns multiple backend processes (e.g. multiple Ruby on Rails processes), each which processes HTTP requests serially. One of Phusion Passenger's jobs is to forward HTTP requests to a suitable backend process. A backend process may take an arbitrary amount of time to process a specific HTTP request. If the websites are (temporarily) under high load, and the backend processes cannot process the requests fast enough, then some requests may have to be queued. If global queuing is turned off, then Phusion Passenger will use 'fair load balancing'. This means that each backend process will have its own private queue. Phusion Passenger will forward an HTTP request to the backend process that has the least amount of requests in its queue. If global queuing is turned on, then Phusion Passenger will use a global queue that's shared between all backend processes. If an HTTP request comes in, and all the backend processes are still busy, then Phusion Passenger will wait until at least one backend process is done, and will then forward the request to that process. .When to turn on global queuing? You should turn on global queuing if one of your web applications may have long-running requests. For example suppose that: - global queuing is turned off. - we're currently in a state where all backend processes have 3 requests in their queue, except for a single backend process, which has 1 request in its queue. The situation looks like this: -------------------------------------------------- Backend process A: [* ] (1 request in queue) Backend process B: [*** ] (3 requests in queue) Backend process C: [*** ] (3 requests in queue) Backend process D: [*** ] (3 requests in queue) -------------------------------------------------- Each process is currently serving short-running requests. Phusion Passenger will forward the next request to backend process A. A will now have 2 items in its queue. We'll mark this new request with an X: -------------------------------------------------- Backend process A: [*X ] (2 request in queue) Backend process B: [*** ] (3 requests in queue) Backend process C: [*** ] (3 requests in queue) Backend process D: [*** ] (3 requests in queue) -------------------------------------------------- Assuming that B, C and D still aren't done with their current request, the next HTTP request - let's call this Y - will be forwarded to backend process A as well, because it has the least number of items in its queue: -------------------------------------------------- Backend process A: [*XY ] (3 requests in queue) Backend process B: [*** ] (3 requests in queue) Backend process C: [*** ] (3 requests in queue) Backend process D: [*** ] (3 requests in queue) -------------------------------------------------- But if request X happens to be a long-running request that needs 60 seconds to complete, then we'll have a problem. Y won't be processed for at least 60 seconds. It would have been a better idea if Y was forward to processes B, C or D instead, because they only have short-living requests in their queues. This problem will be avoided entirely if you turn global queuing on. With global queuing, all backend processes will share the same queue. The first backend process that becomes available will take from the queue, and so this ``queuing-behind-long-running-request'' problem will never occur.passenger-5.0.30/doc/users_guide_snippets/installation/000755 000765 000024 00000000000 12233035540 023717 5ustar00honglistaff000000 000000 passenger-5.0.30/doc/users_guide_snippets/installation.txt000644 000765 000024 00000017372 12233035540 024472 0ustar00honglistaff000000 000000 === Synopsis This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/ [[install_osx_homebrew]] === Installing or upgrading on Mac OS X with Homebrew This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/ [[install_on_debian_ubuntu]] === Installing or upgrading on Debian or Ubuntu This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apt_repo/ [[install_add_apt_repo]] ==== Adding our APT repository This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apt_repo/ ==== Installing packages This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apt_repo/ ifdef::nginx[] [[inserting_passenger_root_for_apt]] ==== Inserting `passenger_root` into nginx.conf This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/reference/#passenger_root endif::nginx[] [[installing_or_upgrading_on_red_hat]] === Installing or upgrading on Red Hat or CentOS This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/yum_repo/ [[install_add_yum_repo]] ==== Adding our YUM repository This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/yum_repo/ ==== Installing packages This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/yum_repo/ === Installing or upgrading on Heroku Please refer to our link:https://github.com/phusion/passenger-ruby-heroku-demo#readme[Heroku Ruby demo] for installation and upgrade instructions for Heroku. [[rubygems_generic_install]] === Generic installation, upgrade and downgrade method: via RubyGems This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/ [[tarball_generic_install]] === Generic installation, upgrade and downgrade method: via tarball This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/ === Upgrading from open source to Enterprise ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/nginx/upgrading_from_oss_to_enterprise.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apache/upgrading_from_oss_to_enterprise.html endif::[] ifdef::standalone[] This documentation has moved. https://www.phusionpassenger.com/library/install/standalone/upgrading_from_oss_to_enterprise.html endif::[] === Cryptographic verification of installation files ==== Synopsis This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/crypto_verify_install.html ==== Importing the Phusion Software Signing key This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/crypto_verify_install.html ==== Verifying the Phusion Software Signing key This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/crypto_verify_install.html ==== Verifying the gem and tarball This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/crypto_verify_install.html ==== Verifying Git signatures This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/crypto_verify_install.html ==== Verifying Debian packages This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/crypto_verify_install.html ==== Verifying RPM packages This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/crypto_verify_install.html ==== Revocation This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/crypto_verify_install.html ifndef::standalone[] === Non-interactive, automatic, headless installs or upgrades ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apache/noninteractive_install.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/nginx/noninteractive_install.html endif::[] endif::[] === Customizing the compilation process ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apache/customizing_compilation_process.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/nginx/customizing_compilation_process.html endif::[] ==== Setting the compiler ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apache/customizing_compilation_process.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/nginx/customizing_compilation_process.html endif::[] ==== Adding additional compiler or linker flags ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apache/customizing_compilation_process.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/nginx/customizing_compilation_process.html endif::[] [[forcing_location_of_command_line_tools_and_dependencies]] ==== Forcing location of command line tools and dependencies ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apache/customizing_compilation_process.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/nginx/customizing_compilation_process.html endif::[] ifdef::apache[] [[multiple_apache_installs]] === Dealing with multiple Apache installations This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apache/multiple_apache_installs.html [[working_with_apache_conf]] === Working with the Apache configuration file This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apache/working_with_the_apache_config_file.html endif::[] ifdef::nginx[] === Installing as a normal Nginx module without using the installer This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/nginx/install_as_nginx_module.html [[nginx_init_script]] === Creating an Nginx init script This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/nginx/nginx_init_script.html endif::[] ifndef::standalone[] === Disabling without uninstalling ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apache/disable.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/nginx/disable.html endif::[] endif::[] [[uninstalling]] === Uninstalling ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apache/uninstall/ endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/nginx/uninstall/ endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/standalone/uninstall/ endif::[] [[moving_phusion_passenger]] === Moving to a different directory ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/apache/moving.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/nginx/moving.html endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/install/standalone/moving.html endif::[] passenger-5.0.30/doc/users_guide_snippets/passenger_spawn_method.txt000644 000765 000024 00000003245 12233035540 026522 0ustar00honglistaff000000 000000 Internally, Phusion Passenger spawns multiple Ruby application processes in order to handle requests. But there are multiple ways with which processes can be spawned, each having its own set of pros and cons. Supported spawn methods are: 'smart':: This spawning method caches code using the app preloader. Framework code is not cached between multiple applications, although it is cached within instances of the same application. Please read <> for a more detailed explanation of what smart spawning exactly does. + *Pros:* Smart spawning caches code where possible to speed up the respawn process and is compatible with most applications + *Cons:* It is possible that it may be incompatible with some applications 'direct':: This spawning method is similar to the one used in Mongrel Cluster. It does not perform any code caching at all. Please read <> for a more detailed explanation of what direct spawning exactly does. + *Pros:* Direct spawning is guaranteed to be compatible with all applications and libraries. + *Cons:* Much slower than smart spawning. Every spawn action will be equally slow, though no slower than the startup time of a single server in Mongrel Cluster. Direct spawning will also render <> useless. ************************************************ As of Phusion Passenger 4.0, 'conservative' spawning was renamed to 'direct' and 'smart-lv2' was renamed to 'smart'. The old 'smart' spawning has been removed in favor of the new version. ************************************************ passenger-5.0.30/doc/users_guide_snippets/rackup_specifications.txt000644 000765 000024 00000000151 12233035540 026324 0ustar00honglistaff000000 000000 This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/config_ru.htmlpassenger-5.0.30/doc/users_guide_snippets/rvm_helper_tool.txt000644 000765 000024 00000004716 12233035540 025167 0ustar00honglistaff000000 000000 .RVM helper tool Phusion Passenger provides the `passenger-config --ruby-command` tool for figuring out the correct command for invoking a specific Ruby interpreter. This is especially useful for RVM users. Suppose that you have both Ruby 1.8.7 and Ruby 1.9.3 installed through RVM, and you want to know the correct commands for each Ruby interpreter. For this purpose we'll want to invoke `passenger-config` using its full path, because each time you `rvm use` a different Ruby interpreter, RVM changes `$PATH`. If you did not install Phusion Passenger through the generic tarball installation method, then here's how you can figure out where `passenger-config` is: ------------------------------ $ which passenger-config /opt/passenger/bin/passenger-config ------------------------------ Now, switch to all the RVM Ruby interpreters you want to use. In each interpreter, invoke `passenger-config --ruby-command`. For Ruby 1.8.7: ------------------------------ $ rvm use 1.8.7 $ /opt/passenger/bin/passenger-config --ruby-command passenger-config was invoked through the following Ruby interpreter: Command: /usr/local/rvm/wrappers/ruby-1.8.7-p358/ruby Version: ruby 1.8.7 (2012-02-08 patchlevel 358) [universal-darwin12.0] To use in Apache: PassengerRuby /usr/local/rvm/wrappers/ruby-1.8.7-p358/ruby To use in Nginx : passenger_ruby /usr/local/rvm/wrappers/ruby-1.8.7-p358/ruby To use with Standalone: /usr/local/rvm/wrappers/ruby-1.8.7-p358/ruby /opt/passenger/bin/passenger start ## Notes for RVM users Do you want to know which command to use for a different Ruby interpreter? 'rvm use' that Ruby interpreter, then re-run 'passenger-config --ruby-command'. ------------------------------ Then, for Ruby 1.9.3: ------------------------------ $ rvm use 1.9.3 $ /opt/passenger/bin/passenger-config --ruby-command passenger-config was invoked through the following Ruby interpreter: Command: /usr/local/rvm/wrappers/ruby-1.9.3-p392/ruby Version: ruby 1.9.3p392 (2013-02-22 revision 39386) [x86_64-darwin12.2.1] To use in Apache: PassengerRuby /usr/local/rvm/wrappers/ruby-1.9.3-p392/ruby To use in Nginx : passenger_ruby /usr/local/rvm/wrappers/ruby-1.9.3-p392/ruby To use with Standalone: /usr/local/rvm/wrappers/ruby-1.9.3-p392/ruby /opt/passenger/bin/passenger start ## Notes for RVM users Do you want to know which command to use for a different Ruby interpreter? 'rvm use' that Ruby interpreter, then re-run 'passenger-config --ruby-command'. ------------------------------passenger-5.0.30/doc/users_guide_snippets/since_version.txt000644 000765 000024 00000000044 12233035540 024623 0ustar00honglistaff000000 000000 **Introduced in version {version}.**passenger-5.0.30/doc/users_guide_snippets/support_information.txt000644 000765 000024 00000000415 12233035540 026100 0ustar00honglistaff000000 000000 === Supported operating systems and languages === This documentation has moved. https://www.phusionpassenger.com/library/install/supported_operating_systems_and_languages.html [[where_to_get_support]] === Where to get support === include::where_to_get_support.txt[] passenger-5.0.30/doc/users_guide_snippets/tips.txt000644 000765 000024 00000031715 12233035540 022745 0ustar00honglistaff000000 000000 [[user_switching]] === User Switching (security feature) === ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/user_sandboxing.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/user_sandboxing.html endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/standalone/user_sandboxing.html endif::[] ==== Requirements ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/user_sandboxing.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/user_sandboxing.html endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/standalone/user_sandboxing.html endif::[] ==== Effects ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/user_sandboxing.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/user_sandboxing.html endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/standalone/user_sandboxing.html endif::[] ==== Caveats & troubleshooting ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/user_sandboxing.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/user_sandboxing.html endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/standalone/user_sandboxing.html endif::[] [[user_switching_rpm_caveats]] ==== Red Hat and CentOS caveats ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/user_sandboxing.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/user_sandboxing.html endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/standalone/user_sandboxing.html endif::[] [[finding_out_app_user]] ==== Finding out what user an application is running as ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/user_sandboxing.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/user_sandboxing.html endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/standalone/user_sandboxing.html endif::[] [[reducing_memory_usage]] === Copy-on-write memory support (reducing memory consumption of Ruby applications) === Phusion Passenger automatically leverages operating system virtual memory copy-on-write features in order to reduce the memory usage of Ruby applications. Experience has shown that this reduces memory usage by 33% on average. For this mechanism to work, a Ruby interpreter with a copy-on-write friendly garbage collector is required. The following Ruby interpreters have copy-on-write friendly garbage collectors: - MRI Ruby >= 2.0. Versions prior to 2.0 did not have a copy-on-write friendly garbage collector. - http://www.rubyenterpriseedition.com/[Ruby Enterprise Edition], which was Phusion's branch of MRI Ruby 1.8 with a copy-on-write friendly garbage collector and other enhancement. It has reached End-Of-Life as of 2012, but remains available for legacy systems. [[tuning_sse_websockets]] === Tuning for Server Sent Events and WebSockets === ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/apache/tuning_sse_and_websockets.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/nginx/tuning_sse_and_websockets.html endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/config/standalone/tuning_sse_and_websockets.html endif::[] [[bundler_support]] === Bundler support === This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/ruby/bundler.html [[add_passenger_to_gemfile]] ==== Does Phusion Passenger itself need to be added to the Gemfile? This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/ruby/bundler.html#does-passenger-itself-need-to-be-added-to-the-gemfile === Installing multiple Ruby on Rails versions === Each Ruby on Rails applications that are going to be deployed may require a specific Ruby on Rails version. You can install a specific version with this command: ----------------------------- gem install rails -v X.X.X ----------------------------- where 'X.X.X' is the version number of Ruby on Rails. All of these versions will exist in parallel, and will not conflict with each other. Phusion Passenger will automatically make use of the correct version. === Making the application restart after each request === In some situations it might be desirable to restart the web application after each request, for example when developing a non-Rails application that doesn't support code reloading, or when developing a web framework. To achieve this, simply create the file 'tmp/always_restart.txt' in your application's root folder. Unlike 'restart.txt', Phusion Passenger does not check for this file's timestamp: Phusion Passenger will always restart the application, as long as 'always_restart.txt' exists. NOTE: If you're just developing a Rails application then you probably don't need this feature. If you set ifdef::apache['RailsEnv development'] ifdef::nginx['rails_env development'] in your web server configuration, then Rails will automatically reload your application code after each request. 'always_restart.txt' is mostly useful when you're using a web framework that doesn't support code reloading by itself, of when you're working on a web framework yourself. [[sub_uri_deployment_uri_fix]] === How to fix broken images/CSS/JavaScript URIs in sub-URI deployments Some people experience broken images and other broken static assets when they deploy their application to a sub-URI (i.e. 'http://mysite.com/railsapp/'). The reason for this usually is that you used a static URI for your image in the views. This means your 'img' source probably refers to something like '/images/foo.jpg'. The leading slash means that it's an absolute URI: you're telling the browser to always load 'http://mysite.com/images/foo.jpg' no matter what. The problem is that the image is actually at 'http://mysite.com/railsapp/images/foo.jpg'. There are two ways to fix this. The first way (not recommended) is to change your view templates to refer to 'images/foo.jpg'. This is a relative URI: note the lack of a leading slash). What this does is making the path relative to the current URI. The problem is that if you use restful URIs, then your images will probably break again when you add a level to the URI. For example, when you're at 'http://mysite.com/railsapp' the browser will look for 'http://mysite.com/railsapp/images/foo.jpg'. But when you're at 'http://mysite.com/railsapp/controller'. the browser will look for 'http://mysite.com/railsapp/controller/images/foo.jpg'. So relative URIs usually don't work well with layout templates. The second and highly recommended way is to always use Rails helper methods to output tags for static assets. These helper methods automatically take care of prepending the base URI that you've deployed the application to. For images there is `image_tag`, for JavaScript there is `javascript_include_tag` and for CSS there is `stylesheet_link_tag`. In the above example you would simply remove the '' HTML tag and replace it with inline Ruby like this: --------------------------------------- <%= image_tag("foo.jpg") %> --------------------------------------- This will generate the proper image tag to `$RAILS_ROOT/public/images/foo.jpg` so that your images will always work no matter what sub-URI you've deployed to. These helper methods are more valuable than you may think. For example they also append a timestamp to the URI to better facilitate HTTP caching. For more information, please refer to link:http://api.rubyonrails.org/classes/ActionView/Helpers/AssetTagHelper.html[the Rails API docs]. === Out-of-Band Work and Out-of-Band Garbage Collection This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/ruby/out_of_band_work.html === Hooks This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/hooks.html ==== Example This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/hooks.html#example ==== Environment This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/hooks.html#environment ==== Blocking and concurrency This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/hooks.html#blocking-and-concurrency ==== Error handling This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/hooks.html#error-handling ==== Compatibility This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/hooks.html#compatibility ==== Available hooks This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/hooks.html#available-hooks [[flying_passenger]] === Flying Passenger ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/flying_passenger.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/flying_passenger.html endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/flying_passenger.html endif::[] ==== Requirements ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/flying_passenger.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/flying_passenger.html endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/flying_passenger.html endif::[] ==== Basic usage ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/flying_passenger.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/flying_passenger.html endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/flying_passenger.html endif::[] [[configuring_flying_passenger]] ==== Configuring Flying Passenger ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/flying_passenger.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/flying_passenger.html endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/flying_passenger.html endif::[] ==== Managing the Flying Passenger daemon ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/flying_passenger.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/flying_passenger.html endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/flying_passenger.html endif::[] [[using_flying_passenger_with_mri_18_or_jruby]] ==== Using Flying Passenger with MRI 1.8 or JRuby ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/flying_passenger.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/flying_passenger.html endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/flying_passenger.html endif::[] [[flying_passenger_caveats]] ==== Caveats and limitations ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/apache/flying_passenger.html endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/flying_passenger.html endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/deploy/nginx/flying_passenger.html endif::[] passenger-5.0.30/doc/users_guide_snippets/troubleshooting/000755 000765 000024 00000000000 12233035540 024445 5ustar00honglistaff000000 000000 passenger-5.0.30/doc/users_guide_snippets/under_the_hood/000755 000765 000024 00000000000 12233035540 024204 5ustar00honglistaff000000 000000 passenger-5.0.30/doc/users_guide_snippets/where_to_get_support.txt000644 000765 000024 00000002007 12233035540 026225 0ustar00honglistaff000000 000000 * link:http://groups.google.com/group/phusion-passenger[Community discussion forum] - post a message here if you're experiencing problems. Support on this forum is provided by the community on a best-effort basis, so a (timely) response is not guaranteed. * link:https://github.com/phusion/passenger/issues[Issue tracker] - report bugs here. * Email support@phusion.nl if you are a link:https://www.phusionpassenger.com/enterprise[Phusion Passenger Enterprise] customer. Please mention your order reference. If you are not an Enterprise customer, we kindly redirect you to the community discussion forum instead. * link:https://www.phusionpassenger.com/commercial_support[Commercial support contracts] are also available. * Report security vulnerabilities to security@phusion.nl. We will do our best to respond to you as quickly as we can, so please do not disclose the vulnerability until then. Please consult link:https://www.phusionpassenger.com/support[the Phusion Passenger website] for a full list of support resources. passenger-5.0.30/doc/users_guide_snippets/under_the_hood/page_caching_support.txt000644 000765 000024 00000002516 12233035540 031135 0ustar00honglistaff000000 000000 === Page caching support For each HTTP request, Phusion Passenger will automatically look for a corresponding page cache file, and serve that if it exists. It does this by appending ".html" to the filename that the URI normally maps to, and checking whether that file exists. This check occurs after checking whether the original mapped filename exists (as part of static asset serving). All this is done without the need for special mod_rewrite rules. For example, suppose that the browser requests '/foo/bar'. 1. Phusion Passenger will first check whether this URI maps to a static file, i.e. whether the file 'foo/bar' exists in the web application's 'public' directory. If it does then Phusion Passenger will serve this file through the web server immediately. 2. If that doesn't exist, then Phusion Passenger will check whether the file 'foo/bar.html' exists. If it does then Phusion Passenger will serve this file through the web server immediately. 3. If 'foo/bar.html' doesn't exist either, then Phusion Passenger will forward the request to the underlying web application. Note that Phusion Passenger's page caching support doesn't work if your web application uses a non-standard page cache directory, i.e. if it doesn't cache to the 'public' directory. In that case you'll need to use mod_rewrite to serve such page cache files.passenger-5.0.30/doc/users_guide_snippets/under_the_hood/relationship_with_ruby.txt000644 000765 000024 00000000621 12233035540 031541 0ustar00honglistaff000000 000000 [[relationship_with_ruby]] === Phusion Passenger and its relationship with Ruby ==== How Ruby is used This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/lightweight_ruby_dependency.html ==== When the system has multiple Ruby interpreters This documentation has moved. Please visit https://www.phusionpassenger.com/library/indepth/ruby/multiple_rubies.html passenger-5.0.30/doc/users_guide_snippets/troubleshooting/default.txt000644 000765 000024 00000004364 12233035540 026641 0ustar00honglistaff000000 000000 === Generic troubleshooting tips ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/troubleshooting/ endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/nginx/troubleshooting/ endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/standalone/troubleshooting/ endif::[] ifndef::standalone[] === Why does the first request take a long time? ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/nginx/troubleshooting/?a=why-does-the-first-request-take-a-long-time endif::[] ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/troubleshooting/?a=why-does-the-first-request-take-a-long-time endif::[] ifdef::nginx[] === Upon accessing the web app, Nginx reports a "Permission denied" error This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/nginx/troubleshooting/?a=upon-accessing-the-web-app-nginx-reports-a-permission-denied-error endif::[] ifdef::standalone[] === Upon uploading a file, Phusion Passenger reports "client_body_temp/00000000xx failed (2: No such file or directory)" This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/standalone/troubleshooting/?a=upon-uploading-a-file-passenger-reports-client_body_temp-00000000xx-failed-2-no-such-file-or-directory endif::[] === I get "command not found" when running a Phusion Passenger command through sudo ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/troubleshooting/?a=i-get-command-not-found-when-running-a-passenger-command-through-sudo endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/nginx/troubleshooting/?a=i-get-command-not-found-when-running-a-passenger-command-through-sudo endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/standalone/troubleshooting/?a=i-get-command-not-found-when-running-a-passenger-command-through-sudo endif::[] passenger-5.0.30/doc/users_guide_snippets/troubleshooting/rails.txt000644 000765 000024 00000005364 12233035540 026330 0ustar00honglistaff000000 000000 === Ruby on Rails-specific troubleshooting ==== The "About your application's environment" link does not work ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/troubleshooting/ruby/#the-about-your-application-s-environment-link-does-not-work endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/nginx/troubleshooting/ruby/#the-about-your-application-s-environment-link-does-not-work endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/standalone/troubleshooting/ruby/#the-about-your-application-s-environment-link-does-not-work endif::[] ==== The Rails application reports that it's unable to start because of a permission error ifdef::apache[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/apache/troubleshooting/ruby/#the-rails-application-reports-that-it-s-unable-to-start-because-of-a-permission-error endif::[] ifdef::nginx[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/nginx/troubleshooting/ruby/#the-rails-application-reports-that-it-s-unable-to-start-because-of-a-permission-error endif::[] ifdef::standalone[] This documentation has moved. Please visit https://www.phusionpassenger.com/library/admin/standalone/troubleshooting/ruby/#the-rails-application-reports-that-it-s-unable-to-start-because-of-a-permission-error endif::[] ==== The Rails application's log file is not being written to There are a couple things that you should be aware of: - By default, Phusion Passenger runs Rails applications in 'production' mode, so please be sure to check `production.log` instead of `development.log`. + See ifdef::apache[] <> endif::[] ifdef::nginx[] <> endif::[] for configuration. - By default, Phusion Passenger runs Rails applications as the owner of `config.ru`. So the log file can only be written to if that user has write permission to the log file. Please `chmod` or `chown` your log file accordingly. + See <> for details. If you're using a RedHat-derived Linux distribution (such as Fedora or CentOS) then it is link:http://code.google.com/p/phusion-passenger/issues/detail?id=4[possible that SELinux is interfering]. RedHat's SELinux policy only allows Apache to read/write directories that have the 'httpd_sys_content_t' security context. Please run the following command to give your Rails application folder that context: ----------------------------------------------------------- chcon -R -h -t httpd_sys_content_t /path/to/your/rails/app ----------------------------------------------------------- passenger-5.0.30/doc/users_guide_snippets/installation/run_installer.txt000644 000765 000024 00000007705 12233035540 027352 0ustar00honglistaff000000 000000 ifdef::apache[] The Phusion Passenger gem only contains source files. In this step we will use the installer to compile the Phusion Passenger binaries, which include the various Phusion Passenger agent executables and the Apache module. Run the following command and follow the on-screen instructions. ifdef::gem[] ---------------------------------- passenger-install-apache2-module ---------------------------------- endif::[] ifdef::tarball[] ---------------------------------- ./bin/passenger-install-apache2-module ---------------------------------- endif::[] At the end of the installation process, you will be asked to copy and paste a configuration snippet (containing `LoadModule`, `PassengerRoot`, etc.) into your Apache configuration file. If you're upgrading, then you already had a similar configuration snippet. Be sure to remove the old one before pasting the new one. Different operating systems and Apache installations have different conventions with regard to where the Apache configuration file is and how it is organized. Please read <> if you're not familiar with it. That section is especially of interest for OS X Server >= 10.8 users because the configuration file may not be at a surprising location. If compilation doesn't succeed, then please consult the <> section. .What does the installer do? Despite the name, this "installer" doesn't actually install anything. The installer checks whether all required dependencies are installed, invokes the compiler, and tells you how to modify the Apache configuration file. However, it doesn't copy any files to outside the Phusion Passenger source directory. `passenger-install-apache2-module` is actually just a user-friendly frontend around the command `rake apache2`, which performs the actual compilation of Phusion Passenger. endif::[] ifdef::nginx[] Nginx is a different from other web servers in that it does not support loadable modules. The only way to extend Nginx is to recompile it entirely from source. Since Phusion Passenger consists of some external executables plus an Nginx module, you must recompile Nginx when first installing Phusion Passenger, but also when upgrading Nginx itself or when upgrading the Phusion Passenger version. Recompiling Nginx and the Phusion Passenger executables is what we will do in this step. The good news is that Phusion Passenger provides a tool to make this easy for you. If you've already installed Nginx before, but without Phusion Passenger support, then you *should* uninstall it first. You don't have to, because you can also install another Nginx with Phusion Passenger support, in parallel to the existing Nginx. We merely recommend uninstalling the existing in order to avoid user confusion, but the choice is yours. If you had previously installed Nginx with Phusion Passenger support, and you are upgrading, then you don't have to uninstall your existing Nginx first. Instead we'll overwrite it this step. But it is important that you recompile Nginx with the configure parameters that you used last time. Here's how you can uninstall the original Nginx: - If you installed the existing Nginx through APT, run: `sudo apt-get remove nginx nginx-full nginx-light nginx-naxsi nginx-common` - If you installed the existing Nginx through YUM, run `yum remove nginx` as root. To proceed with installing or upgrading Phusion Passenger, run the Phusion Passenger Nginx installer and follow the on-screen instructions: ifdef::gem[] ---------------------------------- passenger-install-nginx-module ---------------------------------- endif::[] ifdef::tarball[] ---------------------------------- ./bin/passenger-install-nginx-module ---------------------------------- endif::[] At some point it will ask you which prefix to install Nginx to. If you're upgrading, then specify the same prefix that you used last time, as well as the same configuration parameters that you used last time. endif::[]passenger-5.0.30/doc/users_guide_snippets/installation/verify_running_epilogue.txt000644 000765 000024 00000000656 12233035540 031424 0ustar00honglistaff000000 000000 You should see the web server processes as well as a number of Phusion Passenger processes (e.g. PassengerAgent watchdog, PassengerAgent core). Congratulations, Phusion Passenger is now installed and running! ifdef::nginx[] At this point you may be interested in <>. endif::[] If the output is not as expected, then please refer to the <> section.passenger-5.0.30/doc/templates/bootstrap.config.json000644 000765 000024 00000041317 12233035540 023133 0ustar00honglistaff000000 000000 { "vars": { "@gray-base": "#000", "@gray-darker": "lighten(@gray-base, 13.5%)", "@gray-dark": "lighten(@gray-base, 20%)", "@gray": "lighten(@gray-base, 33.5%)", "@gray-light": "lighten(@gray-base, 46.7%)", "@gray-lighter": "lighten(@gray-base, 93.5%)", "@brand-primary": "#428bca", "@brand-success": "#5cb85c", "@brand-info": "#5bc0de", "@brand-warning": "#f0ad4e", "@brand-danger": "#d9534f", "@body-bg": "#fff", "@text-color": "@gray-dark", "@link-color": "@brand-primary", "@link-hover-color": "darken(@link-color, 15%)", "@link-hover-decoration": "underline", "@font-family-sans-serif": "\"Helvetica Neue\", Helvetica, Arial, sans-serif", "@font-family-serif": "Georgia, \"Times New Roman\", Times, serif", "@font-family-monospace": "Menlo, Monaco, Consolas, \"Courier New\", monospace", "@font-family-base": "@font-family-sans-serif", "@font-size-base": "14px", "@font-size-large": "ceil((@font-size-base * 1.25))", "@font-size-small": "ceil((@font-size-base * 0.85))", "@font-size-h1": "floor((@font-size-base * 2.6))", "@font-size-h2": "floor((@font-size-base * 2.15))", "@font-size-h3": "ceil((@font-size-base * 1.7))", "@font-size-h4": "ceil((@font-size-base * 1.25))", "@font-size-h5": "@font-size-base", "@font-size-h6": "ceil((@font-size-base * 0.85))", "@line-height-base": "1.428571429", "@line-height-computed": "floor((@font-size-base * @line-height-base))", "@headings-font-family": "inherit", "@headings-font-weight": "500", "@headings-line-height": "1.1", "@headings-color": "inherit", "@icon-font-path": "\"../fonts/\"", "@icon-font-name": "\"glyphicons-halflings-regular\"", "@icon-font-svg-id": "\"glyphicons_halflingsregular\"", "@padding-base-vertical": "6px", "@padding-base-horizontal": "12px", "@padding-large-vertical": "10px", "@padding-large-horizontal": "16px", "@padding-small-vertical": "5px", "@padding-small-horizontal": "10px", "@padding-xs-vertical": "1px", "@padding-xs-horizontal": "5px", "@line-height-large": "1.33", "@line-height-small": "1.5", "@border-radius-base": "4px", "@border-radius-large": "6px", "@border-radius-small": "3px", "@component-active-color": "#fff", "@component-active-bg": "@brand-primary", "@caret-width-base": "4px", "@caret-width-large": "5px", "@table-cell-padding": "8px", "@table-condensed-cell-padding": "5px", "@table-bg": "transparent", "@table-bg-accent": "#f9f9f9", "@table-bg-hover": "#f5f5f5", "@table-bg-active": "@table-bg-hover", "@table-border-color": "#ddd", "@btn-font-weight": "normal", "@btn-default-color": "#333", "@btn-default-bg": "#fff", "@btn-default-border": "#ccc", "@btn-primary-color": "#fff", "@btn-primary-bg": "@brand-primary", "@btn-primary-border": "darken(@btn-primary-bg, 5%)", "@btn-success-color": "#fff", "@btn-success-bg": "@brand-success", "@btn-success-border": "darken(@btn-success-bg, 5%)", "@btn-info-color": "#fff", "@btn-info-bg": "@brand-info", "@btn-info-border": "darken(@btn-info-bg, 5%)", "@btn-warning-color": "#fff", "@btn-warning-bg": "@brand-warning", "@btn-warning-border": "darken(@btn-warning-bg, 5%)", "@btn-danger-color": "#fff", "@btn-danger-bg": "@brand-danger", "@btn-danger-border": "darken(@btn-danger-bg, 5%)", "@btn-link-disabled-color": "@gray-light", "@input-bg": "#fff", "@input-bg-disabled": "@gray-lighter", "@input-color": "@gray", "@input-border": "#ccc", "@input-border-radius": "@border-radius-base", "@input-border-radius-large": "@border-radius-large", "@input-border-radius-small": "@border-radius-small", "@input-border-focus": "#66afe9", "@input-color-placeholder": "#999", "@input-height-base": "(@line-height-computed + (@padding-base-vertical * 2) + 2)", "@input-height-large": "(ceil(@font-size-large * @line-height-large) + (@padding-large-vertical * 2) + 2)", "@input-height-small": "(floor(@font-size-small * @line-height-small) + (@padding-small-vertical * 2) + 2)", "@legend-color": "@gray-dark", "@legend-border-color": "#e5e5e5", "@input-group-addon-bg": "@gray-lighter", "@input-group-addon-border-color": "@input-border", "@cursor-disabled": "not-allowed", "@dropdown-bg": "#fff", "@dropdown-border": "rgba(0,0,0,.15)", "@dropdown-fallback-border": "#ccc", "@dropdown-divider-bg": "#e5e5e5", "@dropdown-link-color": "@gray-dark", "@dropdown-link-hover-color": "darken(@gray-dark, 5%)", "@dropdown-link-hover-bg": "#f5f5f5", "@dropdown-link-active-color": "@component-active-color", "@dropdown-link-active-bg": "@component-active-bg", "@dropdown-link-disabled-color": "@gray-light", "@dropdown-header-color": "@gray-light", "@dropdown-caret-color": "#000", "@screen-xs": "480px", "@screen-xs-min": "@screen-xs", "@screen-phone": "@screen-xs-min", "@screen-sm": "768px", "@screen-sm-min": "@screen-sm", "@screen-tablet": "@screen-sm-min", "@screen-md": "992px", "@screen-md-min": "@screen-md", "@screen-desktop": "@screen-md-min", "@screen-lg": "1200px", "@screen-lg-min": "@screen-lg", "@screen-lg-desktop": "@screen-lg-min", "@screen-xs-max": "(@screen-sm-min - 1)", "@screen-sm-max": "(@screen-md-min - 1)", "@screen-md-max": "(@screen-lg-min - 1)", "@grid-columns": "12", "@grid-gutter-width": "30px", "@grid-float-breakpoint": "@screen-sm-min", "@grid-float-breakpoint-max": "(@grid-float-breakpoint - 1)", "@container-tablet": "(720px + @grid-gutter-width)", "@container-sm": "@container-tablet", "@container-desktop": "(940px + @grid-gutter-width)", "@container-md": "@container-desktop", "@container-large-desktop": "(1140px + @grid-gutter-width)", "@container-lg": "@container-large-desktop", "@navbar-height": "50px", "@navbar-margin-bottom": "@line-height-computed", "@navbar-border-radius": "@border-radius-base", "@navbar-padding-horizontal": "floor((@grid-gutter-width / 2))", "@navbar-padding-vertical": "((@navbar-height - @line-height-computed) / 2)", "@navbar-collapse-max-height": "340px", "@navbar-default-color": "#777", "@navbar-default-bg": "#f8f8f8", "@navbar-default-border": "darken(@navbar-default-bg, 6.5%)", "@navbar-default-link-color": "#777", "@navbar-default-link-hover-color": "#333", "@navbar-default-link-hover-bg": "transparent", "@navbar-default-link-active-color": "#555", "@navbar-default-link-active-bg": "darken(@navbar-default-bg, 6.5%)", "@navbar-default-link-disabled-color": "#ccc", "@navbar-default-link-disabled-bg": "transparent", "@navbar-default-brand-color": "@navbar-default-link-color", "@navbar-default-brand-hover-color": "darken(@navbar-default-brand-color, 10%)", "@navbar-default-brand-hover-bg": "transparent", "@navbar-default-toggle-hover-bg": "#ddd", "@navbar-default-toggle-icon-bar-bg": "#888", "@navbar-default-toggle-border-color": "#ddd", "@navbar-inverse-color": "lighten(@gray-light, 15%)", "@navbar-inverse-bg": "#222", "@navbar-inverse-border": "darken(@navbar-inverse-bg, 10%)", "@navbar-inverse-link-color": "lighten(@gray-light, 15%)", "@navbar-inverse-link-hover-color": "#fff", "@navbar-inverse-link-hover-bg": "transparent", "@navbar-inverse-link-active-color": "@navbar-inverse-link-hover-color", "@navbar-inverse-link-active-bg": "darken(@navbar-inverse-bg, 10%)", "@navbar-inverse-link-disabled-color": "#444", "@navbar-inverse-link-disabled-bg": "transparent", "@navbar-inverse-brand-color": "@navbar-inverse-link-color", "@navbar-inverse-brand-hover-color": "#fff", "@navbar-inverse-brand-hover-bg": "transparent", "@navbar-inverse-toggle-hover-bg": "#333", "@navbar-inverse-toggle-icon-bar-bg": "#fff", "@navbar-inverse-toggle-border-color": "#333", "@nav-link-padding": "10px 15px", "@nav-link-hover-bg": "@gray-lighter", "@nav-disabled-link-color": "@gray-light", "@nav-disabled-link-hover-color": "@gray-light", "@nav-tabs-border-color": "#ddd", "@nav-tabs-link-hover-border-color": "@gray-lighter", "@nav-tabs-active-link-hover-bg": "@body-bg", "@nav-tabs-active-link-hover-color": "@gray", "@nav-tabs-active-link-hover-border-color": "#ddd", "@nav-tabs-justified-link-border-color": "#ddd", "@nav-tabs-justified-active-link-border-color": "@body-bg", "@nav-pills-border-radius": "@border-radius-base", "@nav-pills-active-link-hover-bg": "@component-active-bg", "@nav-pills-active-link-hover-color": "@component-active-color", "@pagination-color": "@link-color", "@pagination-bg": "#fff", "@pagination-border": "#ddd", "@pagination-hover-color": "@link-hover-color", "@pagination-hover-bg": "@gray-lighter", "@pagination-hover-border": "#ddd", "@pagination-active-color": "#fff", "@pagination-active-bg": "@brand-primary", "@pagination-active-border": "@brand-primary", "@pagination-disabled-color": "@gray-light", "@pagination-disabled-bg": "#fff", "@pagination-disabled-border": "#ddd", "@pager-bg": "@pagination-bg", "@pager-border": "@pagination-border", "@pager-border-radius": "15px", "@pager-hover-bg": "@pagination-hover-bg", "@pager-active-bg": "@pagination-active-bg", "@pager-active-color": "@pagination-active-color", "@pager-disabled-color": "@pagination-disabled-color", "@jumbotron-padding": "30px", "@jumbotron-color": "inherit", "@jumbotron-bg": "@gray-lighter", "@jumbotron-heading-color": "inherit", "@jumbotron-font-size": "ceil((@font-size-base * 1.5))", "@state-success-text": "#3c763d", "@state-success-bg": "#dff0d8", "@state-success-border": "darken(spin(@state-success-bg, -10), 5%)", "@state-info-text": "#31708f", "@state-info-bg": "#d9edf7", "@state-info-border": "darken(spin(@state-info-bg, -10), 7%)", "@state-warning-text": "#8a6d3b", "@state-warning-bg": "#fcf8e3", "@state-warning-border": "darken(spin(@state-warning-bg, -10), 5%)", "@state-danger-text": "#a94442", "@state-danger-bg": "#f2dede", "@state-danger-border": "darken(spin(@state-danger-bg, -10), 5%)", "@tooltip-max-width": "200px", "@tooltip-color": "#fff", "@tooltip-bg": "#000", "@tooltip-opacity": ".9", "@tooltip-arrow-width": "5px", "@tooltip-arrow-color": "@tooltip-bg", "@popover-bg": "#fff", "@popover-max-width": "276px", "@popover-border-color": "rgba(0,0,0,.2)", "@popover-fallback-border-color": "#ccc", "@popover-title-bg": "darken(@popover-bg, 3%)", "@popover-arrow-width": "10px", "@popover-arrow-color": "@popover-bg", "@popover-arrow-outer-width": "(@popover-arrow-width + 1)", "@popover-arrow-outer-color": "fadein(@popover-border-color, 5%)", "@popover-arrow-outer-fallback-color": "darken(@popover-fallback-border-color, 20%)", "@label-default-bg": "@gray-light", "@label-primary-bg": "@brand-primary", "@label-success-bg": "@brand-success", "@label-info-bg": "@brand-info", "@label-warning-bg": "@brand-warning", "@label-danger-bg": "@brand-danger", "@label-color": "#fff", "@label-link-hover-color": "#fff", "@modal-inner-padding": "15px", "@modal-title-padding": "15px", "@modal-title-line-height": "@line-height-base", "@modal-content-bg": "#fff", "@modal-content-border-color": "rgba(0,0,0,.2)", "@modal-content-fallback-border-color": "#999", "@modal-backdrop-bg": "#000", "@modal-backdrop-opacity": ".5", "@modal-header-border-color": "#e5e5e5", "@modal-footer-border-color": "@modal-header-border-color", "@modal-lg": "900px", "@modal-md": "600px", "@modal-sm": "300px", "@alert-padding": "15px", "@alert-border-radius": "@border-radius-base", "@alert-link-font-weight": "bold", "@alert-success-bg": "@state-success-bg", "@alert-success-text": "@state-success-text", "@alert-success-border": "@state-success-border", "@alert-info-bg": "@state-info-bg", "@alert-info-text": "@state-info-text", "@alert-info-border": "@state-info-border", "@alert-warning-bg": "@state-warning-bg", "@alert-warning-text": "@state-warning-text", "@alert-warning-border": "@state-warning-border", "@alert-danger-bg": "@state-danger-bg", "@alert-danger-text": "@state-danger-text", "@alert-danger-border": "@state-danger-border", "@progress-bg": "#f5f5f5", "@progress-bar-color": "#fff", "@progress-border-radius": "@border-radius-base", "@progress-bar-bg": "@brand-primary", "@progress-bar-success-bg": "@brand-success", "@progress-bar-warning-bg": "@brand-warning", "@progress-bar-danger-bg": "@brand-danger", "@progress-bar-info-bg": "@brand-info", "@list-group-bg": "#fff", "@list-group-border": "#ddd", "@list-group-border-radius": "@border-radius-base", "@list-group-hover-bg": "#f5f5f5", "@list-group-active-color": "@component-active-color", "@list-group-active-bg": "@component-active-bg", "@list-group-active-border": "@list-group-active-bg", "@list-group-active-text-color": "lighten(@list-group-active-bg, 40%)", "@list-group-disabled-color": "@gray-light", "@list-group-disabled-bg": "@gray-lighter", "@list-group-disabled-text-color": "@list-group-disabled-color", "@list-group-link-color": "#555", "@list-group-link-hover-color": "@list-group-link-color", "@list-group-link-heading-color": "#333", "@panel-bg": "#fff", "@panel-body-padding": "15px", "@panel-heading-padding": "10px 15px", "@panel-footer-padding": "@panel-heading-padding", "@panel-border-radius": "@border-radius-base", "@panel-inner-border": "#ddd", "@panel-footer-bg": "#f5f5f5", "@panel-default-text": "@gray-dark", "@panel-default-border": "#ddd", "@panel-default-heading-bg": "#f5f5f5", "@panel-primary-text": "#fff", "@panel-primary-border": "@brand-primary", "@panel-primary-heading-bg": "@brand-primary", "@panel-success-text": "@state-success-text", "@panel-success-border": "@state-success-border", "@panel-success-heading-bg": "@state-success-bg", "@panel-info-text": "@state-info-text", "@panel-info-border": "@state-info-border", "@panel-info-heading-bg": "@state-info-bg", "@panel-warning-text": "@state-warning-text", "@panel-warning-border": "@state-warning-border", "@panel-warning-heading-bg": "@state-warning-bg", "@panel-danger-text": "@state-danger-text", "@panel-danger-border": "@state-danger-border", "@panel-danger-heading-bg": "@state-danger-bg", "@thumbnail-padding": "4px", "@thumbnail-bg": "@body-bg", "@thumbnail-border": "#ddd", "@thumbnail-border-radius": "@border-radius-base", "@thumbnail-caption-color": "@text-color", "@thumbnail-caption-padding": "9px", "@well-bg": "#f5f5f5", "@well-border": "darken(@well-bg, 7%)", "@badge-color": "#fff", "@badge-link-hover-color": "#fff", "@badge-bg": "@gray-light", "@badge-active-color": "@link-color", "@badge-active-bg": "#fff", "@badge-font-weight": "bold", "@badge-line-height": "1", "@badge-border-radius": "10px", "@breadcrumb-padding-vertical": "8px", "@breadcrumb-padding-horizontal": "15px", "@breadcrumb-bg": "#f5f5f5", "@breadcrumb-color": "#ccc", "@breadcrumb-active-color": "@gray-light", "@breadcrumb-separator": "\"/\"", "@carousel-text-shadow": "0 1px 2px rgba(0,0,0,.6)", "@carousel-control-color": "#fff", "@carousel-control-width": "15%", "@carousel-control-opacity": ".5", "@carousel-control-font-size": "20px", "@carousel-indicator-active-bg": "#fff", "@carousel-indicator-border-color": "#fff", "@carousel-caption-color": "#fff", "@close-font-weight": "bold", "@close-color": "#000", "@close-text-shadow": "0 1px 0 #fff", "@code-color": "#c7254e", "@code-bg": "#f9f2f4", "@kbd-color": "#fff", "@kbd-bg": "#333", "@pre-bg": "#f5f5f5", "@pre-color": "@gray-dark", "@pre-border-color": "#ccc", "@pre-scrollable-max-height": "340px", "@component-offset-horizontal": "180px", "@text-muted": "@gray-light", "@abbr-border-color": "@gray-light", "@headings-small-color": "@gray-light", "@blockquote-small-color": "@gray-light", "@blockquote-font-size": "(@font-size-base * 1.25)", "@blockquote-border-color": "@gray-lighter", "@page-header-border-color": "@gray-lighter", "@dl-horizontal-offset": "@component-offset-horizontal", "@hr-border": "@gray-lighter" }, "css": [ "print.less", "type.less", "code.less", "tables.less", "forms.less", "buttons.less" ], "js": [], "customizerUrl": "http://getbootstrap.com/customize/?id=572bbf800700ea752a72" }passenger-5.0.30/doc/templates/bootstrap.min.css000644 000765 000024 00000100723 12233035540 022265 0ustar00honglistaff000000 000000 /*! * Bootstrap v3.3.0 (http://getbootstrap.com) * Copyright 2011-2014 Twitter, Inc. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) */ /*! * Generated using the Bootstrap Customizer (http://getbootstrap.com/customize/?id=572bbf800700ea752a72) * Config saved to config.json and https://gist.github.com/572bbf800700ea752a72 *//*! normalize.css v3.0.2 | MIT License | git.io/normalize */html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:bold}dfn{font-style:italic}h1{font-size:2em;margin:0.67em 0}mark{background:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-0.5em}sub{bottom:-0.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace, monospace;font-size:1em}button,input,optgroup,select,textarea{color:inherit;font:inherit;margin:0}button{overflow:visible}button,select{text-transform:none}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}input{line-height:normal}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}input[type="number"]::-webkit-inner-spin-button,input[type="number"]::-webkit-outer-spin-button{height:auto}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em}legend{border:0;padding:0}textarea{overflow:auto}optgroup{font-weight:bold}table{border-collapse:collapse;border-spacing:0}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,*:before,*:after{background:transparent !important;color:#000 !important;box-shadow:none !important;text-shadow:none !important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="#"]:after,a[href^="javascript:"]:after{content:""}pre,blockquote{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}tr,img{page-break-inside:avoid}img{max-width:100% !important}p,h2,h3{orphans:3;widows:3}h2,h3{page-break-after:avoid}select{background:#fff !important}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000 !important}.label{border:1px solid #000}.table{border-collapse:collapse !important}.table td,.table th{background-color:#fff !important}.table-bordered th,.table-bordered td{border:1px solid #ddd !important}}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*:before,*:after{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}input,button,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#428bca;text-decoration:none}a:hover,a:focus{color:#2a6496;text-decoration:underline}a:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.img-responsive{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out;display:inline-block;max-width:100%;height:auto}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}h1,h2,h3,h4,h5,h6,.h1,.h2,.h3,.h4,.h5,.h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}h1 small,h2 small,h3 small,h4 small,h5 small,h6 small,.h1 small,.h2 small,.h3 small,.h4 small,.h5 small,.h6 small,h1 .small,h2 .small,h3 .small,h4 .small,h5 .small,h6 .small,.h1 .small,.h2 .small,.h3 .small,.h4 .small,.h5 .small,.h6 .small{font-weight:normal;line-height:1;color:#777}h1,.h1,h2,.h2,h3,.h3{margin-top:20px;margin-bottom:10px}h1 small,.h1 small,h2 small,.h2 small,h3 small,.h3 small,h1 .small,.h1 .small,h2 .small,.h2 .small,h3 .small,.h3 .small{font-size:65%}h4,.h4,h5,.h5,h6,.h6{margin-top:10px;margin-bottom:10px}h4 small,.h4 small,h5 small,.h5 small,h6 small,.h6 small,h4 .small,.h4 .small,h5 .small,.h5 .small,h6 .small,.h6 .small{font-size:75%}h1,.h1{font-size:36px}h2,.h2{font-size:30px}h3,.h3{font-size:24px}h4,.h4{font-size:18px}h5,.h5{font-size:14px}h6,.h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}small,.small{font-size:85%}mark,.mark{background-color:#fcf8e3;padding:.2em}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#428bca}a.text-primary:hover{color:#3071a9}.text-success{color:#3c763d}a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#428bca}a.bg-primary:hover{background-color:#3071a9}.bg-success{background-color:#dff0d8}a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ul,ol{margin-top:0;margin-bottom:10px}ul ul,ol ul,ul ol,ol ol{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;list-style:none;margin-left:-5px}.list-inline>li{display:inline-block;padding-left:5px;padding-right:5px}dl{margin-top:0;margin-bottom:20px}dt,dd{line-height:1.42857143}dt{font-weight:bold}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;clear:left;text-align:right;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[title],abbr[data-original-title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote p:last-child,blockquote ul:last-child,blockquote ol:last-child{margin-bottom:0}blockquote footer,blockquote small,blockquote .small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote footer:before,blockquote small:before,blockquote .small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;border-right:5px solid #eee;border-left:0;text-align:right}.blockquote-reverse footer:before,blockquote.pull-right footer:before,.blockquote-reverse small:before,blockquote.pull-right small:before,.blockquote-reverse .small:before,blockquote.pull-right .small:before{content:''}.blockquote-reverse footer:after,blockquote.pull-right footer:after,.blockquote-reverse small:after,blockquote.pull-right small:after,.blockquote-reverse .small:after,blockquote.pull-right .small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;box-shadow:inset 0 -1px 0 rgba(0,0,0,0.25)}kbd kbd{padding:0;font-size:100%;font-weight:bold;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;word-break:break-all;word-wrap:break-word;color:#333;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>thead>tr>th,.table>tbody>tr>th,.table>tfoot>tr>th,.table>thead>tr>td,.table>tbody>tr>td,.table>tfoot>tr>td{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>th,.table>caption+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>td,.table>thead:first-child>tr:first-child>td{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>thead>tr>th,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>tbody>tr>td,.table-condensed>tfoot>tr>td{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>tbody>tr>td,.table-bordered>tfoot>tr>td{border:1px solid #ddd}.table-bordered>thead>tr>th,.table-bordered>thead>tr>td{border-bottom-width:2px}.table-striped>tbody>tr:nth-child(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*="col-"]{position:static;float:none;display:table-column}table td[class*="col-"],table th[class*="col-"]{position:static;float:none;display:table-cell}.table>thead>tr>td.active,.table>tbody>tr>td.active,.table>tfoot>tr>td.active,.table>thead>tr>th.active,.table>tbody>tr>th.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>tbody>tr.active>td,.table>tfoot>tr.active>td,.table>thead>tr.active>th,.table>tbody>tr.active>th,.table>tfoot>tr.active>th{background-color:#f5f5f5}.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover,.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr.active:hover>th{background-color:#e8e8e8}.table>thead>tr>td.success,.table>tbody>tr>td.success,.table>tfoot>tr>td.success,.table>thead>tr>th.success,.table>tbody>tr>th.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>tbody>tr.success>td,.table>tfoot>tr.success>td,.table>thead>tr.success>th,.table>tbody>tr.success>th,.table>tfoot>tr.success>th{background-color:#dff0d8}.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover,.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr.success:hover>th{background-color:#d0e9c6}.table>thead>tr>td.info,.table>tbody>tr>td.info,.table>tfoot>tr>td.info,.table>thead>tr>th.info,.table>tbody>tr>th.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>tbody>tr.info>td,.table>tfoot>tr.info>td,.table>thead>tr.info>th,.table>tbody>tr.info>th,.table>tfoot>tr.info>th{background-color:#d9edf7}.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover,.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr.info:hover>th{background-color:#c4e3f3}.table>thead>tr>td.warning,.table>tbody>tr>td.warning,.table>tfoot>tr>td.warning,.table>thead>tr>th.warning,.table>tbody>tr>th.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>tbody>tr.warning>td,.table>tfoot>tr.warning>td,.table>thead>tr.warning>th,.table>tbody>tr.warning>th,.table>tfoot>tr.warning>th{background-color:#fcf8e3}.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover,.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr.warning:hover>th{background-color:#faf2cc}.table>thead>tr>td.danger,.table>tbody>tr>td.danger,.table>tfoot>tr>td.danger,.table>thead>tr>th.danger,.table>tbody>tr>th.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>tbody>tr.danger>td,.table>tfoot>tr.danger>td,.table>thead>tr.danger>th,.table>tbody>tr.danger>th,.table>tfoot>tr.danger>th{background-color:#f2dede}.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover,.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr.danger:hover>th{background-color:#ebcccc}.table-responsive{overflow-x:auto;min-height:0.01%}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>thead>tr>th,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tfoot>tr>td{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>thead>tr>th:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child{border-left:0}.table-responsive>.table-bordered>thead>tr>th:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>th,.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>td{border-bottom:0}}fieldset{padding:0;margin:0;border:0;min-width:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:bold}input[type="search"]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type="radio"],input[type="checkbox"]{margin:4px 0 0;margin-top:1px \9;line-height:normal}input[type="file"]{display:block}input[type="range"]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type="file"]:focus,input[type="radio"]:focus,input[type="checkbox"]:focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);-webkit-transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s, box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{cursor:not-allowed;background-color:#eee;opacity:1}textarea.form-control{height:auto}input[type="search"]{-webkit-appearance:none}input[type="date"],input[type="time"],input[type="datetime-local"],input[type="month"]{line-height:34px;line-height:1.42857143 \0}input[type="date"].input-sm,input[type="time"].input-sm,input[type="datetime-local"].input-sm,input[type="month"].input-sm{line-height:30px;line-height:1.5 \0}input[type="date"].input-lg,input[type="time"].input-lg,input[type="datetime-local"].input-lg,input[type="month"].input-lg{line-height:46px;line-height:1.33 \0}_:-ms-fullscreen,:root input[type="date"],_:-ms-fullscreen,:root input[type="time"],_:-ms-fullscreen,:root input[type="datetime-local"],_:-ms-fullscreen,:root input[type="month"]{line-height:1.42857143}_:-ms-fullscreen.input-sm,:root input[type="date"].input-sm,_:-ms-fullscreen.input-sm,:root input[type="time"].input-sm,_:-ms-fullscreen.input-sm,:root input[type="datetime-local"].input-sm,_:-ms-fullscreen.input-sm,:root input[type="month"].input-sm{line-height:1.5}_:-ms-fullscreen.input-lg,:root input[type="date"].input-lg,_:-ms-fullscreen.input-lg,:root input[type="time"].input-lg,_:-ms-fullscreen.input-lg,:root input[type="datetime-local"].input-lg,_:-ms-fullscreen.input-lg,:root input[type="month"].input-lg{line-height:1.33}.form-group{margin-bottom:15px}.radio,.checkbox{position:relative;display:block;margin-top:10px;margin-bottom:10px}.radio label,.checkbox label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:normal;cursor:pointer}.radio input[type="radio"],.radio-inline input[type="radio"],.checkbox input[type="checkbox"],.checkbox-inline input[type="checkbox"]{position:absolute;margin-left:-20px;margin-top:4px \9}.radio+.radio,.checkbox+.checkbox{margin-top:-5px}.radio-inline,.checkbox-inline{display:inline-block;padding-left:20px;margin-bottom:0;vertical-align:middle;font-weight:normal;cursor:pointer}.radio-inline+.radio-inline,.checkbox-inline+.checkbox-inline{margin-top:0;margin-left:10px}input[type="radio"][disabled],input[type="checkbox"][disabled],input[type="radio"].disabled,input[type="checkbox"].disabled,fieldset[disabled] input[type="radio"],fieldset[disabled] input[type="checkbox"]{cursor:not-allowed}.radio-inline.disabled,.checkbox-inline.disabled,fieldset[disabled] .radio-inline,fieldset[disabled] .checkbox-inline{cursor:not-allowed}.radio.disabled label,.checkbox.disabled label,fieldset[disabled] .radio label,fieldset[disabled] .checkbox label{cursor:not-allowed}.form-control-static{padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-left:0;padding-right:0}.input-sm,.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm,select.form-group-sm .form-control{height:30px;line-height:30px}textarea.input-sm,textarea.form-group-sm .form-control,select[multiple].input-sm,select[multiple].form-group-sm .form-control{height:auto}.input-lg,.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}select.input-lg,select.form-group-lg .form-control{height:46px;line-height:46px}textarea.input-lg,textarea.form-group-lg .form-control,select[multiple].input-lg,select[multiple].form-group-lg .form-control{height:auto}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .help-block,.has-success .control-label,.has-success .radio,.has-success .checkbox,.has-success .radio-inline,.has-success .checkbox-inline,.has-success.radio label,.has-success.checkbox label,.has-success.radio-inline label,.has-success.checkbox-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;border-color:#3c763d;background-color:#dff0d8}.has-success .form-control-feedback{color:#3c763d}.has-warning .help-block,.has-warning .control-label,.has-warning .radio,.has-warning .checkbox,.has-warning .radio-inline,.has-warning .checkbox-inline,.has-warning.radio label,.has-warning.checkbox label,.has-warning.radio-inline label,.has-warning.checkbox-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;border-color:#8a6d3b;background-color:#fcf8e3}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .help-block,.has-error .control-label,.has-error .radio,.has-error .checkbox,.has-error .radio-inline,.has-error .checkbox-inline,.has-error.radio label,.has-error.checkbox label,.has-error.radio-inline label,.has-error.checkbox-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075);box-shadow:inset 0 1px 1px rgba(0,0,0,0.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,0.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;border-color:#a94442;background-color:#f2dede}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.form-inline .input-group .form-control{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .radio,.form-inline .checkbox{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .radio label,.form-inline .checkbox label{padding-left:0}.form-inline .radio input[type="radio"],.form-inline .checkbox input[type="checkbox"]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .radio,.form-horizontal .checkbox,.form-horizontal .radio-inline,.form-horizontal .checkbox-inline{margin-top:0;margin-bottom:0;padding-top:7px}.form-horizontal .radio,.form-horizontal .checkbox{min-height:27px}.form-horizontal .form-group{margin-left:-15px;margin-right:-15px}@media (min-width:768px){.form-horizontal .control-label{text-align:right;margin-bottom:0;padding-top:7px}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:14.3px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px}}.btn{display:inline-block;margin-bottom:0;font-weight:normal;text-align:center;vertical-align:middle;touch-action:manipulation;cursor:pointer;background-image:none;border:1px solid transparent;white-space:nowrap;padding:6px 12px;font-size:14px;line-height:1.42857143;border-radius:4px;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.btn:focus,.btn:active:focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn.active.focus{outline:thin dotted;outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn:hover,.btn:focus,.btn.focus{color:#333;text-decoration:none}.btn:active,.btn.active{outline:0;background-image:none;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,0.125);box-shadow:inset 0 3px 5px rgba(0,0,0,0.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;pointer-events:none;opacity:.65;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default:hover,.btn-default:focus,.btn-default.focus,.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default:active,.btn-default.active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled,.btn-default[disabled],fieldset[disabled] .btn-default,.btn-default.disabled:hover,.btn-default[disabled]:hover,fieldset[disabled] .btn-default:hover,.btn-default.disabled:focus,.btn-default[disabled]:focus,fieldset[disabled] .btn-default:focus,.btn-default.disabled.focus,.btn-default[disabled].focus,fieldset[disabled] .btn-default.focus,.btn-default.disabled:active,.btn-default[disabled]:active,fieldset[disabled] .btn-default:active,.btn-default.disabled.active,.btn-default[disabled].active,fieldset[disabled] .btn-default.active{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#428bca;border-color:#357ebd}.btn-primary:hover,.btn-primary:focus,.btn-primary.focus,.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#3071a9;border-color:#285e8e}.btn-primary:active,.btn-primary.active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled,.btn-primary[disabled],fieldset[disabled] .btn-primary,.btn-primary.disabled:hover,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary:hover,.btn-primary.disabled:focus,.btn-primary[disabled]:focus,fieldset[disabled] .btn-primary:focus,.btn-primary.disabled.focus,.btn-primary[disabled].focus,fieldset[disabled] .btn-primary.focus,.btn-primary.disabled:active,.btn-primary[disabled]:active,fieldset[disabled] .btn-primary:active,.btn-primary.disabled.active,.btn-primary[disabled].active,fieldset[disabled] .btn-primary.active{background-color:#428bca;border-color:#357ebd}.btn-primary .badge{color:#428bca;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:hover,.btn-success:focus,.btn-success.focus,.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled,.btn-success[disabled],fieldset[disabled] .btn-success,.btn-success.disabled:hover,.btn-success[disabled]:hover,fieldset[disabled] .btn-success:hover,.btn-success.disabled:focus,.btn-success[disabled]:focus,fieldset[disabled] .btn-success:focus,.btn-success.disabled.focus,.btn-success[disabled].focus,fieldset[disabled] .btn-success.focus,.btn-success.disabled:active,.btn-success[disabled]:active,fieldset[disabled] .btn-success:active,.btn-success.disabled.active,.btn-success[disabled].active,fieldset[disabled] .btn-success.active{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info:hover,.btn-info:focus,.btn-info.focus,.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info:active,.btn-info.active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled,.btn-info[disabled],fieldset[disabled] .btn-info,.btn-info.disabled:hover,.btn-info[disabled]:hover,fieldset[disabled] .btn-info:hover,.btn-info.disabled:focus,.btn-info[disabled]:focus,fieldset[disabled] .btn-info:focus,.btn-info.disabled.focus,.btn-info[disabled].focus,fieldset[disabled] .btn-info.focus,.btn-info.disabled:active,.btn-info[disabled]:active,fieldset[disabled] .btn-info:active,.btn-info.disabled.active,.btn-info[disabled].active,fieldset[disabled] .btn-info.active{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning:hover,.btn-warning:focus,.btn-warning.focus,.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning:active,.btn-warning.active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled,.btn-warning[disabled],fieldset[disabled] .btn-warning,.btn-warning.disabled:hover,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning:hover,.btn-warning.disabled:focus,.btn-warning[disabled]:focus,fieldset[disabled] .btn-warning:focus,.btn-warning.disabled.focus,.btn-warning[disabled].focus,fieldset[disabled] .btn-warning.focus,.btn-warning.disabled:active,.btn-warning[disabled]:active,fieldset[disabled] .btn-warning:active,.btn-warning.disabled.active,.btn-warning[disabled].active,fieldset[disabled] .btn-warning.active{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger:hover,.btn-danger:focus,.btn-danger.focus,.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled,.btn-danger[disabled],fieldset[disabled] .btn-danger,.btn-danger.disabled:hover,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger:hover,.btn-danger.disabled:focus,.btn-danger[disabled]:focus,fieldset[disabled] .btn-danger:focus,.btn-danger.disabled.focus,.btn-danger[disabled].focus,fieldset[disabled] .btn-danger.focus,.btn-danger.disabled:active,.btn-danger[disabled]:active,fieldset[disabled] .btn-danger:active,.btn-danger.disabled.active,.btn-danger[disabled].active,fieldset[disabled] .btn-danger.active{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{color:#428bca;font-weight:normal;border-radius:0}.btn-link,.btn-link:active,.btn-link.active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:hover,.btn-link:focus,.btn-link:active{border-color:transparent}.btn-link:hover,.btn-link:focus{color:#2a6496;text-decoration:underline;background-color:transparent}.btn-link[disabled]:hover,fieldset[disabled] .btn-link:hover,.btn-link[disabled]:focus,fieldset[disabled] .btn-link:focus{color:#777;text-decoration:none}.btn-lg{padding:10px 16px;font-size:18px;line-height:1.33;border-radius:6px}.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type="submit"].btn-block,input[type="reset"].btn-block,input[type="button"].btn-block{width:100%}.clearfix:before,.clearfix:after,.dl-horizontal dd:before,.dl-horizontal dd:after,.form-horizontal .form-group:before,.form-horizontal .form-group:after{content:" ";display:table}.clearfix:after,.dl-horizontal dd:after,.form-horizontal .form-group:after{clear:both}.center-block{display:block;margin-left:auto;margin-right:auto}.pull-right{float:right !important}.pull-left{float:left !important}.hide{display:none !important}.show{display:block !important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none !important;visibility:hidden !important}.affix{position:fixed}passenger-5.0.30/doc/templates/markdown.html.erb000644 000765 000024 00000006761 12233035540 022242 0ustar00honglistaff000000 000000 <%= title %>
<%= title %>
<%= content %>

© Phusion

passenger-5.0.30/doc/images/by_sa.png000644 000765 000024 00000011733 12233035540 020030 0ustar00honglistaff000000 000000 PNG  IHDRXc pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-gAMA|Q cHRMz%u0`:o_FIDATxZKl!2"v}JtuyJ,iSe-vԤ  rH([ڡ1&h0WjZmRpFP$J[awA;;wCq1 Ț !aq:X˲8,ˁcYyeY0 a0 0@':tHW]uD ! 0.qFbR:3Af0_XSdkNu]ځ~@8x]d2T*¬CQ;w>24˪ƀh,0XڦNs1{o, clQx uǖU^[TVFwSSSRvv삪i4j&Mjᕘ\1x^d2Pi‰ 6Ȳ ˅޾e)u 55/ӛ7WX-8 +#pid2IAd  ;b1xˊ b,s]L޾y^Z[kS 6Xw588$k>ӗ6eL5= %fz n0xy@%/ ȦK?sx0Uw2kWSo!SDA#>55kW &’JO$YHm{5?e?~%@PTl)LRpBm( ~?|>|>ut䭤%0M7). x]utR6>Znc0`Tp$( |>(PP~^8]%IB"@*2v6`%+T\,y,$5<'޾M5(k*dz䇗럜DwW0z{Π ::NZ`Rng r9Zgm>$Iu~џ%_L: 9x9bV?x8Ǐ70mK+Y˝};\.~?e<~?rp"tb_IqZ[in^~`\u-b||6D"6mrge2yuX\u Ķ5ݼ{}e,CEA( %J1뭟ۣwGaDdigi"Pq^xG/B<͡XCQ-XTYq ]4_S6+if> ߄0ꥭ },~g7s+(eW|,zv<6VhRnb\<Nthqv1j|of]+l QE<^GFtq@:"d^dɫ|Z($NJ.#(_"i2v/Ezz{hw(noȝ|!_ n2== Ӥ@ع!w2c#]0NigxDž G+>2醪M^_ i}Y~Vϰ߯DSg`4H|@nGv$s?Md{yo, ,VR\5- _JrW\]~^o =kr*FϮ~bn3*b֭۶3҅mp#~'Sx| ##k @\kӰг{lɮ]سwm߆\Z}U(qƚM1x)? C0@ B E|IENDB`passenger-5.0.30/doc/images/cloud_licensing_batch_job.png000644 000765 000024 00000166011 12233035540 024067 0ustar00honglistaff000000 000000 PNG  IHDR:E<]$iCCPICC Profile8UoT>oR? XGůUS[IJ*$:7鶪O{7@Hkk?<kktq݋m6nƶد-mR;`zv x#=\% oYRڱ#&?>ҹЪn_;j;$}*}+(}'}/LtY"$].9⦅%{_a݊]hk5'SN{<_ t jM{-4%TńtY۟R6#v\喊x:'HO3^&0::m,L%3:qVE t]~Iv6Wٯ) |ʸ2]G4(6w‹$"AEv m[D;Vh[}چN|3HS:KtxU'D;77;_"e?Yqxl+ pHYs  @IDATx xT?$$, [ a ; ;bAЪ(JݱVжkQ n-jUdT %@IKf,Lf̝{=Ln|w9#  ؐ6D$@$@$@JB_   бвc$@$@$@: ؖm#  wHHH(tl; $@$@$@%@cۡeHHH(t   - -;F$@$@$@-[m/vHH{:3>$pB9rdgg)u„ 2mڴ2 ^o)s{p!O; Q{{p/C^qO?g7o^۷{~& (I& ݻwt+bK駟i]wua8Y\\|c|v|/BN܃'GP#)ꫯ8g< }ýlk. o yP @Hղ1 6Bň#xPU43;,;*HX1X3@ȸ'#P9r Np?)|Fn7m9Q YEG}ôr$@$@SI6BlΦCRx [n_ךqƒǫVҼ~~ҵ~vXKU&M3fh<.,y5ܭǎ;$:&Z?:9-;wVNՄ~Ww}'gu?^Ͷn:=7}t}7x  7 ԩS9ĉIh"1"3[n|X%EuzϪH{5@C@A:۷k/YYYzW^ 2u q>AH뮻u=zT8x$ʲ: @%^&Z'a1:\C슉S14Q8~\E&?b^Pqnu1Bp9ʐ.&S'g\\=fEYhiڊ:+Kȏr=Tv/ @8`N8~݈k f}M A{ "p1.L9 1go F^wGYh4B!K #㳹xHHIGj1 i&^ڴiرcRNyꩧ^zz>::Z'ϗ7|Sz-yzzzddd/+Vиϖ|q@Sζm'~INC \Kӟ.ŋk .5n8ٳ5?R^L7=3dϓO>)pqqqHƍh&pImFOyx*HH \ gIHHM {|;  k:a=< ؛Ǘ#  &@Γ P{|;  k:a=< ؛Ǘ#  &@Γ P{|;  k:a=< ؛@wD.]*ؗ`&   8P͊cX2d86&0! tFl8w0An `ѣG+&&&r*t pN*٤ xIBOEi ס; TF?KѮ-}1?[s~I&Ӆ2jE=+ԇqs椎D4\G +K4= i[n[˯#ʂυNvvS %"566l - ڳݲ4)):!Ѳi&e)O… e̘1Zgu(p*^' Z j>o<9rd-"I ,8fڴi.sS9=l a-9?l! @Y0vعs'N]vcv|6N_H  9mbvsNB` 9fWgwN ؂xn8 ؝@\WX]vrcrɧpvݿ% `'zח;vre[.;NZl opG$@$q<ϙH:xw  m h\nKmG-@c1˓g/m_Kom:j-Em+ҠŒ#RQzݕ$yOD+~n%m#]DHѮ\v9Տn<ׯJt 2-tA),oocZ&6Oǥ䮒tEB-Ý8p0LUIX/IF 5T8·SѡuQQQ K%2\? I" f=#GeڔrX.[>Z:߷%:'r0@6&)_1Quozy@S_;-J+1[ Kvn)e+Z^1Yyݖ#EzGd۫lD1+QD4IOEڹO/[RTT]k2 $~.]mm;]p.Dz1ĦJ]i b `]P/{Sr=Zg'Fe@ҷK4-z OeDGHdvkaewɑ%G%kSXZV5XP96~?:]p< KS@l+m͎vKl)wNXg~5 h[&3,&)Bn7Ԍ*>YS`1'aItc[B<}E@PJU͏]Q鮪Na\ i-:-ok kvpC^v{mwaG}n[W+}A?Vؗ3/TX@\4#n+7w+@J褤h4$@~$P+]++z\cW-ϼQ]]9 ,e k旝+rcSXlˎY.oScJnXX;,C]mWfEZ\3[T?Uھԕ8gTQ9g9jÖ=v%џ4u+Խ7%OG,cP8fS_Gw =@₁r}@8wYrw1 eq$B~VZaA pO6 ,>@ՠfj g#rN.0B*&?Ac;S>Z(_džh垶1uA|AYxA9h߈w=KǎX'eSmfc\>M°5_K:}̅z7:X,~?ZE,ު+b_$A]- zs.r]z /i, `z"-̵,6X_X8 Ar@jbFzՏlzl_ݵNr橕'g=vC@\uV׼YH ed|yGg (uaF?pš}3*h\~# {ZFpi_{VEk2X{LuSOӾC̔{}vSg fM˖g2DbޓčVX5: Zٍ;G]ױ6;_?Qϕ8\bHJ~kP/,@ƅÔѲg~-_dsGsx-c4B\JrkS0m/?ղݽa\3d@@q~/PGƾBI+܍tZ3kM֜۽kwvmfj+I TPHټ?xa |X]֧.z#N9_\P $ǂhv6īA>v~%[콢r~ũa|u:ZeP&Ď{B|5AYێߜ-/#*sn ɪ8|xRHp! ӺVė$@5&PנE, &9Ku,=f {ȏr 3F+^5Ttvc B;a1r6i3X.NKeXEsT^`'H8ֵz֧ʁR]d[Vkd,CTb99tnSsӘztl[xH$"(tBdLXnqtZ*0[R..buZOnQ z ɿMZ",9 P(q1CAd-ӕ;ʁi5{e5?+Y'7 ^ w8Bf!…r el*,6&C$:~*'q2>X^Ld X7!U \7vx)Hu&R@@< XJV܋ %Sbn69E LeE;_r:.Q#5WY~ZZ0".]Ηsvj;Û$@$ t4rHYl\2.ovApx`Ytw7zZ+cJ9\jnfoKOŷKl,")uFW&JVaB*NhU)hl{*Y7Fic[hʎ]ͱbs7gcoM<Z -TD_?X_ϯ+IteH ?x]qϙ~b׵27!˚=8gu/M[Le:ʲ&Ӕ^5_8:bc(>;B>[*:D/~zǺ69kBȋ-()~ޔ\ݯjdsƜu$@$@U'‚3rU%7 .T"cj#qܭI\k?7PCU "VHNfװq=y迃ejUٲm<^es߯܃[/`{ 'BO=cn!R"H% ͧ};scKt۲1bp}L$@$@%` bt!%,#H.&>9=Rx฾vmqvnsT\@zBE:g$ `iYY_1zBˤ+sCL`4s{5Qsgj=uUq4xKxӴ]j-‡tbKEdq3fps}4 v//A/xwdΫG &ВGv1 ¢}/ tZXL.wɜrõQ [ "XV b4n l`} }5e) tt3q[Abca^-וqХ#!мoS=VuQ #8:8f-+_ i#k, =WU1)uJ-ƪK uՏ8wbL$@$@%` b\Ɗ`}c@ڔ,k uXT|Sw\FSX>Gj};b jL02Dޓ$saSt 1*O/ЙUuy0ІJ7S6,-"ò+.oS&1{ӽ=Yi:[yXebv#$ )[ >;kˢQz-X+.+Xsܼ;z QdzX1;V_+Qo&uefmkKeNS˛ӾUW\yb| uk@5d"&LNl+cqynߛ0eߒ$@$@~"` UE ,`AL ,2N#a6)܇ @ A ]|n1;45S9?fj@xM.m4[EOcUT5Ot*jzU`  E*.od1$̌ 3&p,-X"#T{ LIe>Z>#Q/1U$ԋ굶1Hwd(G0&D$P<[۵|#r s(k|cLmGpiLgaA㞏I:0Wi)|-;=JzvAmWI@ -:~`*` /yt;}^ $r`Clm`đt~S맗%GJ$kb$@v$@Qvs-UjI 3k)_>4kf+8& ,#%8~|TY^ܦd;jњ,YMl:$z{)1.hW!< ia QbI xV޷^>8,`?c ^Ջn Vbc˘HIkG;?GgVku#}{6d@;x W̜$@!C*5KWk j  kh6LyW (Uw VܬVs57l'4ӿ$ V1亿.uȊi fHl!t_vzfj2mM_f_Pn+e$`3@;,W5u[*&Uqk$֕vose\c"[b*/UeJ6D´N 3ٟ0vh_lAy\WBA_ \[L$@#` c_4{  IeSU|ЁqNM֍sX7ѱ֎5sʴ1 Ԕ`d{MX欤8}rZtާS3}xy|{%..; ؇NEÂ-1V~wKu횤q2eB݆[1 ,@sVز7Fw9b2uq+/~}d5eX305 4#yA g6i=TK^O&zYU5eM_C&誖\*~ lBຊ7[Zl np!y˄'J^1m@u 8[ osO%s?-;#p'K 0NW(:D n:Wː'jDMo+0 rK:z6]Yk ,zbcwvkLY|'o`4]ޔW[y'YUYeM:x ZouH|p+yߝ(-Q,*MSq~ Sg~tMlSa9XuDkY$@U&`+ ql`&Vw64f$yոhPEv7BNfMk:F2 K( ;G7'ln2X]Gwl<(qC\x@!C!}6"̺rZƵec{ʕi; @1W5*cR~f's'ti$^܌CM9:1>V7A7]սL$DD@XݹxMتo?GK=8߭'3V $;HܸG!~ԤSt\NPtq ߅Α٠/|@nnrgD-utqnɜ񘨺ҷssd@;%Nm"1{wTjAY5W>YSnˑR7~GKAz5 Љ?͓%z,—0.HJA_=ʞeoJnk)Fe@*EsX"CNAd*hVW\Qx# ZJI\r+M贿lxz6evkNa+3.'&c%OHaFL-M$ocKˏc¹=ZJlL}ܱums\KGJ\ud+k۬ڼWݭ3~9@MlF۳4#m$g N:RUI9R-[& -e򥿗bǜ3T @]eR]%$P+`9Qta=g/߯Sq>3M5&JԼoS8nZRv?Hbݲh#k! ]qa0dHa5ZcQ{ׅ;\:P J-GRO'ezjTN;4JO6unL4\jE2mue @1Ń-d$@J*,U4k^A/\jL$@I _縳$`kn؜; @ fOI ,Xvg 1:cc"O:95 ؖ@glYH(t; %̅{N鷧sd  [б尲S$Z\J農1  klO"UElb +$(tBsj *rQUtCQau 5?Άj  ž@(meO!Q<~:TEڏ{D$m-USi!6`n;$ ^Kd$@$k3 #Y!g|{³O$qgMB m`vFps$|87@u5da)[L$@$p <7S2 8NDD?^;*G$@$u<ߙH:0zh吻yO$@$6s<æhX~VW ;K$`'V8F}q'ЉI&sLJg   7VxDv%pIGsҮ};91 :^%od~ hSyu_8d5Q_J;)taøR(PӧM,a]' #6xӚz{GZBUr--U$@$@!5x~9Dv'Pm6mR>+[v$@!OFSC~Hف*A2oɘ9vat.hx= Ԫ1h!xϟsejҭQs@$@%6Oom7E}SaǏ9A'W;D•@`8fS }|$Pjѡ&C{IjB+j!P+BD^yT9\xXs椎D4gz @ȹ4^atCݴYqOx.\\pzy|uOn&thpz!wpr,خ];]C ^tEgUF$@~"pz/X eSD$@D \ѩ`OF};ܱ{cU,j)H{ڑSCvp p%Nss&39fm_l7 -<=x3] x-tϟ,w]VvZ_$@v'7Hn>IBt2 . 1d+'TĹs窹+$@$ uN5bjB+tR{I@`\I/ 窚pd=WHk71 yjVE~!PǛZ5{Ƽ$@ L9/M5KK~Ļc:"_`U!ybUyH xe $ r~\} }4#G5 ,5c,\`B~{J Srw\ûIx`}L~|F=to{^=n+ xeIIIEi=c IN!3eU)R^=D$8tE)#Ag*?S4Yqn1|4T8[-KLm:$ޤQG7i{P㚿0xHHO1K.sQ=yn+w@JnpZ!w(+]vftk!x(H*LvRk?k{+ $w6RzE@+[/I'ʨiym(5-?"W9 TK`?dK9k\,)58d-+ .=:.a٩D-%X}(q&QđNˑD E#"^FfU:s/6 wI;`g.ܣAd2u*j] WO96KY8ޯ7QDv$U0O$`g"S\`5˖gUv).&^ 3ѱ=Su'oaNe\RER4RRxBujP3r*-* C02 Ԕ-:5%I uDgb,]J?i) ,)ܥ<^qkW߼Zj,c^$ 8#UƑ#GʲeaUHBQ&楪=?٫@ʮN**ϬUEװz5bt+$b csI6x+rszCӆ`2IEwO){qaoHHHl@B. x&@ ϒ ؀ ]   LB3%    "@$@$@$g.{Kf̘!gϑ4B'Y n٪"'`fRo]޶O`m7Z-|?tކ֞0J$@$@~#0}tk(rF=+|?jb $@$@"%t;Z&{q;wO:H,HHHWbtUQB3,HHH d0rRQ$S x(ظu{b{5z#In% CkԌq?Yv|[fm_ݵgecA7`)B/Upd-xr!܂yeZ0$l%-ĕ9#^lxz:VAܿd Kƴ䮒tEBH&$B}ny}7SJJEB|s|x9gi(J:$J =eY{P~|i<*!#"oua  ֏9]?+~3j6ǎ$g⇜bnF@IDATPgF ΁*pbFM.oR[P/!Qo?'K%{_<;ևI**=,M{4n5/ ]3V߬8 &Aw~uIҪV_v ҩy>U>yFW' tBh\)pQm\<|D|0ؕkX9c 0&>ciz184R;jNHA%tuUu҄l#?pm=N Ŏ&  cD& Wj6Ƅbc  qjDabʊBq f g:v5se  ƾ:V PQbHH<9?"uxj$c1c"  P PSHm9PmaƎHH@@Ί{+O^+h1VHf @;ezBʸ0L$@$@$"t̺tue_m3.,!=3Y  {YN% W cu0L$@$E9vM"(iꅂTVxꭍ:gi_3vf,P%  SL^W_=0R DL{ 7|;%u>)Z=#x!򍾬u\|{UR䘢F:m?c8͏crw/;N&pIBڳ}MERNDsWm>RWXvLl\WeŒ!pd*I׌ +=>(W>\G>n;28l82WB{n3fy ~ẵ'Nh(״x7ȋ>xg$=8w e`H~:%OH<9{Gn _6`O%cD$@@ 7k6eR/WsUz|yO$=aeұI_L?oL+ byGwJEҁ=Ah][gXl ʛ3eC|-2_vmwzwk N>{6K8*a~N1@.$sٛAN|L3D]=E1v?J~}YbS~myWb'ïMonz_XN/WTY 72-B?+4[~1kܱ6ʈH~ٓx4nNnՓS_1'qjA&0qϑՇnjv "ApA̙p?<"edž/ˀzo GƔqHîr-ymlX~޽e 15 BB(X_N=9+XvԐK-N av jܼJs]ue蚓`J 1G1?I}U Sww1u묗:9%Jc!M %b}1HGPAV~b LBuմU$p%Sٛkķ(`xOcm!0L$@$kMy׋jTޥ *2},y} )* WĖ{k|{V\SAϞ>[ѷ m9S\d2ʂ&)Ɩq:HFY.׋Ip7k $2rZ\t?d{sF e#cXj vosdc^.6F s AFp}(Ek6CE0ù >=1F_Z 7XLLǘΝ`MR>%pBNJ9ͧeW֥GR!/Wc/9&nu+W\ IKu+v5Q/ti)mHk !2&VVXK`@p}U@T Y;W%:?u훅10J.Sfy#ƤC N4񂫤^y~Kyyir!9hpG#Yg~ ۛ8HX`I~:vV;x7iY>yd+O7yP]d˕17=[%<|JL}PI;wɼ=.y ,bǴӼ%{e  [-Tpo}Brqw8gŜf3WV}-@zS ua}Bn# ؊N& IYaeɒ&Gj)qcv2=d,,X-ۜ+E*es(0!+ /Z~7@KXFYXmoLW͝UwN7s-S\4w}.ש.{U0M]EcY9 VyBtkK|%u_UTWbVjڕcB/YrSi?u\W'w+nCW'yPknZTPyÒ|mZ2ȷyk K>1/p+呂vX „1-RjauGOwGbyl\k~8#(  Mk5.@nC'jAo. 2}*n0+ .%$utL ETV̩wh2$p̎S8f1SoO;4IR_FYs@5"̘"SϽaYOG~ ?JT1Dk|k6wk 汘Z\Oߧ^b꘥~Sң*LHŔ'w5Ij@X  * ^`ӷV9j?lw+[!& %dt7m\e>cIB>H4nj"h4Ayl/ؖa+b\]l Lݽ]zZϰD5n.;Jw7tM}<+YWɵh n()"&tb{5V9ujA1DEe~2pΙa9lk:zم+QUnM6Sr=f@Oy+:WHm+j@b{6~ W4^s#?(-"h49[5x*׽ <?!ks=2{qq?>Vtx6@&~#;?їk{= j*b}6gNW'" kf|֝dٛk~M"lVT^以oΆޒC֬1؊cY[e9*tLY|e12oBI7_~uTvaL!rgփ VZ^szMK~ZLKEJTz C5ղLWtozr,$ ,LV,OXG'ecwMʭDщ2q\5]KX``Xq9w1w(8|'67Z}(f)Ҹe;=EM;%EjxV-;PNpa[-Ztn cuks.&t:ףXX%ܿ5?ɌqM˳1ѐty9k }\֎: "gU)ZΞHCX {k,I"_\]1!<Nfeџ^wwRu`snM(oĻOa|.WF.ztAn1.&NALS6)tq|Ug:a 2J\B2hO;-eN򃋀YjW<)_ đ-=ksGsSr!0>R%X20HE%^K/Uz12fE*̑}c믂+aXK~V!`=3$ yT <+YeYr0!R,M  %Q,1& De`i1n'.;IQqհgh>l Acxv ;~蠟-߬ླzX.?>("B$i>J+UPdvIcni.Br%s?>HUmI.15`9sLRab↴@ˏ)yj:ڕnbcS0BM]WυN\<$%QDr0sQzFJ-<xGyL7 ˀѮ<Mpfms 虩|=+#[YwVY%,ȯ0HHA(nQHL9C6HFhd/߯V%#tppuuդS *OesSp!~uNǚC^CKrN$F v&ov bf|3~X SuXJ(g   Z`zyQ~&ÛJ{L|/%WM.NɌ; \=gVR1]\ AL$@$@5'W"F {:163fLk7ՠf4uՙNVV?8?W7)f]UEy{oxL.QJc2F}Uqj}B;fCv\zBqHHjNഓTbF)˖-SU:3/r,~n1O5l:"LS1a $KآatC_ɲH h\,rN4 mHȉ1h Pg0n?`$P;%tj?rPFp eE΀j/GSC!P􄵒g^ j)Ȱ6; NgmLl Ű/rUs!{rWD I<HK+3݁:{'+;GzA=;k(cs *741>XGO/8E]RTc?UF0 T7^ݶ> NRs3դJB^Օ oH|+ԕ+V ` 75bcPY౗UI])xʠɇi9g<<̘v׍2s|@AN5ӎoy}7SJb%77{vƍCB+rٰf=3Tσ/1c%ybԯϽBuPywWǡtpWq<Ϣ!wJGV$ oWCWIaFL-J$o!)/)_ĕ5#"$Wc8BtQ `J8Zπ `bm[Br[m<w9Vx#Dv%  $..NWT/]a_C I |W RÄ\ƥE'77Wbbo *Ll/]W^#e4ir jf3{i}u:)Io.ycOU0ڢNӮ};91"LN0}q魒6O;ټo/1cʞXs%K;&th ow}DŽ$9%!?}tQL$OfmCƂY0vpJ?Gu2<1@ZsM(d}L"Uˢ1Me˖RXX(=x _˨[5ãNIF&-{ l[J^=%`fR a&b>yy`h@1rw=3jxL6Mf̘A/.K}J:ؙ&7V3eyᇃ!.]/>==Cr޽|26$C ;'B1cv@)@|F -LOǂHb0bIw*rI2܁LaU/9+"3JŀЊ ؜/L٤&~3] UF)Uve^~i@-hۻO @gϞ:Av5`SL;+{fWmڴ)(}Mec:EQjHZTT51eq̙(2YFk$P`\+,/vg~dDLq8xy.s`]lkN)~էEDO:Uf͚:ێ-\T/E$Wܻ޵vџ#KV]GIl'] } u^zڵk EVi95w\s;\M5*sH(rrF%C Gݬ!p05_{brڱg/{/|NJ_u a^^5kh\5/IHJ 4n(X)tAvx_ 0;Yw$k&w޹Xf|M㒎kNr=J'HDi%tAj.MHh?F:Qa&\b08(#Llgs}^=zZ^m%o/+N9T<Srhȼb@1FZ"XW)Srvv))"+ :.(PE' 4i гgOٱc d.u]B9)(P|40^hk@j!иqcٻwbR|PN|pR; eS&SRE 1}JC@a%D̤LNn4v'MUJL45O*B4")۷oݻe$ի'oT4AVAv[a)j(_lEKR^=&:t1K32))"`?}HƲ EogH96;d/Z=v6h"*:@QP#>" 2{=6xEB9zƺ*:I^+NX*5ŊkAFEx@23f&OKHlY$lr^Lα4iR R7?C|H)dPeF{Nh@`fEt}@(1@*!`9Æ+UX%>LyĦ<\{g̺ZEy(U͚5UVf?{rrrC9(<%J 9#Æ ZPR!_кu|4aukE"|̙c'0gCmtG1ǔ kG}릛n g}}쓗c~;r\-!,,XnբS" Tƺ1WR\ ,ФI-K@>-[VLvW'pgee ^x۱}G ș%[3 4h43A3->ezNg g2xK`K/+@h׮n/ \12=իWݻw2q]1ƎkW6Ϟ={nJ"[Y,C[ȏH!hxwdhW`D@kjf,b|9򱗦f!ȏA)S(@@8qf?X?{J~􋗶hCxhд=Y`cx_{1}D~Kps>WP 8;Ґ0<H1eA6I5E?ws*YIY EWH8$<0Jl~g?H<,Q>0~δar;Iok<-/·;e>mz*{yk+ ygUPH㼇9g/{۲Ť!/=ʳLunwluRK:>Y^yb֊x H0NJ:#r7 m6o _L=8k}_;e:}epPѯ5'_A̓cK}d"?8+}/`Y}i,l[`C be_x^ݫ<0x;bIm"KI?76M=LVJQ'{2"%xp`X2ST"m mKh|Ec,sTps,Ö(<6*f\~%ۺ_~"_͔M^XXh>mֵ:l{6e"eoJbX#7d0USkIq+پi"O' PPd˴"'ԹPe9Ekpg^Z;Rߴv]:<ԭba^Can[ \Nn{GBYӚAOz˃mʒ}!ٴ!m[8Le?ʵ<[>u7 c OG\Ǚ*VO~@: 1ۧN_q*)˩0֒o~Lkqm9U}:3/#xVEa y;_$xӁURطVdsL( V۷irbgK 9{3\VI呖hgIO䔄V|Ӈ!CLIG?GʡHK:*:}ۙãe8n+Q\z^ /-_UEE> cmb@(ɶra_I߰ ~4zNHW0~t> 7G0mr/psuŋ5%}<xFb&EK"[ZUt(Ge_~2sL3Y%Av2>7MW<`ڵ sz4a2gFNk3S\MfF11YSݜ8TN=hǾ/}%n_9#GaCNgNzU[Α{62UUʉ>sAwF`M*v>+|/kfydW@S|? &VPn~2d!Nh(eEׇ`MF\1Rnvukn*);3ar`rڒY+$"Oܼ@rL0*5!Za*U+ˈ|óYfr)QN4Lڄ z`Ɨ/h1R]VW.TPv3RZrVM ˥Gw͋#*}!ikOserpR| DPL̾ڠAdϒk\&Y7U%'L:ps=mcx}4VSrz@^-#hJNx2"KU3XJdU}}GI"1StX5ٮs[y5R'}kZCߣBIR+;^A!V桭T)^z%F5%un>ŠprBz>D3j(㪺zV9]fHN[JEܧMUv‹ɩ 20UE^{6BӬ$W[D<ˬ`:JkquX77wUvԍ^ZNe/-e9<$  ^z5|kSb+uUh$kx>oʤhUE7^XeY@a+ȍx99/Gxk<7Vt*x6n?"@ߴ1;YRmVfϞm[C񍮂q%%>752R>1;|w<ϥVt'-pј [K/Q;.]e ܢgFWiLNx#Ncg;+RͣïArp^8vגs}xɲuVA9Y[ha֥+_|bgNF r Q՗L1#Co_Ρ^b2_Kٲ9RYtXց PHŨ钍}>Kߥ+رcW싇Yd.ȋ PH4>9j_VfY^iRsU_,6EU'ѻwoaAގ;&|4}氬 z4Y|PN|pT:4if\,]EkoɁ erxQٽ?S8kͫKFvUR?CW %:,}w٣k袞={&T>ydٰa,X@pgaIgkW5'QG_F_7rl0“Ԯ{/}΂z>G\R swȶvzmk"<ս ץmߍT< 6gRvmӵkW!`9d9,ЩNf=N߽؝ʼn+tRh2L{|CjX>r]0n$Q.u=)>}al{oGujɽ 52Qںu#v} 3e]&w[x7gUaa?BCIjڦY]J\]y9Ir5e 94jJnn[jMU0 $fѲu|#lIyRGd@×eΛ-Izd ,lN>v~ŸsBϱcs-#VPr*9AxEY"S8QmZoTN^^nZoc7?XqG/{UoY=yqk+Z":b}Jq*~%gyZ%M3/Ege˗vYf "K.-sZ"(T WiE`ݾܧ Xr{ 1#+7(Ȯfpb㆑X>\dg$CϕE@( ^xY,eؼVt#89?5(ݺ dR!&2zht)-R"YEB3oK2O҅qdk) '1"-RO"YE! oM{ˁ +;,FUVQF%|(Y䊀"&xR{u[e,J@YY2TJ=Ph׬O.Ɗ,(@2C0뺿7uJWua!C7&5+'ۙwM1[#MIPd!9EPdžSUzM1TJM9GqL05V)@𜢳n&A_k,!`egej#0ydW03\V(O):;Ho2V|У ;d,Rj"n…&8o߾bV)GS}Xe0<VVSU"@θq U)*ߊsckniq^c]}p"[EOnCe"J<^xﭿp%TXYZF_ u6|+l+"xFɟݡm]VVJ"@]K.!+jm":<\̝SLׁ dɲV)`=C *+C t+@S&b2E;L\åE2sLrfA)o@ ! EE ۵jtu|UlFLM22Nvs;g͚e]wth1Je/ڵ,HtHjr^`|ztIyi~U`^Z}*!#٤rHVԦRڦ ̭Ð˗CE 4r@fu땗.~]P^_\QN>%1?>mҩ9l9&*64 abqS_T.1&# [+gʟ5L2?fɝCGk*%nq)"@!;v4KD?>D8O )Q~{_푥 SLK(C]ȹXeoQM_Z{"G5̽j|>4摻cV23ӈSeٝqrZ:PKuʂ•2o9 <)A74C· ":<1J-U~Z,W>^4k'bo%en+ɸ_uqe#yW~gش ߯|BVD +cϾ@حm/VNjN{{Y% E6$2JƊ5(?Z,9NNzڋoKZDy]R -̖92LK[{\voonQv,M/rkݻOjLCݳ cܛ#;@ai3/}͹od%US~!۟l,)0BL;ة,W22,̩-g9;Ô(5oL\oa LqL&8Kii^xmaqGͶQƥŖkέd겶懵ٹYr?bK kTR۶2JLۖe&|(uԒMO7zE\?E m֬r(,\XL0Z"Jfo7C8X|HY Zf9\$ڒ?=YpƩLV7  -\[3PeR6@ YmNo+*9KLRtxB9}1:WYYIfjr]L]hE`Bفp}YBၬ}i{GO(L%DYb4UtPzP,.HnfIu{僻5KfեBR*rrfE)_  dܹ g?C#Y2J{2| H.wIs;d0>_tXBgy͞+i\&w>Hy3ŠtAPIt49.44MqCZCZo6(wԩrػ?d>TXrpm]wPY8+JƉ6n"PtFu.hءuBͺ^Ij6fZO*IՅ*GϹ`OFa(=TrXV,=S* (:z|Qr^(- EPLbIPtxgG%41:돵Z'(0;˨0Ut,ᷞx3-Sݣc|fk׍paa1K{A[be31.<9L;[4%֙}zvl,:vg-HIܩպ\~\9yR3:$(@~SybGRou~3uUG`H0J=6V@eXO>` r^"ޕR5 6 }˾2(0.KgӾQS DST $F%հnyo?}f4.%9xG !tTaL2 ]˦FaO劸FBsHFMůJ7\\[J{6|F9ץwF8La"3"+D2=FLa)A@QhPULALL ^l AƿQP 65(E)JSeEyVSiUH?7Q'npdG9{}˧eORa]Ps觏n>?Xq~w3U0ۗ*Y(5U43iHѲm̞}X2:SWO!_wd#fh٬K(MP{\* ?;ǾHܝv{-ғuY+mL'>qa=C]X$JS)8 rr_tMis{/2H2޹Nb6.g1 1y}W$N`kKr1$ݵ@?E*VoDW<]bI{#.E@.]2.DҙOG FWGJnCJYg4?KλQL…erFLȑ2E^h&G1Ϝw*r4Cbx۲rbfS{j텵?mr\oBkVD߃_ NފщU?=t0 .*~^xKkbv%ϸݷʸNYԔROjРq_j>rF՛61B:M:Ptjd}?\buOsٵ)de/FQx.}Y%0n\|_ˍ:TF%~i5"nzV$U@.Y,R +S+xf,(Փ;OZ 4"ILZK$xB1#o2E&M!iylq"*$vojƍKD1ua}+zZPCsh_S[hFE<`aOvvl߾z=NԪWEf_' T(gpvOdLvMTJ:ԍm֝;2c 7< ~Ƨm*@llm'Ne-ZZ`Qd, رcef%V׮)M46x^=y,fiѢL8QڷoNwL8_Vx):gve]ydd}x=zTs K/3.^X:vhbbT$N2 'bIP<ʝ$Me_?ztd1w2D^#\YsvռysUv(^CϗSVw/t1BU)E{oY7VOYJ2CvDΔ)SҥK^NN'\ly[U!IE.ҠvMԅq͸deqf&ٳ RƲs饗KKYM):h,YgJicE xVѩ C +]">%Z2CvB)3" ֭[L'fdF9T 3~j.<YE@Pg?=Sdc]V,*;,#Qe432gdd$eQ T/n+ps7KJySY/녾<̼,$CsL3!+7 YO*Wr*;(9(I5j0B333_r X}QI?aF&+;a0z(@O+:ipe^dJNy##d&:ZxL\&f,1vJ 6[|41ϟ/w}TWz:V?Ǐ(gu(P('*T8JC~&?ty " ,0ytXRϾ՟RtTzn(@R):s*~e]eM_d*9BǫVYztM'@ĺ[lg9r<#ꫯg}&[n5V"KPvMf$fc袋.2V\i.M0(>jݺn1ƎkNo,w ?Hzm۶6k-*,L^s.dĪ&tNQ):5ktuPvzd]_Om%ͯ/#N ]ͼǙ`Y] s4n$; wR8W G}^+VcZk((>(7QPPlOnνKEe$A 4 jղٷ ZBv$jIWw7 h1GnwPjՋM,c,Sy_EX|z}MƸEJUAJ75K;=2pSql}:z,C`03( mL(VQ:S/˒%K[ݻn>`a.vO>ԮS\ڻoo ɖ[~Fsa~Z!^YL&ZwMVM)L&mLqsOcөS'sѧ6&`9Jixɍ^2afFe,X""C,73Gaۧ/СCbA]7beP>Pj: nּY`n&aձDUrsslvРAңGcE[b#,Aƍ3?n97%`-sGYbu4Dл%/jIP2D2CU )G?(xxwK8۵sI!<oq^cYK pAwdM!{RKm9]fnփல40amf?"RqԊΌ3o߾fR3RwRP/k}Ge$YUZRZyټT^^jdW*3|r.22VS=ЛBs(#~y˦s&&:e1.3AU\PQ3t(:Pv SrC/EVz7~ 7E/as!pL7ʿZ#լ5brpWa5 JY6#j6usF2u _2/`E ܣ0 P8Jiy UHPŋͤG6ȝݺ,_=̚9WYʗ&.[~cDb΋b_-Pp @҇焂Fb9X..3/\9Jv>+g~Tm+\7V jcS&kejfVB @_w ;+4i EXyu3`]s 1#fm_bXJLZKL(×#)?}akLBx#@_wmq w %I%_]s/eǻm xwg]aZT(?~TREOF4))nF>J_wM#ܾRx*Mw>u5ŷ4" poO\S{LnbUv#C5 (9M&׎ N7Idp2u:Yꤺ> w-ʤT A*;~sk&}vS::tipڙ ^di jIRN,`2SVwNFY+ʬ#9ÌfȅͥF689??_B4R'.4̫Uǻ2L&v^&xxrJ dG.%1Qtuc6R ElL}JmSO=eY^ǕbnZ%6%k)Sy})c_[n z6Ux;<7 }>gGW +:f2,:P*ݹ]b25JWJam%Gr-*:@AΝ;i+tO͔/#Y=}Ų5}>H_DXdiUGW6 tNz],=`cr@&|2-[E"1s~q-Z(tfE j}S%}.@ey`buhkbVI:@qUݐqs>}[O!]Dzyï\#!([ZKz.س~ *JANN%sPBdgge#Jph # ,kve7ՠ҈KD#Xϴw4Mƻ\3!Kϗ<ٽ{YQ˗/8"``m61,PfMʒN:yrShiРa!PJj_cwK2hHtCG .MrzQR)6RH/Dy)Sݻw7^uǹE9wddߒ?Z:nLVpkS,VU))@Ϟ=Mի5^'R.''IQMIM/!!jSÈinT%K,4͢$ ɓ'KzdĉV @Ǫ Xva=n۲_)༰*8/~BUtDLo"j…y]2VHs^ZEEvL118N }A)=PIF0zx!VX:.soW,a>ϹZ-8n^ySE'jъ@p1"VŦe9|ws`Y8ssZbj&adetS <4·j=D &W:4j(YtiHzugpAz=|emyRRZ) ~4%~JA ^E - ^gчYff͛7'-(cYƿ^Uu2l/N*T`'\,{4.O qNF3m˝"HIZt֣&61kyÄJG#Lf^J@ \1-`Vcf7^QOEh;VdU{8B?Y˸E6aeۭ1<9cks^mEd4"*X74K#3|pWfXKV^"Xy܇ #kj2keyYhްbN`TMy*GWY>׵??0|bB/>+7(:Sv9be0^%|%啧ee:f%7hrU%Ԅv=68׾rTYIpmt2఼}VW j6PE'z4"tl),HǼ7蚈tQ)偗?k09 [41̙bB֟͢cABqٹiV%7ĉ9 M۟nNpO1(E*:ᥩ :e/L2P X3P,"QV2:tpaX.N,,(QX dg]dyB;m~G#NiE5ӱcGa=ǻ//13C͓#5pQb11 J?FZ|dK(e0%fVF)M_!@9lЎ%\*:%c)W#0sL3dWFbV!|3Κnh ukHFp!pe$:kT0 -5&!~=[cUtb$ sssM_r=z4ixbjXft1RXJൖxpْX47_p#+o9乺2bK=f:38݇K]V\h~B_ҳ"%jάɸΝ%֋K.fRl\˝/| и+ ;!L\VQsR)7)%%ƭeR>OA&t@C*oκl[cڞ_LEdw&n#G7TE4iE0N-b: (Ps^.ΊK9 ō^"vv^X"QPIccwؖO[ȃN!grK>(8}18Q^ך複i5^v5r%3'e;䳞0c⪊fv8鎀*:)^|)_tM}cmKZzEc`1SR⍀*:FXW@۶meСfk6{:^)k,>!]oWy4"PZT)-rA^&gΌ3ƒW*}nGf۾ ˞pբv5E &h)8}"[6mf͛_.f^^|BTҀYfx}ЧӠ7 /]"*ddUX[@z>`+ȰäO"tRaB6mڄ~yz:_<.lI8uTNj/X< JE8m4`X:[)ODpohN*}tbN=Utի'm ޽'.#i`@,(@NZdea*΍eXKQe 紦9f;guř<\G[V0`g:}w"@0`e~ɤ BKX)6 K:UrA"Nƍ;h Rw_A?/ S/K/Te|Gb ]`]P܋nݺo'@=zdfffeV~d˖-ِ|*U2 ɇRžuˍ5R.>Vv8Ö-Cݺz(X&5^xa1 /PTu"TZ=GKvİ NNJ@H[E%X/@(֚?Av-[n˗m2[n-7o6b E/yLfݸ?"dч :v2Y[ꫯ ]A(,k֬1iQ̠€"eNW)=zAK, Y+Ϧm@xKq0} FpD(PMmbkY\c\J\e g2!⒬[ ~-9@S(nаÌ"Vj޼M[ AvNWO({ ube9%kL" %RԉZA/5wM7$sNkש-6XUVkl\Ovx UN9 YYY&'n4YPRPlٺ(# .4ikչSgJo(XӰzAA[x'#2.NVbC[ +T3/y7f),; 'vƾ+p?VRfs.E@d8^&(EJ-/CEa& +N㪂8p'-J |?q3 |Q- 8`I?SVȑ#M,.?(%o"0vX_P!ٚr=ht{YVٽPSoY.Do̒+n>G]YNPt?nSZu*Vc%Zs1!\Y%bC AŸ^ӧHċa-3ԏ5Ô(.Ȕ"uh۶m,byDyXMZB" u/)k>{vR/H*%"@l؆ ,P2q)y?F~DڧɽR.hDWːFuk{/I@|m!{k7ϔg~!?Lz =_7VѩR?Ibx0jIN8(#Ķ8G*aȸطg.K( LN:E駟6:ƚ_bcN :,05k44E|m'lu.Qv=1XBk`}lԨQC(JbS]9(8U+gȀ]wbN?>xAq[FL-Λ_!/1X{P*xkGWV)"~%0&nZ7*;#yxPy^j+~kϓϾhj"hH)x:sGeAC{SYӼ:hctpO*EzB%kQr֩%kޙ$U%'*KkX9أh" d Utbx,&61e^~UXM{@~ޔTjR!k @& JUtBtB*;pecyy.$cADaXժ褘@9@iH7e%67{2RN1NUI7k{bVvR5f{ڽ)b IB*;ho2SRJ*:EN))SIe!GO$ŘMBAF{:(C.ySjrNrpZ"`L5e'?o9r&鷮2G! vI/K;FPETi&E @3~eCꇮk֤kI2XC= 4 ,)n@m۶fY`p&2zn]e J@Y‚t47d6.UqDEE05keUϱv׈`^a ĸqaUP(;:{2L& _늇$&YII1e.$cm0LJEegǎfAZc@f:aݵrQl^ݮ}lP&%7cCɐJKZ˼Wf˔Ϳ""oU,77XZI=zŢ?HX|xbY/UR)dYˎ=(-"ڵl*3s)xI_T] Õ5|pYfq5Jeçaݢ]f{ﭿp OH\ZU6(&wCw[#UpҦ(<= Do*\V"0W֝޽{ 1zɦ~fXжEYc3a:|X:\=.3 Ҧx }>ITe'Rĺ(,; )ǏO;ky63wNґ%s fK(;r5O2+:s5Cʎ/;6mѣGwRk6 O-<0nĿZCB@-X۶1y (CuWʍXe) 8иPx ,<,#(O۵jj%$|,SeY~s>v̤dGg݋uPOI5~̖Zayf gC IK.MC*YNS aKFJcqI-l&IKZ4._|]3LtWrZ2ae?\)ӕL?^|wqmy"mR+:̓cGWiLQu%Q;8SPP g61;ۍ O!2tq8X VR):,MXo\zN҇A,'>8A0-[& vMǿ-ǯ )~Xyzgy]i3uA}gɪuf4(9 /Q4kAc"DBޘg(sÕIlOk: GL.b]+f̳e??̹-O{0#=:X6nB@pƲA(ő;cBE_|CΌ6W}>7}XJ(mVzw74yDΚnv6nOsRrȑ=!MO `!p8\uY[sn}o)v; Y鑻wLBe(E\ta]0jn,\W_H]h,q׽j4}rړ@T73p}/U6=_oh\Ь16[L#=V)߼mW( |FqDy,Q^ NwK/OH %!@,NvZFiyřR/zFTE n' [A{ߓҢ˭Rұ}+& ܃̣ciO dWÖ \OgakiPߟ+M67HN5[ƽnO˳skOX\JAr"Q):yyu;ZtQ^7<2;{W{$CJ0[W$z|ٮ>'x#pֹ~Hk%~,zu\`ԓier X;&/=m`q W&ebсdbFrzksLFSg!meYt ??̔bxRݻg C_ݾ\~uN}6^S=|YڟJNï^j7t'.>CVҼ~Y&?uT/UplPNlMi+ ,"k\wPS];آ68ȇ %FlQ$f&ْY `)&*gUr (9ZbϛRCuHxHYEاp"dY'gIDAT6}ij^H2mCƊ "ˋdcyoòq E`Sp ambiee\eq[Nң6!YJ"Vb9?3WmcpEJZ^)*DMӥQYt^. z-ve -_.Д!P<prp`j*ЂygCXziJ酀9ZxL-9.ǕcFV-T#Y-(E*d䗆LMK*R:?ڮi>M}<0G $ZlBfJ@0u \@Y=DCfBM9{xV9 F'ʻރ?}8FFȊ!NIHy1$Z)5 :[h,g^O]<|8d[~ۭNl ;1Qb@ӋkE=[[)5CCXGNޫ.+;V-DVօ x#@BdS`xש,6ȒCD2kۆ}cPKv%Stf]<Q+TX )09,z\_sݳM"V>$%"S&^&dd]fcC˪LONf\XȎ!RI7v>Kjխb`9%o"옗4r StMdWFNM",]ĚikF)y+Q0=RxJٹ@ }C.6$AvY"S%E t̩rxWd.*h,ݧ=-VqFπ>MN)^+C+Ӕk6U[W揮M)d9BJ WYr"sckniq^c]& k!Kdlx#&Vgf?eNˏ X+أ,^{^#{oE(ekeie Ն'bxrTgr' X#e@ UKMSiƢuv9X(xB9X`VMHIpcu/+"c0 IIͬLK_+@ Vm fȹ*;@4ve8spV| 7 ^xmae:cTK PtotIyi~UkxɍRYVe2Ya.deje.v[h-ʟdzܭI (9Ƣ9"*ܽg8a/@:(cׁ ȺEdin,|"-]dwkF0RBٶ}F*%'X`؟7 ąk*<^ZJ]f5]Ug)^_.n E_\,(\&Ǿ%[g'kkeje,>C|/?%SG.U"_=Gf_wSR99iEŖ{T)1X9J37%Gen4(7@$=Eb찼JϾ(V 'tl'w=Ly w[ ȴ1<[b-sn3eRDlY~!޻u̒fɴK#&8o67XŲ^- L0c3,h,xmB3b*}+7w9 lFuk˚w& [nڽ=_xamivn(?b "2Bi2ޜA7И7?CSEc-*))ȷ^g|f=vWȍ:59t|w䈹oqK3+キ 52vr*ub7ͺ~~1SigVڕUIN*F iȪϴw/.nʅ \'!~U 56Ƃഒ ,cRgU:,|ԅ9??xJ|Ω4 CƧT\Pt(G?T5|bACeN-VŒkۇʢW%'\U2v'N|8lҨq}ZB dJαC5^T-kcD3Ij&UjdWΟYQWn_rV*r?&e)F?:f1^ߵyvE*_̒sm[>lVyBnb *\*KV ҳ[g@a$?>7#P_ awj)>~&p˽#[/\۳~_@i*N1B֟KNJ*Rγ;y{e Ųr![d_'sO7ͽÇk-d2!;X59XԚcQI߭'כ@bBQrLUvβ`EbщU}ۯ>Yb|ga@Zh!M4vI֭%;;[VPX}{Wa Vpjի"O%dP[fʮmcg\N؂wߖ!>nH4EfXߚ{3hGoVwe.tJ=v|#Ae%O(:mgF2("'Pk#IFa@й4shdϒnkPS܍ e˓%KHAAQC1ٱcG]tEҪe+i(Pe,5 . Ƞ ~q9k t}ͩf4V }_TGc+%ƺ떟G<J.+D" U+ pY`(8|qrDA @}vٶmaoĉtXzkӦ4l0P^ DJGs啧ʮ&uQa ZPib4m:+qf4;FYHR>'F@@}f=U]j5m۶F P o ,?Un] vT>ϟllR6輀ʞǖb{ V6.i\:+rc1V6S?!:!:!2ܺP c,+W7ž}|9sfs1еNÇz7~9`= %zDc׷FCRDzPf&&W2C>]o$l Enj(/s_Fk!A9=ܕ,mNȖH;dy&իϦat}ie3.SN5 !8E֩wcɾFVH>MlO`+4&'D:v$& CKh_SbuCLdB093g$D3%(8g͚5&צM?NkVyuKnn̝;W#8 ٹs*'ʻq3q̂ńėL5$hL utdsUYr*E^,=OZUKm,\5^.d ![%?6#b8|w&Atm㘜 M7$]vN:ɓO>)FMHN桭+0[,zJeG+^x(8{k%·TڵD#=?Xgͪ>+B.(YBV.`ɵ,s LMCQXmc(B{r\/e]vU NbbUˤ꘰я7k;֬逊%\`wVߚy϶l3IY, [xSJ(:p0v_f^4Us-!Yjh(X re/** XOJ ?gB~ n^PzʷWbk.-;Q WV>!{(F\R ޹YYw7V4fwq!$HV0Ѧ/XP.mSPgtl@Kp:!&uj%% \ Rt?9캻9>3^~}=板YIV|%`d;i|& w~"D O~A.|_[?/_:9/L'!*)) zr_T#? NN};OwOe^Ӓ.BlknYlhhh<:`XlX'kCjw[pO34,I\-Zڿ'BL{2eB)p1푉MQqq;$w+,S7%+N^w^Y :_迃mŁ|%tD@ DsF ]`ܷl-ő?4EPw̝C]ʚ 0G9cu7vO*79}9-:sgK;؆2aPgARuz0jaٲeaĉ}-"Pi3:G~f+@$M;}\VO7` :CYa?:_&d5իW[ŋ(5}xyglnF|}VWWtȫ/JGIo7>piE)te#Dㄣ#@QW]|oL?̐o u:ujxüylƍCEEE4hF؇=Qgs PpUWyvwn\k̙gmD鸷z)O-^TfɯH57yr^BlPI7jJ'oΨ;Y&cҼ\76bϙ3DŽΑ#G/s/gmXRaܹ6TEQQX4ax~±c¶Θ1##mf¦u#I!"L^}Wf 4a1/,od˜1cwgَ-Zd}Qo&cB<ۙ555󫯾jPB z[oj".{z& -s?cq ))jCgo8lEb'ZRGU: ~":e[*ȦYibrs43":6vZEO>O=NzC\1i`t"@<_!+H^6Mzح_}Fb'/ԕ$C}q_oLܩy} W\aÕNb'K5VGUtǡrteA4m4KMK&0'VlD'C1/7}tK4[ќi+Un5|?i[`K䛾xȡ_s#>;%t>xJS,Mbgl uǔi'ط9cM3x^NK5L4%UUUY`:! ,|r,tfTָqZ8IYF8hh{M'fqzG-/Qq {oFLRH>9ܷ.r $ԩ]0tΰoδ?;uuž:.Iȡp~ 5p˖->1Zd \.J~aKGUbԯ_?y(Ə9A5>9ܿIסE=qMcSQZGO}߆k7gzitp@vShMaDՐTɉVBʴ 6hCGxTߗuxNfFlEfA'Oh+ϵ/;\!ac<'QU Y)J["8L<9xq]EDʎ'5tv;?Q5e_9ٚR:|vf*Ú`Oq6o>^mn:r£ӞF;'׾Bɕ.=xࠉ?G~CUtx]꬜ @i,_gR<:k/))& ~=|7ޝ?vpi/SŸ 8$\<=Mw ?sΰO}o87 !i8^S6?L=||F=:qzhzZq!]v . =X , ̺$|cŶdCڦxjwxr;?j~nܟ:$;sWi[pu$}@9O 񭭑3"yx<.ٕ3^/9que@WfM9* `PBSW~xpM_h>O8!AptppCs0yh!=C{/( eg1$oK, D2Qbmm S5i&ӵK҉@Xj `P] rp3"0^^h|/7Xէu-)" " - t32Aq?Fwش%)'5˵۞)/'" "'^A_>·_SyuZޜz8υ=u{ f\xʁtNƑLۨJ,v-dǸb@1Go߾>|#jM1UFݽyBYYY8x`j:S:"" "Nyt۶mxy0dW&i$5qͦe$@Y)O" "tZ }7N/./$&Ƶ8J ݏKedܱZ4r׾ttU8vB=Jn\񊢚H0Z}Z\/gyYeC Cq;ތE<;0W&$5ǵ}r&6e"] pfϞm+Vd~kjj2hc>x)hL!3Gf'͛Cjd&n~= ~ҥ%`5,OᕝW^7㞗SNu |z~sjZs@o*C*s6K[j($}뇻̋*:ONa sN7~Gham7}ew}7 80l߾:Ǹq®]BEEEK&K7|sXv%sLX:~xXrϊӧO,1qD i{܈?x' ?uvi{P={X8xX?>p@ФWWWFeew^Ky #)+ٝiN@l1ra31ۯ)Y_H>"5ŵŲ\k\s\{\9qQV{˖-|ayoњFx1wMDq ѕ9wƌvk/+"߈{g퓷իWB!2|_D{9!/QFζ"oAy=!D,Y\( څ"kd]t}0_/'oۧk);mߺCwߖsp3dCݓ!*e<.UZI;Κ5+s x@|>y/0xQrxC?j2_s~^{߅̙3s3ob4[&B0Tr SA}fy~؏ a!|^V#(tVCoHDxPP<|p}'װ]'9*~Dp]\itx4GqOYq:фEeqvu~Q?~|查HI{ >`4iRi/\  {s`xPf7u&L~SP>|AWHrYm431IT71E=Ax_yDquv?axwrq*\ED)wx {'N4Хi09sXgTx3Rh̘1-fh";~2šO, 2$̝;ׂ?(W<קO B{.]j"~9V 6ԲN"=>wj'P0H..<*| /XGX<76]g/"$*r8KACX<,r3rwv <uVtœ8 Oeg<%DG{8Io G ѿ!!ڢ-nk=Ѱbf=De}ofXy}&O gY)$E7))u)" )&,t:]&U7TPU*AH<:IPU" " "  jP&D@D@D@ U)" " " :eBD@D@D :IPU" " "  jP&D@D@D@ U)" " " :eBD@D@D :IPU" " "  jP&D@D@D@ U)" " " :eBD@D@D :IPU" " "  jP&D@D@D@ ێufIENDB`passenger-5.0.30/doc/images/code_walkthrough.jpg000644 000765 000024 00000050750 12233035540 022262 0ustar00honglistaff000000 000000 ExifII*DuckyK+http://ns.adobe.com/xap/1.0/ Adobed       !1AQaq"2S5B3TtU6 Rr#Cb4d$E%eV&v8!1AQ"a2qBRb# ?Y av̏j m8@e@j15sz2 DpA: s424\  8qC A'@#9C>0&ZPug7@Y"g- 8`T>tK"f.BȓqG011o4ђZH [X C@<+ h0¬vP@,1ϸ\#@(&M==$0%lMYD {Q0vJP@ @ 9 o3#A0 `pA02@lLrA P .s#840jhȇ}9Pd Hv WjZ 9іCD&:S Ԏk/w BS43eexDLc ;%TZS[="aj,L R#0aT)8CwB#p.&`0 (&p@P"t殦]80q="}KqKhhQqK!k~uR>'S:>|Ou[\892S:Ք撦e91p= qe.b(@ (omq` b4G8 pAP)oJH1o! HR=s`bP&#(WW5`b`10e3R#Αú&Ӗja0Y1 M$}JDpX=JєA0PEdIG PinJ AN]0^+!0@@#$p҂G49J{` 9_/*7&Ay;_, 3'ݮt&Nqe=sv1&\e[[i>wMv:u]hg<,15T{&^G.78Ne{n~ҹlK NasbU2[SC:wL_9Ȯayi"ksc2K$ϒ]-1 B<7| WF%@ 6jm䀁q bp !ӏq@RЀ,A@p(Tb P#D`;(xh&ahdؠMN  yQAs@2My zTX 2]wֲa3BúM0L*1،S$PFkb x>%h#J N bAs$A'qA'a#0Z|#|%A) =/r~c~#ovn&\֊x2m$6h!|ЉMn+7~V]ѳ.׻MkCn43>Ls A ˤnߵ-to;5DtH,w\O1'c۷#$:]G"hήmdYf[dhmk9g F@ ; jgĭެ$;4q @C< -!BA2ҁ0A2A B1p1@iqa5{ oJ KNC$QSDLI$J渇(d*1ZL1D+\E#%E JFBAA" aVhN* p'D v~DHAȏY@pPJnJ (9_/*7+Ng6c`0$ԶݦyWv|?إ=ͷd9ͷۥ`R/L[k ׯ| 5ӧg F@ ;ⵏj` h @@C08X@ X `n#-Pf8-$>pk@B0-$zN=8@- h&Y -p8A3.' D$ T{|8d aXG 8HcFtáw!Ġ \+B` *#}AEBp0A'N0A2 N BSq bu]@q qǡmf? ߮][>;' ^i"iq yʃWS2k80ˢLDQB@ ;jZ+m43Df8qc1fH1nywMw)l|E]$yP! &A qhG5g{La14P@ l2Z{VlP- x !A FdR KA8@gFZ|yC8d8z('!ә@L`pA75F,ih$ lF`ubJ8AdBc@r@øp*NtKH9zEA,8@ A7b?-Ӊ9ր9)D0P#N%4"x+!(Td*(9_/*7{km7ME2vJ̝Q=cb@Ĝɀ~ŨOc͵'g:_vr-!'T:dd]2ĝo {!Ȋ+ymuL]KAr&ʨllZc@S.~5ܻn jnz)뵢mʆpd,{Ff XxKsӳa@ dMA2 m470q=(2@iOB #C@4`P+[wPHq@ü"CA79Nl $w$2(1(0tO ;?h%] n# X"-~Nf"埸D]!+ "t I3('5><+5sNG0COqф1Ap1ˣ%(Paȁ@Bp^ PLƩJ8#eNOc~CW|]gScn6*w㮹!77kyi^Kߩo]vf5]-q gwflmɾwna^M޻tf6e4u>O:Y,.9p<E$LW*^Jm-b\otW:KA3oJP->5p&,#+G@0A'dUN80R*~ӓ@߳˞c]me^'7E꫓$ܩ iQN:f:yt3D|9nFHUeM%jS>_YSO2TZɒ.@S}i]ko6`6Q ԲXcub%"Քۦ~ݭi,ۖ2e&>tK|%Ԗ54Kgnd:C:gۓTm8yWK@>\%絤/;0OxmRnYZUwi%͕IM0,8B&:nGrky޳V;pU YɆN'L5-t]sG[ghTgٷ=ahh"]S$Htfi I:ۘ7=6ݑ6QM[i<;.ҹ֚A彥ktǵ v]/ͥmvh[rSﴲeI*Uf-|ɺ]ɔ6 7QWG]eSki]i-P9PGn J*kAǡdE a ~H"Gá(X@d)tbPD($ ǩQh,m9? =JӘ |2۹mXXj/3E4u re^t򲳒Ǻrs\bUڦ<ҲdN&[E L,ׯ| WF%@ 6M_j:aF AOJ@@h0{0(, \#A3 Bt"n }ʶp+|U2n^3)eS\3I|b!ha70f7k%lZ,T[KL%K^1 go)yqaEYGSYW7mp]IkɶL3GVPr*ɍ,`notMIWMO.G%$uIosv,oW17]pnYi,7$.EK0/NĒ0A~uwwR=$5vITUFk0:t%it{KNż;?n Un|܂\5:̷L<$iܻGpۭ&Rʬ껜qQ"iZX D8W}By.A.ve_p[*"˧2\]f5&ۺ}qVm; pކC:h_JON-8'h*y=p;j<ޥۍr)P}]lҾh.zgK +IlM?jQVrގӹ..n86۬sYD>Chp0w{8l-US+k.~Wڙ2jv91 8-aԭw >s7ŎbMcs_nMUJ%*2\lz췟hI̮t׭btn觚iM}]+ssa-7`sbV804wGF@,2!Bcp% +!^p foA J F0V5OO|+G dsli_Uohknu-yuuOljY tɳfj]E*Xmq4ک]=7.{#mݵ{[rs)TopWwOri)HSC*dj*%O!=C g $ᇍP!$A@=M}rn EFw"zVO^\J:VU2d@Ih5!dͦ!GZƳP>s';6e~ʩtul%EqvM*麼 Cө7Y7鸍f۳%]̩rR-t2Ku5k8zKl9[]/LM4#\'h^fɎklYmm6[Cd)VVԱ*<53DS:p!Xj m[nhSzaC UٜݛJRj)(jnŮ44}l `gVjQ2Qp&ݴٽO]>wUtl6~^ɨ;usQJͮ>PlO-?ey{?shYOxZnwQ[CYeO954c=p+{|%53g9ReMJkAqs"eʱmZNJtꊺ9;TUs*6[fwF]em:jZe>_5Uh,%:Xtؗuye7v+ot"iQKvW ̨*\v˖ AGʹ69.t.6O_o'Tl״=ŏpsNJerDҪcsA-0NJHcJ٨xcA7tPJaPAc =V2|!QY@ @ vK/5pЌDPk!P>C@DP (P#d= B =!kal-w'y0-Pk-x}Ɗ2}as&;A:aBDssR\ϡQN>y>R˷ɘךW_,¥fːz7x[,mTʨ$0lLtŠ(:ܛ#۟mvUTUWw[pi{[=2)~ߗ-ɲcfݖ=PMemD&ΗR.|8sfh-sIl ˥m$]\\[Hen;C(sX4"JW\)ʱT09|ZKwO-l`I$FrmC>c/[zu쯩PPuvپ)yc.ɷoMOY.M%+k6-k[1~1&27+Ss%%a6}luKej=N7/9ϒJjYJo2uLcǑĵkͫ#בrrڵwm͹$QCO](3eĺD-%p;is,6eNKC[.Nu| t;".)U1fjqt| c edˮl7S|h-lF M#CֳFђaͿ:[oG2m4il\@ -7ڒegi֙5:uTٴrÜRns4v^N;eZf\*$&9Gj[PSlZ@DAu󛗗 -KAAfSvn_]L'MrjzK 9y7'c*YmRt*C@L{ ҙ,/xsZ@%hyl=K-ƺ8\.8qRslޗ] T at,[YPpeF=9rwfppV5W|+G d@ A?7﴿,[i@AGJ `(A8RsAH='Kqd/i-pAnqqē{gu"wRklex01_4dAs^_1&%Ǥ(%J"TײTuҚ5?@w &q'$#@d%[ Ů-.oA CD3@ ! :s9.{.s$ēA 9}N_8um/Q'Ks;H$*&H)B!ldݩKiJ3f{{MɯqsGOer:TOV dKr%;oWح5;ո.3^si:Z 搬v]g6g-,;e+kt(fui$T]t&L0s&a`U~´p@@ @ t/5^BF48Rq8 BacLP yJ"s<y@Ĕ A7!@A7FxPEj01(&=$a GA؜#kIE1vH1r(0XA,ϝ@Xy b!0 RaZRy#ςDLLcE'`z)A<2JB@4 =zҏJ +GE @ i?lRP~ՊF36& J $qA0X4 0Aݙ 9s8qcs\b; F# NbnpȎ=$Aq# qk` "F\zVD!A'QCubUKZ@cpib V!8 HR 81x'Bzj&1H`IUw@ @ 6E_j"mdrPz:,D""pAҁFD A2\s @T˂ i (&N>$$G HoJ㧣Wt8 ?" 8 aK>.-1& D6)r@JTa,81Wx8yúBp09kO ̨ p(3 A7zUGZz@ @ %ojQ0m`KOu=( )p@g}N1p(&㉇qx@pwHĜ8&I1LaAAϡ XxRgbbnӀ`M&D&\L!b80pEPVB`"JІ q%A'ǧv1=G<*QY@ @ v[g7ʵ GA=8 "3AAd#@jȈ 4Aˌ1X8J0@tLG IiB =9 A\58@8 J "](t(``nv'. 1D#`vI6|]UI/ _O+,?]IY(:k:uj_qo o@;>Çx}523`]?X}52>,w=mOsnT͙.hUG9'K)cf00:I9C,ck8 WI=#(v҂N8bn|Dq@˄I{3#3($pA2cPNئBCB P8 Qx)BaYq'<HNc7L 53,'X2V8RqR  a(Ğ WqAJatx*8ݨ Q Ar{ыˋLGZ}^a@ /LAPJ.k~k{T/Gi;\rϺin Lmϝ:IΙ% L終oM-kWwnkʉ{MzXj["6dI–]=*L1fCaAĽody6Ǹϳ+mb"CWoSF:EDT3Pƹ跬.tv| `n){nmjjmH~ tS$1lWKt[cڃjfsl|5xnU}ըf<7ьoS/oSC]Jg{XՔvzQg&M dNQ1*t#ջ rȝnJ&iT3>D9B:?t?Pu5Aê~?9D-L!ԼCIԵqRP#+DP3i5a=Gy9Dy+ag"z!ag"z!ag".kK\ FEdej9*A=.n)m%sR7EUuo{㺶۫/5U >`:I@D9a0n>ɾ~El8x+nqn;>0w~ݞjm7˸.vJ$ΤTHk&7Twy!dTPb  TbLpPdP"0Da(Da+ Q@ PP*J Ar*q k)UWVT2D&>si:Zp\zed -zNw]:vpl _$/+QͿw9V{ѹ_⭋57ݩݛIaRgWWNlq!byh.qTnwh̜f%/4to8XY>pl`Y{?&}^nUwVGB>QǨ5ەs)STK:_.d)kFDykđ+iD&˖X>8Iʩ4_vg{}C(d~'|Mݴ?2G{}C(d~'|Mݴ?2G{}C(e_Em!r/h>e Eh~2?\&C#Eh~2ú._Ee!wEh~27/?\&C#Eh~2Sn`C#E>C,Ssh~2Mݔ0JwU(~ W??\"C,;4?uaS,Ut02?]>"!~+|MvP{LmyRV2T45kg8 $/wй;e]672*TsmeY1n-Što^OO}e%ooC_kLm5M/M:zt;Ok>OQ9Kmd˓ ,*dt!ĸGW~|zZmˤ/^_|<+g|M?- ׯ1ӧӣͽ+̸JҲqPԶPO!t<6,`#Hz Myk[9i_{6ӕp^[LJl-CIcH}LȤ /5\[,#_7gk8Kd?=?mK3\qӦoy9c\ʱ+d2_6Ls$|ӁL1_'}}|Mfse[{ilI,i:5WKƇ#ΐa3N19xO'۷O/ '%o??gv͹sOYh]iE$yNlֹ-D+_'{;m׆qgN;}+>=-5o|mܶz9?)u,؜&^nY[P3L'e\CZs.)?O=8㾳N_N~G>kumn|?;/oyGtpriU5Tom69- ,k$M{=XZtOeu}ۘʺ{mξt!*tRJKS}1ښІ^λ)?oYv;.UD[eYoSrԇH^0Z".=]r|ꊪgӲM= d5M5Z-a<^Mھ:zʑG 55sf>?k>ٕ2v4Ygn;ql&Ϊ"cSLQSC%ĺ({AK)<#;l-H}U-6&\gkf{M[%p4lzZtYzۿqsvU;rH~%UVuduN4Ko l||ϿVv۹lgmm[:)OFf:pCn ^',~Uׯ| 5ӧg FQe2O2s` eٷ ~U~*7PSϢl$K-|ٺqsZ7Ūa6||w;7W47U꽡)sdeJn5X/7DA"1|>^S"3'aCUH{0=yA s yEjIZmO5wZSo~ASo~AS/k~:fڟī~:gī~F{6#[|!ўʹƷ?LCmJ?LC=iO=iOfī:fī:f_ī:3ٶ*14HUwbKw~FmJ?LC4DUwbi̫:3ٶ:ﻤ1C=hO 6']t*tguwIbCw~@i:ﻤ5?~GpWɺn7JpʬQ)86l<"08^_˞`Y~vK|cĚq^#4d?=ɯqӯY/O|'=O75nY͜%=M/:.+K[hQT܀&3:]3LqIէ: vnt=~Ľͼ^m3uۤM鞮][7ؕOT*%uֆc(Jd&"a!p0qkxdۗw׶1vߧ}/_߳Sػk=ݦ8vnc[MD(=lc^fB^,Y叽K~K<]n{1u}dl7LeS͗% DH` :/?-Y8>{)c}/vLL\=˭GpAM$1'Ok٩ƍ2ۧWxCy&]Ltۣ?>OtǕˋ5?Giyղ27Xܔjp .3 8=_ ~w˵?:_Ľco[mWo;ftno--mkRg͉3-q̈́}M._[׷G|y<l~359|ſPȾ`maI[{iU\ݭrNٮɪ))r7G<;?soܯv2e 5E®t9TjDi#T|9zxV|3wt;ͮZUʺzSD%{\ ť-yygNoϼXQ->]v'oۗ]E.y_K.|@H!son1\0}?RP>Am3trꊚ?j[IkrJ[%2s(5p8 Xzsq,3ڷn;~W.zFe\h4i&Ig\eYWܪ;>+S4\\U-sO1oXLw|feVZft(AYn4̥m4na0.0V}n]Q~/oGQ~/o4{uvPGQ~/o4{uvPUW&\s<I *k5N]SRgmo2FN%td?"&"wf>pL. ըFDc-uTˍi?(oنNJƃMR!Sdi>)ީQ8߰40TTfuunJ'=8߳:uNJ/:ީC0Ȟ3TETYMPA7^5TC`'#ODKZP~KE~-wAPK8"qCa*iu7S VjoHPXf3ơƗdaxAC)/N5t1YI‡ξO7qϓiMDoY4X!Əiz4?gXk)SJr"}1©ƹL[it u{kw]epꮴ3ɓ5ܞ{KPuu?%'_n_*=~|qۨhʇ=~|qۨhʇ=~|q5u.}|ْ8d`gGF@ 9m߼FCHMscPcPa{sN` ..lOf5f8p@E=_} cɀ A<TOt!PTatA'6 $҃& d+'8$q@GaS 2Ze(4&DGJ2KF1Y&LDcJDI 9tKPaho3TIt` '8J' PEnj8 mίtlqɄK"`pQύʁR[[o(NcECcEÉ̕Uc3I[O6 K b枭t&8E(U @ >(iJ;A+ y qA3EMw8&z'E *z#g(A'Gſt<;m*ĩ"JneIg@2ZÙ行M} PiBtEXtSoftwarewww.inkscape.org< IDATxwtTE-w "E XPA **(ذ("bA@ł( T{K6Yvn؅y9pwٹ}oJJ%@ !@ K %@``@ 00` @ a@  0X@ F,@ # @ K %@``@ 00(H `ۣ\c"\7f|Fٶ@ '@ lO!3(JCdnf72>Ih6m>aZ`0?; Q ց$)MlR !&0ژiMalOݟJ}22ڟP[ РBllO"L6fZmA~miW<x?ۜ! *[m̴&[0d:VdhFk=8ڦ| ev':4[AJkgؚ,Bl1j T6y*IwT6k"T*Yc~,Ob̍-O闱 SzaL+nmVZw^iu`8-?zgFDC3!a'"&1M9b6Zu>AmI`)jkݶ9kcq-ME1LMlͬqOY꼙j<>oƭx*pg7,@&ϕ 0Q ED51*B,핮o 'R(U0W")uv3l>ͺ }c7(FEy)+R O=ZkbJhC?'`:.Vw;{kbI^N̙^`U5`z4bn^:%`f:oXk1qoͧR*y\ߌ [YS+0ppr5b틉QQ*|U}M&T#t^?swvYl”Bf|f+J kҊ**ksF*U\lZ_NL'qJ%(FkB:VC,5BlugmḺ!HObw (-)KZ292/&ѫpz8'\=<>LҌ߲">|a~de%lX5qGɸ_׍7M=OE.ϟsA:rO9vv9.x'!ZuHK:ǏK™Q2r$r u:B?~MtDqL uulX $ >x]q4,,mR͏_Ke]љn vxܧI<qtjڇoVӄΛ,תbfv0`bba2UYR)#N"/+V/!B'KAۿs'(/ߦ xɃ<{ػw@eڈBNOyvRn&XWPQ]U[Qqd<1\2yY9Gg7d2 J 04"Ͽ Cn_}|x*r.rNٟD*EQWǬ'FrtF8 6n'+4H]޻($ HK<ޭyz&NnW> j  mCo qk]jEnBv}ɿYuN=gg\ʳo/' 7]cxcz?1[{G |ŅͶC=wo$ t==W=Gfq؝''P(UN{+;yl,7B e,; \=3D™%(6W?LFD"!%4gQ*a?\nU{pK~;_PG+KfWіW1=i?3mB׵`10ZfVиMU4jJ%Dtp'ϰ5<>Ss{K~~ "{o0M WУckK6"[g(]J ػu=G?Fy-,տXoX,d&U|Y2"Euu]3m }}IC(c: n]O^jOcܣ3H 4eDPxWBP*a8y_!J |Gv˩#;Yyy*"a{67 ^_3?A}ݕ_XzO~<2\}tN8iã1[M:o\:o9X 5"~T*?3;3TWUys\,{G»U߷oږr2.ߜw8R:}~Yͻd^νaim>֭pgh\fTρ?0e%:VνIQ~6IcQ*!5!Ny>o^<ԟUԐ|K+͛ dkj ![?ΞS8}3FOxg7/ U_Ρb_-5A#汓4v5O5n6Es4!t^w֭d moU$q3GpF5TP#tgRF=mܽ@"S߷2 +CH?IyYV7E?+/-n^R^G7QQV5DhQԆ]4l P(0OUTG]]$+ס$/;^/V XJ)RNϋx~" %79oJN‰[JY&ڊFLa"E~ΛQ5!gẔa5E%qNz]2`?;=ښdV) / yj3H0@9 f'E& wFzWO?SNk|VY^JNF2~P(C̴D<}UתTnxoTӹw o~QXXZT;Sgc/}ƙc9q`3{[CzyK㇅<":vϜR\9_n^Gyi"v.Cl5Mrj`î~\Cb[]U ?*c2ZؿsU+6]J4*/-oeYH2it?oDé,? ^>}[ϐTWvyƙ!LC"qtv0~EsE,j$J%tO}o;ppU>2=>svNEQA.n^(u=m$h|]l[o+>bY#Ka~e&vETRYYB *GCQ~6 ~>ƀ[3DaћPQQt4Dhfj s,&&e7w{%L)\ ).f)MgZJ t냸i}"/|\nZã/}?5-| ppv_2Xuu5f»%3-\z8Ȟ1-"T*B: 7 @5Q?m\TO˒ySZ\bԋx*ʊc CLF7l6Ȯyu{g8Ļ#;P*uϾ:]L1SHeUpD|'gŠ]@ey)g͛,W Øz> el]ڻjؘ;(Ձ>?Dnf [~V]=>1^@F?FA^kGZbi xﰽlYsTkgmLLj%J=k>5Lu ^ m>#VjIQUE*ө&ӕ "S]x?APDO qH8}7O"{`c>8?oQs\l'#<BұHfZS]CT*1'4 ob>@= (?늃|J%8w|NwꅗhC˾ I:w<|gV6Z| s k|媧S}1j<}gw%Ey>t,m SAZۯԟLU[+q!t^?̶Tf}뮞ii>W}PD01ng!c}wsz L{Y$S4AuwpLnI>7ӥǛCyY~DEY'\Uuau2^)E1̰jU1Xj#V?6ϼΞooCO2FF{3#sSWB6Bt  g9v V!anbkj 7Ykr  !&[maf:nK6BlC~՝1}J̅6Y|  ;{kb[0g5VӄΛ,BƬ [E6Ty0[M3cz")[1}Xl>%L6BZyBؚ4TkbJb S10-&ǕQ_o-~[6+ŘZ`w$%|_N߻ވ&J@/ۭ<~IB} mq<\p!=Iㅫ] @,6fdtZԺFk' %֟j+WuP܀@p"^`.6P`b.).fwmx L) n%)bVT~P 0; ZYj/0!B*+n@@ 퀘.@`Y2\,bXC Qm&@`p L1D(\%Dkx}nH"SCI]O`j@ \\7Ca 8Y:Ź^[p*EUwVnGJY*)0)YlT ʶ֮ %)+e __ >ckքpdˎҲ /II] 4zW{q!)+Ç;^4I|SbqD'O7A\zj@HgFdGAxw3h:.|P]fq Uө0f4k/vz {vV \[76b!:Nnk8 OAJeW- iCC;֙:O䣏i\.!0W׆ 0xb0kbojյ ppP]qSBd/z>l嬼4iխV]È9 [5?綩Η3_F~@] B@{ZډOiܵ<ƞ(SɈZ>{dHn̦@>2ya{ͫX{"ڏBr'MrGٳY0cY@cd}GFjE^=:}`{osdovB.LՕ!Q*Zome~MIV)qrV }9܅wz}Lri"nE|9go`Y; zw{}sTn-3(BK`I.+wL&8tsy}?Nilm밴Э jutQ[\(kNO˰г-uWБT VXZCrrF:GGzhIDEFD5u?_E]f[YIRTTR ZyyY&н 55JΞTG# $>fՆ:d""qd}_R\EΙ cY\3ze_=iZgj+\!%G=/0%>D΋0W(jxzcvãaO0cJsxXy"?t87ʹ 7z ٟt!ʹ 7ڃӅڪ)Zqﭜ,< &8{wP<'KgyP~**uaˁ*kqt!k(.埋vK-<άe֗u8uxDC8rr0w5,JGNdC:C7XJ-ul@݇'|F?gU*mؕjg+$<$P 3~::^|1nJ>ݓ>'::/(^]/4g鄳 {.ny q`Ѣ:wQ;qNرr<{Γ3W͋8r=T|qRuݿ"3/RYzel?[>Q^f-qo#l0%^VU+I?Rʱ4Yg-'{eKuY٧+84Ԧ;5 K;NVi09q*[? BjCJ.$?zL$=a6T֒{K25A{?慝=fg7A1TrNCn-U숢Fչ%OrUqakWueK~X;4+Eas;`a#eKI4x? v/HTc ጓy+8Wg| Oj215?j9Mζ$ z; -"ttxDV"iG1ʢn7:s[ȩmfnAhWouLui건)o|{ai/#D9fQx;ݭ<5ؒo**=u Gѩw5yYb~uL9N}άno3=%(IT:չ|h:ԒzN"ABIM1$&ԆʕU1y <*H/M)q%z߇n8{&G#qQ{ bP@}(b~eމי>~H:iUuy[yZ{uՊjj-8clqzkmQڑ0z!}>hGßrfs&HV&MrC+&dT}|,X<^K'88;ϒuBqs㏻3~cybb駐V zxٽ;s IDATo㥗4Ӧ*?fi /,3&0wnJo TO^0![K;63t+IPו$::o>ϑ#,]/k/ظ1*Ə8^ffBn><޽xD `WMfÆ0LDny$-طر,XS pdf0gV/W_rT= 7ı`A6<ʕAW\BMN6p8ɞ yP<ypP&nhX8!Y:dzSZH U?:h&s$=X9g{'[n|·ޓxNDveX9ʰ1ikZFHjYL? f:nxқ"V^tȃk"Ŀ#~ v}#5Is>9< }yT(aǸL' Jʜ;X25\sCf3`/=&{[0lnTB£;sϪs[G$Vk zԓ^x2Ntם>Sq H>W4W} g[n4)`qL" F2jP'Ư`/H r1eW\GK[Agu-ܚG1@9TB'xxw:᪕ w\+=zlLEm9^?'O㡝[q0':>C?4F̴Ns _o9?\sAӑ38+i3\$H:/ľ݌~#:i]/G2DU^O8qs}88qQۇ݀fYf]jBzG'ޠ10$H!Dˡp}oT7 ̬#3_/NQa&%Ul֑7gSi&u.2>ȟpeʦʹD~Ie@%K:p=.|i6G;\C%o_Rf{ٶɓ/]oqÖӦyЧoܹ۶Ŝ9>Zŋ ܭ[K '$Tq`$ĺuMOrv'ta޼ |S8rjv`$w-cɒ@,,$|yNT bc+8|8E:0l9$ RRxݻ9y7|IOa^:TɓݹF{i0!|z٩vRH^L"|ϣw^)v1ewWB;ٖS?/J|~Lwρve _V߯0\beVa_2M4>2f nm8ZFiHS6Oe%RVdO69ЊҬ{Rk5ϝ襡G(ͪ OA6PEnz3xJ2T~g|`n|ޗO6™쌢NT&!tGU9osA"QE@MNl7ͳSP*U!E,T_O`GN^'p$t9vS.eat&cf;0kn]DVl9+F,G _tv̿݅eD UY.> 8Te[[ +F&?սanU襡Ø3#|,ydSc "Z1Ё0ǎ&]1ml1|lT*E TYYyYL 9X5DNc$rvjƌWɩV}|pr. ~Ôi|:"-wc їQ$*I1;xLu_#8(.L}G}Eubft~QcYV٩>򢺬Wc rvї=h;F?w=3o_X΢3 ֧%>=?dr41& B%ҩ3qE*ڋNNQ}|Vګk|{`]a<)Y2ggưXcRsHeͷ*grUYѶ-gt- V87OUA6W thsphW4`*JQ=J/FJˇNV s=-\sg)Ǚ2%LB~vlT6W=Zή]#K YƊy+B U5f {,]6W͇ sh/BoŸZ[G|7Be٬y\Tqj9| \O9JX>}3BƗ,Ai=S$tu:,WugIꡮJrTg&S29g*OĹCfTE5 xAeJv } ىK=[AȰ\)ϭ!u*2Yr*FQ*$jyOz6Hʮ|r3'TRw6(qaKV2;^ݠQzi~ũxFRpAxYN > {yDP]VGHgGj][79y[]%(JB9spI&^t$*u<uJ|-reAKU2g,l\ZDQSym^ksk8Lܬz#եu[9p$\ CP[ eO ]q0k$mEDOR#R5U2tNFZ[wɆ`Az*JvŴA!\,=rR˒5_{iv|culgyt-C}ĭV3 _MS:>զ՛˪C=mQQ~:euEdBI<>[^C*rwlT=`55,A+/o_;:w!5҄j繹DGmˡC oMMԶR^hpEdi)qkkW|?3fx[Ν̙ΝtlA-.R"QKs3.oڦ7-o*MS(POy: h{nz39.j%ڦNƨ/2 uJ.*%m_ toPZ{E歱6H(F OT4_Kj9.߁rnr6aeA^s@n-^ƙUK *G4F";/PEw/.!֜!nsg{$^wFwZIea7SꥡzeKs+I<>7: rcJjrioOvgPLtj-lEy_az^%3CV 6GmB\vnO9{t)4٭[=EժH$2Jk4j~H\ns9-^v6ի5>X:FҲUks$GMbfɓ@NGG^Q*qfWW[[V戏G[d2R;vm[)3fxq`7tNc^ՈxPEϞ8;˴v;75\">!C $!A|YYY5|AJBCuG:vVIFFz_ǎi5'NGϝb{{)AAVm2X t9MN:oXz˂"A"Ꮷ.HðጤQs 8K;#|]sg?QuP+B"t2fY(]s7Y[CځRj+[g]b` (˪,Eݎ]1 P* LMY5 R`$CaNX;˩)W7R-X}6~M\9l};K{)=Ԛk놇ki0oc3fxi0mJ6n,uj7LI׮6*ٲaSso_;c**אTĉnm@{q2v*RZ&tbcԆ5ދ=#˳q/CC;c,gǪy6R jӅT.%Z Wqjm?Gܯyغq 7f2 t%?ߦ$X=3J=ˬ59gQGZH`ס&KEEA-)KLP'RvP[,rB9q Jҫ*#(QcEۑwҖE5c*4g v왞WxƒHkׅ Qf*-M]O<{e9RάY>,XM790f3۶EdĉZs?gO)3gz~ ^,[HYwUϧRSy  '=0yh_^ʎ̈gV,5w$2Қ;#7΅[nq<ٝ3g*ՋZnDiV c\K?.͕9!c^DU[,U͖Oco,/ڞZURW0s E aK1"Rq+FE"QM= Vjpf}>~r$aSMt0kAPi/f͈R퟇`,gׇ_fI8X2jaHi,Τ:Tf!!?,e_G?Kn$Hmr .˂T],&d=&6E@GzO|Kڦ=K7A5j7atFa[7p,/u͙Y{bM8[0Zܔ$$HqIU(܎ݷn=J.ԗ^9^6>z~nszAy `ھG6pmU#>Ok/VMrW hĪU͇6Gu'=쳆t?0o/%%uOO>5 6l(/:`i)!3EyᅖMTA߾q,[ mZԩ)I>Ȓ%XtƍK )+C:۪VcWυ#+& s|?Sz^(px9&$il/J%g ^=N`ƪFNO8*ŋ; @YݻK6-Ec}e>k( _Ɣ)ɬZ6wniiռkք͚>UD+^S[TscGF-RmDraFbom= 0'O_?wv᡿#)J$h{ꪕ{$mÃ:P*aׇ ϝɅEyX۞c+ ꄣ~@7~c!/\MR#lp$?q[ȏ[KaK /:֐g _FJV)l[~gg 1ɮ[رHHTsytKJJWrrj;++ V?_qbEzz\.!(TBBO\ w*T[de\EiH,-Ue58vvR-9wJk{@P}创riGwm&4r+>=8MV?Yl\PH,eOO;jd,T2x?I_r+)1u\CIغYhDW,Io­$d/d˜W(ɨ芆 ?kNwNl\䞫)87ڲtJ:B\C|{/ v5eG;]񈲥-.`X6/U퀥/R]Á/4V*%q[„}P*a ^]m<΍<.|؃ҬtaCdCeaƄݢu &hIۋH٣mVRB;vUT_QOb[I=B%Vͷa9|Yf}q<;C>Yiib #D³m9wkt D%Kp%ӯ~ČܩL'X-=W[-vPz|J 0 ^4 ]DhFF"@ h/0g@pM"]`j^!B "@ 00OK3!@` Lk`g @ "/6SEu AJSCy=>ʗ!VQ1}NAr]È߻Ra'vsծ׻˔vzy6_,]ѷ fz5@z[C`MnHv bdn1aʼֆ=ǘCManM(`J[{cAL[UCD ͣc 9Gk劈a5:TO>*cfMcMp0!L&=G<'51ao\!58f+.f2S}9&,f0!t^;[cb}Ys9`A5/U߼Z) w&!c0[qdZSx0>(L~s5ym@YLnbZrjcJk'L Smu桷P*ڿ9ژ>_Bl Oj=1[M:o R=I4{L$H?̶F&H3 t^?h>%0|liu~2X>g^ex!V3 V?̶6Ykc^iˉ"G)͐fbE jcJbk'l5M輱 l`%0x= )9""a'"&`hc6GW ,"6mhUO,f !ژiM!a'L"t^/qFߌfp0 2(8Bl1j T60Նl~2uبrۭP@ SVdZ?5_9gfۇ-= @ jW@ ZC,@ # @ K %@``@ 00` @ a@  0X@ F,@ # @ #)tF/`o2agĊm2t`'?,h?|(@Q hg2'VdZ?̶LB`0?Ն@n} 5YLm2Sf|B͉X7# , 5[0bkSVd:Wjw=kk&}uPy^m-F5*f|*5bͱMZψ߻ٶ t^ȁ7TFM$;+wf:A ED01j "f|BMV_5pC@NDJޢ盕`پ""ژ O輞Y+& jZ[q 6fZmAD0lOD0Lژiۋ3'm[Q2if ؚ,Bl1j T6yEhIz n= CFZ^K#OѾ\!ܚ _یWh;s8ԤsHyGUtqlM=B *UPDJQ>T HTD@ R^U:!޳de ¼ϓI6sgΝ{sf > X.LCG]TWumbhXuK`Ts|zP i,¸hsxOTUܽ~CDujr ss+,nVfI86Hn\aBb#qvE$E!HwrCnlRaٜL1 Tz\FOG0dÏ¥'iu CǪ!MV1X h7>d0ꍏYn{*^<!1eDg"~#n۝!62㭹?bim@zjP1{]<ş-'3=S9_zͩT,l'M[LrB ~IzJ"& =71oHm%^`W[?hۈo7HNL5-ǙHD"ӏ&ͥtwϬr He2yW^S?59N?)CnlBHǘ<;]<5RcY&t'h)&Ng7/Mٳ'Ǔq3 +[|FQίqK8:n J=nkk 1S_D^48(`e!@,<{^LLX(?V-b7[ ]j͹y,Nn^$V+AD#dF$0D]<|#Ċ4u&>ӿiu?`,xw.&оGf-­kxfgޠoŚ7~HnLQaށ,̂4^ߘX,Ԃh 9R<-W.bd$_c /oHn훗x}h+r0_cb"&1/+1 BD"ˡx]=Vv^? t~7J*Qz,kxcu3cA(,xH$w-9[֏e[Ym yvN k{pj_yrRA#},X}3W.;%d`=GMEFnv&RgǾO{n3-%w?*$D lkat?ڴ ~F\Mh5ʯ?G[Wr!>ZWX"?ƭhں+ (Up|Vr02H9ƺtm{`ljV{rzd'}Ǫ}OT݇Fw!BuhXjqL KX]Q͇&r> gOԭZ!]Ƨ{0⵹wp;Mߡك^m{g+X6Ew)[ ZT*։~_gWQT>F|z$=]GpFNN4o+P)޼mzzl7^GЬCǾ hRwo_cz<=F-:ӦS;wVӟ?h)v{ރjEE69mMקxs*MLuƧ+w5ck>H ֦7R%꣝wacA$al˯++S\{IQ@Yf(ܤ>j®9:"_ ?/C܉|"6ڧqWv =Wsߋ}CϪ;z!Ee[kCa5(KdeG^2Rb0/';;;zkr$J~Jk4;yW8e?qv53ղ-Ґ\b-ԫ=}Tw's c2A8ТcܒL흙測T-5+%*(..ƻasf,݁RYL#s}lCǾOjR,a_&r. 2O/߇Ԗ4a-׋!N.5,uhlS=ΗkܚkN ;Œ1]r:^MXX3Gқ 1UhwtkAw?}ɌhҪGEwi߻ILH>x4*2~;ڠ+ۺ@[kcj r5b|~o]ŵL؄C8ydRLSW@0=;rc~#‚P]g&X ~߾ _yFR^R_ZqQX/jmh; |iGt%LH9W? 2SvCzrϼ##fƦ >#Z|1+S#LMnܘ'Of܌]]oցkBis(vNqeY+3+ώ˸˱U猕]3] 9{0PHNBMrppikT*0QX0{:}̈\9{cW;iJA!]x_7iRY̵q̈^/#32<{^C ֦ ;_oW6]ߣѨ\N r&y,ށ-Je1qWutCau3) Ghx~ҧsWdFƈ‚|^ޒq2 򉏾__/Sn_k{oCvf*.^V}EF^{'$?rHIW/f֙Ljb &f84Aca`uHm4ЅNiS& a]k! p * 2+pn\n([Fu# w^rr_ܰvpgyLOu*&f8{6]YV[k5ؼz R_.qFQͽ^*GUcӽ^W<41M3jz'Bm\C"6u1~͐(p+hEHdrw?wnpH"pg+Djm Zo.z}rV9Xn rCa_-u`ǣQ1n:M=2Xے[>@x`|-!C S@B8X? ָQ]oZ!Gj0X;0UR zbVz'9kΗ0W]dR zb6@>"4ĉ ~c[~յSU.D~ ;'k  c0!~c[~յSU.~=а`YjT`aƶ".a6Mzjk4DXsq>ifOO+a>K r4$OG 1X#@ X ?3rd[Y[8{1OУ>ȍ9GDDA)O/0|˗{1du#Vڭ+8q3fG֎>ߩm-tTȌ-$z(Mڣ,R98*8KEy5c6㏴seuJ?[K <܁)2h> i̊`r㷉ɉfյ儧_ЩHbļ ;/ngyߗhf3i dh۵$2_^kWId7qVv kB2Z.bo.nf^Q۵|Y6DB9-+d; IDAT2o4!v̾\͸ՖԄh;{.zN&)c,5 'R 匋LNN~{Rn/8/%A;ֺiƜf,Yu)iɨQJ6̎Y\>=y܉-JxhM3\iȔ}2ye"" ps#7ٰ!>﨓թ⃇1XXȐJo8Q}KXЃ_wB"bdqZ:;u#)?Q[*-'f'/L`V\]Fcf8+\ H;WVޱgSNbFo'yo LLNr~iAdeq(~?>YKCrv-q0qm ,'mK{Nd[K/׾<;7WR,$6'.YZo".Wjcl/ݶ3)x^jL?5e@*2!p2>~u@VQ&9E9r uιMw^ imv4"&GmsL|-9JLN4x?ϸI=3/@vƌ`jCRQ2~cLqw?]V%{. bUׄ>}h">>ps;eb7M(G[[6ab"W+X[ѠYmKgGF`H{&Mrpw?Mmڄo_&ݺYr\gƌwәrە$]Cf ƎUVVa{mٸ]W/‘xak͆9UgA+PpifN)n?LSgGK:Lve~_拍 O[_z}܀;_<;Xԗ>]p9]cb%Ø> 1&8L7dW^kl2&%/ז џ1bj]:e'H4Hmw.rn<\׆-xr [#,ɤ-n`)g/jJAdԌxhS(ܯ qhH!Bo/՝6FߔgKafft|ӭ~?M][q+=˞I= @e oxPFyF|#&t3;[h__Os/z Ӕ8= Õar[CXɤFSSΞ}vtنC*OeBnG><3'Sgw¼3x']wbNXʭxͱ3DL?5M ?48y-oPmݎ& {GS'wsshٓn;[3;],Zm}v9=',v}S>Wp>q8M)xhVct3fcY^dItC9²(Xt\$2_qac2^h9ډ|/$_Ajvw }N}s,p%3.qkmA9>?E|ڌsYy3`iitgu86Rg7RRqS#*z ,VX ߢ55}ƞ\1O~%n)xwO#E'Yw<Ս?̚D6pr+(Qm3semϲa,\nt~۝+) 7-ϰxZhc'}mw<ǚ~|rKWc{~wW+vMPw}xب/?oݳlLNEY8*f>)P*f͍[jLm2l柛CvڣW*s0)hNzTNTUrk+z+R7*՞1;0o#q+&\^BRu)fȥrOļ/ s|we)J'vWmyYGDnd3 y&7VjA s 0C\sc%2!ޥ̓NVsAUBTZ V%,x?:kkV%i}zu2/hτ ߯swx/7W޽ b\.шB6i |BB̪VemDFФI3$Ҳݻ4k6)LLf[Wuo`iYnٲsskWұY3׮kDP EE*~=wq!0Д3gr 4eÆT=6nLeʔRch;3t`Vbա&ƝiAv1g'[7~w&;J]Qki<Ȯ܇aE[   K5-B/?]oGpW텍-+M?&3VmҊ8Aa(+#e9̝dM7e[vco:2ΩdRgsc_:=K?a%ndu -/.0ڌw#K+ Iڭ0s̛S+5|ŵ9.rv{Kkf:Lv(_ɖWS]:B'xG8[dD2‘K_)z#CGΈ]HM0G4|–seDj:W״[ߣ$(d ̌5!{q1]XU˝kce^ᵠ7uO+H%ϋ# nd^ڿ+Nh2.]ReS.Q5H%RxLRJIC ۊʹ}-뛔nXK7Ngg^;6^s#~ XSwMBR+9ǒܹy8N[o`l,߄pU^l{tbFaJw#I?ށ1chSS)*\:[2%P(*X!!j1wv( GQOyyJ$|}՞}תܺL2j >ґHŘ`nq786Q|ɊC39NM0ǵ :FUw2]Dۉ.RCyB?8*(ǔ3?'鈃/~ He u=w[Vdv*TOVexY ARp"=:wSV眛1u( 0jۛrW,PV[ҷ@F40R~" #͊VԞS&|mG yx[hci+8o˜Xr52|^\jRʭ+τn,9{6Mm܌}xeӞRRrRATTar{4U!+K\.LJv%%K 'Ndᇱ>ʼn4m 4Q+!۵$X;;•u^6 K捻OYYJ-kf-2JVu.XʕoX{UFth~)5@a?'2f Y6^&_G@_u 3`*y:N'mr#;sqS+ENbŅ)@Ϲ h?+*%ğ֑L\BɊ-*ίY{RBȋYfjrJo`ɮث=-ז웩^l"WH17?#DnobfLckʹ_h2Ğ+Y=S6 <<$2Y3ys3._ڂWWWB 6U{z) I8q"7+^}ՑKJ:3) P׮ Qp.@귘=ǻ ۴ww9]^|L17%a+WHJ*""@ϲk=V2^~فgs njcaNR!„ +/D#w6ߔ}osy[ qgrPh͆9 fdl~{ 24bO =E'Iޘ,@<6݃;8E g^D^e'|[C B)p;CZD>mHG&p}o: sȌ- *$`a",YnUЕ 'azk@tyǝGU8}t5Z`$(WI~f11WO~&|5e +wPY~<8sjw+}23:9uea鵻9E Ml8T~s>6w+T#dclW~R>J?f\F\{,_K>jq{RۺNS2&6p4ubK*u2?._@ lXwŃ`*j'Wa&I5ʁl%[/tn*` <=y)8i.Xк&,ӧk' rVĺu)T{.djKHI16 KPQ2MS'ߛ %Ymk[&ް!\%siydLB^~xuk:ż V0p JZL+ }l`ھ=-44Tk/F~=#Gx[v^r,^A./x .J:ٝ S?615jQ)+gي1˓a%w[_$l_sc{TJ\ݙBiͽr%^E&]7 )[Roߥm)>^`WHuߍt"i[Ro4/01<&fXh|(_Iy=ʣ$y`nr1f& 76}$ ķAx);?Slu=M~lP)!.,}pwysZ!T''L%ha?1{?gOӧkLJU*5ʞ \ٲŏ3\Y(Zĉl2m8z4ٳغ՟~|fΌ!)τ]4ɉ>ĉF(ƦT$xVbކZ­?M:ՈwNjDÆ rCyҗBy>r}8z4b^{M~Y<2:ՙݻw^ߐW4躦ΩS8v, |G/fv CZ$EӐ+3Ǎ2~#Oxq7MC,):ϥfn-͑^OVtT/՘&C0ad*ūZ^~RYD KgTؒX;R!ۆ4gL,eč}5"i;󣑛Iet}sN~O~57-Wy5F кw%kyt|U#$h#N͸G2=dr aR%$Wt$M0ty:;/ ugE3,mŹ"$3Ef5r~8p!puu)tvWkSoq:%ۯߧ.>Nkv>\ۻp3sg퍚ce*B}Rslp3MmDK |#3ƞBf͊r*:v'3qA=aB$˗k9իʕ|$/7޸Mhh6/ |yshQ?yۊ3]t)K??$س'ٳcFr`&#K$pt.S83lJdSR9x0rHBCRlY"/.=EE*mKcWXE^W4ȆIr%ŋ_&$?_ŀי8ё~ӊ3=;իUfٳ4n|9shޜl}scY8vs͍FUJu&=Dbx.6&d=bZs@A M98.cYU{gFۚ>y]spkeD~f51xu݂< pIDAT8ɳG28gKL,eZ+&ylwebNfc$>" B ҋ1q}ow9pi[ G-7޺ْ9x`ouI\黉<ɿ_~+ݦ{T+.P = ͉&ğaoOgt @'t+/,}S3:NdDڜkhawoa׼~ٙ[@rzu'?/, eյ<`1r,0#Ӭ8, ;؟.=(Ra*3X!柛C\n">|)4@2tvꎟ?DؿE+鋋 H/|CE?7 wd/+,柛éϒyrO7y=Kx{rEmHMbU18ϳR|M]yg-`kbܳ38Gk"e!R =>/3)h`?=X5J˯*cV3 /yzs a~=ۍY\ ș3)avB.?U}_jnA3' ҶTbNVupĜFZr»2 ݳ5f2|N.+Ņ*Zg{k |Yol.+N47RF…I{'[N;9t6]06rtCȣ!#t3&-"oI~ [Hʵ<|zX͊$=nW'K>aG; .x<fR[1߲8ʬ< ExޒO٪W^t"fhm%̝䴛­\T["G>Y0PsG9: U1DԊcZYFf$_%`7.FBNVY It΍q jYg` %E XUdznj'xtp4u"|`K2͡bn(*HogؽK9F5j 1"$6gXr4B&Ay n[}lM0Du u Xu"@hpyNYZ3}l.{Wz  @PC{IP_/L)&bw̃ɽ*9X ;OUdtoZ_-x@ ]dO:iL0U@ |U> @ T2@RaM>*B@ =Js U`IDPP/ A n%A}o|K"{}@ Sȃ JJJPsR !$T-@P%j+4XXĕ@PST Ľ$"yZ!BI|s>*lv$8&*.nDV⹒KKmZ?8* ƸaOUqڪZPEA{VX >.ڞ6~x vL ml 1>=# piKaRVo pՓzlX*4պ;/_Tc0`mau1;@_uLAۚ6oaluy0Q4aUJO c[յUU`]Wf1!-"YvAyr#:P xԭ(zAzjf+ m C zTRcWP, xզ=L-u`ǣ!`ƒ?9S 4 {,q@g&{ԧk/0..<"ƃ፡Tk+!@T#QnCCx0t1nC? v"6_=@O A^p[@]oZ? v8 90F@tV5Zq `@^"~ c[oZvAyrwM/Y '!.z0a'DuEy]jۇчzMTV+¾XZ#0h BTߣ' 8;FhM6PK QDZ@ xK K K K K K K K K K ?[pIENDB`passenger-5.0.30/doc/images/direct_spawning.svg000644 000765 000024 00000022410 12233035540 022120 0ustar00honglistaff000000 000000 image/svg+xml Application code Rails framework code Other memory App process 1 Application code Rails framework code Other memory App process 2 passenger-5.0.30/doc/images/glyphicons-halflings-white.png000644 000765 000024 00000021111 12233035540 024164 0ustar00honglistaff000000 000000 PNG  IHDRӳ{PLTEmmmⰰᒒttt󻻻bbbeeeggg𶶶xxx󛛛Ƽ몪֢UUU鿿rOtRNS#_ /oS?C kDOS_6>4!~a @1_'onҋM3BQjp&%!l"Xqr; A[<`am}43/0IPCM!6(*gK&YQGDP,`{VP-x)h7e1]W$1bzSܕcO]U;Zi'y"؆K 64Y*.v@c.};tN%DI !ZЏ5LH26 ɯ" -bE,,)ʏ B>mn6pmRO wm@V#?'CȑZ#qb|$:)/E%nRqChn%i̓}lm ?idd",`H"r.z~(bQU&)5X#EMR<*p[[%.Ọk7lIoJF lV!̡ăuH`&,zRk$|$lXbjߪdU?Σ$HW$U'HE3*խU\}( zhVk}guRk$%|T|ck獳"D_W+.Q)@ƽHbslTDR2Xm#a 3lYzj㒚#! 4J8(cvt]aT D ΅Q?^-_^$:\V $N|=(vZ'q6Z׆B5V!y3K㱿bv4xR]al!IoP@tVyL٪mlڿIUb|[*lke'*WddDӝ}\W_WߝrN?vޫ۲X%0uoui*JVƦb%}i5IYlNE-wςf_W3mI-mQ)S kTC7m<"܌bT|'$ҘR&>O p6tSN\ׯLm\r@3uT b7t.5.q3r0=8TiJ\6uF R32^'ŪxI F8O{%8kJMSȴdBEdWCYO:/ON/I_=xFE! =i:o~ y?''[͓[͓[͓[͓[ͭ.U>$PƦc%]\c:| ,eSZ,oXrX!R@Zv 0>?* <|N60;{ad2v+D^t[q!۞V}fۨϏYeॗ)Vyl|" fUq@Ǽ4Y-Y-!6aB:o%JIUQ|UKO`=\ :0x Pau@!KPdxhw1>$j΍vZdxSUA&[URd7øzk/rU^w:I.VǮc>q.!zSr&2)Wg R -iQ 8Pa\ОU%iݡU_=p Lu(N?0?Æ:]άtB%U|NsorNf ,P !v" Y6hL_@@bscqgv4||0lϟ$S9bʱj#~?o}}7sAPm:IV=n !{{hEࢪ8suoLT$;VscqD3 ༂3.DBB4&V' T `D6Ϸqyj8V*X%@s\jrN$|=5Ά 'mUiKi%CI:ssaƅ`*`=l)>u՘MeuSI_OL_}o&jzp{lu:O)s%Q@$<]f xO%PCbhr2PKpf5Në3^o]eJiB464^tuٲU֌:G4'22YpuG'/Py4?.SBP_>I 1t3ΓBɭɭɭɭVVVVVs]!67(g y@ 4>Q VF}^Xׇڼje26 L%YGh lC})< !EEPZWZV+@†R 5{@ouɐ4&H6ey V݀VťcqZޒrJyByFzFN$Hb*+jՏqэ ګkݿUXle1d0d^-B%} {Y%r*j5Ak5u",:~ҸY~ hSA~6 fulՇf{ȵQtATHZkƭ/_Sn u']b]|m`BāJ,O$du]Zs FL:aǙT4o~by?wpj滥A(x]†f~an֧/^dڲcՇ,!1i&xi_VK@ip̓9Vi%a; L?0J*Ū5U'x^6V[^ {eU|:0=0d۫o*Jq%[YN.sQLud[29I:WnmXlڃ6!lNlVէKUjV\J%UߊBLcKfb>a=b~R]aG%[js@/9MطݘU>yɲX@} Ftg^vO\Ӹwvpz3K5i!$P>ā'VƛL2r@UMKZ6tw맟¦bm1h||]}~0MjA(JJP68C&yr׉e}j_cJ?I0k>šW |Bޝ."TEXd 8!cw*E(J)![W"j_ТeX_XB;oO0~?:PC (.[!Wq%*leY)E<^KZT60.#A\5;Rmtkd/8)5~^0 #Ckgey)ͶԺ6ĥ<(?&uAVm0^h.txR*a':,H|ō l5z;8+e#b'#|}2w(|KcJ l6 w^Տoi3H R ̔9,YgPְ:N [5SR![)]i}`mN4Хv`|;f(FltL8÷Z#AO%Y)NU5YedJE3dZذݣHT1 ;8MjnʏӤqp 1h^<<>yt{?|'j)}YUU{@V/J1F+7䀉[OWO[ yUY!?BD%DWj>-Ai6xz)U R7 d@g\so)a4zf[W+> P> |qLG8vȣlj2Zt+VA6gT *ʆUz(m)CD `He/.:zN9pgo &NC׃އ>Wհ_Hj)Xe6F7pm-`'c.AZ=^e8F;{Rtn(z!S7o Iew3]bܗ85|iϠRJkʱZRO+8U&:]ZieR(JMޗ7Z@5a^\GzsρU*rMezT^:ɬͦX=>$ bi>U&XQoybbGk8 Ҙn).Սo ^MmdZi$soo*{4eLbLٳ""mx:`:mk[geTެ)'0*TB{!I ''''[͓[͓[͓[͓[]Zj Q.e '/yvQ71(Z&X?(_Z){tڀmZWϏ)-C jqn,̋"IvUL!h꛿skAcrN佚фVE40yX~4zʸV㳰%,)fqtpu~  *^0:ܲ33JO(ZB?K^ v]unlWi0p6[착C_5X#[wX3b廫R{NKAe Se|wxso>P\儔ԕ6;nVmfI$V͓J-J%֌0UwYЎSnum藮xz˗VƫIvnW_qLZ"_Xz 8]Ap?C543zw({7e*Ȳ`۰!AQ:KUnz]1yVGaCm0PY ٚUx6TT&hV9V ӬzÑ 1[XzZ9erqJND/gX*9oN6D` {I%Mz9—TQ7f\"j_3~xB'ܷY]*KЌ%"5"qxq~ƕ=jS>jV&~]2xzF1X_yD<#NRB}K/iy !V^˿eJ}/FkA7 S+.(ecJ:zWZ몖wQ~ä́p6,e5,+,tv%O^OO}ן -O7>ekC6wa_C |9*WA)UJg8=:mjUvqysܒLglC6+[FSWg9wV31A ND<$5e(s[ ۨbaF.]KIENDB`passenger-5.0.30/doc/images/glyphicons-halflings.png000644 000765 000024 00000033002 12233035540 023050 0ustar00honglistaff000000 000000 PNG  IHDRtEXtSoftwareAdobe ImageReadyqe<iTXtXML:com.adobe.xmp glyphicons_small_dark 5k1IDATx}ol\EW^zD$|_w'CwG;4<1uij6C" Eʼ0 "=, f3o 0`snuݾT;1Kmw֭{~Uuʘ1cKά:kb10Q!8dM` \5e]OYKA*9c8 ?'M7k1c]?bѽc}x$/$Y3sOqY+*bQ8C A,VuXx XMYRژ5@4 E얌ԏ jt(JM>(=BJC71Q7% WY}dlP"oHTV-/NPάVo cqhOg6pwêkǺ _)M+I :Hz̬/ɞ{9OdnV|_xH0kt|Nn3#3u+/Ua9%XqNzg;y9y9O%~uɗ*=Ipcy}Y(o(u±$^j e\iX;X-rѲ&>>eìas??1aۤ^=765^8R6Ҩ*4|QN$*>skUqSE -?DGƯ M#۠ ֙ʟU[3HVUwd#)~@@V -s'A]Λdn 37UuSCz{v(8l-;ޙṹ[;V~ )t>7QeS)jǹOhc YCb7: X 'Y 9 Y1y=G 6`=GVB[\yQ=uo㺷c?U|gdD&tÓCv߈z&AIg)ʨuGU8G*=Wb6yhDM$?.&rvQCXԙvdrQ]olwc*-73pyNV+xU^4 YiYqD,48c~T}֙{~2dnsզS ;]ɨDuF_1glGz`cKZ>߷h{>IoURΕN'0i2qˆN԰cA$+-y6f?%xYUiJU?m}gsVYUt&qyYT u,U. Uc,w'alj"MW$)҈ʳ#i5v:}D6w}O 8sDJ,#`F^gw?ӑtxC~TWyf-̶B?WԪ-_5Tlfc%Z}h ã-oM۴t<-|sޫBD?<0ځ_9/uspƔM&xӤ=_h9`e8ki_^ gaYݫ-(x j謮1;;_W$t]=⣿.^"_^j}PV saF-+Xў/meUµq;>E4d,sdɜX| bNg(G ԉjS,}ؖ1{奪n}t ݽ.t\}p5v*ʵ^4БoR7F+V?Ooz:tzaxFp6&[R)dndKߕ@jTQg$*茲\ЃDAJRO*OX| n @;3g*NN}1υxǹ|ic˨TMF3X ֐GX#;UF;v_Ɉ[m֯wK!lT]"eߨzx-r;tݱ|Tuׇx7 *<־PJڞT>9TIx{Urp'ZU,ۜ[ iNU>=_5~p:|M>7!9ѕލGUw(sOZw/< 늝>TGQQRΪJg5t@ꁜDg蚢2S#E>;|iϛd?'?%`}Oƒ$x9SMoTMxOq==NP@ٙM0Qz5D1 uٓ YgKUe7|I9uVyZtV<`VIrB!.ySX0'>Q].\,:#?,۹>_SC#4XCU5&*SMM4SUucǨԻo#C7}.dÛtкxGgU+5tD}p>Nŝ+֝+Z9w"ͪ.x@ꍪPժz\FvivV+#*pa[Vkm:g0ܗyPUdMU]U̟ÕzaTZ>wXF+!I\h>NŝmWS>H-SݞWKfrH-Wˌ9710Y=_C!B|뱟ZKyY#ڶ8!S~ZC"s!6<E wA oo;T})C[˕_x3sI{QuDO<"6uSdFP\B__UU>X#|\nSyul^WӅڐoѹ%NyS>u%CBepʘ1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f\[im ue4hs+k)Ti&ȥȨԻdʲ%gRap:Yh۪.Pz*VrYgtH= _SQpm3"KXI|5鯳vGSշJ,g cK Od?CW.īejIFԧ*JDv5 kulMXAtI>WLA %7`)[(h6lqӻ{ݳXM _\6ea'uy]1*t*k]SS5"*xYTm{ҍ r1LtK)鞙l b*Ur*p14/#F}7~l ^RZgE"9"TrVH4t]3uG~pUunZƓ |JUlU[q,&JkXJh+'5*FBUޔZ.cKWN 7^FR֝ XP;/}(uKm#̅XȰU!ԂBRUWB7@ NomZI>>\{[|cï"QqUgYH~xV3Nx=3:V@PV6c#E*(jR&ٽSUWB_B?\$S{ުV:${UA$j@o{oے׽}V婪Kw]؊_7v]&ٽqP`-MԤ( @"$pSWeg pÓ,iJ_z{ub0`4wkWbpt/^YQgVʈWU'>(;=q1J!Bg]j(к\3tQw{-㏷b W[!yM>QD-N~BlՏ..5 ] 't{RZ)`\OOvVM+^*pq.uI~psS_W#G!j1^FToNnNlh!6 b$1(01{ zc칹䯖=< &9C;I HnAJ 838^U|T֨~!?޽ ')B[ S"i|]PZ 6N??P[~NUe7C7댔">BLZԥT->v)S,ǃl!UQ(PGmAlq*UR鶠.]rhJcxpH)g>Z}JQ5.%D`L=?1f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f ,zLga̘ KqLQdl 9CrnF*Z-)+3Vm꺜n*Cפ8D&\}MP$L>i(Hk:^cgJۅ?JʟN ש&*+epK#Bx$vj4Miuz eB|CskDXnؖSO濶)f[b,`)gv9E1NGR,^x nOJ*!Q֟a@zCvvd=QV6NX`Fqʈ!k'Oo09ޙ;jkZR})/ y,$TD}VI=|1J!Sc o<]-$:"D55VgWUUvpE֜{=JEÜ 7pqFjhI~6zrZPw`,*S3⽡{;'uo۞NUK9Z=|1?S}17NU 6/>:gz/t~EÓN3Cҥ;,7?geus$v;U>{OjY&QFBU{)T)N`)g GU}}@-S}3INTҰ8f:/kx݄x JgcK&UlmAf*aRUϪQC 5>EB$Кs^^jZo5EK;K/WuVEƭTINXSD'Qafx=#?ƪlq{|JjFp9On̆ŝY? Ɵ D߶,jߓ_%rA6Ax1vL ᮴ O[CV5kpoUg<.aPtx#=$q:簚S Jo{`?[~nR3~ [z셥%Pk~%V/nMkJ]UGIM?li[]eM΃6" aF0f)STzacPNZ;vu+.x?Yժ^XV2GԋoLR47BV5E4@M1cnFS]91cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌtuzoxҬ}(5鴦nBҺWbRiN7h:78ˈ3ԧReK ayZꂹ;zWbyɜ] "'phVJ4<_ԆWd^Ԭsq6vŜZ=f;3;NUyFe4aMDG7J( j훀ģ_ F`'Koa  = Nw; 3F\F\`K/_ADNןvh'I v*0o p#:B kN? 7GN k(M亮nZ{Prx'vNSa@@< :m/X>?:U:x!wfq5{8>N$`[{k) P.Uf_b6y庬1(ʙc#t. TpDl!5 \P߱԰睧r2:(cAZ=/N[%G_~;(+GUV{]P5 Fiuz8+,j< [$X?R[quoꢌRqj]biI⌐shR1=k`?|FBI">GTx2<!W礒rc\Jm.qDQxÓ4zj(e^KMCt~(u]M&- (p~r- ;زƓ*a  Pej5 {wGVV[{ZJ IQkƫQ-./\nv(f.7>=3ǶӲBT% >!5uD`O:ݿYGZ%ULbJ<Rg>g}=Lԃ[1Qqd i}?nǶ;RUM qrr-ۙ`bxcK T[,n)YSP,$NbuCOb8wVP-+J;UE%fՃӘLBڟ/~I\B]}VUvAP r<ŒA\xxjCUYSO;rɠ6ԙюiT{!eqK QPK ,Yd:Qx́ssQb[JB>zwr]bN7R&`>V! >t' <a*;^?F)O\HBTG383/_,qJoO})lݷ~(tzKËVʯ;;IF⢣{l5>x;Esߡ ]lQff%2sKz2I7K|2a=jמR.:T^)H{ܥUJ@Xihoi{d[|uHT:9m嚢Ō]w-tV tm)YbUn3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3fX 1`X!-4teA%v<?|ㇶ&i_qz ] eR_ |& c*kր4f,J U_h\1AeL-L\^~Phr*tqa0fT:MUp>yduUYCA>k).,+ɦ@bjE)W F+As$e6\Q9oI}4xjH}2o^՟|ϧp+P;昭?^$^)tn9VOdDTʘY #jǍvR!*]|6!~.MPxG5Ø9G6QIWGjRT|^|Y߅&g|JuKw-wS=3,7< {o-xL(Ex? OY`# ߑ ̗kTŐHgEp#Fiq3O2_NƟtOrR? ./1IE;WN˜5嘫b+Ontutu6RNѾ|:Qb[A򖘫T @܀e-8hx?tQCSC>Ho=5} n #}oU(x U< $kG/Ut^""9䮉Fv+OF8kTm:ꍇm*v0&գjep[DYq3Cӹm!HXm-Q(6]^#'vvͪ\Qؤ[CT>I1 Oʧ+"!uwϴѼJ5׽W7HTѓuevʷJzNUW}L%up&:Լ m6f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘1cƌ3f̘ WYɜ$P,;AUӺJ]v낀sէ Ţ{%kwned--Yj7bɦɮ@ -/7p]b@ ];`1kٵր?*<':KU? ~;eAI:ɥ-k} !^ȯ-5١o\_]0/n xIU&XefʬdkԵ БeW$mjOl%ep~&RgĞzQNU^ OoC鰚:ϥ{f4mP#_SaD[џ nK<.d= @}N| Qs~^etkj]ܽS' vckeeM<"E:ס侹̱囫s-]^ }(#J,kܫȸw!=3B5Kԍ^j3FҬﳁrS7Q3R<'J9"]|_З%/#TcGqn"[cGQ љgƎ*iV!jT2%Тtuhc(;Mе|' RƸHge-S1 c,+j 25{zu͵cx'-i6γosw-4ΰg؞^'1 K\d@f#i0=@%5zݷut!]M|![@QDL;7U Ѿ3ՊM5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Ic , j*\$V, wr>;Mb 2>5?/О|탻FW&ZLWJ*tpęmtzEp_'?.HDٙ*.UrZV_U v^^~.uEGf@aY=DćsG.2 9×f`z{#V ྦྷs/bԲ@/up~?`Flz?-&G4׾tI 6/^ŋt| L֭]Gvv M}}=uҿ9V0^֯_իwԨQgK_~`" Q ĢEPēOOc61h >SL60]5#h @ pe4?>O?4aavp%M}Xp!^^^R__V%::چ+8a?&"8 WlL,+V` &ۋYjj*/LJ\AuB%n>K+Dߓ؁ؿ#v-w_w˧-  334TJJL~+V %%ݻcX0ܾ\~"P"7R˒C_O^fz5}ai+GwjMLL@Ѕ)))G!77Ȇ 1c #2VRq>.`C=^Gӕ(L ,Y4ٴimzm{ң&>~;ƥ"[.~=ܛ^"JK1!$<ܷO Zin# —]Zit-wΊ}7X8,_ zLßOOũ+ G@WC.;OAǥ#U|WqHLU:wD9o5v@1JJJӧ$&&r…6/,[|P"} C1t PqͯGߑA||^9emIL?Wyl0ƽޟɫlȊsgr>G6`<.&Jp?מC0)\BWSq=?"V.>LE}u={K1/ۆ1= ݂|9Oo;G0dd5htO13GKᤄ ],)++#==,مŹ!rLnV[I?IRG7g̈́/뭱 y,>2! F 2O#:Sэ:SK@,Ee&*՗p6Wpr$Y'6 ϷJoQvI>8Ÿ1[`׿nw稵@X`F֭_wCmnـdzCcu/oi}G3axdm@]u25{~•ҫ6H~_MԨ:^ + wxnST:Où/+L.;md;=b$@b/^Gqf3ȴ-k؊$*s5TӶ2,:e}w-Ø~?\AQ9ә:! C ϔHDL#r9wSAAk:9ױ ƼK2r^s׳׾fʵvKYANlfذaddd TnYeQA}6S[Ye T]8X,(k N,.@oRs_Y8}7 $187gLB!K#|,A~%WK)=bE.$Ɓ#x%~D4ST]5SDK9\6BX`A'>0!!}IFjNmWr]ÐxIEkp Ur&* ۓ{QWe!} o!Bݧ]ș}eN2:=[y82#ۓ{9۵6OaǂLhY7{}?*4f 1#@ 6lXґY$f3RW_lge߾}>0aϕRۈ QBeqpmt 1`0Z i\n!t:YJz u~C7Z,S}potR3':wrR3ZkHڊ_8}xx탷7^^^:N, ,X-FoO+Dye雬waW(ۯ!@tFkt>ߞ~M~wulWui;ʿ:5@@ЩP6<6ע֞)@@I$##ؘ%^t*g(Vmb!`A' +;TF#{oGYv9@Ed! ,%%%LLZp! q]:0fxgmBIg&ܒ92ϟᡚ9ƒ`_„ %Kʿ曞N8G L2p! '-- ֭[=]CtNFp :&I&˽&9!UE LL3τPK&t0MFYY+V`KGWYg=0Ivv6 `)E 4&ImcӦMNHbرYF,bAmٳ 0C̛72;]!8vL>}:y7Q5VϷbbRn`!sGBFNkٿ?{/ |uZHbl6РX,HڵkyW=]UAc<z ^^^  zNV۲  .6¢B:pQ bq0*!I l޼]v9 # :}>}:^^^::j LRSS?~&YZX,V+fz,fEfXeYm4Q>=wI>K}W/AAoPLӡ[l|<:Sd41$DLL EEEފٻ S36\梠c/^πVE7q!jZՅROU 8H0L&LV84 k׮mUY%))K_hQMܰ%!NVuRdI,]tQCYY:}cKѴJD?0ARr6 [Հ޽[!il6SXTȪ&11䓑AlLliQRެu:$5}H外H{V, SQ!`41  `~wlz4L8s.d2Qr5k }II gϞ%""Yf~A>a}šPzbդXaMבP @wl>Z-$*:!`] 4:uK+]W]ZV EQXTȠXz5Æ cAxyy`֙q̙̜9Su=䓒B`` ƑPw}Gff& 8l¢BJJJ6l{dllBI\l"008 Ի9WY#/?^,R1bD/Q3ްޠW3?,zz -ϤHFCbb"YYY3h F#֯#xfϞMvv6UUU԰{nfΜɆ 3f aaajJ1ć'ͬY]ؘX&OLvv6_5w}j!mذ3fqFfΜIrr2/oׯWRukĿ]gKo%L,Jju n=cJ/ey{ QvqAm6f̘л0a2TRR_|Ez79;r?a< )F@ɓ'o_dĈf(//P]֭`ĈL4rf36lh4rHJLRfsTϱ34Za$5idYVdα1A9eBmysJ3۶m ## ߶nݪ Xzz59k,} ~zU>fϻޤn?٬Z]e ؅b5'` BȺ6b,TI/&0=ذaj"0(L6l^T1jzRBbcb _~]4%5YW+VtkaID[%"S^zܙ#8e8u  /#())!;;d/^9{,j%''mpx'%6&VǦnR3[dR'|gZ\NG>+Qd .w?#BJ yqdega0HLL$--aÆdfʔ)7TfXXF׳`RRR{HKKY)‚ W; ~iCJJ )))믥ӢEctE:7u֋k/}W},ߍ,\v%,,SPPODVVYY,夥aaa nlĎm۶<3Y(b UcUddd[oԩSzUUU|r222 "==W^yEi2֗},}hCsk~dYݚ"^ h,X@FF۳wo7kz}VZIqiOWU!7ogǧIew0͔NlL,' Oz:nrbw_VP-h=>ۯ~^l=:N]%bB7Dvvv؆222}mm-QQQٴ01e{qtvNTWQRR:DL7_Ovv61]Nc]Ė Y9iOؼɶ)*|yld$${AOyaB:]z޽{qCwtU+յLBdT$܇R&[R,0I-Zyc͈ ߑK]]u%mX IDATġZى-A@&Nϟ|kv(JKGk(x؞ Ōbb8R n@;ali~_&? XC}:`PK n@5i;-+0JT-0I(6b;O؍(L h%4lnWB82Ye-"&L heȊ03ֹ$IMֈ0 ֗+g׿WO,g !`A 103m9ؓ0ܪ֗t "&hMϟ,L h4DDL105)&$u- Q/:{fd&$28U$q10 /(o…( na}5*&8(10FI,& js$b`@qՈC@^d! ,L ,1$1 %C&jFۘ4N"&"&36L׉mKNcކפ~̕rN1PE1fIHt *>^8!1H=g:S=l?؏?A`o?v`!g QF<6@vG{=פp78!7Qk 5 C@瞧.fH0<}f1> :]ܦ"0V֡Nav;O?6$wXcB( tu:4֑9nt8o@TܝzF 90+KݿıNexGbz =g;g;iQ NfRSxW=\MWJ*tpęmtzEp_'?.HDٙ*.UrZV_U v^^~.uEGfYz5eee̟?OWU4z%?4g\ds29/?̎-!QF=h{j0w"F- R?Ǔ6?o"& f4@X_7n$&&C6I9_kxmחD 艹c ._ߡJ?/;8W"V–`ҫR}W nlX&bŊ[Zf s=̚5x(uesQ]VԆzB"嫞Fo|oDܘpsOJټ`o|oRۅDS[^kP<}$uNŝsh䶜.S DL&1\p*y$U bرL4ABSVVOzm,QynzC9V_c*A~ M3'w'nl8~SYI-DbGwdʼn;֭>>>`@ף[d L E~ˋ׵?? 44:e!h cKuw>?`Flz?-&G4׾tIA_OFF1֗+.^~JAG@@t)+VpMڎ|rvpq +PԩS7TuuuRIDZeBFaQ9#{`3s6mթ/10Ak$B:'f+ [ff:dTgd2m6m~KLL'dC[|^q3f0w\.6+stt4 W&p~4zwj-,MLXpIqFb2HKKh4vNˊ?9ccby'6}ۄ8x6lc=Vb`6`jufRy Q$9v+<y]gKVDZ~]_מѣjɲ XV :Xt:eYܙElѢEr~A>V"##C]~;ƌ&1c Fh/?PppjEr*9uqÖhڋִ!-0EZHb5_ʕ+9}t+'<̝;ٳgNCբeYFgH7lٲEHi)֋/!DWyi},KG+v=nGF9"ccẅґ*;+N0ve#3NWb^V+VłbwW^QMLLt[eOvv6 .d…,]g}AN)bRf~lL&-Z)+}y1bkwm#u!J>JHx'>mħzң&oL  m[VJ3tZJ3UksV$w‘ugcB| ^~z*N]aO0|n?zǸp6wq| j=.x;gE2`rֹ3%Q=~9| E\+L7;\{~`>ȲD;392S^TMp5.in>0wpJf3f+W+`4IOOg֬Y-QrL&k׮%--_~3gҭ[7XI:;133\; (¥ bm90=m/`ѣTߟFt4\8\I髜}ӹLX:o.DߋQ2_e`zpp%G*8'x%^"8Ÿj ?`?zPzu8xnGSAѾ TxO(quې60{K 'j/t/tFӍ⽗ 4Ez^JLIBm!/m #~FxoN wܯwfhhh`͚5d޼y궂΋?Ce^xSO9l#"'n\gyi"H?6·?ql5cf bG!D xB)L; Wyl0zs{uA*Vx8ϑ gj(9e{tkwd&qK|) g(#\0][nA> xn' L$Kux5l2~HL B:$^$R>JL-G7L->IԓʫT_!^_/ˑd,<ߞ^S@):k"Fsb%uDbz:Ll{ c6Xl=-<ʀIamt;+Qk) ̾R6իjP vex衇ϸz- QU::0+;lD{p6II2rdY.?3{*=8}wZ':Ęb)^F+yyzuV?_/7ށO})״95ܔٻV+V $ͣz ^^^ 5ǾXG7-L]fϞ;|tQm0_r< WV{pe kN|#M16-UjYx ܷOmIEa-Y@H#Yқ:Yig 7D,I F ZZ̽ # K2.$(sswYLV#߮:EςuweU|yY i<3rAEIWpf+:߸q#ׯl6믓غ{Ѷޅ,KV[[r<lLXG\:VJ㪜F$d$nOE]ܷOo7j`729IFGw+GuCfs{r/sV\5Xp);r_E`OƾeP2WE\f3fKuLz砣&qL&^x]gҤIjkuz{V{9k,đ"`?ȅo3p+|=B 6aW#3a"RZ%`qVAgl63rH ?[]3+ejuuN܅( " Q}'l # tMen+~dV\)fh5O+%K8#{JHHpxeegm۶6GU \O!b`Nx#]y.e¢B2335kG,|=:dkn!8N(z>&ī5f Nvv6?ŋc0ّCjj*%Jn̤$Z\'8 v ho !00}2bj]m'E,NK#Vի2e ~mcʔ)|ȲΔ)SXϟgʔ)?]_KOQRRB>}Ԙ׷~b2dffҧO222e{vZMVa6),*dÆ JkfΜɁ2eu O?bxN3ͼ)(]vqvĉ7SNsN裏sN*++aΝԸ]Tk6;#FVX3(cǎeǎ<\v>nر,]**̙رcÜ9s(((P}!fر}QJJJ2 Xp!wuWc744zj..\谍]l&))4F#ϟiR\!!!LL7lQb|87n$11wy_nΞdGU… 7eaL&,YB>}HMMU].ѨZ= $!!f70Sr"f_My}#cƌf,YBaQjMʖ#/?>a߿_|'Frv\|OLLZ=SO@MsX}:/AAA,[*|h4mP^Q3<3 B ޘ%%%޽7xC.1wf]qO<jҗԩS}0`5[?x d2e6yIKKcڵ<Ͳe_` +;I]^oLZZoZYUNb$ Fɤjh5j_1!b]d YU/4h5gF6/ֹ/өb`o6`k/_.5k.5@tyE9IIv(ŋuƌW;XB999Wٳl2>sMu?tj͛7#eIZЙfsH(**jV Yd qq3sLC444ox9 ))M@xx8qqj_t:V ' >>aÆh7 aaa$$$J\lj1 ̟? HJJbbDFzjغu+IIIsEiuZCä 2dkج͂K[KyUKUEJ2Nn:f6k.̙^h7Nu+P׿ ,2J/<d %1A 6 VrM)UBP+B D9ZImm(VbEE XM@Jx!O;cf!& ;3;sx뾮h9z(~)eDkƌ\y >^{گEvv6%R$L6Evbrp](noEtGl6d "H.mM67 ]C~얖4¡C.999=QFuH$8(.Ui#շ1zpŬϓZ,],vWbO,8-ݕ~K_#;wh.+!CpUWq5p 7. Aĸd2( fm0o,/sXi-4v!O:16m">>z?~4ʙ3ghioi__W,Bx42DscB3j;B qY/81~lLK|kDwu&`"d}_1@OZ`BHNNK/ő`ҥ^Qn@*U6F!VVViB(tq0RA{Їп]eWA8N:ŢEعs'w9w}lذ_~S ̸HїH[&t t #ֿ0ZW3 }3g'''h"(+/##="… ihhЏɬY(//G+]xb23(--e 8'R^QuE=[;Z}JH Ϧog0 $AN-?yyylܸe7ŋ#>b8(i+Y\K04w̚򵸌k"` XYyv= U|Z[[YlO=$uW;w.111zؗ_~5kְzj6n̙3[:ugyիWӟ\˜9s{2e Ÿn>c-[N{n[n3h yF ?l۶kײdɒBȜ9s&`z`yyy1ZZpi8iߙž d[`޼y`\Sww^NԩS6m8p}v5k֐ϠA8y\bSNK.aԩ۷_6nȜ9sXv-ƍkĉ?^_}nm޼,)*Jpahs.Kt:ijj"55ċ{ȝ3"4lśu2N?ÇfTĂjZEq]woq 7aEᥗKk[+N+ȣD")?~<7t3fgq;? /pظq~.@զ r+p뭷d) /+pe}v=X/}fŋS__OJJ 3 ,رczΏchii_ 'F܊K4܊xoOrz0]j3grA~~08]NyM'O7x j,zo`f~PSSàAضm~m۶u(,Z2upnǑ`o^V\[d锔H~6L1mXL|4n-MnML|4eqAZZZtP{8?+c_xF'#NMM??wܹ</b,Y3<Ú5k㋊0=J+pfyf$;;#}ϷtR~0w\***HIIa]CS!F6r>TՌw|ٱc2eSBGXҰllzӧO{hUVVBIIIPnhBU¯ȹY`L,0!dΤ|nv/؅ksϱ|r2Ggڡqhs`&)X`YYYTTTEYcyyyTVV2&s K,5||kW!tH7l@zz:̜9Wv;+Vgzr+L1 y(DA0QdL`"+g&MŋS__/j)[4=cܐLB$5kׯK Zr{׫XAA#2&pgٲe$%% H9VV^FFzEEE > B&ͧ џ:[f}#6+w%q8|Q;pniҼYpPHmRaȦM>|8G+ёjrfcꩯfϞȌW7?xqs^_Wւu,& uBn-H l[nH0ŋ/HRR!_hYt)Nr <8]֊'ɗq$`ƋMfO31[X,M@II VKȪUt%''{M+]v] ?~tzݵdɒ mghAVQҤy`3NpݸnZ[[immFN>usB{9s& $$$π%::ՊjKlĉTVV-WbwihhzCWUUqݸ\.N'477LSSMM<|Gׁ^<{׫Tm۶K\\qqqDGGcz4nt+^3M&n}l6l6)S[osN=B`&LiӰtfKd5XΘ1coPw "իyw1+--%#=x li3d!p.^L.,V}@t2`SrPC$3hbbb&** q!X̖ML (**a]f1k,f͚Ejz)(,,,~_p 7,TR`D1F\ QUU\.Jhni3gx\ g MʹljEem6cZZ<&ѺcbbEb#t \8N60v2<ü;z뭘f.fZZZ(((࣏>}Ba~۷o'6.ؘйO((l5 ĕx Y+;**c肥-{" -#??~KKfqT^~eDlsw3 ]bـ'sjcDYkk+ I(o ` L*E[[mmm477OeZ0ĘH0QؘXݭ v|Ffꪈ-}Yfe멩ڟCvv6N.}Β]4"~zO8pn}ߥGjs ɞc∉zj]A{D(8NN'---kA5өG- 1r0ZfY5K[Ӛfilٲɓ'aECC?Lkel޼ݨG2c ."&\3@7fdر̟?qq7S__Ϣw ˪{p0rp8N]4KQ/3C?&a6="e` jK;./ AEEee}/ջݻ8]vҹcbbhiiw{B?%`ݚ\MEl6cYrE 1EhYQg5.^O,}1cȟva"stfס餦Sz6o p^3 s`Bҭu`&uSVQ@1Iךjy/_"!>lvaާYcذa>}3gΠp|9z:vtQL&b6\VXi6Z\}2裏ネLW\jppY&twX nj4(wġXdxg^֗O?oo|;3o}[ZZZt!L5rc~ 6oimtR [CB1AbY_F7Wp.!/q}QXXH~~>wygD,nw}7/f„ ^ cش4ΠC _c;tED 诧nh<K~]2%1}鱀'fBӗqŬY3 F~B\2y ĠzPKb?o"*/:? zBBŠxGvG-Db|,6|&$o =ъ D*6+u{+|΍J3Q3PLqAQQoB=s`¹Q#х(N:r;wnI1HNF :VQQAHdg`Ózf {WF U2q>h|FWs?Y  qO<,_$e^/_dae,0U&ͫufBsnVX rrr'ؽ^g`KF [BVL#ջ9O[u`Z _kk+--1o<>~u/S IDATfT,ψ3k嘴དྷ^3 +,dBh# Bʭ<~i c^LUE 0Aby_J)#LX`rdBh Dˈ?QЛ П0#n6;X=EL.¼+QBGdL:G\/|Ė \ byB/b BX!V(t_D֎wxX`7Z=0q! B_0cE] (1n"VPI_:'BELz@Lf % Q袖"`f20S_$ Q–jiH*Dw+ 8$CA{ F2ߞϢ K'/*MOkGց B_ @B:{BL6~5{Cݝ &+BYa55W`ˀI!.+i\M G>jbĈ^L ր m.,~~{82oq7p&TiPU0w\ p¥ߛPq;vvq\8Np:8N\.\yD 4ywN6@+3 _Li;PU]ElL,V(l6QQQXVf3["``>LJJ }f.v(Jkkkr( ˗/G uW0cѢEu]XV]l66 Պbl^f0A .XaFs\vE^z~+mP02 &#ƿSN; ** Ղ_f  8$lRPUUw!nN'm.ݍ>L&Ϡd6kD&ﮨ rߊgAk֗fffXZݾ7$^n"~i,X.ٌ(L&,f YⷨX_}'MQ!8NN}}=NnKh5K Є"`}~cGs/[-l^ D!蔔OAAK. uwz̡BoSܨJ$7-f,f˸KgdHjjkDfƼ֭/Z'˚Xi-"`+W%'''^ELkpL;VZ`^ U.&b?dҤINЃ6|/f6b+TƠ@ BQ1cT)UsaBwQĶ B/9:jjk()) uw-o|Z]G^tjkk#:ô׾ۄ&R@{y]KLzV/ø׭9Ȥ3!򵸌k!d"`"97Bʲex'ٿ@*4ϟܹs"`lɓb֭Na̍߉HKl~}?̛7OOk\&!B3gGLDmVVEKDjj* g;gB]!Di8Cي7e<~ÇP Bc)ce涶6}Y-ZYZ aKOcqO%:*ګV."`BHOZ[[iii?16mc0 !.]„36x&#RGDEE͘H9A! ,瞣r9Pw/цz=BvƭC[!\fҴ,YIKKaZqݘL%{vZRRR7' _RѬ0͑ p\b.K/D!$''/VX.7yni7Ykkmf4F0@s%2}tf͚.ya\ͅ)vWn)>l,r-ݯ'&aJ>}zXF%˦.}PҟŅ(tsohK/E[O?B0J uWұ_Bc=ELˆ ꫯRQQAIIIX,pWw>LC\/W$n܅"&aƋ/Hzz͕or#&n+J JCCpq- N v;]_ɓ'sݔ.^{zOo)RRU/_Θ1t=g12&sLu0&s ƍ1c;wzҤI,^zf̘ћ]HՂI,0!쩨 9?f\ӧsH}}=~{F-ZH[paX-!s`BG<f6^ L)ջuzwt:] ={]744jժ ^ZuU())~e+ZN]!))IO6oLRREEEB614i^M58{2^ 9p89\G.ú Hw=644pBٳ8p shL/_ΪUHOOg̞=ٳg3w\N~ٳgOJJ VVRRȌL<\G~r#3FRRRr[PP@Fzsxqgl6v@#S B~Mc2"rJKK1cmmm|љUU)**"++*rrr(--׿~|yV`ǎTUWPU]Eyy9WB?o[[<喟ϠQU]EUuIIIV᜼9[NNIJJ⟯z.}?ɔ0nܸ1-9[uIdL$%%&MDNN7o$xb*2Ggt:y9y$lskQ,_ )((`5bɼ;L9:ŋSXXϤI\q苜TUWUdt s`¹ \r^gggw*` TVV0y5$''眩PuMIJJXIk y.Y6ν~2|衇x>}:uuua!C5r:~lxk-;;1cC?39]&M"//w>.]JVVdggw)$xMKs4ULPTT|ϩ'**'Mee%/rwmqpzz: .$**Jw=9:$r8IPXX3g\x衇ٖ-[*7oCQҤy5-#CznϒI[[+W=2>^׽Xx1ٽ{7/СCcǎ]v+Wd9˩"//?ŋk..=#3l6֯_{86-"f2G j[qKռI f Ag…Į]Z z/6Z[[inn3M455_[Rrwv(){}U~wxm>g7saY{?\u ~O_>9} ..ظXbcjbZ GƍcE 9]/wOӍs$%rYX~:R' ׏癭$HwO}_B &.DA,]2uaWG̹My4ҷ}^SO'NqD#xE [s`FLL"))BG:{VWybTUotC-=56oA2Dvd1^Y16t:}K=_^y/S}42ͲLINNl***>|8ׯR l H&sUE;.1R%ن}x[޸)s38m S$DQ>38} f#DKC+A<,^@;5TM@e!I潻Bl6nʒ%K(,,d3VW9+dNK454NhdtCaRM-'ѪOyKrߖ0p\e~  5&.DA,ZCEEE^q>#ԋfF^mA `p CR/b+9qguQy}>}}/[V}B=㆓0,F֨.`ϻCٌ* ~l lݺUO|<{lFfrmN렁TR} "(Uo/Rs}[<7S*<ړTq@o9s-Vlb׵=kNsBfAzĂ ~YyE9=ILL~QdUt07`u%3i6mtVsXՏѮkA?ߙIbm:žu^}&|A7N^%DI @I((t+R3|!f~4vVZ񏻶*;Vcyf-yD+Gu>rɪ/xOObߍ9y4l&:ʬYz3˿ .1Y'?C jq?{#ԿÕQU7{^;B\b,Cd33ww쵓i:K>3kDxf|TT6*~yd:E77(N6>=ϭ++ &s` \w=I׵=͓)d51o=Î}0-'R d5*u:;Kp(-(-4SoC{+=΄C|QUNDx8!|8O?CJv{Z,q1^wtRϥ}4l`҃i~皛PQU/ڟgS5y18? L,0A;s3x/)S-˻}诼h1l;;Oh9z&z=$Npb_#ޭgq x8gjIw$..OOǦ-Xߕ_͸ 'xc~/oL cdd6X`Ѧz]fS1e09%Ԗw8N_&06o8Ie6PTWÎ}/~mMogPT˿7O| T8\5RC}˸g)tg}Wb\; &+|h4/M:{X`  UQl\'ʮS*:ž ĢEYml=L LvQ˅-6\n[AQx饗x`d"MFNwATTŪ[^6 ͦ[`f[ EUU݅vq:rt7f2<xB*gxj/͊jb`Z}o Q.c6QɄlA1+'hP t(D>F2>ĘL&̖v lwY, &B0_hnŌU #.\ fDUTLf hI.cɄyбZX]L&S7 p^4RUU|E0B%WF1& ,\.8*>-J)7BL U Ţ_u%:2K iMV(PZmBo$ 4~rA> }*Qi]LE_\bY+ '!ih2s0Wˮ*vgUSTej~*t.ZZ$(;y~@pgLmBQDfIDUcO1[OTQm4`G!6*%!D% EB>8=2\ BL oBi61T1ť'8":(JCDI@*JiUd K!(DIiStf3&YJwKB ,(_ZFFPC:f`W7F!^M lODTJbqU\Z)AJ@pbRpxvfjGJNz4jQD\.^z+PVQI~I)yE%TV{7}aT $"J%*VQ BP sӢ>Bd2UR͇sɫpѨTH#1:J4ťNϠB.*5 JH*dnpOQ Ĥ@ 871)8e叴؅ >[`T(h$f1(QK +_ȑ,zm2obwJZFV K!(AC Ĥp'm2*pu.+,(mZVs 'w'HjF$*jAy&']HfW|7!IIE&Ԃ\.C$|9!WEf6-g !&ƛ{*Y5\'׶ hHR\L\ h;d6Kg >]Lzv S MD5ij4|6J`̈́N`k$LVeSTN^`0`00L&ϱ;@  eRP/@)#IBR 8]‚InľcǥuUfk5 jRdtbHHr:0Q/84,ĤxSYm௔2r'_HBDN.ڏ̲28x#TEv]IAWd2WJ ~*nmZ!/sJiA [!_#cRt6B]R"eUVTJ붕.h6̢N;jM puu.u!&uiDGwQ+~">¾=B| "w~zA#04cGP^ϸbؾ*5c W*RU^J9}ҝٹ(U3?LFۦ lw@ZW`xY1eNx;&1w~ y&]׵\mK,ģ:^ppo嘤d2R 暋GiLtC5qx䴮uijQPUVIJަSNMay 7UZ޵w C>ymF@X%=&12ePa% j'ٌBp Mm"TzOJ7Y=oWk>Jm.ń-zSM_hYl2kf[ 'Xeng?QVMN}5# >x=(ˠp"41vd."|ꅬ['-/&$*3WmUSػ~ FNWM_*c1X=#vè}oՙ~]ܽw %^\i+JXxZϩ55mVˆ7 ߽A]<:{9 C;68ϙIv>b17irkLӕ7qwlf-zԣtSmnJnQ.KGind2$!iX=!"Oo_OޜKO˂KS'k噊J!&8f=NB_O)RQR@AO_@WU 9lI^w7 7?Ε 8w/K7 -DYA6J\AQNE9iڶc\;yL'";ڊRt brב"-e6VS?l"y8o \ Rߟ3c.]YQ_hյAank{@nb| zޜԛuM; AC;>T:yj|U0!Xp8 ͊%~SSxTWbiiJm*&-!TWJqE['M AY?#"Goc9ONpiQ!MDzr}3AIA8>\sK9 ={Άbќ$&)/a쳟ע>~WSZ93\׋t{QX-rν*/f3:r;U!M:iIYQ.V/]u%_?߼t'&KHbrHBrmsө(Ƀ(9o=Dˇ `etZ.׍ ܴ,x$$tޡ4?<Pil,bk~ύPqkx}BO*K ~$*ˊ0s׫?]QNօ*y z+jN[8%0ww}'ڄd]EXZZ S~na\@C~p|d!V!8&.> b!XGiuM: BAxpP̩*+b%--ضwp` J lW{~}O_m_%b]OUeELé*/hQAxl.fGƑضǶ4ʹ8{=:mڊRCd5O&eRbQs<U~R:q2ӧF/4fr?l³>#Y>gFLR[I _9>n L\};y2 Ӿ#4*P)PR^!3@ףh;^qi1$&+C/tZ.($ݞ&$Y-Γ9l^!3UQe#ͺ9*]hɍã6YVrbRKcp4D3*'\;WVfӒ<G>}Y$$8!/!YFs6RݴMm e2hXʋrLT>new ,-Qֹ{\P(UnC"8Y##KC丬w$oR:vZmlj4NzʫtLZ't>B\:[l&Ǽ(HC"Ă\VrJƉ):!X9UA)ĤV JLTVr٩)Ǐ"NUYzm%i9o3:rھ{\9^bo][o":5e9ܶҭ-;aךE?6]kM{)_RrO2j`Z7:YQ߿"Y;X\广#m}߼L:ҢK_t/-B"(Mg9| %/d}mݽ?Uedw/9"/T4j(`68IT[IwC:HTi.`rR29jB_-V Fوn)=Q?A0pBq JOu='ԉ,v> [L:於i 9@d͂OuULVd|VؒJ+*Ɋ٬67/Y^޾R좽zԣl^:{HK+;ؚB4{)ߍMgתT`X[u0+&# vYDd| ZtW#z[{7ඇYsIݻ7&[d|S>`2k+ a﩮*q=()G+0]hsp>F'htJ~ީt BtL&惬gy܇JC?|Tj#fj2}J&K +٪5ɏҹIdN1P*X,IH:fwpv+(U,z? 2i%KL&!tx+rk벹n)k,l-!2%`~;nd2ڊm[ɐS6})UjtW:K?LUy11MOC#F\ ;WF]D熉 0+^VI;mmi|ytfB"Nl 0pR߳YS$g>#4:- 误S+]hIWF:Y&hGi?sm0H5*xJmdSb#S!) M"iՌ6M Rd%yrL;FI^>}h>'j{fȬ"[hZ^]LnesE5`?̔RƯtf%T\ei!yD%/ +lB@ș'7a u <ź6⏮HF~?&FxM[Xjf@HHD@@H'R=KӓEұ?elbr6}"WA 8TdP|(*h].nؗGȲ[1]u\"܂:[8 /0SrؤвJQ 2h.켻ƏL&ih >*59z\Oiqq|Mn]]c_gǓbRPo, ݠF@+o`'jxtr^OaV*Fb#&]C7Y_!y)GuPϏ~BRoгUYSMCb bRO&pw7x InnoVں ՆJw/'z # !LF?RJz OGoA5N}i+WKd4 evFq0]Zg ʦ,`y=]bRP+ƅ=E6Y%]k Cz;Zq\<} LW)jU~KiQ@rbRPoeC"N)y.Y K]HR[: Ob`PHD5E8IKhZs1œ,v+ funniNpCiQi|E ڬ]q/8߭N +;>J5*MMBs}y%cr-VI q!8eDIemپRƓжO9;-%~ h |]cRVt^fsCX&by赕|mؾ6]6VrмSn~-Bٽfn{IcdgO'~*K lyIhۍ!K=woj &dMkn#2;Gw@`h*^ilTqݫ1y_>ToOuͦsR}>P^gSos~m8$>B8}&5EUy1wy~QJV~|fޟ| zmMmE %NjGr`-APbZ(9K/s$~?+Si|M;@nAWP^{^? 0LRnd]\>"4([UiP-d*F*1NctT*c.$퓫Ϲi[p\,/l:G>VM;k ߅F'pZ_UƬ6e` .mLF$jp]5/`3@XtO|fdmz^;+y=a4ڽX&;{= ?quNs1xKV>q{6uH1!w>hwIB{_d+M|xIH^~8~g0 `ϥ O.N[kUSNމC0o(v`%||Nش 褶dP]YF tj8I=K?Lځ}!v#Ug z ˱Z,ًBIlםzWc>8JeԫJQ*URxQ*`2HM%JӰbUHzK3&Nߪ3j?NhoЎp8ǰX̘&r5{ϟV'&1i(/cMTkV+i==qMLvZDBHJI~ft(8*;G\Ab޾JC眐'5RCct(dvY^[ɺ߿mۃc_i blh#~pTOrx܇ēgRNʝIAcFêUD:Nj 1)te)dr9w3d܈fn7eCFӢSoYNDơ8ĺE_2WkݗLŇb}rMfVb\sAcXMXjm16@pgN#+;KUr%} b1(ʋ9e$&jݵ=nԤ䞔gbW k9e??[UYC'rxlA*G\d>~tZLލ4؛]kQZ`mJpD,>~Vº߿װ>fdԎf=9m%`:q$Jϡc9t oN}8ﲼ(u\?}=p56/bÓZ-} f f\wOSs#pX,NVHO܎8 J!&SY^޾^_٩)R"WG>z d5O&(x3i$cgٲgsNl^M(٩06 ~4wѤEGB)K$̼J"[ ڇc{6HKlO[.RY,iu|=L& XE*.fڭ[ytv'youPo6'D4@py3)dvj y<ѿ<Il }u%Gve_9RE呙RusKD5cכhd2iع7%$:˯܏thݭ?=H.W0pc\{mkORPk#MǾKe0^(˵dE'Yk}yu6BIqn:;WF;ќLmws|fd2z^äWJt6fovYDEI>!L{=3(r`ϿeN[<. /M)1i դ)}WNE{9J -GwQ^k&-m{W udVZ&;"}1,_QFN쯨@YԽTx>wfC֯c28_/dVy=GR}GԒYES{^NCRQQ[T|\ѹAu[. E9i:hLFYhK_idSl"8<ӪǑ" {Mg!4:'d*/%-mZg_TmE)Ź'qb1(MG_]IPx !njPIUy1 Xnv+(/E_]p!M ld) ~Bhh(aaaL`` VQTRc;/B(7zN'=着eo '&ژ30߿r'S l[^Xk6>ܚ=E]vWVOYyX-Vd2;">L`Z WΛ'SHk \j+ ~J@N]Ϧ7MaaLщ Gw`g5?[cNJiNךdirsv,dӒa+WĦ%yrOT\ړn 1,r; g|V;9-~NnoW;ɃlS_ɂ\!r'1c{*3OV5%!J -Fz tZ&~s;pU'?C qߟkֺ7`"[ H(Ujz OmNM~g|n)),W ˀb{\˷)Wr*2>ҏ?Nc̛y]\k)3i{Y|Q W3tB"E&QGqq[z ts)G:-|X-VjĈBW/YeP(eZUV9eAgHpk<=aeT8(և5 8m S}?<ԕjV$f2XX MJj\-q+ämJ[l.ۗ94 ZR-gН 3j|y^?B~\6$~]/cƟs6bw䊷wBT.?fFP`j9q>gx2jjPsVw"Ic~?hl&ohN"Yn"5@ .YKM}0,(T2"xH25gONoŹ::=?XE^-l_@ eR <L%WRQķg,rӜ{KNgӒ\z_CL3?rR,0~$%QQl䉫sx1-]h0["zt޹o7{I" JJd+ 1(+cX1'WEiI.:QCv&;nŜWQYeCPdjYKܙjO?扉e_<|x bMrpK O~e vLWfvAZv fН uٝx:?F<،8s\U@LS?&}i_IWknG~^DƿԆ2s>BxE2zj+>|t/W}l_o@@W3aZ>;]ΪYdU#WHh=|UtU)s2!Gǣ Sp^;M|9/s{'ؽRLJWp[Ъ[>:ǏdKk":g7@I~/glX*_88WOxW F;"oeC{P3d\Eqkw&i)/w໗fTz]]v6vDrGDr#:i(?I˵Z-ZJi*[Rő[(~ ݔ פ =DJZ<00i\RV安˅eR )MfSlKNATWXc&'&Ӭ?72BL @@=$ 9{_BL1J3&ȀL.#S>;i<&i,?H !&4>''_Pߴ\c6:_?^u.2 >lrԸVL+tUfJY N 4VP|M{iƋ B!,L hH//g g8zӲ9W4t.* &Ǐ+(U{]L!4!$A |.+8xHJI] j0&*{Bk)W9%j~m{L7(OIAC#1J%9[, K֪\|N>^e%"]OIe!&  ;qW:ETOFj\W崮$%bTNWQi . lRw9ëѽ}2\.G,5O QQ-lmPRt-^g"04JlV@'+u]DӼI%;Ω~"uۃ!sNH.U\օFW.]|.#8wf( {恥3(Җ5TbdfQNׯ(6 J!&\h|xsmx#0Kt'H$h@*Z+Je Rht$z $,4TLf;ʁ-8eGw濫i?Njb6K?qpزi?1eH&umӞb>VT wiY<7ZƶM~wM&q(٩ǤiRx؊'c/?;ꝷwZ>ߣխ=]}6qj|}~CGp`F~aՅ;7wZl]7o=ZrO|Ҷ7C2K[&um`ָLgc|<;|ӹ=nvM؊FQV<ҿ7KyfIۭ ~eR}r|3f/|!*JGfT*T*l^}VS:nI#\ƈ/H(7!ifU*4yސdBL .^,f3ǎ&;c:{9kWS׹ymR&8-rjm?0;¨ҦGO޾o"^{y(T*&!1L5Rrׇ|*JK G[YAhT4})}?]:zJ^7V]1mB|1My8##yy9N-1ЮL;0eH2-;Eks78k==Ej/?/[t-M=/?eИq<2 Meֱ#',=xc>Z o@O|%7tOH{n_|> ^c3zny |c{v;c[ٰw?|-7'h8\+\ݞ\/bZJ|e賜32Y̤gq(j|"RpA:饹XVmE{86Uf]E' e}Ÿ -Ues%y\;i{ei /8aXc׫]kWѲsW.d2my-P5X\N)-癯3h8\r+$i۳z{;u-E9uj5&yF\ѓeoԳݽq  w V]Ku}SpK՛[7M& ; a19GĆH۳eWƟv5'Ѝ[F埿z b^B"^zu`˲۳϶yNҫFE3;n)۴cb6R])׳a|m/I;߲5woΡ[i2m4_ғTFF#j֟⅗7rQr>G!: NL *Ze2w Jgi6dmQv\TgϜoQi44iъ^yv=/[`Uo}sy+&M|˞8xnW_q٠f 1z NׄssjmTWUO~y5oba7X1wYz&C+hܑѭsp1NmщM7VL!rw 9ʡۘ1nԎ"J%[wAIQ'x*] m8g2~oOvUHZV7āJBlm YGc1,-sVOځ??S__NOb."6L  IDATFL&fٌblyg OŨLWI25(ըJTr%*RtHXb20M-&zFGP+ B& 'FIPz\T$Wu0#rBrڱZj0V+sbvx?r]5i>۾$(JVS?D6Y]Qc{`ѢOg37xy4KHdx^E,yzr᯼6hɬU>0۶z\>~1x)ضֱs߫hֱFu\PHnñO~tDe\7PЪ*SjVlQ~۪Rk(2oEkoAdnXmIOVI&$- V]-8JUGoއ֊JJ/%XpWEa,Erh4n'!]BL GDpdZo: Fà14f5߼G1ض~"Y',[J\(>>;Vw::` ] d3/Hn6x=kܚ (7RIZ@UyGvݼc'IP^qÈ= ru0t$mRAځ%ud2[&,:@v]-u9Jnqf:QQRLhTt]v)x~V$$-[Ra6Q՘fbCQ_Nޯ 8>1"Gpv);GY( j(]\:Z&k$ Ĥ4]@ߋK4KA?v-hqMhޡ/aG|!7VJ%7o$8"~7RKL{j-./?#:=}>I^ z".[QcIhݦڝty>xNB5:wc+Ì; ?ܰ [OlQP G]`hærWë/BcÏa[,zՊM~&>pupvcZV>D#BCbH IaV0 4@@J IRnu VqwɆFS"1ܸw%;t٬lA]3!!onO"̪IA~Q1|?*9{ n'l">5M1,TBֻ-$YHD:JPJGɶJ:RrBJgR]A{9ދKZ 1IDN`Xp! 9դk{~#H4þ-ePJ#DMr/%oNA!R3&y=^1A4򋊑!fK fZm-w5oHi\ JY1ɏ@J d!1WbRJTjځf.HL.2:$ "R1CTB<=kBS=Du0L8{5 plePZ+=Qx9t`Red+)7Iycp.OF7`  KR:%0$& xFA/ϛؗ +c!":CztDȱ2 ^QwܿP+҅xbbNginBdK.k-墑UL#2#%;7c]HLN{R)nVL{gI2"Mf \naFfxpc%KkY/}墏q 9% %!d籠kRz.$& Yu@Ȥ8*v` OA"r)*)E( AF*ATٌȫ-,le@|ADt:=MVRRuu/~ܘ৹٩ly"ЯF8-^T^Aӡ. :T-3p BzqSP\WcPfmEP\ My:MnRn'RŦ3X$cILNhM%+&V+gAWx `V Mx W~h ^FM Ѡ0-(3p3/y+(u%ySB+&ٚI$Ic7߀$BrȤj$+"ٵhm6Pp7TmF8XQI)JJp= tZ 4]qbZa4`4bPGBrjj4 0 I^_DA߻s6o_HLnïkRr"R,&YZ7+`4 B~ QfZh(@}(! FVLT$ MHL.[?ah40 #b!SVMY 㐯m0^EQTPAe̵Gz)nKJMXN" ڀ$6l6'&j5'&Y)'&)(Z{  Kb(P'C=_LS٬dRTI_ $RIdţVuXFLn4К[2ᓛVٴ6`c+ܚϣFŭqV(%PجB1R@p(ʦ jDjZ $łRjs0\*/)v8' MQucO9( P(-ZW gHZTnnIgBV!. 1II),ܹIHV0 k cr+KE&ϊIg'A$&J# HwK/%$F#Z[b_)'􄗗W=^MGn5)w$+(vgJQ$*I6%T[:MsKE'5ɺC,BoHł/*YΦ" HLՂJ>RL&h4X,NL;.U7IT ǫiIq${>NzD%A5 I(%?:)W&NWG&QI$ ˪CGJ̉orbG)"Sj>IJoQ$T"0Z$kEZF*:ߟA$&j#'(ME$_HE$0S$*5܈nD,.?|T'ID]Ab2 JqjBRjBVKHqTTd!1Yw褔rH)KQ$+A)tH~jdCjIX,:$$ OHLFJPۤTd%0RH IU/4 CJUVPJE-"$$ HL5XPGyGbҙt6c|DLLJJgِ$ 1IRHXTET4R."Ifa-$j^NTJm#>? I Dvbrsw$ITV_H,rNj@t~AIVRJ IQ>M#TsA$&:VF0{Njs3&&V$Q;I~:к";q;AbN 1IATiAAU$AAQeHLAAU$AAQehN  p =>{,jzX,\zmڴo}_} D @by'p 4&MK  nC(MAATL"ݺuW}_A FSWDU#>>7oy|EDei֬Y}_QE6ZԸF4h>~8 TWD"##z"s+˖-Ü9sAin  ʐ$  I  ʐ$  I  ʐ$  I  ʐ$  I  ʐ$  I  ʐ$  I  ʐ$  I  ʐ$  I  ʨl6[]]cgV(} AA!&&"Hl؜. nEjTL51u:fwp+v7"4]7OC_֦FĤ?mcP>q%9W _cKwh,Kc GD:TKL5XUT kBpY̾V9>4D{qK$֦JbRO1 9UkTӮF4T{uϭHCpnU[&n_*-&]B6gh4a)R ZmuOC:>ba)QwSkAj]\9H-?:]t%*]ǫ:`8}ثRVEX֥.u܍?sC|, A"n_r}eyaZaZ2Me,q< 4xǕ#wLm;>B{4d1ɧ8H[bҙtDDcLJER#YWq#ǧj4Ǖ_r|vVߝ+&@cj{ I$j bҙ+' |(sNA]) c6" !c=ښfA 6mS0ZXB`h8 Iq'-κ&#ri/WOcP6Ȥ+;);,  PXܞ9H$(TLJ5&ģ@u_o셪 5I͔eQB cjihԩz4;>fzPr#v\ɝܹow[dҙ$~\%Q6'\,ucS&.8%:,X|{ csdqasGH:3iUa4p$(507 c:ŤN%^p׮m6Paw) knQ\\ h/Ozl_0ڦҤ;,>Pi8wDf]3p Ia`c,Mvg 4@OC 2T  48 QI$ Rul-G,"(-r60 l6YOiZ|%퇹Iw W'X\WAAP'ص.6f0knљsD $Ť;Bm|>*B *E +ƀd\2lPh91΢s9*c /:,bŝ +*7BxycPi}mD=bgJ| ?(РS06#eDeq:gND^쩀6}nuv-J ITҕ^#BYZqp@>6FE@r8Pm*y_RuzBPVAB5.-kh @^@ӎ,Ѱ1뾋[܍sPpjŋ^pֈ1 Хn8@{j  -KY >jY)))רIJZa S#6NZiD<1^e0R6j[MF'`e#%(YvFvN4|:-(if)ѻY py Kb7` qO}36j,l&fo\P<1bKuP瞭80YBawr|27CN9gF2 &s[E<ũT#vr'@;&fug֖7hXSH@LC眺 KEж__(qn}0 ^7wr\LTqrLhoرeOAcKhhҪ ~a6ڪvZU70| UQEk nCj hK[Ea۶̇D$.IgnlRhS (&$PVy`1a6]F~*#"uɫ`LZ (Y) 98;Hu|݉J2 EA44*vlwռK4@:  M 7ָtIXՁlc̍)ob/&9sP1_q褝݉Svڬ;S۟]Qnߜ]N-#$k"-%(K+vkO%#Jvn֣ 4 w_;8D&]}OV\i1is`AbˮH9p̅˩sn nqd5`&  .!`gaS>?J['SPomqX4^|jh*Ecغ<20Vl&A&B3 Rv#&Sjj:>cpv9z2V+åg#&>L֯pc;>J%V+g 6go vm̃GlSˬ|׆#/h47?G/fqۭVSf}{g|TC)QC? *r F"7ncDC ^qԚƣ.rضߝy/ĹK)>mǡ1C+&NvFj ŽwAXhs0 ^Ca%5Ozzz :b~h퓛W pc{p 7sz ̘iy{2 0yB/\gJ}嶝JQv=NrL:p(68S:5V4LL& IN+ٔ*PUO,9jHg NK l%5bԐPPP[ra2g۵M&H*0 `!puQC:cźp\^Px Эs++&>x,a!lيY/Eprx{06z3'ڵ8vCyb ,x{-Lmrqh4*D{^:n{QqES:/~u_S;pz*ׅ }ys ݄,m5ޙZ#~{i%.ZE)7q<{̜doKqEJ2&SGk4* C˩/Ŕqx '^;3jݰqY6 oKߍǮbPD\H'†ߞ=W8r"cg|Р EF|^TJ<sIS⹹E_WЁe&vnX{/GlsGvE6\Ï f#)A"W0 T:rG(3bR߿9YlTD >) G\b&:kS ]͆vFة84kGP{SJO=ƌĻ;;iS.!-דReW$<0x5ݽ-2 0ǀ²?uK_LUpx8vYֹ1~a(cش,NK´G v#/$IhnAcG1#˽܃hBCB9ohxUDKόT*(3hQ+QPX{/^[P\lBHpVX^IáAF@Sch]Rs. jV GϮmЧ=]c2 ^{NӠÓ Õt$^)8 ;!)BwWqf3 Ӻ_WAVMqX'{#.n6[1Q_b;1>=,ހl﹀s^ņߞ]Ц=&o0[xl ll‡Oקzn |k)RDgyc+Tc2${ŵ,5UCs+^#1٢7cv{d,<<2}{ cЮuSN$zZځEveuXd+rK|]8O'#^mg{صƆ|d+s0 =F.ƛ|>6nB\NEgc̯plRq#/X`qrdeq^zg=M+GjZ.^xOnq$>r<iHw3TM3qj*#LuЌ~dvЮuSA;yzNƱS82Չ^YBȠGrEs8r2 cÍ|Da{dd49E8 xό>J AJZ#7\lg#'Jl:^zfe4Yqn+F?+ ̚9뷞Ai۾b]8ƏVW^xr 5`q0kwҞyu5K;aӒG1cr?\ߊuhiwsX\D˞ŵOW0rLztisKyٳxZي-KRm. Jp Am 1i\Ѩ00:E1cӺ_?9,"l`X&4/dl /gaøkX$upLو~|gOXOO1xd nR>$gWhf?#b4CRAVsޱZvHuK!W+)HsS}`0xhq0<3&ÑiɣBWWRq5T8> P(x? 6æ⓲0v0} }ƾL Њ.߈?^iO{b}f?0[vEiw'&`/Oq߀hAf[I=͉TO;O]a͜ZVVkzYE ~:QC:q|hW.F +]JA~A)g+d/L%-=:<3b2W'b?!:.CnCf8x,1 `wjXGkr'mI{cIHĝ+;5{y0}R_Xw^ĥ|W?B~Xv: l?6Fycc$(1X, ~i )T UuX8|} q4bpv]C[E3`]4j[# ]Jԉca4YЩɮj~7G`øk`ljMW4跈p ׅ1= ݻgѫk[O],3Ja-uR dn\bn~s{P=hb`΀V="C:j?oȉXlN``piF׹IYhpƊuxh@X>JRKhK|xu17_y ʐW.ޱ]3`kfyGޝy~8ѰQ;ōq61rp( C18| ?TvG'  ^m^ha'[3 ؈8t<c0d@0'ztm ?/(.`oVąk- B7C߳ΗsFJZVm8=5};Lwua1[`SY{IsWud8^Q^?6м Bq0<=Nlg6icqX4ڷGvg@RlD<8>wѥ5.^my|˿85*NTfԐhׅc,_N[`XTJ .©OgfIVD3/GLJMJB_˟18#tDS?O'e!+{7,tKJ<:cq0ipnsol\ f? ma`4Y &X,( \vLC ^18ϼEextFE½wAhp潴R .b0Aŏ{0yBOAaQoZ0Z̎0.XZfFn^1W8d@0 "Cst.Ol68-Qx`,xk-.XԴ\8w{Kaѫ(f}æ5S[eӂ@N,e϶zUNKH T*6#'cq]}cztiO[V8"D^LOtW<o5Gѿw&M}=ѫ[VlS(|^|4l~o,:=AlBFEWjB~p47{/b.¢ipdk()5 5=2xt8v*Zm[7Ũ!!A8+ӽxx8YиIpl"o ?1Gw hkTd".G[aP.M"s&M ?/ta|};؄ twhwTz=}ѣkkAdZ$*Ky};CAAa3bP( i3xdotǛ *1 0H=Ɯw%%2Ѩ} NځsRRi i <2Wt.s"/rKL4LS) ^ j?~|O wD[V(,*ClB&wi}kIYtTڙFs93~Zqֆg'^c3CEe͛bOpYMlfN/8_nЁh큌0bˮH9;嗢e&;bK+yٳfڝ1ÄN؆G ef̺W&7>ڌ^ٹE8v:m=`Ĥ3DeQ0:~:5.vԗhh Gz]'QeV&QOOOxzz`0`0z:ZkT*77 ,`;zMI޾mQuqOKBq{u? {2 q48r21lжF w9yŸp9w BzFC18D23O!.1efmw0al?q0-htV Z IDAT"%F>ʎN lE8x'eS2 ĉn"=#iV8G|TzVvNNF٩ZVaowMfΦ=eΟnh- V:Aw} H:ȉXzwd,D0[π^+_~YuO Kޝ} a!t5 -y㟵/"Xb"K>#7P)8V8u\r?+b2s'v{< b LшȉX}^vxUj%49ňK=zrY/ETuݻ2_`@}-MMB@Vsu6M’#+&ӷFAQOG$*OL. &َI4dW,&ʠ=ٿ߻$q?OI9LJlN]1)vY6܋Vkֱ'`Z1t@'Ҋ bR*1|Pq%lI&47Lj!\v%=#70bP(op&x\¢2;k7a6[ѱ}3L]p}־?F ,ɕ5%^lthL6Zz#3'# otk%Xj HL|?gî$RJL4hnsJ B>l4х[jx@zI 7\F}pGV:ia¨nb A{Di\1S ca0fzt(>N4F%j%Fin u;A5UzM ;)מYde*A! QTjmnQ0 +*߈ks_$Q0NOI˥8H5@e$ J]>dTDaj(*K9>5*d}N D4 l68gFۉƃruQ]Q-Xe=bMl*Q}*vT) j /w",pNn6~ Dl $¿;{OQt&S3YYlỉDBkQ%d#&>/a5^5)$+^x}gr*>:-x۬%nMyWI+d@DMPՎs;;V%.)ne"0r^r(uΔǾ' Dg뉪 69k\Ua%fbøm[wGBr=:[*58g헜M#m1ubo7sz ̘iy{2 גU[N{X+};"q%6|0ebot\9ؼ#hTx{$c۞0;< ť5%f?D0!Q+ԉiY>QC:alX ΞOR0}R_yLf u^GI A9?7o7mt ;{{pIʨ4 3`Т[Xk̘~{^83T*о?ǭu؃,+fL.N !QTޜo/NB$$7I][yṲl]q=_I)ٜw 1NX8/يe<`_mucNh-yq7pC_#Cs<= Lf &;"۰۹xq C|R~\~c]릲UGO݅^h7>ڌ_WM/s:xGyF |M?૟b.غynuLz[$dc.畇qz>_<pU0ﯝxo6,zn_Ac 2;}{GFV:&9?Rro42 z>,Jۃ WU;hw E}BB=|<nbCĦc?瑒1cr?mUa'ɩkg$f?/hujnkO¶=1_uⵛغ/ ;GxyѧG[wO_)MMŪ ǑFN[`=}u+v*TӥQ[mZ<Lݝ[+G#qc9v*S߿ Fb#f=Cc/OCoy/]݁WӵbYΞO7=VקrEEaȮضniϾãq0t}Չ_]}_V ows;Y/Š~AX0xha0xUxЬ~ZqEĿl6ٗ۸,Kt]Aij{f Juc\m>Z#x>*Lj%Lf xq9J9$,^ 813Rt{w{oc{JLS",۩DLz[sF6l=/.NaXi|vlZ:Gv!!Fub_>HCjxkz \s\_z&~7nd`P de"'g~؄L= K;AX\}.ǤaߡFtZ’wĄش=sZFv-S~_sO.\A=5Rs_Ծ9섨41c }P(/(;/O=T$:C{Jev{efLԗv|29p 72 (X>*: {]sƞQ8z*#%|T ]4"Rѩc <4m 79}. "1(3'&Ox>$]}]C[U/ĵ rqi.!-eW$Gw}z \IF,X%;붜At ( nwl>WbӱߋHv:@` y_AvNx{q]}_L|n9y5' 8Ù(*I!;iyeuK_b_ BX6AIǘNM@ⵛGSj<'|_4z'8nY6mόoN9k>ͥj%}|4 3 kgNs԰@<8ml>)qѢ;Gw$LCc3xnhdew&VosiXKűSq:7:kKp%6[LPpthŊS<J{csp2vWݎw܈.x8;l@Vef} 7_[_JDI f|g^]+G?|(<1?tls> %΀B@zF>X;?|4ʌfL#潴?N[wx Ig>E떾.?Wة8 ~_sT`GO7g'p*Z\3{lgٟ0vwLl6.XJsfCn~ {)g@Qh'~¼V"ć4a=vVrb]8tZ5"E*j:>N:ښܸص"'&3+P(0^ Y:=g Y~=#,$| f'vpl쒉!Ox|^zg={gwbן/oO{=t~a?@ЊyӿqGߎp9tlv 4(QiaWng~ܼLRxVD|vEԱSq>Gh4*قwz-'\Zq Z >(+\10:"!9 K<}:C[ _ SKEF|w74pw"9['!I!]@{5fXYB;|z n0BW^"y,v6#&*:  IH'閘,!RtB 7@7wN%$9t m}Vbo|iEy:Cʿ1:t 33Grс{ =1ml OE."=Weo5`ߑ+:!ѯW{;r/ϟc`4Yu~w׾dw5xhƂ0p|gpBĒwgbOĈ.ή: zl=q/̺[B:w 8fCv¸]fIhTxɱ5Oi)yp>j} Sq2" "P(waRUoLn,tK )""(-*(?1 1@TR.΅e~\23{3uwysa$&1ad{> hA_qXkEY*SgqbY*tHFsKs6j.Up&% ~qJ lnqzuϥ+ 7;q!/ h 6.> os:Ņ=S20 /oto^V!!1=>gks[i&nJN"5:eiR_[t7OAnԶ዁ D=Ĥt{W3e ;֡A2?ʔ,b˱SWy_;N%1)6=>gȘ9r&3wSf~_nWzmmM)΁m)E+#m^vUsMo9wSRJ ~ l:J䜢3'R\FTW=4Z?ΓKBCϡI!h2[tNRjѠSh aVm}vʳ`=Y|Q5t~d͢1<ڰ<%sFpU.͢e{f&M^K;j5lRΘNr'ZU^Hq&Qx܅h4' $$љT%k7㯿ħIăVCiNqb3nXlg qrʒSҹq31h4TPT+N\Cd/B kE2n=eNGJL ~٧{-C.S,%M|lMϑImcd-TBӲv1(6գUXt^]vqt:-o;Kq `6 ?KWbjHl6d)7s~^C5wFW_x\Ի)/Mǵ N{H<ִHU2/Lb`&r-=+O:/oV4]q ~9r+N+5w3z1oP/iʋ~x !T8ݐNYRY]l^d/fnVôkMeSգYd'ރҸ{]zp0u6sF=?gżڗUJb6[ ^Ȼ̜:{t}.R6TA2{BHnrEj5l}'ZeRbKe۰NN!+d٪|4od{VEE gcؽ<¥j:fo {7Q<^<^h?u`ݙ" %ʇ[HB^yr๋1B3ٺyep; 6sXgah~HMː`w&z--dYTJETg]M]tFJ(Q@:Ef5l6h#+V$vfȘ)Yk,ꖥ_\f4sxZޗ0|c^g\tV wqvKS;~@NM*[TZ;DS׊^Hdn$„St8mZxyyzuOgf1|U*ΰs+ooXY پޤtl~' Ԓ-;N3ooگ9 )a[ٲ㴼]JjN]CԨR…7oC'H׷tIbRIirGiIl6cf&Y=nviӼ &3M[gq,\/89Ǒ _V 5Eש:%WJ nLB"lR)ӣpx :z9>F=idpxmϨFܼDBj5tj[):~\xjE2n>-͏=~ {yͫnq]Z7LUk7CiѠW *2m Dj 2QxY 8MoP퇴]b؀ۣK]*7}!j>z;mWEשTo.;|Djʓg٨-{HIQj~OLX1:yoKvQw2'9>!3=QX(F[l.^tҎHO7$o6[};S<^u\RLaJ/@Dɂ/S_kve K7cLj5fKSDm*lz5#c88rjT0={^t<.>nW(e r'JJ˪=x}eS^]˷9ӘLhѤ"4NJb̘ )YQCjTvXtQϢ/i3Y0Fu˲h {i6uK% V+'\CУSltm_E0s&F=^fXtX㾯 myԨR.]Ť79Չ~7/Ͼ=uj8rT 0ύtU*Njg)Y<&JhW B¤fp׊d͢1Р$bAY*T-Z`|V ooiL@@G^ʦ/™gR,؝ɹnn a~3L W`2enVEqa c*D 0_׾ #I!>qE / ی QQ@pΤ@  M::@ p&@ F8@ l#I@ AΤ@  gR @m3)@ 6~Ig}=V0'KkFN7F A.9 $9Mz $N B!l$9 n@ {3W#|O8C 0r5cVoNDpEC 4]Hy NfHز rI}p$#U[{WPKt+ ߢ`,ӭ9| I֘--!ج9@O p$):_V;#%yHU‘/)UU}}= 1t Ar>Jt+<Ť 9 @:H{#J?c]HKKɩ%`2pyn܈f\iݺ13xoW̜69s":=EBXb͚#88F̟{ST1FHE…([F&iٲ!sEBرulRWhҤFObŊ v6jTDm ҥ̟7nҳG{5Œ%kشy 0n`t:cٲut҆G7ٳ]d23hS9qV<5kVf-Zkb3/*W.K-t̝Eb۶}q?Js=HOWB߾] Bl:v9LP`  11yK^<ū4;*V$))Oj2cvbuʕ+߹Gϑ:l6 2:N^aѢ_?4/HO 55 #~~"׮DŊ:Mh޼~۔b!((NƍL&uX|.]%&z]G}̚;N]mG۪mDF ,,ѣ{`lِ &44+qrI[W'3mnˇ~˩S8s"mOg3k臘—_odL…(&Oκu۱l$s5Sm6o統h?qgX9mX,̟I#9N9Lll<_|Df5_ m ϳҾ`.]݇>暵U>ڋa/CxuGwu D ϓ+ɕ+7ҥ3aP֭Nέҥ ǎQݷTb{J"[d tnK,Y`FKƵXiW|Ĉ׷ kW%%%eشiujWcGY~;e˖H\pi_LPPz_,bt:ZjL֍4)6o x`4eʔqڤrZ {wCr XϸCW:ݻ`'|z6n22LyI,_^S?j~r5/^ET5^055_J||"Gfȉbq'/^%+#̙?b6BW\gʔ&Y y\LvЂf/RnVppݡ $>^\z 2eJa/4jXN(W`ɌK$,LZ`;0F+^Sj|̟9ul0iw|9mcf9s"#_~6oWp! ә 2c=@Ni3r BBhذ& X|^9 %%BCIL~D-W4eؾH_-/СxFnݘ! %*T`տŔ,鼐ūxydj׮qCdSb$}tuƪ1~'p/͛xm~]U_g"#KPtqv> (Z4~_AbR2?NʕԩM֡nl޲Tҥ :nYLN%JaȐ3]x.]ku ?:thA>!-- v+hڴ5`ժ,ZGf͚=줤w""=6iRI@̜_"!!Ea+'=RD)3ԩFObcܺ^x"(Qwߛڦ u+A.g@v>$GG:~}%ׯaԨQ ʙ31df΃?v߶d]H`Z) A /IMM&44ͻyMS֬ '44ة]G d<ΫHNNeelڼ QZO\Ҙ-+̎س0  @ ~w>t"))UN+u3(\ 7nb0ر%_~5@+Dbb2/j[ ~_޽:/y~u2AADD'0Пgt1XF[*Rb$:O?#5*q|lfh4}nw.>ۙwI,| ,*;uVgYr#xݻR`(qqS(|Ԣ?4U.K`o|9Z}Οi?q1}w$Pl^@XXC#~~]@g$^/$$֭ӧOۿ44<-ҥ *DrF,O?ݞ||XVL&3˖(R$??_6nƍIyAX16lI~*ׯ[ٿ]:V*ddHII%!!?5%YvJiӺddxuL7dtچ ~j*Xn.;wަ)cM\rKRR Z0t;ntINNadlϗ+W3oR֮&cįxXV+[x !;n {u$::-(CV0Ab`ӻwGuj#0^G߾6s=< wkKJJv'"8;DZlVw'һ>FZؿ ѦxEqXG1/-nAt!!A8/j0^NjU3mGޗwtT:rR2ԩŋx.__#:*TZ WF- l&xiш^M ^#Jh;ž6YVL(Bc4GmܹgvGiMP__ّ Kz#YC}/P $ӄV޽q 뒧NC'HLLfL/5G$'9}֨QLWtp^Cӈ~UD6,;hAv,!Oz֢Q1?xƍMx dU ސii wxf AŤN;4(wdJNem{ >J "3$H¡x3HFqBSo+[X!w:.kav`)Z>%sݧ oc.l~̆Ul,2ClHJL;K:`|EL|> RlVH<}М&L1U;4o.Â'w Hn]T=5l6%HKv2C^ $oKpp&Yޥ:rHA'#n /#=V<;dՒn5* TSȞ:1%{:,7Ⱥ%=& Wc3A1j{VV VW{5*F>3C$ޓr!8HW+3wlb,=@ pMK&[ԙ%owgʈ]} h$60'IΙ;9!9*iw(KPcLfx֤S`@kE r%i!% ;qG v93tnH^ (0Omyk$n[[IP z I 3j$hKSrX(/4!GLy_^Hv]qt&]m5TdK0=2n. uy$zLIR]oVp>*GI@kܕl؟5 &Ú,dyoQ,{W *$~@J0dY t2,JyWL޺EL Jn]_-NsZ7;N @R=2,}m6ip(|C2-KA F&= $[T-W{*aKIkTA[W }nMV-gHCڡ9iv|eᣫ"9qL >6cUA]mև 'u&]̋_U#z$"[!M+ZpvU1V] WZ+{D_V G&"YIw{ks'/˷syFN &f*ۥ;[{3*E7]m:Fp'A Pbh[NP\\3sD[a5oтu)O͠Cjoߺ̔*E#@-bܼMUH6Xw$@3`4dv$ݮ%GQ wjN4dUS<귒Qpj-$\|B}钣 -%בw-TDqUkNm"Rt1xSn$] u?4&f> kC@yE>,mN a*\%ҝ#T!84Z:*d5U8H f3ŗi8]t_p8"KEzfwT}`v|㬦=٩XЙʆ5Φz=7MX؍jU!(Itǜ&WqRf($$r(^,N, iÉLs$]u;3pۙ`ZI'ckPME|ԧdHGd^dՒa$!݇ *P4ZZ'Չt7]͎#jVث?q$P)>>{Ɲ#Va>v{M-![n֤*eP'@C:A; ϒaI'Ç jr0^IF&l5QtoqgvLD^ @ʗĤ%$nCc4ꜜF5Gқ xWVqt$Τ}}pՊbf9mP r'.V4ع*cTZ[ib볚QUGGjeh4DJqf1'['o$|iZ>5Y)ɐHSt_obG{v<= 06jʆi7FpD ijuz3gp>!¹V`ڳ@rT]\ \уWgAˎkTRiPG2WPJ3۟]Q*w`k!u&ݵVFWUG[nݶ Oin;wSccZ."9Sҡ݂=;ڵ#sjQ.nZtR85'vhi*0];#}zSGəڀm*N-uݵۢ4.~J1{ݓ>%9sS52'%]\miOVlZQHl:I@ 'J^vVȪ3BT*Vtg`^!E]m.+Ӡ{Z ΤvțH鼃~і5GqdJ<:5:ΡT*wNf[t@ڑes.r#O;Vr(K%ᓕF']_WdoSyHy%UVzms Jxk5=81_( jO3<٩A]xd3iv7{vgrugώq:¦j"[GR8U;vG ̢ʛs(;%Z9,(ɟxcjqN $CioݖE'd $Aŭ3ة9w545aN,ڹڃMsc܋Nu0vœQUay8CdJhǎ۹GB @Σ ;x4';Ok܋*ٹGjjp7[U:Ͻh{"#?Ȫ-{+!Cc7Qs={r"EGQu.s77vyd";ǝR:pKw]$l:>ywxL]g%ȏҳgj*B w6ɱtG.,9#v0WkwCvMvσWOdE yD v-?dٙRm{oNu8:U#@&|E$+yY v&A3i8F- $O%ŽYq*WL r&WDV>%rolVM~H=q&dP?d#NK %r!JE$Sgс wvXB<%-B ȋIwΤs˿JإȏIxΤ@W> .B)?IENDB`passenger-5.0.30/doc/images/passenger_architecture.svg000644 000765 000024 00000033055 12233035540 023500 0ustar00honglistaff000000 000000 image/svg+xml Apache/Nginxmaster process Phusion Passenger code Passenger core Web serverworker process Phusion Passenger code Web serverworker process Phusion Passenger code Web serverworker process Phusion Passenger code passenger-5.0.30/doc/images/passenger_architecture_overview.png000644 000765 000024 00000322535 12233035540 025417 0ustar00honglistaff000000 000000 PNG  IHDRhW< AiCCPICC ProfileH wTSϽ7" %z ;HQIP&vDF)VdTG"cE b PQDE݌k 5ޚYg}׺PtX4X\XffGD=HƳ.d,P&s"7C$ E6<~&S2)212 "įl+ɘ&Y4Pޚ%ᣌ\%g|eTI(L0_&l2E9r9hxgIbטifSb1+MxL 0oE%YmhYh~S=zU&ϞAYl/$ZUm@O ޜl^ ' lsk.+7oʿ9V;?#I3eE妧KD d9i,UQ h A1vjpԁzN6p\W p G@ K0ށiABZyCAP8C@&*CP=#t] 4}a ٰ;GDxJ>,_“@FXDBX$!k"EHqaYbVabJ0՘cVL6f3bձX'?v 6-V``[a;p~\2n5׌ &x*sb|! ߏƿ' Zk! $l$T4QOt"y\b)AI&NI$R$)TIj"]&=&!:dGrY@^O$ _%?P(&OJEBN9J@y@yCR nXZOD}J}/G3ɭk{%Oחw_.'_!JQ@SVF=IEbbbb5Q%O@%!BӥyҸM:e0G7ӓ e%e[(R0`3R46i^)*n*|"fLUo՝mO0j&jajj.ϧwϝ_4갺zj=U45nɚ4ǴhZ ZZ^0Tf%9->ݫ=cXgN].[7A\SwBOK/X/_Q>QG[ `Aaac#*Z;8cq>[&IIMST`ϴ kh&45ǢYYF֠9<|y+ =X_,,S-,Y)YXmĚk]c}džjcΦ浭-v};]N"&1=xtv(}'{'IߝY) Σ -rqr.d._xpUەZM׍vm=+KGǔ ^WWbj>:>>>v}/avO8 FV> 2 u/_$\BCv< 5 ]s.,4&yUx~xw-bEDCĻHGKwFGEGME{EEKX,YFZ ={$vrK .3\rϮ_Yq*©L_wד+]eD]cIIIOAu_䩔)3ѩiB%a+]3='/40CiU@ёL(sYfLH$%Y jgGeQn~5f5wugv5k֮\۹Nw]m mHFˍenQQ`hBBQ-[lllfjۗ"^bO%ܒY}WwvwXbY^Ю]WVa[q`id2JjGէ{׿m>PkAma꺿g_DHGGu;776ƱqoC{P38!9 ҝˁ^r۽Ug9];}}_~imp㭎}]/}.{^=}^?z8hc' O*?f`ϳgC/Oϩ+FFGGόzˌㅿ)ѫ~wgbk?Jި9mdwi獵ޫ?cǑOO?w| x&mf2:Y~ pHYs  @IDATxՑ>^V9BȈd0q9 9ml0>3>63H&JHB9kzj;ڕVz= Uy-pGpGHA ?GpGpE_#8#8m"DMX<pGpG_#8#8m"DMX<pGpG_#8#8m"Pf':#Ѝ*]vKc^#N{uoA"~yIS cquBbut7F.SMpy-Rɠ3fK6Ns]{G mx<ފ8M[|%dYnsFACG@ VPP Dagtx @#pވcۚ;LM+c;@!/qsKD#2nnnNM7ɖ[4,C0hadРAJ #i|ڥw(fwv@"JIm / sr-*7\ver'ʢEB-$ $H=8@f"[qfA$I4#/~ }{SA@!;`?|I?J|r>+d"p}}9=Y$ (666*\n\|2vXd8M7x KV~,r3HD1=;C]ϝK9@" #""-H/,[L>Oõ4xNH'M$^z\x矯?25$1;*;;H$;q-G0ȘϬXL,رC%[pƴ6z^#c8Y|TWW' NN9E-;(o&a ad(++K2Z y-L֭[FlcSqbv^Up>t AfQ$I$AdEc`.&4^薶6w ^#N >/8af$HG<=d/Ԑ?GcZyMX!{,hKd Fo" D$0|صad17Qqh hKd4$$FH`ySdv-Xt;gq) 9Y[]G /Mb@"`DIBt F혖E۷kZZG E1z@!&T[1Hc#vL9jj-],csRq3'A.#g8,;gq{e}?X~5t F]ݿ#-ZR۱ه}qi9ى[_]+G c0*P{8X3q\t5tGbkdN2;* a*!6;@!.+r"&-@[}$1Z}:D_#8iCaڠnA]78Y@"[?S;c*7pZ:@43F@K>R82GpsC :Ƀ]G-Ǯ#QCx?ta.+qz((޸#@8~v(f@F!`DF t)@x.k+r#ŴC 8#A]eG pbvkd<fQ*ZmYl>ZG[midNXB~1#۷o_'d0izI\XI/I-"޹s\|_N*j4|iګtn@b7M8@kHu5I1RxȐ!JٲgMBV &LPRV\\$7o[oUN;5eHHؾȑ#.)?޽[~'> ؕ /,;N99䓓y ݶm[`-DnJ )_/UUUIh2juY+)$a>b]]rD^ʲ]˜]U8݇[r &m$1&F bJH 1$c>#7y\g9cI$ˑq+,,I&){02\̻yfyOӊh"%~$vzf\p ǯ+**ϖ)SHYYP]iE馛TO?]fϞ->p 2h 7!gU.ڵksz{}opr 0)~6Nf!On}кw˯kuQ>gرcbZo}K$c0吏z9T/Z5i3grꩧ,$guT2j2T4Рnt#c-8@ FLh1KG`=Srת+~'&IL{r0u8H#٢?$XUVyi>^(ӰaVBhSE>u4`^Znr IK/$s̝;OBOΣ:J6k׮UkeYY|ӟI2fql#]!Y7c^Gz(v=^#FL8!^iW_}pRE'dyrb,fZi$y$Y?0nr0e`HM UfD6ap!D1r];GHWkiK%*ӪDž9+_җdرj1qÁM/~Q'a"\ˑ?Z+~# _kr)s=i7x.ss8\rgJ4Yw!<H˳?yò~=X9<#t<7e𒎀#+-ezтE7,-c1|UK,´t%[/-̘Ϙ$Y$]23W IRc;lm.m0+73S?'r&G |u0o:m 5H$Ur,lύx0?e8[3/\G ' q-]H~8DYLN׃]]sL.޾5ÀITXIm VI.+gqjI-׃4|Zl2zp '78݋@KEh}3rm/T~hܢ9ه4r2D(F\RG P (c$H:@(}GH;JmV/ꤖ F- MG84(~^p:>`D5?Gp2w=gdP#8#<N{\G 'pwdvw٭kd/z޾uB _s=;ȬriNfbs6 G 8QL+^#"`DF$Rqv @r̵prw=N_@F!$% UBqz( ު#8ȝ7hqh:كK"![?]ڳW.!\mԢe2@$!Mx XF(v#ޔ#8DsEo6m=8@@Ow=G:/DBW47#(F\RG PU2I2pD#> 83k\0G :$۷|к! Bٸqz뭪H%vXhJ@ps @sROB 13ΐnA>\ /^z+Z3G`+WyrWɓU0Qtrvr'9ɮ#]؃?It\ţ>ZiU$y6m'IY>ݮG $8XX,&-ijnʊ1zԉ1-[i]vtI:Fx(.loU?,E 7v- K5uG mkeZHqC]]JMMnϜ9S*++8:s0u:d8qZ+h=8Ų2=IRm?(GpbA ;ه@,$Nj )09p@u$iJ(Ze+ܨV0Qube>p2'.#y7HGR&|F&H͚Hhp~T!i2H\(QEzcl-Lm<<ܬ8 %{XW1I& (Z$D $JE?Ȣ$ܷ-LymxpDbfKDq"?ISĈɂY-f#[(5LG(o:LSp2'7.#I~#$̛MDdDÂi\q؊"a۾w8JGV"Ns]IG?qH!IBK>)%An Fۺ`Qn,p2'?.#I  )uΧƑT I s o;\i0[퇉NHsfQ4ivyG :8QN_@dT2ֱ*LLT"I͚e]MO맶ti+{8#*Ra(+} N߇#d06IfȆd0.#t1NPp0 zki{iwG ïݖЅsG l۶MH"n8p`rsUsG p*:يεf'Ӹ.=V޶phF!Ͱ.#<N{\G"2Q&0"սGp҂QL ^# `'Gզr˲eZyd~axΝ^X|ز1$Mo+o8-\ҭsHCYmM*8@N"D1'ݕv Owl߾=9_+B~[[oQn6)++Kg$G6Zɺu<[T|[1i|h4o,1C8lIX{rʪU47YecnG N:pH!`ֹ+RU˗ Ĉ7xCN9ݻc׮]JlCg}TVVJ,[j^^`B8QYFeF>Nta`Y%tHlڤLclYۮSK%[`5|h9$f]w%7nT$ƛ+byn&V#\ڻ⎀#I=G>"#FPsꩧ$ioʯky/ƍ_|Q?O~%Q$#w}/_W4KZae$o~ZI7M9#Z?,KQQ+V=#gIK8Ngdƴ@msڵ}M-Z$e˖-}y'ThY?OrwQI\ǎ??%zj%~q|ӟ5k֨Æ SYMV#L)\YGW裏VCm38C***1^P*Ξ=[JKKߔL1e$dK/UޏcyeԩɺR-msJE )//k9Y`,\P䔅dlr-(9\ HrI8-o;w0LI+-\sH7a&WKC=$\p|srj20><;˓O>YVXpBp!`UZ/BrHDѣՂGxƌ:>SGD+Ò%K_VIӗeꪫԲ8w\{U§B&$ᣋ;N]_/LR}H:۷Z'M$wA屹Ibi~ > (gԵkC Q}h=\r)%hơCOJIi3?G 'pbNv+D#3~AכnIlCN ppvnd?|$! :|EŐ.vZMI͝ }cZ&T [oU>)$ Zi pOG pbwd+U{L#kL [o5IH855I LgE&&>HHHGK>}NδfYZ8m1Xmz,SFϑ#GDB.$M;I{5k<#RXi>7oa\xp rk4y H7 ' ǏW^yE@q" X@3N|VTD &Hh$6HP9c#6SM/<gkBR'j:.gx:Y:OS%l/zZ',̱ nvץK7 曕TzpG`OVp!@BEEȘfl>SԚN1wTgH92Upr_.d fݣEVF:o:n3-&p !f.75i#1;DsguXll.ccqyꩧt,$]$t3pZX7LZi$9.ú(ugκŔZ׿*|ҺHI,MG-#8QnwC0uK IIh "ZH:IIh˛"%aD ԃnnFf:yߎS[:cr8ٍ];G gSʬxƘ$׶Hp^O ?._R@s))+,9s&x}1*COYFuArlaYR}IY3/x7u=qEOӜwlERX0 q{ySe8f.dNy{o5ź$~"ozH^pʶ: H,z2ABbU#(nQюw\A LzBg[Άg-l\ӑ |[t;[+8p(vފ#8#8CbvGpG{p=8{+#8#D'2pGpAb8#8#9(F\`GpGp'݃8#8@p.sGpG(vފ#8#8CbvGpG{p=8{+#8#D'2pGpAb8#8#9(F\`GpGp'݃8#8@p.sGpG(vފ#8#8CbvGpG{p=8{+#8#D'2pGpAb8#8#9(F\`GpGp'݃8#8@(.#8@KKpc͎Mp;ǼGD(fbL#I/**SXX(,Mp Dbv8D`۶mk׮$$!ܺu*S__1ϯ^ZbSSˈ#K8Y@\$UuGH57|ܧ޽{˞={\jkk[袋䗿Y=2#8=OfAiG.f͚%sQ & PaMM24H4y瞗*&G(fHG#]M6M.]ݻ%Gasss%=w\?G pa8@4 ѣpҤIS -mRQQM]jG (D7#]wq2o<ٴiTVV&IO" %G>)++S;#q8Q̸.qG >"iUq(r3ŋyha4u8;#8QG +0WXT({T'NbI%&[8@!D1:Eqh#+ v:و >}Ȏ;ӟ_pG 33\G S~ CNPKo~ŶyV͛7#$V6[I^FV׷}V(fkϺ^@#!NGo[۷{JI wܩcvnhhJ!|pt#h!qc;Ոrr'9vn `qxڇ87PIĕjr'] Wd|r3R< uK{D ،}eZCߊRɫ7СCVGuC'Ƌv)/1E&#&&-L$3:#6yxO{#D 0I$csqz*y衇u/~8q9g/,O>p>_r,3SDO:`ԡed*O#),U[1f]HcL@"D1;U^"$MM1< dڵr?" Ϋ!&{f`ȒXHSW@X(gR T!0"uG?H 9~4պdk>}}8a&L&Ylįr܂i2mP)/-ǭKW']py&Ef[8]Z xM}L^{k_L:MN=TPI̪8m"DMX<FK466軇_UK;NC )1b ŀ-ܭWy :M (9_Y@4xOnvx's8A|66EIpN~pҿwHm4c?$Խ R[(=,9V^{{L1PU8#OS]-|;%|r"'}Cbp\@F#`qNvEZxP"_S8rC:빴}SG^gNr>IȦMu9"Nl=aL 58Q~@+Z00(rKnjYliZR@$#ӕVvAE @M~쌅һDqL?t6UB E8f "wH$<8p?r"vlnġ\T,=$YCߊ29mT;y`\Ɔәb'C\C1BڊOH$#D1}8D^%r' u҉mY]&WaNܕa[1 h&))W.Gh'cgH"`?Ɓe\tw"$ vi `I'pGMToh@`nL|I ٵ{q^u\G=Cy0tv$䃏nQd{@ҒiJO8+8nѿT c]v*?4'I oS)90@7'˅Kw 򘛍KnhڸQ/7nȓg,uCxCࠉb2m-cK8:@{/ж,݇t{89;.Կe<Z>`l[utn@D7C8ⰼ/A Lط磣YIʾH fj}xF{C. !cyqy2"vsƴR0n,Vi-@GMwS#!SՀǶ1M'DTBq!y_&%=qxx&O#-x/1<8@kHyfXvSU`Xr%nˏ">FHaaayEFaDDT1Nhk3c@"HgϨv{+5;4K#\bI7n(?=@3C(DsZ`\ eҿhYkBbMd&*Ily<#$q-|]BG h(q$rR3ɕRw<{\*O bFutgaK}m[[o.k{?^kIJk&tm/NhI4k ~m 1E^Upv" ,$|6l |Yt"ʋdQg@Kd4$}M&9s剿?!߻{%r饗&; ',&ɸ%gq}:jrv=LRFqYO'eы}رc_'_kts12}t@R޲,Hx>KLEW/gp:@D,Ca/T5ш⊗WTL?Et<[%״d>~<W^I&̼6׆`4#W85z\Gh(Ib9bdqc}:~X1e1;QԢE":y) 9݇@D0Ql7؄phfz & z[ O gk8uב D }V]mAg(_/`L$ny=/_tEQ%B! CK;@D@I",gEu;c|(t*$HEC׌ I:=θ3C8\Ɲ @biCWex>$RR)œNҼ izdC+c |nn^p1_>@5%sZV‰V]v+t:1|iT͒1Gi裦wK|s"A~Ulor~/gH~Ҽc47bnDzDGNu$qؗ(=ahDQcX+̃ĽP#p"m9":x;= \yX`$Z2bw--kA"+P?Ki?kz@J&RI h;1!-2q` 9 EBi*&mYe2(۪^gn?<$'K!M4ӰQ.li!]a';DӲ=՛(S8hU(wAG$:%T!ڒ!U:8@[7m\*3h$1'4`xFbw^yx:'-p5s2SgYjLMScPciZ_X (QXkWA/kcUڭ_a%- 5Ăk@QD@H%V y9 Pw@N)`.%t2 b%h#]kG5pR+S9[@'%d~C^` 3.ZLM#^ĊI%ux-l*aE;F $V7%P1pj-ˋҼA-cLdF h%\[&=l=8@؇(ˎĽGccY`Hp_v`Èd~č~>Iy i, b$ Zޣl|;RiP.XK!<%bW>XZw#Fɫ#c@.%ye Y XmE:dwV9 і@4sʔ~M Aå2LKvYv䔊P/HRH3dIZj@ySjlAwZhMbqZ,)DWqU1 @=X*Wz=CwE~ld/}@:oQ/(ELozI~X Kiu%5%g'|Kn䅼&"?pYDP,A"!bzZX&dXXu/8IMPܷc% lIbv=]I,i E39p)Hލ2 jHRTJ9#,H0~hViZ*XW o$kypUk#*ۀ|}Gv;ҴU8MJ(y\3X߽N&y 7AZ#^t̬$oއ`%L$IfAX2c+ngGd:@IDAT/,evαǢ]he\hDP k;Av@>X Nc$e9>@ `#S3ʵ oQ) \"9coz%~u*?zш(W&O]J$d)s~`e.h,q{GH^baЯ}QҼUoC-3/ ,SXQjMFXHayp+tԼcZP.! tޅ vY<$غQs->*0FM rٴM+&H^ɴ3'p C'^$5˔ skP)mxB 5TwVB@#)hx6 n֖&;qv zXYCӂ>A X=-3NI۩xd^3MSxi|N(ԘrMxҰ̶5'tgcK&_AdbuA WvpѪ1a[yR8lk,; Vӛ%]K~li^ ؋jlkRMjq;5O'XmX j(5 \$%)HӋφ!'5$k쭧A\C_q +݀;vȁ AHy~Ip/ÒrJۂ12(|nyL*0[3ͪ~y0VIE|XK[wCǙw|[8{@6R}co;ڳk< G=:OER? ه@s m$B i<۶o .FX8NVAOD>R-v[2A8PF1؁YwUHw imA|M' ,I n7ؼ#R:̠ $*+z啨eм]lo*IL u"bDŽ^C%5~.ä7 Y!E(kBKc E> zk65nZ,x-AQ\zQzUE"]4ҴxF֙ PGb6Wc &䱏Hc Q]&aSn+P~x &'qrhrzD&zqߍrL&]-\a =rp}?NxGh ED1:>jӲQ$xXZ$`V!c_(b<'⑇AOBl3Ths2 !lTfg>Z#0=T>c%L,8K$* *|LKZ?QV11y :yyR ͔ۂ@,M=ݲ9˚%ט\lA__Kg4<Z W7)^$|!AXǥ?4-۩bϣyJ,ˑYoX^(|,-f[cNI u0׾!Kkw*~vXg K.c:[Ũzhyp:@۠sUI$pb0B)3ӿt_h>[_ :=UJ8ikX\R9;tẄ́!$XІ"*%jIfTBMKj'B4'(FW+ܾTWa"Z֐nA\ &kU u ca aAc =zp8Rw7\yt5P~ׂl)I}00aE wPl3: ֙XȦ`M?sc:݇v1nd L(_Ai|k"fb̦Me2&Ṁ w[ɓ8i8u @FK&ނ{qͳh|L: FvQGX .uFCbi瀐H!Mڼ蘲۱nq]bJ0&k^',"G\@*1x1!1@zdUy\ܾP lK& 7FQWoy:A>[u?䀻x*^%xy*j3Wk,b3hdCŘps㵛#a/~n|iڼ V^,d/8DDOD{^ =@9{7aO` ULdL~ .|<{',Տazq)IKTZf]l)Ix:N lgP $u LOmâܥ3cb,^'~KKWt^2_) f} n'@Ѝ\Ӑ3 LAֆU>p&e dّmũNTi x,o@5c#wSȺz%Qw@ qְ\#^*,h"G}c‰-zc4׿vZ1b5rx-º܍x* ۛ;VmqE'H`V s@V5ېN;.j>eϦ yYK!T0qyE#A`2 K+"H#g*Y`=W"yyhpݎRѴui劣/("^| 7 IXeG[{F卑Ĥ+EGM[13 r.%\,l҂eu< I^Y$X cR4lɃUM_Y5$^I1M1{yzr*{(?'4mxYgjs<4Xo I(uv5*^CbXh\% MJC'ЎlWƐrAOAX alkeNʖą%-hiXjXH|T8Z햗1T~`k\$D.ZF)1eiqD R4MVSpS&6z` EȕÐy*# JJN^FMԋk(<18Oī8{p"pHD1lMt.F9~ZGCb]ha!IZZA +_4al&ܶJ5y@* %QcL\\ilAH8&d;4&9Cb {ABO32Ngm9^,:1ςm+3xGȢ7j<Ub}%ߪ-1%)m8PVn'1byD0E(o+,KNn ݧDrg#%VFˈbn57Σ߻K~h,K[ii_ª10 iu҄9#iK/<(M\{_7W0 kG[ϧ֝zގ^Ҏ|VƦf9QnU:8:8^RGti7~(ut&ra{#Q4rhq[3qN.Vo`݋bZ@f!bv.&I3I9\9$i\;i5HV(^um^#St Qtd_3${m?pCvn:k8S91M'DuGvli D(k62 "Uk"8ED0y :{`e%gڂ*Ƕ@&daN$!Qق]LRa?uw 䈁1 :?BDG(f2L+H ?$IQ " g8# $n)g !KwL8G`Yz߫g)(*~C&x*q<ގV'Ғ-O[ei}ևc&igs_ pg Í /˥jl]0Er$ k.3N)cgKO,sN#1"L-{?\BϛnpBtyCHnbq^kEQ6I5oV:Ow$FI:xӵyɑrw 9ɒ˖?{[䳏}V*o.`ݻw+ӷ9ߌ_;kb@KIEW+A]-M&=L9wUKQYubZ6jj~wT (Ȗw  ]5RַLڶK/-)X fq߮ dH7nebf;gKiޝjuU!xVs\*T \6,O6zgv{t1?h9D@|xbc۶ xR2xT¯5CZ)tŰLCR&C?a .,.eJGa\%^q%2|!,,-!qME- z#@V"yn%4$7A;5ȆDFV)",)-Q^Rرa]V^EinlYgϒFȠ1drW/=Zvm%w\u>,BU'qZ-G^|:cnN > XY*9JyeM^ Zg!夫N3G+|d،aJ"ͳ2d|1mw[O%LO,c]w)zmG<-[F o^b1p+a&"u[[/˒+Ȥ&I!}p? Ayٰrs1R_W/?Xˌf_8^n>fyץ|ŗ n@ʫ+u؍ފW|7?z}uH^MFƳoΗ=߾G~Y _jG`Hxk![l㝛v%r}HN/k[+; |ٳq=Jf,!W\>3*B*iVXCL2ErS 'Lx/;˞{dWS7;9 W uͼ$$e򜸒_ %3ed mtqd ɿR.ʓ>Qfg454)U:m2h#`Ձ"%S 2(&H"{O=CFD2ъ<,#߯%gɘT&uo?8FhkuG rrt2>$Q!9!_@U:q,?h ="[ݢik箝̯c> 4H&TlbDlMʤ'5M+C⏤dcL7)Ǿ'q/J,ІV²Bٱ{;e'ʒ,FJYuBkQi q1-XQwe\*m3Zb&9k@vC8"-\trf!Gi9?%f~ߵ>ԒE"tl rLU,rI%ڮEt&:&Ļ7얾#b/K4i| \ܦSLm+ 25H@}]m?(s?Wӡ[ t=Z*:,o/~y+/KNIM=NNvISm`49߽]yL%t?˿Y&6Y!mg9ӓD\ zCOl"p3cxϔrD q΃#"=>kB IʿدKN]J#Q$D$wPDep_.^X'[*yuyru˄c'5A+e'Æ˦6) 1\,Ryhu0q,Riɇ 3ϟc {sIKrd,l֥R#:R%.d 6PTˀdG Jj|:-~o ,2o 流>*Uf?9'HJ)1㖌篵Ϗ)7"U'EHy][_'-Upk:ҵM&0LΛ x* *Ҍ5LkƗkuW@Ffd+&)u{G]1ه"P*@#pDȞxR7JG gLlQ+hd$E\pf[`P+yDYb }Iq.ߪ\Pq$,Bu&ۂ VO rwmSWӥ%=g!^ԇg~0$+0jQR9`vsrJ28G@"Ji'ʎ=@w)eQu#2Q$1!TGclN U`]]L;,sx:E|@uvs@#G҂p ,O c$йB A^N]|<˫sDO=5uytz %D$dQk֟ I  c{ۗY=SNa׈tpm8Y?œ m> KJG-!˙^XVaJ_Խk35~Vδe,BM{5Z dɘpэq6#X &~@nQ[Q*Va P3fJ9&hLc6+eRlJj6sUbO!:JA(nG4n$ -+XÐ0opE_$N탤dh\Vk}YY8@]3QIHi0xCfiq'tWHKMǿR,HEyNC\E$ 4S3MBʗ$(م]&. /'Sһ{gϐEkҡ},(Eu+M!Y[7Izu,,pT@x HG{" 7_|'VTj5˕}zHX8l^SC5c5e55Y#,EĮ>Ε0nQVm6!X# 5)5x[#u'ᷖD!|{q%ʴIK{K>^C! dqtQ3Vduh"Y2etQ2s2ՍXpm,[xIf.uS8Pr[؊'|%L9tq$ "g,uڝljKMAۇ}{kc$3gϓ?;SN3ayi;S7X[ZJ1Z }Jg}3K­~s-n$= _wN揟/99_J{cJz&-0_sϑnLUWɜOȾO+]*o9V߈ORTg҇FT[e$HҚt^wL9$}3ҕ+!Z&cRȒ4"hŀ.C~2_BY^k d=v&lk괙+IG( [(컫8|^V._{$g$0͛1[;NFsҭsGi{<;ҮM+׻- /dݪ5֤9ʛ.dn4E |)ϼlv[KߞݔU9g'cʄaX6k\.X$={)|edY3,E.) ]*c'{&}NnK{8 h7-zuSH%p HaZ^tPlcj p]Up"6Xs"$qoҡduȒy%͓Wnw& $e : W*|p |^fGm&C/*Sߟ9]r s OO_̐'GGØHe_Л4)lZpYmzSK*Mh~WH&w̟?NÇȨS.{yrW*ѹ'e.ȏ@E3"ˊbixv?Zr1;#_>-']p ,R[zDf̚ķ?-]O!"[}qM2c*M>b~r}Sm) {'=w@I|0ECK@~G0%_~;^{-l)LŐi yL}w8D.Z}@/8(*zL:]?itb9x#力UחtvұyǞWn)ݿzFkӪL7arEc\g[|.%-[C[.==PyUU,<[KolN6k Է#~f\[[#;;k = fwe\ާJ掝+y 5$ '.Խ^rZH8˙t k'c#O-~.Ż_.7}S< jtܾD‹D+DxȋcF'EZuë~WsǞ/r_DށsNm%6%!Ǫ2Yx 0xE&햐kt*)_^事OO=Z@u ;|$vl9 7'.%|=k# -]ɁJxμ9C'0w^ naK,mkMd$JR0ixTcrbѺq!h"QIK埃+Z(/˦/v%Mz{Ab@ l)Y^^_̖A\/ҙ4M*K">2%2 O?/V?:6veI|۫8IlTSjAN.Gj ?+d7O)#\ЍY$9'ڟtDNu&ON}A ;J7t=<])/9ͱ}+еMy/EoS, 4#_ڼ?Xv=Li yr)dWwQqׇq\yDSvGthRk7?*iŮo%yl KC:e iݢѕl[l14|gR <4b+|3B $!g.SRZvs\`:zC !܋#Ԁ@݉bo9|$5eduΒſ.- 堖RB~Gɺ4[ߓ -Xl_Wd! /ooVֳӥL3r+zHk/J~|g7o %1LHWnl)Í-RZrF'AXט)cd*Z1YaPC>5L]{ LZ9_賯l%~^c LyV2~ү4A-H0R-eS<t!dM$e }zts+=,4$͚RmbRKIihKAswDeήnK| (q bxC_GT4jNð1;!ċ4\ڏh/d4͐X}Ңo )T"4L#XyRefn3_*cUKZfq<ӲJ;]eCMuBZD71PRDijCIo@+K5oT6XױgZlix`-K䆟4]+E_|/1pЁJ&GCkF_`H)frrұ{8j?j$ 諚>JN -b\O ?83ZOAI4_Rkשb.+nq(?I7? 9q2ѣ=k~ǣ]ݟV5;<r4Ž,w0#U ~=3LmuTLu#=#͡k{XĸN^NY^X( -Hmdc@#DDQ@%"6Wo#C."r\nVv{`7mAm$%ސ潛^t )[V&po3Fiu [$sB3P'+Nx7ьw='Z$/Zmpkrjub,覻fS*0xFU* ~*1rB؁˭P˗Y#wJ;y7 9#Y c}ϐ|ttF1m)EwkrF1w0;x%Z7c^˵ݨa4m`s%"4\SH\k]xkZ^}c7?wedD8VzYWbӏλ*mvu\10>KN44!!/,@h X^yewp8,{"v`5VrxęWh я0XQ ujS_{jDjM=HbMIzBul86!S8$A f6͔V}[=g;'`aӦ{]3D@IDAT!9vl -h7L˫)uɲux[Ém~Ulhv\ ڦvqu^y&@vS:yT\L^2"# rg*b>n'AxHb<~)&cB55ˋvkEzI^]?Ptj/߿$ÁZGvrטvThح01N $Êv5kѺ&霌bOd@ߞrek8rGAP˗? M+ӯ(!=K6 x)yZǟ{Cr@rA{a i&'_~@}]y~cyBᄑ"Qep辻h-k<[r$C@jwQ -eC?`w.;KĽ,y ʙ ap$O5AK qŘrO*k>EfG9^hpd6T זX܀F:A~+ЍXƉ '˱'@S{J*.Pڌ|J ,ߺEM'C6tԗs9vr.Xg gs\\.fr+06 snDž؀pqnD]qanƓ:ƥuح̸qe"~ Aڜ`B-G( GSε{XS<%ɱ| i,6לMxȊtqV+ª|!E+%dqC1+!g:+p, LoJa/=dիnG~qa:ڜ1aXʰiE<+,7]v&p7J֟TBf|Ͳ뮻n SʓMoȷ h\|qBU~c#ΝF@=bʦD6$/VɛF3 5A4tR@CaHfeHRS8ëq5⥻5S ma`A18>BGUmABAࠬtk`rӸ- `p 0n՝hOr9:~MV!-zF:ȋ>zP/똶s3Q QJh>՟1rOqwdLc5бe~b5٫]ZS݃QpK]j_㽚n'չX}o^BVSе(r?q}uw& O@ $"8)(/u$3*mu7VuɃ8:EÇ(*g`YIl"J8j"9gcP6ɯCX5]d^mxR$di vDiGޱ YYqAwcb~܇#pԛ(a$y?7ɋqvㅞXZ',.X& :"P as[Y!j!zc݃S,SY f/ZK>G`MNC7f~\C$Wan*$-&,`d*Ӝ&jm7;&h4yWYߜnwF+AN?`bCNJ 7I]Czʀ=MLtjc1lXkQc8 u;[cC7Aj$4J2xy-3 )Rt<[vH[DRx ~TL5g-`Fr$u5q5rr; gEĿax&8n4_H }7J*ÜEԍ9sd*) !KUH)8$a֗e֮/Vww?"dF=&ɖcltz-6𗒎Y2 qoߑ?M˰ I gɴf+!ࢅ?å`G%(a%ؖ6R;ͱ Y5 gL n ILq,>s5M&mAei2UV,_i;~XWKEP$PgN(8kM6Fzc5,Za͌6ژ; ̏-,Ua ۺC$>VI_+.Ǟm 3?T&OR^wyo9;d͙9[`|ŷ2m]Gȶe~Jsb9.'\D|S'_/:]Zn ^%~L5W.<8}ǖxB+9MHƲ& NcDʗ8C+'z u'>G` Q4mۍ5j_$ڂyUeZ]~2Z{%s~BW)*I -J#=5{'_-8;ĔXt@&,[B?_'E 3~2iPf~)ܗy]_sv)#d0څ\"+rw(6-DwEUsKMM#v#2u,y[}vN\=%!xqS JfO¸'}BAHۍ#k [7t3ɘF'44L@ ,ӿf]AqŗG&p陲%4 fzvMJul+#f6{,ϻF>y^~m4Sb a,A{t(@;x-. *!Km?ݴIn#1n= 3H;Oݥ{YG]lK|!\i1ro;CPl-ԅ8Q\?ZX;u'8m,.,cMW"qX?j[j!7m.Qj,[,ihV-30\L  Iy' )^4:u*uLGmZy s俏(>bx9z@qZt+D q SާF$vނEz=ss2dA,蔸oEeKp KdCZᏆh+I4F9˻2UohUɣcpb%S+nRg)1Q-n=q6/c 0&i SI0`K!oDy̜3_ +t af`\ &kI V=kߩ}<% x. lWD $n|>RhcTFMf* 0 $c1,G41(G]mjkכ(8_mwsb׺SY9%-K*Ɋ铤YaJ"B,RvZ# rW!];_=9U ˑ'-w="/+ѥ6ح[ r9K%7)ŏd$7%ڭMsണC(QQdheU۹bU%4f(„xbSb 5fn'w3;v^oȮHa&X99ė6,5U ,R1t'dtꋵJ[rEhfe*hDo0RRZ*YM-F#HrCM%n5(n+ol9CGӘO=^:qE}78ˈ-ԣYFe=}\{v$ulۑ dEt$KX,Ɲ͉S)9r4Y룒=CcQjhH>vPchfIҶs~k,sMN ~+ʼnbbaPjm HAr zZ9<,,ү_/uG+.ZLƞ^97YgB$c$s^$&F*Qgx}E2fRY:6tsD;w.:uұҬ i4ȜFŘzETCMʉb'7k#a]M6,X*t,dlbh*L (wbWM×o {$|{$v^.k苚0 37 K<*mwΞumYM$@|Jdor,CLBmLm(چOFh "ּW:/ug%spM3x(|@\DE+#by57xp񒟟 4$f"DmUt|1߼ϒ3ɚ+m`pQn]JfY6iu(&lZ̀_mpȠ5^ AN=U8(iժCUEmb1QD'IYȔ}_$hA$֓'2hzR{Бh1^FNZlitT,3Y󓒖XY?yaQMLlAQ$Q,$eKA: 8F-TW *օ a{bϝ;WJJJt[PjmRrcEjāv, X$8h]O0wFu6Y;Q-9SB67ɍa MnT0w,;n.ÈPYY-)YdH R8(ie3Kr כ<~AݻZUŲlW2lWuW"ce7@(/L̞ s#J'4V̶am8mCkh݈">OB]N4C/ti\f*ZxIjz7غP2&!^sHӤ%YKIRQ6195> 7JBf.so^TSApt]v?fu|`Fgů; $1R'PDq@A⎗HNĀuq&yĒ/`MGh#{#{~h#B^_Ue}{OoQx^ %ºmxaTM7f7}N bA ,>G5v[V4bjf{@XaAFL#Ųsn/5A{]uDMyn7bs%3^!=%r& I,^._q'EVO#Hiƃ]<[x/T!1 uh2ew]S$}WiҶGCBQ>9g?E o.P{_ c důSxdYI9%5)@Ŵ7\oVq(5g㣶 RyHIZr# `0nkiE`ڣʞ{)o,.nwy%{Z⪒*YJE>9ꓥK.@VG,vs 5VիEZ6ɐ+dޛ"9]Ov]$]Wm )Di yS3+GDV@{Xq'rBx4 fNςҁCNnM1Bro `Y:y)ܭT$_*Gc!rg c񗝟-f6>=HNNN.QMԟ'IrθПAj*IgsA@ޖ"+A6o;i٭P7 f!]Y:?<eAmq>}U(߰Æl1jG#5JͦAսypqnIvHĖ QG5+) !M̳YfxFfDua9~Ť(MĦD`DфƟ6Im$$pt $kOȠk[|l̃$$vصzaa:]8DG̅M8Is]G3DIXƞ:b'!0 !Q )vPzQr4V02m[=0$dlib"XGRU<~T`uE|Mn1hFlfefeleof{-;!b'!F*x<8F PMD{䦟lRX&|֬l6Q4gu}ݲg2|4,ٰԄpwfæxc'&74WҲse-Z3Dwf7<@ԛ(-!ֆw6ArhlUiitEnVm8a@#D< HmGI(|سR{ <#xpxsE6Ԟ&.j.'$LNWYTKmf40UYY~s+Bny؇Kk4$-5=B-;FqWG ! |6MI \1dT 5$$$7>*M0XxNCC(*$ :#zėڇ?H(52ҿhD8QlDYMbIH0hVfTJF֏ ?6lP$ш"$Fke8аLhjڤE?FiG~^{MIfyeBZ׎=Db0Z]붎8lqJ2KrzF?pR~ ljb K|Ycs{M!@!! q< jp47^qsMND#% ^A.^, dB)Z\7k&؇~ɲ=4; >.X ^B{]waSeoa7֒ Iٌ% *A#IkBʵ4,b#cYxCv2IGϚ=[1)-U{4$$s̑,Fsn,:QbG 9K 1{t/!@ISp> $+ǎ@Mٽٱ>!ҬfOA16>_^a:qTC޵o_Y >G"t3/w?/,@G쵧 mwloKHcϞG˶{!X_sd;¹se3gJ ɫ*cXvY L/!ǯ"ڷu":(K#)XWꄱaJ -uFN:˃]8#Gv>@7O*m:vT J[4ot/ڿ- n  -[xʄ 4u 6}|pɅ vˡyV3wn2r\+v;_y)O/Afii<3y&q ypop@4јh!=OE$-Zݺ~*%~f ept>noFMp@˞(h č $ Sٿ.]A @Ώ1j59q6HH"ݛ4˗! i 1r.9c 3G@#J%yi9K].RGlޢ Vqܗ_bz$s$m./CA<ʬ9x&DwaGpG ."j %,Ę8H:A8Z:Ί}$ !5Wc?DuE˖ AۑG*ATqR d9L;A<ǡ[{_NSUU) $N&#eڜLôi7,ƤAH ?5#cLW{tdj!9[MDD1T8#8 5$~?I!Ct 5oݼ;Vb^-!$~9E@cOfPs keL2GqMN)7 o;ja jy-40y2Ě_abr<\UEJjAG~[g;c$I$i2<&0zK%$nGpGh8@HDL!#ҴhS(Y~nn.f'%cF#@K8hm͘L u V\oppr=񏜐%GᘼBk zA3b%kh5{aDQ&Zx(iz!%xξf9}dy#LZ &p)+o9LI-etܧj!5DwF(JGpG k,KJJG'(Si,#tdggkzvӦM.!GH^ғ7k3Gp6=]t\p.jasZZ fқ^JppX3.~p jDP%^:Z*&矅8äe˖9kMA"&A7?py hw `~݂UUU`i x"6,]T#Z78Of2qA`ĉrˢExTVV #( ^F̏-C;h"oa7׶o--] C~P-:u Tﯙ_z!6=smn 9餓믿Val4]ܴ"#7p4ۗ$´O3gΔ͛K&M"^j\R T.]ydd%\lZ%KH-k2 =Sd4^pmVŋM6?Ι+;uTږ[n!$$+V֭[ka ?S~EmĊrSVA!T!8c i׮jc)?QmlfH.Z(5~#eK,?0+F7gy7CUrssՍXpibתU+%}Y䚚_~E;6m 80={P`G\e: I ɞi3#8BjyהԩS'O>DZcY"Y뮻9 [~virGg!#ꪫ駟oQx%r-ra?._|8 ?PN?t0`/w}7\5$R4~j#_}U9W}YO^xAI*`8[sO;Wͅ^~?$|hӐRwkLye$>;j&1=DjSwm7 s})5Y5~yŐdc)Sӏ?QSI^uYpsp' PH.#P{ɋ/$瞓oVoλ,?_7\o?y饗ԝ]W\qP+E7j IhLúIN$aZ󟕤vU$7IrHJN;)nTviRƮ]vEN9G2r_#rm/V=ܣ^r%rc?ǣ9Z&IƏy~UIOO SOI>}u#'̙;G˅Qjm%ԤRJH<)s0 vS].L)5îk]mc:G .,Hy8Ġ jCHj8oQ"E^jDqPCױcG=&ݳ:KVIQqGa9gS%zvCSH>lD™yyD9$Ď;5H2lj(FJ)/gs|%Ia޽u%v7 .67(1 c2ݨ9dNj!Y|u͈Ff6_rƽV[7O: yw4lN4,)'qv5)'Lm$ޫIM-50 dqV7 1'c3IZMk#5)xŵ.#$|<pY#qP;Eekm֍iHH)FKpl h8C&!!b1\$.\cL mR[I880j5NZ1CHyu&$eAdrRG&e fjIgHrI<('Ӧ Med71I6(Jr|#d6s6R$54-{\S2Ϗ= Ht,pG)7If8aX{ҲpA\;"5CwA<`&K=jh$ >㠡h_Ka-~޳anjNwgq܍#l`\׬oLp: @`D I$% k k n-NRfw, (dfT5݂qRE9kn^|:Eoi=&nD֯7  G~Ӭ^!FGpjilAdŐXpLQG}?::S!)p$GHN]əCUC# YN/\U'FͦZ?2Dq]踛#8q@!zb6J]3췾[\ Mr"=,c?c MvѶljb -GpG ƞaffLVϽ M*ä.GG5q ,-wҢ[`>CFz[:C>(ƊsGp  &HKKUm" " )̅?$L&qZS=id!fz =O9K6ꬭUqYI<ʏ[">죀ek1`H8F1.҅pGp6ҠD2BMkHiHH(a}#&FNTy3<8Z"xn& ~X[fefIVvj0yQ$Y4cuMkkB9#8q5lIH IFE6mݯ ˙y6=9`Zdb;Hyk]~GpGDUc;5F(r"MnnnEEڦM{H ItUGpG $.dvHiI$RQu0^?lDMPD_t0b9=:ljb-s#8@"@b@g!H myj[~8>6"1Irg>o'A:#8qs'AlʮFƴkYAl!XYޯo>(A8#1A$$8BDamaOuCD!P8G10֦ƃZ|[Cfv6Tl ח&/w_X#8BPtCd3/ r=Kz%G؀8Q܀zԎ#и8qL4IvDp„ ’KԞ6mkJKKKy;6.<#08QLrAG ^VhJkrÂ5s4mT>S=әg!>f#$wG^pC #DҳgO%K,5݊t>}H׮]%#=C;H~#~8&B5xOp#x:u~iƊ.hj y^VV&sΕe˖^zG pb2p6$z4VdۈԛrF #8x) pNxp $̑8ڬ磎:Jrssթ&BiavG`S!DqS!:@R"YC -0`vGN@~8@ D1N pF!m#CŬ,5kf]nGg|2K<l$G7RI ږ*C&M#‰.r?yԯ:C;@4NkG t/!`Y'pl 7 W]uA\rKwngWTT(`5 cM ,MG8Q7#XJ)k:ދ$t\&//Ova ,2|ϱ4>>Qah#f#^ϓ /)O' %$F$'kڵ۵GrJ-cǎ2uT~fw3l',]6IeeeOCAB`紣=17uG';-$+A2s|a9֨I"Q!ĘD /k.ހo]w^_v[>aF%ig<=>nY *b45V&KXR1j1EDMԧ{CQ{[.oue{w~WsΞGInݬZ~6Ա.!4ujE] @y'9 a9U#X?~nѲjX-HמK[s:,?ÈVe*yQQiwq Vhlהk۵kWنԒ7P0o%#ք}*؇ /7zr'O>T><Z&~/K d1b_:k~}1r|VP{lS=D.rM0b$R w޷Q-wK.aÆlGGs,_>*NK''9'N`+7ׇH{<PQ,+K/Q|0r\;YV$=EN赪ѮL˖|@v{DzKe 5':E}ulVNr6bNm"ĀA-Wjݾ9;;KZagYX݈8!8,fΝ7olR5o&7m$"1lҤYV&M$i3sX`եm}TN>ޔO~f U(ꕍv+ Ljf(+V&:wk QS VhX---SRRm[N=x 1yT}fϞm] ġXbͼ9SeĈ2uTѣĝ}0K 1kf?>}X[o|P1/x㍖q'k{};,_~;~gn2yd5j{҈G)={1ꪫ,3 _ Lz=?oC=T[az,jCTMϘWR,VT}g3CNA[|K~&-Zkd> p{O?!z#B]gʼno-r6',wy0mGax@{or-vك!A~/Pߐ!Cɇ?vmok?Y W\qO89cmK/DSxM7.gq|w\p_~F*OKA!@ [^Ș*2-ʕ3E;Q:U`beJZrt3pwl277ρ82ʟ;tkV7tYK/dd 23ϔb s1 wq|7裏ԼB'?}41e6һwo>`3#C<2Sz-r1c4⋭Nx`w51C{o%(?az.M$I$YDVi/ʠ[4.6v]֯R޼yK9SUEjCqmD *t'A֭Z U;<# :A2DF%#_!_s#HwFs\3'ؤK.9rvfmۧn#[NgP`(o{s[Ԓ5)K)_͐ [~ͷg qr%pDbH!3DZ QN}넢*gpy/c ?|1+3)t mѢa"v<~lyS$1s!HvB?TOSN1R.sd u F$0!抗 m0ct8jyl^Oa*EqWM9ܶm[3r!V<&ewA袋,@x<}tG/ï1c'N 1E{Y5>!N@~i3CCLjW\2Z@ X 2Q"(>[~Tuh['boNn."AßN0 . ֏]v!|94z 40aD~mz|HkSdr~[ȯkw}͜t7DH]7(W_e_hR̍@#/Gڑϱk4Q>_6{\|sytHo< 0$[nfСQdH<"zST[<"> 8]{6S~fi,AȔ9yȴ: `7>s6fƍlzriٲdu#oOϒUx[Zp /Y~S9hqWI=)w;v^ Pp&i2c9u W޶$1cڄ@* h Q?]YW;2~S12mHq7' +7 h+(֕+ (iZ8HD)& G5wbmm!QW'TYbB"@`"6-3_zިՉ:@:|@e@iڎro}Տ RDEA Q0jayܣE8QYܣC b]hQ N8m ?X"i0uub#b΁@A bQ@F@`@s,QܣD ^7McREGY@!D.h4'XE@!SׇY=ӦWq PXF%LDzc5PrEӞj()(0(C;O?]\^'HޑGN6#*(ւUj4ցCD׊4{lzDӁ@ P!(VLG*61PpXSHG9@-AmiXkɅj>bbBS( ͮa)?QO0{3pMXU͌Վ@j8 j,n3Ƙ:&(]lHuf<n>,kbu uQ@!D]Q PX1= 0 8hn\8oJ e ȏ|뚔 Xu}5U_(r@Xy"e P(#QWPXZ%jkl[4iHWA4^5nK @5;kӸh*2TkbXuaW:ؖuaF6+]E ;͐q@Xy"e P'XiVB=v%ZC [bi3׽,[fM3V[,W˜&wኔ Dn\g⎛B^ziH& eΚ'y}RLeO2mfEd-$QO.Q^ն_Jyjo3Z3_E)^& 5 PjyD@ X+A MuCnJ )zh-;5~4<ύ_&5ĝrs%uqUa&~T_F]kFcqlc/疊rO#ـڸm2νn]|<>fSxNQg>-dM:OR|kUq X>B 7H(֝k5 )ﮑit7I2oÒgiEZ7m }ɶ-E$\[Wz¨;޾lڱ)Jq=κGfgA͏_F tq-0@Ѭ@`mxHG jh/rK6}m@dҁg*f4Ic' 7VFՈOM˦ۆHy!䩛lޜmde&_%dMT8KmLKA˂XڣhmLJI]SKHiORr^Ӫiz,% X *Ӿ^#HR+NhSخ[ fdjѤO#>:ik l f-_\5k9M PXHwimW)cJB'Ni"@JNP9[l~Z&fȂѣ\ZVek߲ihKWO$R\( Zaаh?u[)񚺰TyF~Ib`UzNm[528uQS6p'4NzeS.H}2= Y~[Oąc!/GLR?Nz.Wh}YƲ@3YVM5S %H1[wQH]:h M6(T:%Z>ⶍƟi^/ (ц$ҥVp_5 !z/?#C7n,M4sʗ_~)GZc* olo]:Z~:z'cE^Ce#I×^Ӧvܤ/U8CL lDÖ+hJ )ո aB0yp}HK%cnZ(5[ >Y&^+Yj4JNپtn@#Ew۪cڥ>gL|p+Hўˮ[JS k H%2F})]=5v=~<29whPk31jRDlM#m\-xҡUciHd2S$>2$گ!46-W^[bChC'+F-zmY2~R`'#AA+OJYekӿKS9|@kÂkBձ|W i)>32^lZ˖UtHK3_.Jv$zʹimfTRD1Q!sŊ2vX5jn+{Rёx!G1AscƌmvUJX1P8!d뤐ֲe˖ 沥K /^lyI422y'*U#A!xIi2vJ:C0JS=+2W5a6KVM6vJO~[ C{CIZI[&J'!),&.ʢДWNT$Czj0ɤ9M{SK6^ qDv<\:l()唹Jߤ WKd%lƒlk$!fȜe)9xr"iˑK:/%ʀPl~S[.!\:e~Z0mq1$~}[)lfdz\4%ŝJ/)͵=;*Ci~z&U` ~RZ4< .O?T^x1b|g2"dsVzX>H. hLD@.d4#쐜nKJJdΜ9dYruxO0AN*oun#GT JI(Ao&vt&m9 yHg yp:o2~Go6]y\O:=Q;eIRPrB)fQ@敔}4́ uĥ3i֢RptSN |CL"C~R2p7%o3HM_Q:m|pvn@+Q:HMT#2Em4A]3MfVRJ*;[S#z#ʻjB?_km@P/B[ޙr!|HSSU[ 虃ۣlޮD*fVT:;k镱/z׭[[yʆ%rD_^fp8w\dԹ;۲:?VexbDh!.nK6"-!AW6Mfn7P תab,QlAgK>O[ڇѴfNVbr d^j}Mdj%B 3djs8TubpL`: _DVb*C1vSR/eW"׼@iA3X[MDM/eJt·o;%*!+v21{`Jr QNAda˔a0?9| ň|vpq]k%thH/%=7_gȝeLеWT#uM_-lG#\"F`Hw\jj}+남&\cD<0VUz 9X}#%xh2G"%ۭK!.bʠ=v=<^47hudJkɲva9-;Έ"X?ށeDvjϏğ:!UG b1r@9A$/-4*.RD|= qm6bU)K /3L̛6^eJ鞎m?TZ 3twFxvQmy3Hg%pޱ2M7 !~I*օ nQm>?\ g*\7|'ۖg/ ̟:[py&߿nAk9xI)Kj6W_n)۞^zd%?>NKe;KϞ=!ļL 8'~N=_`#:Kpeh K 5pj&!!FD+NO0p#LBKf8mt,j2WZ'%U,QZ7%L.u04%|˨bF{c^}si^4U՜FK'2 d} ç5`a/ejYe4iiƈjz #ޤ!T\:+D!8[۲^ڄa9Z?_2$6-Ls2fN'vh! 43K6mD|rg ڠA?iyfmkf{1Fn@C;OLt5Mz,quFBX 0@'yٌ2;S B^R;LQ';<+z !q#X<|Ӱt\v֌HJT > QdS=x"a>^*[(22H'+)N9U' Y0*  moQ}r8֔ÏrǯΩIe3:5ΤI6tKT쐤y^1]өo Q{ "Ŀb?|?0 'F>DGndM7>}X| eiLH9uIn)MΣhxզ!RZַ,he6!晍Zq٢·%%J/̸;OܹlI3>pjBSD蛞}>`Q&Jض|պ0a6I%h3 GOW];Օ.iR{jVҸE岣]JOGz-[ۏdDҏ(7sW5}3i8D@%밉jt7ro(d5kgy*M1nKA}Oڥ~o}Xӏne4s"mErB@RxCǂ08矗n-Ad` >4D.'pr)һWoIOaN #OQF|>$(i&R(]^:[Ⲧ1aSфSf4MyIO B"y>iN;]ּLS P.鄜8k!x>K)FB♏;:5j6HIR?%G!PƓ#}:xg3ruao.z^3$]ɾ]&wf-t_C2g.;FCXe'pΗȪ0lw<پ6v]0ۘ?3Oڳsp%C8yV{ߚ+3՜MvX C:bVP3)dtfggY._re*&g3y]q#,OQOW;ښ7|CÇ(f ݻ0/$yCD3<#䨣=zYfb묔x޾8B@+ځ2%SН%F%8J 0zG,q0qmZXс,' V T xRyX'?GSk)RXck9Je7Kdj羿S;XR4)Q߼'F-'T 2Mn_p sX%vh! !OݵA O܅O܆h43\8,_jzuAúB2Zf-ZVG](>Ӊ-9VWid-t-gfw;[x ZT렖Gu?h=[h]5yz7ذEIRyhBveds7qpzrMO;.G3:d6dmkh$jXj\҃4AUDJ(V%HA68\wuImٳgKNlN69[X.,)N&dxW /Ռ@IDAT@kt,SRW5^Ht|A[d>N{Y͘DI%yZ/ORIw1wjYd\QDeK%n(/NYST~xMP"h4;fD.D/:ICV?@Uw8]g(QΦd(=^m!R p頤<4ewݿO<_XG(c.O6ͳ(BFfhtn,;|܊ue;tFV}Y)m2oJ+uZ"eOh2dgolfTˠvf:`b]fXsCo̵I)GZVGF/"{J^`6|*!p@Xy"e=Cڥ>(LS0zhX_-"n;蠃䬳2M"fhA8k .;yzGj.VaeiJ\ tM*I|%3LWEC S?%+Cko/4WV,QRsi$Di"5J yodpiJ .K6 U0z!ͩYʘf.J5u阘$Q!@+jLV#2M͹L4 frMW#XEwbj9fj̳ #/T$ UL+b TZ3c5~&eUjAdhÄ9|I3B+i49t?K0a`d[ d d X?!8HH T .Rxcք3_!S0:t-J?x`9χ0)oEB@Б<>\ȖQv&Qz㒁Ƈ]D!dJ^yF\|:)%zRJvZ8#:RLKb5sJPLIq:А-UwDf|!UG|<{thB~$6g,V-Y4c]ϰp2ea=h$C 4օi+*6m}LT4Q o!D[ĉ@`5;'uf23k"La1Qo_RT-„;Sկ[j>z  |yڴir! #͌6׿% bm13S\hD=u:jrDMV&Rm5ldsSo @c9Q$O)g}fh133Xe嶾2$q}iqvqGkL@8hvon5%xB>2OR-Ջ@7r#8I 2jΜ̉7Lp rے|'r\%M3ӆ# E__5#`ϥHq^͈F G8^#2l0QŮ]άrEooN.7r+U<<7Xb@DU ({_^-G b-@QCa_8 iܸ17F5_yr1/b]"&Q92h|C?F!@ Py(VHYppBåuֺE#[CF?|͚53M$QLw*!4 Fj 5 wV k˘YY;NN,kCZ#: [V5H"^]J]D b]jQJ#྅Fٌ?"V`fd3RH$疙"$(X6Ű=% Ak( $z37CLb̏Z?ev6R87~U(}xv(Pݿт@ō ^S8I,--{LjiݺاO뮻OSSurYP*rH PhpoX]h6SQY>O=NVXD'CY4_hЅJ(*o~MII0oONVʈbG_  yaaz.+QGG+yO #.\]GZ>bkRszEhbkUn0zq| m0t H"D1FY.Hv*ϗ6m؈oQ;0k:*VEKz0bƑ<Mm֦E]@źt"믿/`nqFMlܸTY=8 Q4[j&BC@QN.WIC t{]",Q. 3:Ȩ| PQ~<#ĩSGaRN89CfηFy.,e13g=%\pkRr5iӦ: &G%l0A7H<&nAZh*:TZjeY~ZSf %Y-4djԄ!@!аn4mkXI&H>/@"QJu,!IB{ ܤ^osN0qtHu_B{?oN~72NR֠3ZqG2+]vZ<$Zߔ` /@ubLxX]:mCnsÓk#cLePO-[~Y)Xx9PJP_UWtjaR'o({cËY=IC6}2 ъŪI|Kk#'2s6jG(On @ Y( ggٲe6UIX:w,͛7_CT[|oH[b1ctAZn: {2{l)..:|In۷1In!o.^{S˲obmLwi O}㤅Tm 6I( hRJ5le bSwCGl-Xӭ!Nmk<<O @ra'x a@M3zя~Y脅ֺ6gyv [3u#Yd2~2Nr8~<;i_PkG;oA$ ,sĞ_3fɍOx~.dK"zlCsdB~8UdMeɴ)dɒEZcZEv6H_ZRh{~< 1G5z*:$D~9Į ~-J5VZl#;uJMCɟ'NПogg@ b^v|M[vi'#'O!CV[m%~xɗcrFN2rb|ÒE{XӁfzdCn;#P~.tt,IvNq2bf?7‡,D;HcI~-Z4BAFtM?0XXK>*UH)+--35l+?-SL&/1c2fv'oO2E^u9Mdye6ӦM~63OL{F/44{[oiMI@ ywlt3ė:Q9sX] dk3#lgϖ+?iW󺒆}'ttFA'\It$:.L=j(oko„ S+/-R.B Bf4m oBT"H#ls8&hFxɒ{N9#ɿ/\[ȋ}*'JhtDO{/D>cX3 do)[li/1x O?tKCӤ! &R6?&uIs` hPҥqO1zQQ}qV!i ǑE8%l\Va9WLm>!pF1cFGĉeD{V5I+缳md!V$M"X#EzC!>Ə/]t3g=s{]wُu̹q/7d5w8>[8I L@|(K<30&cL„XanT@|a}E?Ol`x5cKa{mWx7y<#FR!}9 $s2>|{4=n6 *i!tgu4-¤/}:d'd:7K6uZMv2 p da*ZC^z㨣2=W"ɚ߅kOvk8 B[A"rD3>Ϛ/&VՃrs=_><ڊ>No?q 短|-i)|@BQL?/K2mh E0C ph&Ƅ=^QҢ]t ̧Zf2>{r)ď/[1I< 'ԁ>SF馛f5h5kiH)v )DC#Ec`]w|РB~H/5P&V^. Mc=fM0sC8]h!V{%d Fr(C2؅v0)b6b!$c‡1Y=Dh%\b~ho<g2R_]tz-<9(!ɟayFd{{:wex^|1dha1`v<}b .wa8lC@`4ЗUځiq#FAҲRsg$A'tJ(c gϞBA$K60LC*Fk-D,_\fΚi #i\S "M QL!ģcNA&Ezou)BZF@ (pILԛl!籁;)a X` @ 6^-RcͶuLH?C"= I4L,0;hٹ_ɏ9%c08p5F;!gW:bU!Kj6);淶k;eE妫1"l1mgO"Nr~ؚo!_+x7m s~S*F[anOA"D15Lų&TDi׵l`?@^_<7Jn23!2od>/[2ܸG2Nn9 [_xܼ>^?蚎C37Y0t鴚#7q;1~Vt#N Q,{aa?挂8yyk  sa.]<1aȟʱ]R1_"[LCZ:-!S &Z`۷ogs r !c8oǚ}ȱR79&l=.Xhm@^;TԛNcEN\pr3{Mkke =ƽǏgisֵYꫯ2g{-@ `4R% 5=\[u $k*[z11~2b#ᄏMktwy-Ƅվv3t$ѵ^kv@0?SF&B &+r i gl/4!MeBa#-d_nj#zA{) hQs6ĕhn}k2JGIY 5L'NU1 LCF?Cɗ0j(ۍ27360 #i'* ğ@`> .MVRLX\Dj%~?g}(!u]/.RX%H; RJ(S=tk4Rb6,}j)]9Jj W'p].)]ŎXX?lK5.֥tA gF,n0Vt)]8+?hh: 47۱XZjNSv+ɶh/860K-vyDWϰcu H#x pkTÓAfK)ٷ8N;?@%HGzGЎtu%@n!Qje]ȗdHGK/K y#o /`jRj~JFn%, 0K!ILyœԨ֎tIBGÃZB;V-YG})ȡLAЈKy:sR4#<2&a h>Y|΃O^;B!jr3SX[ƦDR-;v2 FT3kU;7Thx<3CYI&4V ctBNl/(;iV3uPwȤ X#dE{;NC{ B7[In0&^vqGO&l0IH ohwb探Zt9蠃+Ygg$u@ D\ _BR t8|Nv&A*8W $T?6:=.|ٹ8ɢ\TMsSs BsO.Z0|7駞6=ꨣla|![&/;v9yGi&'ZGߟ[(AbʖgH}lT2^وv SG1C 8gh XVjhќ12ޓ0M#$󲋓72λ4- #0PnF9묳܊V ?Uef GhdFD~jA}'P޺礔UU6֌82dM4L믛Vs-2 CH0[E%@hi+yu h[\J8fc]䠃2JF2‡0I:$񤌐@`txxl:>Yt.Y+g9EƁ@ Pkqjͥ|E裏aÆ BD -| TXĈ)efAC ΰZ;LEfbCy!ni?gdçaI17yg[|CBDSJk8R6|Dr,%Nvm1 ?R77 4xLWCh31 d:#48ԝ} ^mE`seٖC<ͬN{x݄spp0Ok'$Nª|u6S?xNxO\Vg"@2xToI; ȅd8'҄o^nYpu:;d=<f 1hch]0`E<~5W-D b EFH'zHɒ9Bz)c/j 9Ki1Pc;Ϧ6@"DzׂQ RȠ%o-@  0130=l[F_s5UEBx[~@Vq(VG\xb˕%tEg'6*ne9eeI_]+J׵xvyQ+ ߸G} +ѧlno8f2SQ#-7FW2_œ/HgAZ?|2)0(3+s^|6f>^,T B͛57wSq~;|DE!0sLˀ_$C 6*kF Da)rNBVi'#tZqe"y&Òy- cH(%KqoO?y?x`vydZ|mnmlS~:zx\]Z`bbK/d3k$U)]Ư,êbCuν8Nvͭ1bح~iyΝkqҷo_fon;?@ P3 /[:(DMYiWXxsq2}?FW;7:uϓr'㳏xv*A7_ixIϟ/' EK:H"+&Š;LvuWaC=t 6m4fΏ!h".Y*m۵4LߥKl'ujؠi @EI  .p12m<ӦMWb{#]t͚& n=JUTTdD `+mL?'@x4&ĽҫW/iKĖav JS-Bݧ_u;E|kD EㄩVdqP_xLlqX7R &!cz3fL9-$=N:UjV/߰(z}Iݺ@B~*!@ 7ofJK7m_WK?]#B@ !,>??[_ѣG~/lq⋔vL)/1d2uͷXDp)Z]v͎Ιkq~a;VzW}qG[}U;MO|IJWslJ}-СC-w.9h({9?3RQ;wM7JSJhʕ+SJSJΪM;AJI~ׇm7OmÎ;.ufϞR~$}|O9N<]COKH VF*q(2A_:"@Up>5$mSP$v\wuvugΘi:4 Av7I3`Mz ?c2LX2☱cLxW]weӚ`2ӟd~ѐ!L̢o%|EˈBI6eʈ#|~I >wJvMy)%\bK@c=&a8D;zj*a4-fG%V% rNx>_5^rzЁmLh&h cn^&KB3M'3Dn?|59}9wx.Kq>@Yǟ 'Oe/|_op%<kV1a69)a0a=y K^zɵ:=ztIl0+l)Hd%oz"^C > C>},%K=hːCbv*$V6TS%߈7ԜRpE+SWa~B  >"YR?~|{pʵsjE$$!B IB ХKW邊RT^@`A)+EP:Kҋ4 RCH=ޖ{w3ܙgLͳ3-k*h+lIgv=_ tN?tGV&"ξ 6tɰ-h/%Æ ҄Ҵ<`4厫)1Yٛo [o@<\<#z^;bf~2vD:dE'!/4D5w녈@/B/ ~z'Җ@;{Gm蒹g,'M@ۧ;1pرcK[0ʛ@IDAT/d-]tQ'1|HVXj'ȼF@Ȗ<뮷ntM)K(‰yܳy?\B:8٤>hu,̍gC4cMg}HVBexbE-(H_ Ydu@oUȷس~Ӄm g>gI=$t}DC7LU>W"tta@>,[հ؃y~-kV8,$a@!|Ia_%r 7L}Bj؛!bFlrbFa nEGjf DHD~c[D,:l*gOqLZj)`[^h>򓟔wD: Z [,^| >ya:,ϋ{a; xsȢ!ӌ0@w)'|Y32<<\<뙑޳; TF%y0I⡓q|\#ٶf иwJ !KhӋ$od cGstB˸ذťb->(jx5oŶЉ!Vrutxa@~H[%r2F wQQ:IS& ̌y/σ8Oߑ<4 r(YШx>2%wS"WOFؔ +-Caz/z|LG BKsA}饗<-fqkKD@  8S<鷋Wnr-1pl rj=>Kcw{daz뭷N?l3o_6:q/MՏ1*o^8E~"Nmr V(NMЇdD3hl}< ȡ] "@0vQNܪ8s٬Zy*l9餓|>"F:3ܣMAK]D {3]~ ~ !UC/N; Il`|EL5_` E#/)D-P_\?x`)UyfݙvI|QC63lF pQ,G$k{a4gql!jq$ ] F\v?^xaӟ;:zꩥQdrU2]'_lZ/^u4% XH :b#>c0@ \`X@9҉IK.b2F䲽g-Y4UnVqFʑFOE,Xp^nݷU maUE3^?0tk#?@ T r˦tw-7fq6V_/KJEz2N<`U%RXjA+~]FSs#2'QG8W,r"@ PCԧoSZ8eN=Tߕ%[T==-x"B!]Db8 1z`u"B/ @?#8tPs}yd \|̆ zRtq_WЋ"o~ۯ˰x#4HZy?SXv&Z"Rj=G7l3?#‡3W]uUWֲ|v (FQZIʡ;HWg -*9lrɗYf=zt+ $D5@A`o.l~5+ꪴk/bIHUjbU U45X#qMnYadX9Frdˊ0@ t'^fg99f]w}_%.!ϳlȫ^QMdfZ2o?47 Ҕ)S4*4)2Q@ hxl0#2:Ln{᧽t/ B[;8ydcPl>!E?xMhF:87K.묳N:Jg#lڶK^(U&lG{~LUy9p̘1c.s@w"`Ŝr>쓴YO~V\FHx8Jw6 j@ŚA /%"JFF[lJ"@{lb_j}ӡgG3YNC޺5Yd\e(VHn p" axZDKS;\0܁@ K,`8pfqvv46gqFzWb:7Zd_(VHuZ2qD_R-{╧@ 4bϦSN9WG1"]}~@إkD D#(@NQjK"|mq(C8s~X JB:\ RǠ8! @" ~p1ǔΎf/=GK=Q^@nlvr;'F=M=3Mv[G҈?0oYdou>cǿ@ 0O3f 7LW\q馛Һ뮛X+x=E =8¯ZJBnu ,yy-}_ڸE/P{z`뢝Orwn?C ѐwN![1UZ @7 v~IS%}VxOȩ,|:餓/ 5s4XXE8iҤ&UdB_q$a@C@co#l"qJgyfs={G~K_*-|ϫq D-M_Ceәroz?4xK{MnO(r|͗ h{r:Ad mQN%דe~}Y;1w٬{wN}sb `QQIl}O~{iixi|駟~GydKg@:m4 P]8y<)\|!=|7C6諣9?Jo8[J>"@ؐva}ݗo"B d,꧰:Pl%b֝s=7]pi7N? +P"Rv0@ ~ҟϰv9| ћni:]b;Ϲg43 XV%Y,M-E_] V[- >t}tE pЗ2r5MϓE];c2t '6g$uG^ٰPz+D((w,[mt׿z{mQ?z DF(@Oj_HO>.t'N.Q1*N [°XHdS.1mjN ˇXH*ꌀ~{ G3_{}M믿~袋|Ygm٦4M%T4+_ Pb2D@,OB?ZlũӦ0h_~efy6EEѤ^N}QYyIT7{iɅb&u]0@!@_kY??97Zƽ+p>=wC>R >0)wbJ%¯kQ~.X[г ?OL$l[ p2K>}b-擺{n܄YbN wm7PO C"!`Yz9}&~_N\soԤ 6 L/E\G0aS ,0 w 0QMڊ&#=OgiN!GVxUW*>Of'F<E@`MDZAVO}#ZG `w O騣- k"YADu]Wch=(n0.HANMD"}٥dm:C="z^}E` t01鮻J[n?b/VW7oP56 @,f6^wml.ll`ytx,iT{CL2гYdճ4L?Q@ h}8'7lFiWI]t_~VP'/{駭uϭljv=Χ Fil^f5ҭgݪYjU]Y?lVveI F p*˜sY"͎ SDQφrY ;zL2Ӹq1N>d?Y%7s1?}4Z%,hzDsՀ$+;gAcDc8͖^,VK7rrQp9z>,E_ ^]T0j(RpaСC?iEIw@ȠПU:=piW[I> ܽoL^euӓ<ֶC3bnI4w-}aeP#YW4m^y%v^/[)CM=XgȐ \҃hMh DȎ4F~aϪ1kFLыcn˝]qrc[r; '׿yoW-{w}F O/~v4O=儈UUS_9Y 603njIY9jdh"C&-6ztk~5cKV?l¿/׍<՘t駧ĽjD B0~|:Nhw)-`4;Hs=Gߤ n9fyB*n7|׿6~Ҋ+/O aƼ̿^rI4Y^cO6L!4:-lIhdzZw?IbaӍ^l{barN?n]+L><}4K.}l@4I,Q,6IX@w @G5W!ܙoą a{Pk"pQ;y@7"`8C.Ny3mFؤU[*yl0DZنL22#Lmz7BH$OÖ\2]`Y f##H^HmW\zMhHN:4ߠAiUVN}睒sNӠkJ}lkVBlNwQ[mmk'lN.dqMS#"dL+nG)s\k2׵r>02zƷ~eobȑNH}JȈqF뮩G\΃M1[髻n45嘯2[v\V/+Nve_:B%QY&pm}x9t \J&>෿S2|C=^UwCЫ,;2NѨ-cpgygĆ~KȬĥVZ)m~JNo)dw.>lk阣>/Ĭ`>!eW]5=m7MK6Lp|FB8H۪aqnޝw^}uz쮻 O4\Fb$si9e0R=>3t͹约yF< K/ѦId%\zJC#nv?wD$JL1w]Dt.4}-eذN;ְw<$ \"UtX_ Дh{>SVy{lXNu9^feS0$#sK@|"=2W#p>oѮ1L ,}R.5PFr! xL!m9mNK 5<׍P?q}ik?/sې6Tp7Ch?6bJ'iJ]G,ş١֨2l ~| xA|4,&n-i¦vZ@#}߁p}^$K/D;l(xņU/i*k<iɥv"C}⋧lV(`s[>/pM6GhsI \] v8̇φc[|Abl#,iS"yyi8@1&>2b}n3o-hNYF tamڭuXUqW@@mLni!\Z'Qtc3l4dvO<Ĵ&0VJ#BM@?-g)tհr#[4s9B0h LkZt oڵ}iiziOCm|#?t A?cgYC ?=FYm`Ki^!Қ~3ٶo:Vrp6b{F;lYya9I#^ܴٜ³M{ IugQ yr)_a9xMz#7$=CN^lHbBZEUQ ?0͍:q}86ޙ =31^3ey9F5j/#_!6]! yQm@*e%s!;b hUHCh!o["ᶲʼn^6b#\wMEdmO:)-bq2>li#pH@XZG+FܓnƯچISq#F|ˁ`㰂3<ѶW6{3R>g[e mlկFfadM6zDha r~e{ R.}䖟1m u1q9IEi IWOG @ /G(:̊ 4LǜJ*[m]|QuQ@dM Q#\p.{4h2&U7q/[M 1cR+mhFqkS{p_9ohjm /qFʨ /o§Nad7Մi,]:ar5&Yټs¶hM_x˵aNhmhQn$:H:'jcOzBֽޥ6V(.[vMYT>A+^@ PТ1\ZC d6h<͗]j{C[#4{F`+8 kdIʑ#- ݺAs= 1RMkE6Od^7$XuTΔWdHXW3A${XInF@n%K-T+RZ0v 0vĈ"He{iEx @B b7 pR?0=_Ov[ZyӪvN$/{~:iN҂`~b|isY;Gydrsd42}f_ivz!Z\[E+>/Gv< # Ǘ1N/ۦ9TYo[6ɚX47SO-~2ǏwA:ң\4wT0?u?$9_ءh 6 @ɃySO5iҤ \j0a=7h#O(lQM}HɃ! ?drVFQ4Q74oI>,ƎuDagIyDdp';QZs5Ӄ>X"!Lߜ?qZ`3r O3=oG}56wwi;y~Z@4 y^r%W^vCy] 7w_/N8!qnNT 0oz SoL~3x! \mҸq|⊆~yg^_.SQ45ldM%?d(feu4qK_ @ `FΏHAki}1_ڵifBĊVA4y ˲XayI7t+_l>⊮IA\hA_aP2,[nԝ}Nze=POo~LkܠɄb:x⦬,9| ~"<,d?g'h ow9N;-i<^u=zk7tSǢ8BoLpM6 _s=>0 &W_{ՉckmX^"٫rQwHc6~ chli-tI"yW'O(@ 4=A{@bLaAxEKƂF>S!}}:k qc@氡a'-.i"VX{9r&V`S&dnM#p#ÝAVSNFid: 5H%\҇%cȝx4}h!9|><z2s=t2dC9$}mǯ9Ć,3LĒnvLad/k+=ʍdso 'm[) @ hn(ֱ}y)jyzh K$/b>S}Ȕ"sk H&DH2 Q =6JE0 2o}qb iJ"(Az+͔ܤ I{Hy@ć%DcHʛ;5Pd<-!nld enRIK͕43ֈx%1ʍder?܊v1s@:@ 91G1GA ?3čfN/|"1T"ĉ)D.UVY#A"AL2(3[0$̢V#ir}';I2R6$E'h@њbH CwqoVV]C.UW_ܯ4H12oiCϐͼڇ|ɓ9ʵԗUɦӔrJ\NtBL^vcD=X Y$OH yAcƛoxYmy M믷,oWM;UFk`˅E@ YQ!ŋaX"-C|X0R=$F  ߾Ga!Ch!I|rY!E3|AƢ.6pCO9SNa \Ch |{(yElOC)+1C4O<0A6r_sO^C7D26PWC|lZ)g$ו4K9ʯ4 V~i} @A b۪@fv(ā` g `D H8p!RFoIdKwyz!}Ȥ;oY1CF![u!?41*+\*?a°D򓑟˿5y|ךpU_*LҔ@ B {sN+Jk/s]^qtO_n--Ȣ%+HB ^oXHZФqȑ#}j44Qt/Ypܕ'aӖbW[K^y%*8Z+SkdIX[U(V$@ Ѓ؃" C4 , @X!Oû"'*o^p}?78DT?qᥭs8 f8_r8w=p}E)'^_ny! O<᧠9Dc3nʂ,B7|Nl؄qa9 .UR:3y#0k@ @`v8;U)GUJɐ? m/~ 'm7N:P8cwt1(.Ȏ |S|fNS4[oz?eرN9ewwm ƍysZ[ov$ofD 7iq믿a>M6 lqTS.>ɟ_kd[?L @ ACy7Bv>;m't/;9fmIo]x73<3| G?2;L|wG~3ɟ#!կҲ.7~ N;I)8s>3rV4C^!ā_bx駟^{mLx74m!#-`5ʢ!D0>@ h !T^Ufa}睠^Ζ[nZCh8&6?a\sMy^Kwk9GǦh9h"p +p#M4N\y|fγF If裏lg7kh\ XNtrSuۙR2%@  D#(HF/Fxex .9=~Gaꔩ%ry4B -as[K/Mt3|L\(^xn2f_L;C0aLlfs9'~%-ZPrzk= m*cn˭s[@ C Ql^.1~x'Y/DEvha$ K3 S3 n/RzG͋/=Ӈ?S` |衇{キ/nAk<:kd-r,@  {gYĪ(O5p }ygy&]uU) B1~ر-w_'c1|L<πFǜB̕W^8 _|y{y +bvmӨQ\kc#D4a͆)ty'<S>'ONvXZuU&!jV^3,M>RE/&G@s欯}س"qh1G?/=98y?Q wcc=d9 wqd^!:I'CƟ]z٥>t駟z&|;I~w}}oF:/ĔE1 ="qٖR:zh/?_ADh+D2t/p2MY;#f@'#@Q'Mbo>os^d4ňC4JGgE>fXț6Ὥ@<+?Lr( :إAN(PB;-,SaHZXC?0,habE pp0E(I|Y24,nQ\ҥЮmU뀰EPab*x2As4W-qk\N kzdC:DbI>|E S|oi%yIs i݄)%'v}ΡM/6 rBy>xiAC(""0'U ^"ؐ DQDZ"KsYHk^+و4\jrIcgpb+~G )_rү&ȍ;{J_yèsr;GA_@9 Z#H #$ X"W0=v&Y̐C C(3k/ڮwŶ@ @!a% (ʈLW h!APϳ (\"".Y4b]Sԏ5~uin4ogx6"l{(JJȍMpͅ@52Y&Lv_4 6K0\9! Y(vxao_Lk]5Ρ@(@!c,AySP}<Ȣ%D`UK\#BV2/5r z PND (cY]]=Q]O/VOBmM1nVr9u$fjf+Ս{ 5]}6[vWs UZYD5B6VG],Hmf4m,@_uykXHh[{ mɿ ]Q^WӋqkm&O-eA)ddry 2*e4]uڹ+zFJF]nWR|"' j?"m/>k+@ =Q=m5ĭR1e29I0} @;s;2X/^;>vƝ(-rݻ&'.JG;@ h(P7ġ,C-TkRn1 @ ~(v?(,en%N UJS@ rD_,h){ws1"# lD=ȔKrp@ D 4*Z!P Z-#] @oG 4u|J WԱ u``5$Ù?ӝPӽrmL%?@ @hB' sѰYI} @ )(v M6jp^q \#H;2PiJ/bNa@ N Cϝ" ˽yE?cƌBɉ&Q<ʣu%>@w!D-r'_>}49҃n8i _ G4ЦL}QꪫݗZjF1$ nrYz @ :@Q(R+#QD/{t饗1[oV[u4hР:_469CO?tk҅^vu״2x\nͮ=3M!@ V(֪"^ }u&l$OSy睝42LYNt/t0y 0M cc| `hS+|e>mzqӦnFEծ1@ZDS.+"%@ :xd@BZha'kFzGҳ>>CCLDrĉn(5tj0>~ 7C IniZir-~'rcGkd@ z AkҼKb4M,pXb%",0N ِAu&ɜ-^0cszNSE:uO4er?-vPs"? ;@ (A;T'xɋ0DQdP/xZ(csA!"o)i.ŜTt!^%hEH!E[ƥgRvWbs|ѣ@+Tx@ -܈04@ 4\= "&rOE/҉\V%QD*AQ;A,;m36Rsm )􊝌@ !Dʏ/ydEB%fdbjQ9Ԑ~+܀\NpKKv0DO$$蹑 *@ @%(VB~^" "!iH@s-TP .$QD1'+],~Djsڋ6RfՆj˟}Lql|%J_ @ #D1Gn^Аl3 F8nlڔ0.cc= Tx~ߖ[iHFa@ E bG$#!C#ٓ-Pds_(2O14S_Cb޲iwL]"r@ Z"D%U叁@14@*]FE24Ea[0C@퍝_"˖LGJk'L6rVܰ@ B]_"^ڐ?  {iD04D"i ΔF.' Cd,FM.%}sp@ A bGPꂌ^h qayE2z# hśj ]wJHNnt63g*, 0<<'<=(ɍ(h;@3Q Z]兞FD"E <'Ȑ3ZEk믿G~gbBl*+*L-Ț64H@xGڊxG}ԣ:"ψVzd@ B b[T9L/uzqbqTꚣiWwn--rN!aꏀV%s۳y6@xYg_ɠ->\[M @ AA"2džp lgivH\rIzw.쒾/,wL&Ѫ,ãt[='NL=X:7g;Y~ áE^Ve@ @ .F=sԶn /0Y\ve,ahf^#ryS=_~ߑܐ'ބO'ǾQ8∴*ן9ZI<]Ga@  D3hu4< -P3fL=ztzgos5dP4n4r1ϑ}{9kiA g7Uiixh`RK|y]M pT,@[حp^_$"iS '@!"EDH"פIҧ~F6h#IJ% j#@cd96m1zXvYq2e@ B b[!,C-UI$@|e-s=$LP{ͩ.CU#2hos#D#DHl4Ma6!@ QK|! xBa1/&$H\tO$ (֭gXmQ}hCmνagC3T G @9j.K2_]9C0Hi6k4J/GMR?vMgFz4{zFsS2;@!0Y7^y@Syhr8q/OlBw;O7pCz&G`-Lo}ZaҀ|u6\IhE& z@ t(v$K^Fh-{Сi6JK/tz7G}]=Q֖5U OR @@&r(bΐG쳈Ƒ9I&9QD#ul"H*y{K+4V84IC=+~S @ 4A; YGK_Bip ChdȚ\}Dv7a;V@!=πy6rgGi@ лdϋb YiI,(@0Bs"IlƈmN \$Dk% @ N(v'ݐ۪0,B DsA !hiC&bG]Fv/EE}BV1b@ hj(6Yr毈B?dD&BQ,'#`F@, !\ęvAXMvn6@ Pg(ֹ=/z;9YT^4MIDQmEF~bn(]  @ņhBZ!H(#ſ&#wN $v @ P (VB DnAn\޲ܲ{n@ ݅@h ̧1׀E"@%"Xɯ$"(@ p(ƃ0 Agx!1M @G C=A6-@ hTbcL+@ :#D @ @ ШQlԖr@ @ Pg(ֹ"@ @  2Q@ @ $HIENDB`passenger-5.0.30/doc/images/passenger_core_architecture.png000644 000765 000024 00000533230 12233035540 024475 0ustar00honglistaff000000 000000 PNG  IHDRBX AiCCPICC ProfileH wTSϽ7" %z ;HQIP&vDF)VdTG"cE b PQDE݌k 5ޚYg}׺PtX4X\XffGD=HƳ.d,P&s"7C$ E6<~&S2)212 "įl+ɘ&Y4Pޚ%ᣌ\%g|eTI(L0_&l2E9r9hxgIbטifSb1+MxL 0oE%YmhYh~S=zU&ϞAYl/$ZUm@O ޜl^ ' lsk.+7oʿ9V;?#I3eE妧KD d9i,UQ h A1vjpԁzN6p\W p G@ K0ށiABZyCAP8C@&*CP=#t] 4}a ٰ;GDxJ>,_“@FXDBX$!k"EHqaYbVabJ0՘cVL6f3bձX'?v 6-V``[a;p~\2n5׌ &x*sb|! ߏƿ' Zk! $l$T4QOt"y\b)AI&NI$R$)TIj"]&=&!:dGrY@^O$ _%?P(&OJEBN9J@y@yCR nXZOD}J}/G3ɭk{%Oחw_.'_!JQ@SVF=IEbbbb5Q%O@%!BӥyҸM:e0G7ӓ e%e[(R0`3R46i^)*n*|"fLUo՝mO0j&jajj.ϧwϝ_4갺zj=U45nɚ4ǴhZ ZZ^0Tf%9->ݫ=cXgN].[7A\SwBOK/X/_Q>QG[ `Aaac#*Z;8cq>[&IIMST`ϴ kh&45ǢYYF֠9<|y+ =X_,,S-,Y)YXmĚk]c}džjcΦ浭-v};]N"&1=xtv(}'{'IߝY) Σ -rqr.d._xpUەZM׍vm=+KGǔ ^WWbj>:>>>v}/avO8 FV> 2 u/_$\BCv< 5 ]s.,4&yUx~xw-bEDCĻHGKwFGEGME{EEKX,YFZ ={$vrK .3\rϮ_Yq*©L_wד+]eD]cIIIOAu_䩔)3ѩiB%a+]3='/40CiU@ёL(sYfLH$%Y jgGeQn~5f5wugv5k֮\۹Nw]m mHFˍenQQ`hBBQ-[lllfjۗ"^bO%ܒY}WwvwXbY^Ю]WVa[q`id2JjGէ{׿m>PkAma꺿g_DHGGu;776ƱqoC{P38!9 ҝˁ^r۽Ug9];}}_~imp㭎}]/}.{^=}^?z8hc' O*?f`ϳgC/Oϩ+FFGGόzˌㅿ)ѫ~wgbk?Jި9mdwi獵ޫ?cǑOO?w| x&mf2:Y~ pHYs  @IDATx $A L<,*`gĜsO,F EE2J vkK{03߽3.>8q8#8#8@B+ vGpGpEIOGpGpGp]{#8#8n#8#8@!#ऻGpGpGpGp 7Nu[K*uD:9#8@V:t_IoeV;LIw&pGO8^gHWq Y4'äEJ-1ĚN8AgU΅W ~:^#vGp#NFM Fҍ *]wU}ȗ-106l#3ݜt1o#8#y2­*&kc:c=jժ[H2ƖGݶm[ kx#  ;@HwoWP?I;pG(9!8 TH!H^ȮH< 3qIztNC9,66OKeNE-]99eWߖ4ے6VL[aa:#d; Y*zI QuK}I0QūtJm$II7*3 ,9s'F%b֬YVҰaC;wH JN6rʩmFZr`1XbD9$K~7ev_~Gտޠɟhԝ5y,_VeX>5pMN\fr&_[%cem"q<ܗkXwpG EmJ۟+YaqUm{¤͍Iǹ#^>ϔ*-x2`ꪫd}S#W\!C ^Ire޼yJ˖+ SjժJU@3wL:U:AXɘ|Pn;_M6~G!jJz  t_tEj믿j{~atTSԏ^s-3zMv իXݚ 'Zͭ_R:6hz;׼,)'Ȫ/IuDwzwvclXE޼ysŊ:r];#8Y!g'Ip;cV3Cr;D4}";7NC@kם:u-[ d矗I'i&G}Z*[ dмW^ynYJ͋:JY㥋>ݻk1ccB9#8χdGQų=$u `EA".2OK 98$OT5&M$rʔ)gEc$³!gy#ATie])7ި𨳲 [ncj~u@!M?_~tMw뭷*\rpd5OBivb*0s5p@%^{Mqx8CC_l߮T}wyGa\x7nώ/,}UO?iСCdC/ qG:vmUI7|`稞={ F/Mz cpGHFńG0;`߀OF`':E|:\ntqYC _@$pi_˛o7iD 8K .Rs$H]rňL>iݺJ~Q@|vi;$ũz맵EGpG xA{Gϣa@͇[u3gaJ /%H-ZM:" }RgdCUK/M: u8 aj%(H9p>@rSU SᅃEZ!EZZ`aB>^X*U/Rp>/}2 =UL%@ܣ'ICщPy>ۣzCMgu^t颋K!ؼf PKy駕#G"K鶴;#8E &aܖ7S?O-zl -!F*S )lРZ/ #48#/Y$ld2Mek‘[&AIyi7i4F ꫕ClEj'M%&/ĜTDpfrjOZtZ}ȑ*mG5y#gT~FB dn8uRQV0GpG 'Yf~Ni=.3Ʒ#3zŹ' d ?˓]=Hb7rA@^vE8|j #lmCFbH m֬Ck>o6|c]H EᓌI4&  mA}%&~A2M?pmV-y5-/Iwyg j _ t~' 8uTdx1@ƀ,t8##1(et2 к͢J(, ͷXs z=S#oHfʱkH9眣*,HD)zx8\;JW 6Pֿk>ÂL>]Ð@*bzgyn06W#~8$,pDTNs1K*6vNM`rT<2rg^5gvI)V63tlcu{U524&B`hƿ+JY Ѧ]`/9=GpGp6 +H:&M˗SvqZ0 AIFzp6$ݨ3P7ʖ/Z:'f,|@2O?t%Ѥ]hтKi6RtfXHw}W-@Q5I; 1Cqȫ}f:5/] #2[!c,JlT>p\ڃ4ɋ{?a^ ".6u^$p_1 6!ِp` *yQUvxp_xi ~xS(Rb}#8# ]"PƹTfg109"AF 2 (#F%T;,>yHCHZ ,XBBG9wsmmGe"Gg9(sw&N͓|jIM;9 8ۢ tY|ژ&ƊEQ?䣍r͗9:{#8@EgC1xGdzA^}UZ]f"a>0n*_03p9Idr%F*u5—-$-gCɏI(Z_5,G2\;xl0Yoad7ʳks3$ᘬF| _+'!nK'oo^YQEe#8#dϜ_5c7|GpD@`_k= ya5@:qÜH-3$^9f$_|EϞ=?A{-/Zm!СCe„ گ-[#w,-<僑N`zyS\4+7Kw厀#8@!`!,ay`<䓺2E -632]KS{ra^&첋ѝ4ͱ+40xh3͞=[CYîēQKzfΜ{ EZgcg8u&N1H@>YGp|!дiS`>ln\G>^6+)DreWc 93mܲu]wfs>t٬Mh7lPm?zCu'h/)gyF7c@6}__փ,B;S g#?졧K1/~N o8&|YGp  t UܒSA=(WA}Ҡ8}7}8vXfcv:J9ynAJ5cAnW^ѽ9 ;w\[>$H?3ݙ} ;I c(c ]w%4nIӞx≊!*-A|q㬮.6ڗm/nӽr`gH1ma1pG(Pj.Da#S5kToZ~9 /T=hzC>p=&n뭷NZs l@#F AsK/JumgݣѻJ'^||ad`pT̙3G1a*z}C_uK,Dܜ3ΐv?kc:u-N&6qw#8@q#aل#<͛'gy.Cjirj+Rg}Vn.m2۶qhu54/ `!d2_Q.mi$."E /4X>j#5RO9H ǧP2RށE&;'ݙ<7Gpd"u]:_~J;o?;YZƮY7E|9oԪUK!ul!͆\[Z@K~'e2m45#h/e&J$>|K?U&ߡ F;IddyJâ:()JZ~ ׋-~A9bgnKtq3)+f9.GpG@-駟1cn ^tNTzܨQl -]CR~+Al3#ťJLj9f4p\wQUZ5pC'NV;,]1ڋn61xkWP:uq 7n@ؑ쳏_G}Wm۶%Kh(NH3 {Vnݺ vME򥢟nc t-O8# FXg6t'T= ' nZH[V.$3yM{6 ԩ# 4P-ȐOx7JӦM+#G^cui)Ugc9d}UmThX9N%,|$i Cn~L:]n߾ZAAJ5K[R6VRty0Laش/2[-E9#8) 'Q1ԡ?M@ǎ+~}}݂ʁ%Ocʕ*'z%v)1bZ7!SO=%w~s=;Ҍm#8Bج^zK-*.es2'ݙ2Gpƍ4hXi$զ[]FM{Uۤ19wiXa^PH,z |QC|M7o捝A~;6M!@Ic'ݙ2[#8@6! O`cD]{)`B-Moҩ:I 'QoL"6jz6䊞x ™'sk 1Ko~ntjJo&qo#8#FVuvdrdlt h#ܹ%^FĢa)Ӥ%@4\GpG 0Vlpuv[Դ4TpSB55˱5?5[V9~pGH[Pć]oq#]4"f~q/JK ax#8@#`{ŊFK.~k !~t3"N3ud_#8@6@`YɢGyD_o攒pùil2Â3}]$&wpG  G\qÝr; #7K_2p32Nw~zGp<# "ޕ?_N;MMB`D;/@:RMp tGpR#4_Jv*;SZ+zSB;+hY{Xf P%ݙ1W#8@)S>i "7Nimv?P 'z7N7`g#8@F"aٳO>DZc&f.E RTĻ(/6) JW/)|GpGǍ-'|"oo^,b'yA+UDd`-vҽy<#8#ɡ$9|p%>C?JdӀs;NVMDmH ^ƒMsGp 믥Grgȥ^*˗OY=hin~90vD{IIwtdpG 0I&ɞ{)nZjJnlDL2zn "F_XX%>~GpG #,q?;x`iҤJSmdvjҥjժ,%I]4FhC˵'ʕSH>O5%ݩ6"GpG ?$q{R 4oVd)Jz*?~J!c..aH ,#FhUwYKIwpGH \ߪw޽OWc\SdIdVǥӬҶm[W"?X^~e J*v^-_mNvGpR# .TK%4 /Tu.~S%%Xs8B87H6 ?}Y:uK:u$HD致qܹ;o/{߾RR%!+❎=wҝmvGp0ꫯW^]vES69J9\)S&|;%\@ncsCxkםtyGp,sգk׮yɋ%(d-[Vzݝ;w֭[ĉeΜ9b;$]z!`cnl5jH UVRn]}blVzMkӫױ:NQ6;#8Tϟ?_ 1}OIF 7D55kլYSVX F1]j!`/F3T ٥#_5kTI8dCt[XtVs'ݹS:#8)r3dy_>%mndB8 \kjV\~Ŋ5Й-S s=:ƼB yIrͷI1#8@"ᄌ;V99Z\*66ႀA![&& &.t?'ީ8ʱ61ݡv R@MMrI/>>66j;k#3H= [KwGp JSBB+ #H7D'>iM9|?)@2qf0|myZ%RI:(2bE2fdkNi7nia߸rҝvG(aÆ.w͕B\ApFtEgdt믷wTJ'-LIw&pGD `]z%wr!iw9΍`[]&znyO?d:s{ӯIx#8#r@6d; /œe9sK5d6vNǢ 'ݙ2Gpq[o;}6… | 锍qYEIwpG 0뮻N{҉'%6dUs❞^$l&JNlato!ppG(j>CywW^&6"Eݮ⨯8: [\0RGp@SL?^99蠃.M!F:n#8@ #`ݷ~[[yEIڵuNnx?GpTE$}W.rԩS689 ;p<pG(.Ldٲesi3z[[\qu#;yGpG0ѣG< =iӦ Gpp]8z#8#`j%͓nM4h ݺu2eR! <#9n2! Gp0R=b9rkҤID RsC7fbGp FI6nriI=0qBIwZ 7pG T#^v;OjԨ&3yo%'%b#8رc<ॗ^*{W:58 ;`<pG(J &z)W^R|yr@x]@!!ऻbGp `'G%?sv[$UrZ]}:Ke83Oa/s#HwSblT`b:4giRre%L) -wI=/sEeY29HwI*W(A]\fΘ%+/2yd=sq)_l .eNmLejcƷN?){Y1uletM[r'l[Qb%umU)2tݵH޽wL%啤xU٣rԉc=巟;H@?pr Ne_? aԨ]Wvs_diM}hPo+W5jW5VI5Or95kJ傴;S_~Uҝ./_Sɾ{eĉ;h2Rre7+WI{ހkV=pG"s/ɺu:L#eQT5Bۥ:%oL~+2qzL/uj)^%'OY'sf͐\S'/ִXeKEW!O}هoKRo^.M~K9[[.C[K ګd/e>.-[cO?WN}+_Ah/}tuAS)նdJ5x^{EX3[/߮GVpGPb cƌ?,w}cDeJk6^^X Vr9vFMۮW$ܿ*|j:<ͼ` سPNZҰq]ڣ1m#f Rfeٶyp+4'%w]uۄN$`UG!zdׯe˕j'D%itzMyCdӨT'oRA 8Ub gInٲek?*|pG#υ:h"yOڗtx`j͋Rujw{]( *Ͼu͞,qso =.KHlaó{A- -.LX.\5ԕ% E TťqAz^XY.SdۮZƫ5AT/, ([˞v)~a10θQԔy8aLMMnM,S7[bިEXDpG"駟?_F(KaOPZ|ߨ_G9ɂJvwKLW<<ǩ*NQIH@L*s~k` 3l<0*RaCmΙ=3+$ʗőɘcmF>\tu 6h CzO&O])v; v<4%:K.]nKzNVN4qG(`Oc=V5%-aQ &K/ z׫+AK8Cnq3냮3.{1D8ƺ?h̛@_)ߎT]"1'ʢ^,TPI*Wޯ=L |AEœkg}Y-Ӿ ,]Zˏr~=/Dw[y|yw1HcϻRi2G2pH{_w '~2[eTnFZ7:{{pX_4,sŭr@{EnHj 圫nzu%{Y 畂jmCmck_]%&{P<-bm|k&ŧ[#ڵ\{Yίxv@HG۟kӭoޢEV-@t.$WtFcȑrw];o#<֩\~'l3/t,_Ty-iԸY UB r| {wkvعCеH1ޱ01(N)M1,н}R!cưNrjXIy z ']qUbLqͦ=C>U.vρܦi"RwctY˕Ԥct OI~Vv$+nU\tEBawBO 끥[?+ُ|KUX4K&{D9kC's@oP7XO>].SO M6Hk׭vZnHys4B5R.RJ ;؎u'aHݢuSiҼ^./(NB6$? '7PVVmh _:bP1Eۅp3nAV.)HG٦QuHۛ'md=<-H{QYved`~u5'M2j}\LoE0^E `õ+d~tǛX=?z=3[|4+_U0i3φ^}M~ 4 pҏ|PH+Jc :}..6/tnHL`-!"`G6\CW^ᄅd|RNC!Bە ڰX2&0)C]NN)S",(אC}s䆾Oʼyt3ћkeI=aUc䋝XqӬ^p 'ֲto5k]WiWm8V$.Cs,AyYM%H<ړ}VMX~|ϰE|bYXѴkIev27s)M^j%W\yɼ}?@r%_N$Y6+E8ۺ#~=hI9%uh}ϼ ~h?x.ӲM; ^x_4fa$|nr?3gͤ(fTTp˗&RV*,@fp^@Ś1b"K.Riڴ'v1??_~E>x}JLKFOoVm'vSyT/&M+JrZ^*xuɹJ5/kN ݺw?6hT/Y 䡷%ݔ̓=@_fB) ʁ!ZʕbR!IS#'>GiX!M3>iL6A½JxpP` s|% C:֩S'n$7GsK9?9OlFӲVZRjM͂Dn=k֬vZzKk3%/p3fЅd=z^ԩi ]^~z ~-k~7|?H6;;g6Sn?՚gMe}—pan]DyLڜc4wG= _7!9(H(Q51Be7n`Y@ż曲N;Ĕɜ3fSO̪Hn[:s˻9?!q Ho&toW/9m2ѬY3t_EOhxA۽r?=c_zYkNg~ra}b^ۑU @إGϞ=Y\q/Z6DO=I>zCu9k?NGr\0]lq#j\7mY2ϿAɉ}.4 ,cc+;vӏT8L!VXIo6Y<0v LM{s$4IbQnx|. ȋ/ w_sԮf 62IDvy_J*ۣg8RkiѲv%E0 c14s$H:w,9]r?,ơmpY,K?{2ӑ$>@EBD=&] _&NF)5JgMXxv@+:6AלrM'ǜr5kJMňqpB9T)C C湱Jbj`0maƊ`2a[`>(|*U&Zdi9ԉ(i-W*tcyv5Ҿ܁lv4ߤVȃ:~Rl(j]x2L $%r=wn#7,YdqB9gmRqɜ:H0vYgt0 jԨ!~}uשwqfΜm_t.ϗKq&9gM6- ><宻W_}U%H_/Ɔy Ylۗ1ƀuQmޓo̅5_:a]Suŋ7oVa鼢hs^^ Zp+Yn Mdwnذ{"{_J!W,_6YgkdAg{uNR*CW˜s? ak@S dRYVZJVû*ՃZ W& u-gֲ!R,|H\4,YDKԞx6K`Vr7}TmҞTptȍAo87Y|VJA2&Ţp`;NěGQH?=\&ڋyO8sO(EL޽Eژ*+n| ؋W aAo ;TޡRYG %K$)j2/FSu-w=@֫I]Ny-(D6Q~Y'_Q I:7Kf͍k0bS!tǀɴT~@n| YF!Hϙ;E\uY&@g56 Fj 12dkܟL׶mj o s:8x؎;V_Iv5T0/{Dgk=iw%0&m\0T.z@CfX{vd|Kv^c~YUaV?_k/}g(nG+?>PstyrwQڌ]푗B4#{- ϓ>-WkX^,5z InlYd\r:h## cV S sSS+{Y/s/Y~{SޓH(y7e:JqM5mwE Yh0O7[:l74qpC_*(^Y88 xq頻CpM |+M $ԨQ, 8LRܼysz뭵rt!~|ᇂET glPQIv 4P *HW* x 0@}>'O~>%6' 9WT7hlNEq6W9!`s ?`-g=9 _4hP؝om{wwDm Uνik;9ҫ2fH>_+;-+'n(G*e˕ zkԅ-Zop7OYo90XjѺwCeaX[\yװ~ _Tc@Qݟ}е@Pĉ!gw#)8>qM=HTM) W/IQ':m0!HATЎ@a<"#Q_Wn\?ua$>;E hsHM1Jh) +]BÜ!~wVta'J~if%@͚5SEUlYnQ9+ۮ§~#DO24ѶEۼA y0_tuk>CdK6Nv]!8,_D+1]njN2e¯rED­I!@dU_֫O Ĺ0K]݉/' LǨ<gO( -_x TR5t4M[ /9u&t]"raN}j!)',2 򨚁tƏ-79&YL ;BU'ݎ&@gD@ J:t^((av U:3T'PnUG7kR4J0g}ذa*F*} xF;,TJQ`; pH;w$L, ; ᬽS&mOTGSNQe8{Du-LDh<W;G`K3(,E.:/ijw4|.`1uܶ%=,o0Qǵ~d+Zի+'"R.$X7^}މ*J>?H~ j e_$v BY(p{ߦIв2)hR;KF68U/ 39i20l1*t:z^ pDlo͇CQ8#Ԓs=fN9bdrZFUO,s<ҷo_5S>2|С l"fUѣG,`6VK 쳏'a 5dC!H?sUKA &Y=Уavn~"c!9bV-#ѹs)rNJ/Ø˂a~,Ʈ?k-lEnЧn]eA矯%ܓhݏrS;|y\dZvL2m I Ʊ-Pb Mvo d\)aݱ* _||;jdVLIvESw@s-g!iֲ~jY3+)Kdr(WQU;m[l'FfLMbkxťZPac9b1} wnc%$^#0?8A;NĴ ӍkFq-6I7lN$a`Do[nI,9HXRi]t#q>ҋ&MՑhyؗH.7 ko%V8$tTF)& /rUk1|N;49cTj+1k,Y =Cl>uR.zDIщG?3}Xy5ؼkMEm"0?pQ?܉#$޼c!{'KZt[ha>Xt"= 7 Ϙ.^^笀5B Y,3Mts뉽d֌JG_ Xگ>`{ͥsr}TEeo?J1l9Q}͠=;-yƘ ZQtznrM8XKy8XA{^tfUniѦ|/!EڰKVaӠm5"/*NK=~n9Eo26akPgchf? t|XU"U/a7|Nڼ<[C4$3ZqQF!asvV ^ 8*b]V-TBjXV 1MzH?AD<ّkԬ"mfM'+x61gWQa&_NX H3v w$Rba >{bcT)EYٳփ"IR 4erz6|k -?eˆj8t︫<43Y7*.DY%5&3ql "&y< `XK%֑rXHhrщnՅNE󒞇~DpKK>[ni-KѲ, ڐ|niVT>u08?w ;6OtNRcf{~B=JV`wotނO:TP ñKO!PF,S]ULjG0VM]$$Ղɛ:+I?7'iիVшax!VT잗>yWkթ+`oUX[=A6Ta2Z\`cmAjtݚA;;aOںc ~v}S7Cxe[}]?P'ևe3O;Z5l#ҝ S(S?O QBDA C"CќNKNn!8pPO25!Jz\^u6Z&ѴsKUϜlםz=|Zh笳(yAKjc\ ɡZAZvYU7P%־TEAVq7==U¦?w=P,36蘣c{$vP-Mn,HdZtzIp&e]" Umn1Jsw@26Gl~K13k`,qěu`&4'Gy:{Y4- =t[z^ˍɩ^=kzFkף ws; .dpyr>;A`ŭzʽ/'~F})R{ =aIFU#F;` GGS/Q\fRyV=C6R) RtvYLlZp%k }5ZXMn)zwC}Gu.poSl?)b*? qRN0 MATe8FҼuK]WpdCe{QAECхCWIw+Ri8ţjhaɓZ:7?9Rb?|o*,qB @V"NH^~e{vF]ch O>c3u+'QFgqy:dhnkgqf0q6}bΓ?;ТnK.D71AU^qd۴oW<C]v;ky4h @2>x`iڴ/٤~[8G~5}M5kyI[\$'#r`."Ҭ$鶅FbiY쬺P[]IH6dsfeuZ)~4`bmbXbUITmٵ ԩeg:$Hx(-1)*}2 l7e"@ ~W p ھ}{%ڽz>ZZ| G}Tm"1!С<*ѣ6"aoiӕ\q>SRI/l_`7k#szqvl9&ɮkcS0Us+ȮX==/:r*T??/мsnZ|a}Ke]Sey g'{Ow 䓙YK`?q@&Q0'pir%u]* "j8,]L\{&ɲ)(^6btCoN:IרztARA}ڮHqHpDؔfn;L|t'I{@O=Yln)Lޯ_?= HϮ*K{5G=f%t>kSd_w~=;~Gϋ'f}ԓr'6Oxl8Zz7'x0Oec HEnCyi:|b8͍`3oʐXOnks_xiѣfB XܔiG"P0$F̾Fky(7`.]%֬+Ū벬X&pIJ--. )K,2ɨsM qq`ƁD/~V][,| )7^wP҇d syh Mn.?s}lڴ.&f~Ink~r ,,,>'Pgtz_x5UoNm8G  tPM҈`DʗO$HŹf2{R샥L`>*I.b|8@r,B([eU<'R3Œ_pmHoh*"c!9zI;T]nE}g4otu{'AZkF}ؚT +U-);]}j)H,!4)%,^#0p͚5"ԇ,G:DR?a֗^qݎVa4]h{buv!ꩧTi:f rfʓgi y!&LP.N* k60Y9q{eF8't }۵upϼ<|pXZŽ@L25vW^4*AYI[R&M#8BUx榛nү?8efʡ>գkkKrɍǪMͦ: T(m-5y,-GR +n^RXL0йFjU)'OQ~03 !rʍ|* aI"<ǜʇcj^6)U;w <8ֆҰeT*_&]>P&;`0M53~_LK)7eiXJB{nZ=rB}^blOC;B>kxcym"]¶ Njv½ xf͚%g!,ZDbС2p@~F(H 1!7m۪Y)i԰ZFܜqFO'7_"dHtq [ȗPGecǎ#G}ݧ$+ EyK [`>Z2FҾ{uxB::(n6m LxQC2).KkX@J^^$ |.maa-X{H?8[\K3a ]uom6|[][]K{,nK|ˏG6Ɔg,0 vLr΢K,}~@=[2=Oj^`J=6~(\BkɮN>;Mٹ O:{[YlL=Oկo%N#} KVʧ?LF*+'tn+  WO>MߩtWk_J mk#O#8]*W(+ۅ2vnYO◇v1O˙p:}C)[i$ jVi%$;)?vY$1Oπ9{xgEPmue^yʻajt~ӊFE,GhH|\wdb~HlCqrhdVI OL\-Bzݫj#(d)I5V+_f&Dvq!_ӹK#M[^J1q@@xC4vi69 ƗUOPll# dF4n e"#Gq çj/]jIq(CҁHяcGX4qp4Q?4G*شq%P?JiT+z! RB0tnFzeijk UصS_dFjrCBY33!.&)qIPďA?D|)aG84 |ãA^ʗ3d?%$H!xrj6 iǗ4  ҉vAM.2Ʊ83 !:׿{~`JK=Ӳp時4CѬ(6's? _L3(vG>_8ga\n?8~?Y$UP 9G􊯋u9tcT;SiN&~LWMvj&Dg׶ҵrr=̿qg![fhdcqIB:Up!sMMx%$qGVHuHsA8AG~+wd0p&?ew D.r#9;&67Ģ_ts@#:kH`ؘW@j!knvt˗Nⲡ̠Eत~QA%!o4龌`! k?CǗ4ڐh,fnۦ#%cC4C@vZrV.V6e`0#-?L [5| 8;4hyjH6j۾Xs' ԏ3S~=4.-<0+H7IE,3T6d rO49=Do/{?RVFx@>"=`sKxQ+?7&+=^sk.J?>KfG+~myod*/$L3ҥlc:@W]ĄK%2_V&Ì&&F8q<Ɨvsr>ʙo~dN0yVWW;wlA5 YUr5@]0ɀ cߔԁATmW.nr>ңkd΋Fy}"-(x&] "j,jח1_x1n' .(/My%?7{*%4/f2x%o)a,M C'нV~ȋ|lv6Y4h1AR ī|DJ`˗ H(&Fh1BqOL \FC|򹋰"h[!Ő?8Pw&B1ib[ (\&h@&Gy&H7G 7q69w{A!K嗟~TMh Av;HnRF*@?7}￴p$#}[2 UJsSIj-LYȥTZ }N:aFزE/щ/QN].NJJKv ^O]jg΂P7C+Dȁw=.xVWsQOk}i^:-uJ4T+Cz;v?a.m4,бm3Y[[9MsjPneVh؛|KynsOzXG`G H9s^uq!Tk}81auaÆfm'm3_ 7^h?!l.vژak^SȐP>$ɤ`rAOXh?ovc=&䏃zxȇ>~|Q M&`S=矷:IϞ.e2@ aőzP?GN+(gQ?2m4f{ypfv\{9){pه@$#w92}L0_gS>q+/?`U08FFo%\G6ӾAW&7$8 S"=LC~DvZgHoΞ][˭/|n9<;o5o]d5͓s}W^Vd9j~%dec]_RFlf%hg( Od3g>g^1ps=^kyrNk*jB]y4}j{rʦ=:Ima#i=FrȠZ:Ypj ^ɊrN6/~TsІoUt@PciArd {+YXh^~J3@>hoi-nμ9xqQp^vhd(dgN&/ T*@M,$9 s-vS7Ϲ[ėhE1@+ ꫭ.d3ެFKV7*ϏNn7^o6SCSK;] %A5vfBOڥg;v7/p4yчVf[! pj|I[Z2qIlRYֶ.FQ"#Q6Ktt;O b] Ȳ|2A0@+9{oVaCL6  Gˋyw6=yqbmi{L0e£gR%dAy6`2d:DgNF+O|p=$s¬yPNJ3.h!LR/^E K~{ﳷ[tus&~2/ܧ <ڶk<Ik i \{0^9αePLݠFnd+h]ȷ#QGSd]|6FJWe\@ &CȝDZŔ7h15AHԹSJr t$~o*:-o{G5)T2ʳ<\VhT_| PQQdّyH 5-1vkA@IDATп2cmnP]Sq5mtZ xaLq_6ZҴ橍8yoLL4LMN0]FRzr+SYJ1I[#uwJSi1DŽM˽P*xRrQINK$#6[Z@E+ D>4LdXdԦ6䇌Ş q2_AbCx#.q8G'lf9%2)[Afc #aͰL`= mcI0Y^>s&?7[tILF#3 )_<ԅԟO#OV4?P[M})Skw\sh]gNLlI)[pE4"&j&3l2>8Λ:"EjۯImTs u_M6:(:wPU ->"YiY)m5VR۹|RDpV'\FN7$D3h۲HڶJsZ[Ŝ[%Ĥ$L'SvlS6CrC;r':&vh$}6GGGGYMk}yGDQqAp_GB@$7 ,@!1V؀%iW ^0H=:A:uț53I ğphC.xŒ/ċ0~!\Sf,yHY~𾅳ݯ&%JOZ  v0,m>prҎ6S"\#xH[1Ņ(}O>)oCW ؐey2[ndQBh<!KV-[;rYGŚ\Ol=xU7Fa:,lLr/?8?74(dBrrRoL0aMu&&(}v`zJ~BxݪS_tes w'۠>vҥ$i ≮qOc S4.>H =_Im*Y#Â$ȹS/ P< \Tpr^3mDr3A0G Ad54ywz6?K1 ̪KN=-'[ـ!UݏZ%PHq|Y ݻwofNXnl\^OهDȏEFbZH;A] xOѦ} 'mHo]0&K.PhZw "TT M|#g1ھG!=?EFK3 $ɲ sLO hzwUsO9eVD6 # oJD Fjͪjn" *$3[lqXWڦIӭmnU BC՗JҼ$A!Ҳ&U5-w_ݸoΕsC^9,Dlh:t -|?Ʌ_iB@ sI9I`.ѶS4;kTL.!8bnu] X8މXSy訜lUF&r\FwŒ*S OvME- n/v8&m*ealN5k}W zr>.# qݲUKL?N;Mw_9cN.]Xi2}LϤ֫!TjYkeoI:%.GOd+=)!ݦzOldR놁m lm:2܃ "lď 巓={nAn;9#>rHk(erD;[rD;d>z/#lֶnȿ[2odAlMx'#M6z'4Zَa~:$T\yغC`R& 2֥~!nmCռJD(AxjǟW`.l '!yWC=TV]uU駟6s6![p#TIB\~ش6̙3-o|YfM^*D5㹛1cyWц1eυ3utH}G~i"X,D.P-zUC2@E?d(l@pRp2i$31RgM $5mFҦMh]vI%C̙c-[4b F>!ϗ?!MfBR7]= [TTd; &îù2=$MMwVWRQd2ep"rTIE2$[*ě3z~q'gϖaÆe1˂fclw^Hs) Ou/믦7^Fm *kh_~)>r)raɐ!C< }g̳#ܹs-?.cƌ1sM6Dv$ q5yI8rvdӔp,Q ːTP%FHbIN3@6G6aěԉ'(_1BN>h3>=\6-jrl (^~h{=_ jf ^yh;Zki/_}2emMs=g 5\#Ǐ>}GVUnsX3591=n&RqhpXq px@TGMֵLK8e@{ae4醗z3~j=9iѢo޲ɏL]wu_~).Ld]y֙oժ%_d |A^[nm'/y䑦Ƥ_8pBܥs8X}uˊ:TQȪH-CI*"ac f~+__]X 11YͱD~!{6aݮTQkJ&ҝ0)|Tq3BHIe5KetɎMuVԳ FȚWQP{άhV[m%ƍI0$Fg}) qfw}g{IϔGŅ[<Ϭ:Qۏs\%d0 BW_]6`ÆɒhW^yeFā<䚴i1A$gp52'MƆx;\C MI(B}"݄w${{yg9B^Y@O8W袋/x`9ވ$}'\~L&'NYf֒ q!W~?舴q+2rԸqR.VB҇!.I\F8 M&@.ԩD.{`y=aa[t0B{ fbvꩧf"'rWdm_~EeZxyv/դeZlw^sΝMVuf@n3Ie̱ w 9g o|'>0ٸM͠ mf@0 ?=S8/Od֚t[g (F?[ 2Eۑ]`M_dRNs|hbyz(KOZ>gPs?³\&j~QG%SN cVZIn&#m/yd0".KYW*pW^fFtMm.K2Q`-,&hY;~ $\cUM ᘨ!aR&/LZX߂FZʸ;Qxp#ж:Ȋwމq_HC5@\.lMA u&D5T<\6_2EF*Vs9 ?Hp~FslX BeC*9cmE6?;`HZ* ˍ{ ~p9V#`}S;mkiּYAF>pg  x_8lڀvr w-]<\@55QlM1į5OJb5 hqcBCroᆶ &v>-UfqO)hEq ,mZ¬K/Ty3Ehv:}/)sv֥r!: uUS>qt"SK$ݩp-Z,';Hn'q4\j@AEp~NqBfOʊGuι@jC:h8[OqrPå!neǪ |FVG/Γz7ޝ)xQ=pVFυr߇gb=t32<8SֆN_}/[2s6vl\@@^ Lcj4{rj[,>"˗3?ek{=>vmm^{m[Mq.Q&goVm7_ЎMgl[z&P .B0?!~^ l~dß\ނ7tLjR>ǩ:_LQYթU| bI SbgtuoHԶLi s E/*wmd[V@Rw6W;C>p|PTvlIRT5-FkMRu[+#k8;N;R?kYf`u W-!>W_-8 |EW_O?B5 ux'j>zCsQRFw܌{f,9L,xہDҝ~:cőP5B1Qgzg _;[4X׃履~/>#ȕ4H3sAh$D#qٱu=Tٯo2W'g(g#\A+O͙mt;(` _ :dc 66˜;0?W re&.+2g<8s 75yΎ qq_L!~$vI]N/7E˃_x^qx9sSv%%>ɰ< Un)8bj@GaSbޟ}xg.Ճ/tyFΝ'\s=Lp&+1YjnyLiĊD quNboSsjaHIqp{褛 H4k\4Z4K%ݖVJ90F J \*XT]Lф`{{Ӧt9o8z 9˴N!e@@v/\~^XDzT&ÙR?0ϗ?C~&Oyߑ*/3| U@8x\ŋz!#q⍩ ~p13DLeN >*1o樠矓&=%~Lt[bDY5g8TAac/ӧOi3q=_z-3[kk!_H >KI22S믿^|A&ICcL߿ͻ G=~,AILzfn~,aI~~a6X?@@}07#)Sl~e\ͼ1@+R묳s1FG.?39‰z衶nZO?3c-@c/ iu]N;9=zCrMޏ'})jw?#܃𢡇ȳ x"/Iu$_!b.d[[l.w:C43g-;Jڴsm5XP!?% 苚D# :q%JEp~tùa"@{8KTbfԃ-4@qh!fVa M&5;dwԨQF8!ѿ2tP1YrggwiSF3E>7VBL#mRLސ^FqqL5٨ 3'x6YBK/Ɉ#`c9g-y쯚updP0x`Ð0%Aǡ?`*\rDf'Z=#n`΀`Q(:Bs?ƢQ⤻RE2RhHmsowڞ6eș_K~L!@e;g/}aGf>#_I7q|e >gv~JuL_xx}?#mGI!0 pCww`|`:lV<}7Z\rdy]'}-oiNCYF48֥$s 9ZhTSqo¦qmoG;| &Ñ+zìY+G㸏c @:*\8&W2Yb\dSdK:6 FH {o&'   Ppq#gmXZ%nLA8/wG Múd%Sb8`?e35~ gϞmėd_6^Cxpvhj$`s4#L 0@ȑ#mѬK7}#&"|՘ M \B!N3Y{60 --$RP7EmP7;v 5o&7\N3sIIrA NP(@g줊s/h!8tHzBѢS׿cRmҡwWs)K>W0ń3@h, ˓1b x{sogBM!D<i` ũ'h{dĝ~!Bg{AxQO^&唯ng]8{|?c38 gi8 zpVR8jɦ W]u-2K_vqFNc.S|~g7w/>l)Q_b#N@~hq X{\ BX~HF yg_gT&ZB|1M$Sq,o&9f$~N;YM7upfaʇFM=yGˏy  "yFY\4(f45WpC)eGA}dL]'W٨IK%ݞ1o:CA:s `m͍Fۘv^a8[z)eȤ"2'?5~tᮼ6~nKg+/X0e嘥3K gP+rQod5|pg.LЦr?_IBr8D"p8Ʈj1ṥO zm&Fl!Lra%#$e?{ 77W~W.[yh!hLAtR.Zb̌ NpYQ1yvuE;=1@`$fb14iy6yW^Gxkܚ4+W6y'A &/0aɓܬ;cQ.4س3!<%+0`рiᘀJ <Ղ/x[K W+xH7$)rA8 zsj&j0x;S ooLBA9.7~M_ƍ+ u/]:'X`/~NQX&&SF_5ڈX!&P(p8wrQ} B'ܘ: mt_}`!Z_&,BB!yg&IL4OpcM8PF;VJarGm6hqA$JL6XsϕoѾ} ҘgveӄC!8堾 ;v݌Lg'՘3m^+묻<w$ziff9|`bD*ChQ1B\sMYpp..jO[TZ$( X7#(^64hk\2zHO#r\ kmk'"H./3U.S͒iϑfA *hw<s0rl^ԓx .fhm1@ɑh}gА>Dvvþ|+oMl=҇td$% Y/cYx\[2}S~ch oo`mA9r uʍC(3'/1A;Z.]XjL؏c-7lɏUWN9˾kߗ̤\rڝqq鹪6R+FQX_ ѯ#Ɣ?)o |skBmvrL2/$^tUՇߏ 6թ'v4Dzf$0I3g(o$(U/8Ote=9f]mfCZ_ayY| kJ ?qÏP?th yAqюX9R 'd7SwtT8 f+Ǚh|לipM9 80<#w`?>8QG /~{9Ɵ=V|7!CLp.^Nşx]F\I73pL,]23U5 wb CɁJ &-1',k\NU9Bw̤=6"XM"zO6pk'wNI4 ZP9 feY %j6ÆdJĭ'j`ia^5sjYp܋5/钖aeZd+7ɑ<-kA 8&bJ,g<%V?lqZQ("ݩ ST (w 6͂2"6|/7./&Gszrk[}"āO,c,Lcbh9 ?^W엔rͳ,tjy) /an ZVYUX9~?~SecuVqr/~ϱV[S=aWs2-c\ZHwS*π@@ C K/+ి# T_-.R\tE%"a0I*2~9s 4 x9m...6 ~nV,ڳ.`]P q@pm>;JJJm/+tӠ|#Gh7&Lbݰ40pnsr{5=Cn]S3aõ0kڲH>)ND9 6U  'Lcmc}1]y7t%ݲ'X,Pe#32g^T@#@ ~믻^N?Z$0K*;]}նn4;+Nx8u8J'V9SP!@-m$ ;݈RpbÚmV8q\@s%ytĢE[Th>[*eb4!ɀ@@ 8oҽz2"gjWI/\pf>([l\|Ŷdp@Љ Kb9_v\^汮< p Re9˜Tҝ9lC@@`̙3H7'Xp@I/dɻl3uT93lkr֋3fm5NyԤ6B޿9SdڴiVM&M\?h%*΀Œeʐu]݇7 i@ -5&{i&$X"nFBba- &,GyǴ?i|CjجϗkƖdB2&Dyk^Bj@ 5+G f3NΝwa 1kG6xc=z<Ӷcǎr]wɗ_~9ur'LO^{4޻Md5,S%#m\.ZfHV/m9 QSF? vhW_}u+TPS35aoܨQdM7 7P4i"NfpY1e !ąpN~<~Α;Nӛ~Jb7,\684*:;4@ +Hw4|˟:[oM;8~gD.[liNv"vI.o :TsO]/d?=zXxVG\c3oG-p8@$RV^*Η5/kKjYj:@O8aIp@D:"s2_~r|`|w>1#|?[U x!6L{1+䢋.-qv )EDmĈ@|K\aFo?q;I\6Ν+s]ZLp2rU 4Ն* G2e9Vrҥ%kAH#ш=3!?HzvIо]{rWi޼yRyx7tLذRPbN y _GэoE{_YwueΟsl3`4^'?8ci<F]"RuƐ@@>"C\.AE@ $5 }9s 21rm%K~GKIIiWYeYeUd+%8i>;v d.L+f33Ǜ6P@tgV[͖)mh} NӁG."?  RA*lT(:wMn+]z9+R@}rQJn(K!_=^t3 T ',|nKZZj58g'\ihɄޖ믅 X~[|ͿԍɔEJ5U :Qm/g4f[J,k;n^~%5; P >MdK |CyU&+%t/p+ d^kmvv;Nv9w?V.G86ƙ5k>l%iU."h# i:ն|~x2㛯_~E=TRU!%3Hc "/*{MSYavRҩk7iڼNjUu]B܀@@ PM&O,wyM:C3. 8ю%NݟpPz>;~ )#o3zN>m>FB I*,hlʢ&RJ]d;ȂsʔxòxSDS$xJIb Zl&6V3)5ijrS^5xk<՞xҝS*44~wXј| Zn!aD~h?39۵r3R{EԉUq%xCr**"}s^`FK><=\\mON! A[5N4k&M6UDb_S,.ܽ*\:dt/"  'o\vevJ}-w 'yKV+q첋n=Fw/pYZj.˕D).Wd[%WE UZn%݋03q6ۯ+~d;FsC4FlȶMT]"t\9&n4ܫ*t+ ,^lU8VK.Ķg-tMdu<7`o*I8K H(.T vQtRTWV.Sҭ$ыՎ4މ(s$z\db*o5&&Fp4dhpq9]B@@ 4xkR]b%v|mosEfV<ӿ U"3ƃcU"V13hQ"3(AʱEpfZb)#bo!s s4$F@!ot9ƦP)Hw] q@-ri\@ p9駟d뭷z;VW3lGf!QNVJIri4B%ShmKErQnfl'V.P,Izy1XG$'֑xҝB*d֡cs[oUv횉B?lnl\ l6knL6si)b!|@ XcaÆx 2З= :9fQ?L ʧt%_̴LpG+iQm=r5 x3xxR^ OKZ- h@@*SO?G}T:v/hbeUq_@ p6YW33f%H̿D3Y"ZTƞ[X4hw:xGBT۵pHv 8ua uA/ 1\=i$9ꨣdСBˀ@!wj@IDATɤs2Zd sEM\C+׼;a 2=_JagSDEɷQHmyW&A۝iЉBtst#C?)쳏 8HG'?ɶQ8R  vTW)@]w C @#z̙r)gum6\7dKq @*y]_H|C 7ny f%z.= |АRfE_/wqr 'Ȏ;h ZmP@@ !̀@@ pR6l#meĻy$[?T0 @kY9nV?'lh\|E–~/! N$ KBIy[nzJ.dM- |h BwNCZ@@ o\kP@@ W_Xvt)LL;!@@ ҝj ܸ'J߾}zk۵r@@`1)LnV2m49 q%W*@@ ;X$:QPʌ"PJ@3ڜ!@@ Cp/O? 8Ъw6l({.#nxF99ѽ\ mз$$BqHx-9 @#/Sʩ*{AY~#j*Hr#ܔYC8_  iX'w%wľ JuD):j'smwnsMn!NmXr65C^RbݨQAt֕M*?"ށt׬B@@ G6A͎m(ZDP-V_yyթq㢈H%feECӊ@DqlQ&r'&ڠ9@Lчr}LJrCer'YvyDlC 9S>N7bFU.b8̨LKD+nڤ:ҵ*7+U]S I+&@A!@@ onmd3[xvqM%G dWeg3[ʹXI8al7x_iס-*Ւ4Vrr#.;eָ!@@`Y#'4߰JI^֭,pG$NK.r4SN:t7I?lҥsȾ;Rv.="w1"M,wy,nRPXhu/LEmwQSp)b!|@ ;GyDvel&vF#.S℆4qG^tC2xp|HC5Ҍ@nH>[H嚋ΐo']WRbiTHw.$4|L3ig;}uֱ x_P€@"Zo%L,繴t-ˋLO)W!jウ68@!`/h-5ZZ*eȑrQGI۶m/h2P@"3g[D wHwN,]P MKYaʡFhѢlZ~wY_K6k!e~waa­GE*SbU]mB@@ [NU{O;00a3F "M6Mlc(w@ ǙyY(՞[m11A- TC IQi' <,Z8諉j1+j1OM@kVdW^~Yf=07/Fkp@ <$[[ Dm&&JE5ݑ@Tx_% Ŭ' %?7qez цKu@AC6@#o&=M?|ݻ F\Umr wrQx|L,DP9s!C .AkjS)mWʇE. ,yꫯO7}Gȭޚ$܄ 6c(X@Jxn=a۽Hɒ&5ms޼_2TFȾD8%L\j Vt/ hyDwyGv}w#ޗ^z~rًZyYP@@z@cGێ*L*%B*FXByZfG]'PӀ@^ xo6[JƍSan(J2#6''HLz yhPԾn.#`2r?9גG*dHw( >K4-)Wz1?SO=Uz!ym={Zqy׈p{|mdb.{Y TBVP%h&.c?'u׬~kv xT_"]GCHұZ-6%H d^e?O?J%l,kMW(1>xV./u^ a%Tjyt>)%g< _󸇂3sޙڱt7j@%@j'qA'٢ExW)닒6M4t/?;+Nyy2oߕ #Q9}B}>H?|/ҬY>9TWوˇKIQ%zI] q)Zǖk;C&Olqwgˣ>j@7jܟk_ˆkG:g}L.HG <~Ua?oٍ?azyj-)ɥ32O:X!4k+~ai߾ 0@L"Z˶ $>;6sy; ߼ys]vpV.5 ,בgtϏt75ۦc}7f;NZ>-ay=/^yV]uU#it7eeOC듮JS,}BwAY_҂>|L%v[Z+ wY?c2}E~Ҭy "iXsF7B0o|뮻98Hhz)3;<)ًAϸ 6"詧U6kL;zm1x衇Gmi;_}#pFD6t8:G::\=GLgnth9(6A҆# cӆ:-D$ RMG3!6C [>bI v B#an#nhby@ĴTSrA$zCt!U'z0?>| 8DCvҤIvBД#ry~$D  eРAEnH8rZT/,qF gRH h$eTp 34uL M8^Hoe}1rOXʂ!CLDXơ&_ ~qpH6m6+h1v7iR>D1h+`qvڵqg: _aD?;"`_ܖ`fLqqn)\q׍~9D!0!阔зEˮu](!-7>Iy LT+tgՐf"qAtyHA yHq6kLNCqiGiuFUl^!$@E@)"<" X błbGQ RUz-I󝻳 !d7[r͝9M33DA@jFCB -`m57L6ֈl >ҹsg ZUX|E=z3hi6 :YAQv gΜ) ڍ&w0ڞGv*tS #M64Bk m'!63!45 IABL;?.]T΋<g@ ӃnlhC{  7H.@N19v~0[vGO /4а<{LD*3@G- LdLA|AбA>II#~uVD>!?C*;<5:$؈#p $c.qe/px3NyPǔfv8c[D8ؿsu@@ 圫"MTi-C}Gb3_>s~]Am=~aQ _6ў__@ JTM`#`"LXhј A=fK.I9zXx>53ϔie`?7<Œf"0YQAruhF?h[Aa?4ҰkF m7 vư+3hi_u!Ф@ChA_z%/`-#A˗/鎑_nҋwY`AgyE &H< 8G #-XAB o^ .@-xȆ{ǐӧ qBF|p{= tchSz8PkfWS&q~)~@uezbKJqM߳wicKX mP6mڼ>LvżwFӕ V)TbL# [o%+[SL1` 9*ز@a>i{o^Lq)`"׀ /b͂!)4N@ҠG\3f sg-ޑӰB&`"đ>4ܫ׬Žȑ#ENhEH%Hf&H4Ўe-5&uch1IӘ ]@11ށ3PM^+0svCf N؈rAIp0`%8cbbj~ֿ__+Zj (  l{. {\#ȉ 0 l=&{|N4ģGES$uڴh+QǡAv=jR0'×RPK)!'C;;p S6a.LL@Jߞ,ͿElJOGIR.95E/@uBF $Cc 9xM1 QBÍFq ։yT]C# 4hhjAH ~T4i /;qq|kh1H@xF ;4)GӏR-Ds޼yt / H-:ڊ`"$@;rC~[Lnp/x 3&IU-#q,$ב#>|0+Kh g@4Mc &T91Qy9 xτLPnPF0H*~O: G' L`6/oH3|71AphΡm#&(0Ɛ|BOex-#it).:-KFDByhHhҁ- #Pfq{*+>N%A7!WR~;N,xd} m /ptAG0^Ks'zDÆvwytbѶ3a΋_ܽ. P ztƎb}=B TV0K%8¼N͠qse{>ѣLCdIj k:5uts|օ4a2g:) h,X Ku؉ {ݻw˚X~1$gTtܷ_0O.Ə=,Òpf%qN=,ҁXuܹeG,Y"bScvW>r$?E+]rDQ {۴cnt  Jj<,*":ڝ vfcolJ/Vi/{ `]IDh权W?Bk? $8w> &|6 e˄՝@u&wޑs$ ;B])9%'' Ibc̞=7o.cb_/P?W_IX ƒ0r [vh3W^麏O?$= d@z/vQ7 `D9 B  hl2 4D;} @qDtС4h Zf}衇hrm&tĻvZ:3%{%\"iF?y6[c v.kyoAС߲e@zw@q\s5"3h# ͉.LU 7;Led[2``K. 8v򘔏 }0)3eeEg87*!Sի_NR޽;$v#M"Bvt]\,w ;vua /N5jԠt! ׀:7" /H Pb ,؁8Pl6l($СC.-i3>hovyN;  qB?v I CիWB>}&=J5c`-7Ld0vZ|h1rCPƬX/4ؓaرrAaS7.M+ȑ227gyA b'm>4=o$ DRyik#NLIA Eh._l (D@MJA6m$IE .6)4А9 rS L.H ! 4ص40xEx1%%Ey믿săAH30%49{=ƃeL9%9I!N8`ah.ftCN/b j.szSW^yj֬)o ?+8CnR2f:#@r3]vBNh6 CM"TWr} 4&4&$ ͢}efRd~bVQTӽi&! Þgh×DL\ m0}:Y+~+J]h/_.+CxYASSS>q/'__'ĻiD0[C\*-hAGI`L ŐvbIkZCL|"\[Wb(dxԩۣj F4c6&pO:UGhR@Mch: IJ(CLVtKZ\:`-kL:{Bsx ^xԨQM3&/ 찻6Zqȃў6h@LT~a7AFZhbs= jhn`u7߰ޅ4H3d V4A\f1 .ApH7UO&-"L3T+.[*bo}7v!Ɨjh䀶9`'l >+{՘+jRR2l0W:Xa!\q&ރiG=tāX#N|eA(7d$L8L4'&Z_ p]Ry} ǝďw+v~W4GSdT4E9K(*;k¿)Ox?,,>BݛSy啫!~!Bp"P9@4ڲX_ hE&(i*E6d+ fùBg h/A6@A(M'ŇqzF/*:JxEߵglm\dTyLCa480%yŜQF4w\ҥ?:E@P[t2`ӫ s L-Y(Ah~!dr$'NH$\4\n"(Ÿp@N*St獾xnXo2>(M6JvmEK4(vqssO((ܷz%VU<c~OehnԩtDIO*"(eD@IwFA[Eg0I4iD2I)IEƧ(4ܖiC}G>OJ}1U&EF?ƌ#ON}KUb)@?"P`3m!ۆt{LUf! _] p9BYrS5ES͌(~0a}4j(!ާ~D܁S2i"vۢGͣPkq>ɯy AA56!,UV%eENSȐE `HuEі͛)--j֬)W@:&aaaw^ڴoՠGFʗo}󲉮l[G%Kرcit 7@7GE@pj :v=l|4dؕ,KaAAy~%{а2% %*m2+vJx ǁd_/}4p@FE@":R%ny]Hh(5oQO2"YkBt`I,(RSS KM[AZ-eS`@?gQð텰xS% =*^Fu1ar4/ xi_Y*+"Pn\ ,b$%Ԡˇ{`7r?:Ee_?؛oIj&Ѡ+(Z 恜oDێn;zxv;##fΜI]vHu/Um/F(< f*;,dI -7*_9F[REgR?pcai e\ic27q.0@lZnWi L3/##Zr1dg:F0ң宴yQ)*xظ԰Y[0bl8aVnmD|kWڷ5kWby.֧0 ,dT2_dT8m߰&5-q=3}0;o̳OW(dB[,0#5܎eisiqЃ+*6s@3))'Lnǎ;nݺt˓ϣ0k|9Qz_GP"hteoъoGZNhhǬU=IIdHusҠ .DeZTMʤvAT5k"TBIO|FtqWc練7O p;B);?jծFM?%yE0%nG=Bf%,9n[fDIv%+9, ܢ޵]=^n4u=`9 Thr˂3zwՔn~t=xe| JPYwE_p 6$ZTJLJ‚C#_ҹkik+$(. 귲# u&/#i ]Y ԫgZ~@NP@D-{NFCBO58S/g-SE TNx#Sq.N O%4kV$(/3;f).uM kv8k a$v9޻Qvv5'Bw*ZE }*X1q`T8E?/&ax;!T$+&8R ʛ S"E(;!Nw,9ٰyB\Ƥ\l#5lO6n(V~TBE@PNgI7 <弗iK(ˑ#["fq! D C@E  ?J$\ޖ{dh߻x%eKۍ\a0/wɠn?HQ2it[Wi.8 lȀʵI0pLz/s_pEqzoh=2pH; ۵ynLm@׭F]12AI>ĻK-~9X2#X6|4yenyIۈ=v~6{ΰM|ȨS8F=RV^k_[Q wHqoĢgǙ [{ORYE@(ϒnoTڰrmSt3 q8*+"4q5" sxDa@8Gӧ+Q6ufrϻdag v-2uvvr=2;FXAgaCBbU^JJ!;($$ ;xYqCtAZ)4ihщiʓi;%lAkia+&:>`a#B;zjɭ^CG29D63qz:? /l p~|DGOUEs_L-;7~ \|@nkXRH_ Fם 5BwpլYpKu"Pxv >!0zghKhO)"(xM)Sc?s9UMKoI[kDsy_II#tz$nŢtpC?ÛjT5 kZa_7RnM)Fe2Qݳzfyq;٪>..a.f3]r'y%ԪJ;"P2YKI:&?>^Њvi"d^3ӳaڔ6L1csN)MkRL23ibBC])T.l]vv:bFG9O_M4N{7Iqkro IX3qo$/7d8@D@mnBй>Η4|lcjؐ z9iR*tÔ!-+V.H)kQ3Qi3Y ϗЍݞo?\L]oNS&E U;AoFnMȡt8?KY85ɛ>C8 Z ^Z0]6>׽֭.$j{r ǿw* ؓFnľ=}9;A$w; ѲyX8ky6>~Çl1yޯm=4,f))^zbǴ|:pY7^+7.,j}ZCgV&," 4s_kZ$ ?%իNY{!KNruє?0t2ڛFU")&6So(k3e[$ xDW!T5:Z3'υ)I-ҷ83n{RJNɻ#kf+ɬg :jGNUeo6?GCnĚIg&ԡ?VVL5;7.%FƋYΈ[PUd :lHI?s5hDNK;/zI/UX~v< 9"?LJ%^>vyzk輾ѥ.Zo&>4"9<ںn9dԹǿ$5 [)d!NgUe_-^u}XL+Gl&!lo5Ą(D Pfm*u46νZ̗nDN@Z~m&z 7*}86;9d b;e~h#l]LJ!f0QO4x9(IJw߷󰄷g*ٻ}ijթ!:t>-Z4Oumk&7i[.}[RÔbo 7_N2y!"5^ZԈLj{V#6OBK[\_*$ʠ!L ?@Na2`xWZ?36]CW|DU~ˎ C%/`k HB~U%dr*ɓ AV.H& j2k7SAgfZl90C!SaOGj5U[ ɌBN]Œ$>cFRyLz[o!|Hx>bCǃYl.A.cv{"VMی<^%/#kRq贲"_ˆv]mhʍL޹F0QdWfÙBOI6r(9`?{uKe)*'߰'MMK_#Q]2q_U:E@<hը_F<8~_&L?Y킶eB](X!na|zdĺr|()'h:!kX~w{ԩgs sG\C01_Ж s,Ml䉋XY砝8=.EzjGB5]}w_ڽ57fċjׯ!+p-܉jWʒwE&¦x:) iƱ,YfuF"ݩܨ/,WN-~}ꝼ"6_B\6%|.@;7UmO_Bm6A$=:̭Ybf]qq~١G(C[$SXhU]vmyؓұ,J?!+OI:Mk*4[XiЍ=Ks <:Tp63X3==q=^*KhJK,czl~Nzdp.d}qa4E@p!b"yM"}|F{t("1(5AaT7.Mn Ä&%pМbM46*P{?^ߝvkE3ĦE,_758 $TZS,S,whiC8ۉ!,kdaeҙ æ~KaAV 9lB--x(e D`$õt}5kj ShX(4`fg@S$9E"?`m"#]H1zxˠHyAYu#b ?Lь:0:fg!$g| ^ofEd,&4a4Bv` ,E`%G%g|z =ut$ PCU `} ❌C';ߦ_/_ц4h_E@6n7/A@@«Xz+i0٬[jc=g B I| lLlӬ#,te U=n%^> AB>EK < 'qx2K~+MB(&pxP ( ;}[]wPWyeFLb9n&k=o3:Aڵ?ɛXPCcq iǯQ#H*]fr[> Fۀ'QuC I:ΝW*6g>_ ˄VwrYBO$lle.rp]wJSg8įyq@.}l_(N$e⃼g9 3y#%݈.x^+'=OK+@I.B)^LRJ=5mbIa3E@P|h]P= 2h-tK5̧I|M-!wΠCky.$wb=/tZ\8{B/dOvDm\W3X};aηœmRc:u"[fMSg#xSItv׳h[t[Y(I$z%AL Ӌ2"1㗬5ҙDĭaEZR;-F%T1R(?  jxZZe5ÿ 7F~0\VK/ȩ2(@$j,p߅`o>QR^)"{t|#(P*/_;&RM_xb:E@]Pw0Lh}5+>}ko;LP,b+|Q"k`2gTP-e2T~"e02*Ⱦ&ʣ(@԰Vt}hͣd%.gvxk9Q| TҍH5{~G >ٟ,g7wr+]-_4WT25bUwOsΡ[GJ3 6 jE (vjgn( F$G;| ͓n?D3,I?FYߓ+߁&,+7˨E-}Жw^ :eApuE}RjF=E񁑶bRQ)wuRbrUEyaڳ9<_zQhhk"_OũҝcJ[xȂJdNzis-Ǿ_HI ÖJ= "("P,w+||jܺ6 Ӎnq 0}_otÜ|QE!tAe?# l~FMyYNeHPE0ߚG:r).& H2/;xQTQKQY33i3z6;gSQ[ܥ P+"('校a*;|4;rTBNaBVo]D]Qs51RIOE@P#`iH+J^T!5UwE>-d΁X"("щŽ3EE*?)fgԥF-0W"("(D[0u*MKMcӒ_!ytҤ T#RE@Pr!\˾v ;76wXHds+)"("P(P5@%!<LZ!NBbUm&hT"("(% =X;'ANߏy&^~HϚE@PE_P/9r2QNM)66rx8GE@PE@pJ݇#X`ZI$gLs#b"("PP]2;zB̫+m'Q\|;JEPE@/tW~NB`}w9E)> B{˳b("(Pq5"D+s22x;wҪ_RJT%ڲ6^AF0wu"("T:*e-SxP(mZFy|L-u{YBM,4ܩyƱ,uvXa5E@PE@Pl(鶁>sgJR%.j7LLewg7H$15jWww3i~;rmPE@PEOPWƪ$9yJJLGbY+`("&̯(-UM5A;,8v9HL^9M*"("9t{[ ۞݄=^;6of'a< J6g5\`f>gRZc~$^= ("( %60 f7n͟r(8: :v4}~5y2؜k~]r4 NL}+,ny\*MY LYq{IQeGg-6wg#P1Ж.tmtjYr|\gQxx8RX KP ;8G>q RWǂzJu7QA(p(#η]÷}Z RWAy9\V.[-Os=/\u5lժǪJ/IW[97!@o4!!8lyj| qɨ@w-=2|  z 5wxtPsq\&!(Ou&G7I霅WUgZr}I`8TX9M!'7ʭ7 (؋ LU$Y38u,OUq+RD*:Z u6Y_qiYD~Bp? W~ty\(,;+6CJrqv/uhxq@Ro>ٚhh8$NB!?j&%R3P꼭=75!1꼥ZD6i3hi] MNAh-LE'ͲFRI:R7sf>W(G9y:kllplTޗ]%(# V͵66*Ԑj7H/|B#uR1F}yҍn@:p{V4/)cПGtwf\SH#3}eP.OCH^i|#m`R5h~-),"Tʍ!# 2Ш0CE3>~'jDyiGkG?~X›icn4nTY-.U-4k`ѯA/ 7򄿪5a`Ow$PC%˱ZërDzf)h5Q-~:S\XiRq^TlٲΧIwAc`up;r<;+zgV7nӋ6i* 8 F?s:rsA@Cg>|#3rPTZ`!=4w͟V#"B9ɒvUKv: W*=i8]ozy}Խ{wJJJi\ n޼fΘEO<e͡O]L1U")670v|=3},7|X6z/+~G;I7kS+g$3e+WS>by]E, Uܧ~}I擏}t11ƌ:M+v1}Q8?@!PVִiۆБ]M)ftl7O\cF^ ٶ\6ƆE9bROUGR護?NĔj»+k"SSFl>M Dhly;sM =zc.k;U[SSQ?4ޭZ&<u= /Xl' m>l- ެZ8HwVb6zެ$[, ҫgH[(߯_?!MᑡR>1CuN~ǐd\]"U[bƃd(rxV!ӌwѣ>Bk`VlTdlhk֨I7|-}wLZhJ_A`j~`! ժZa-t|8|칝^9L=D&Mf"T*E 9 _.!+X&<(A޸!RYs/@9sdъQ=F"7NP*0Ak޼9=S4ko2ֲ4VlKz./V}e_,&h;(ש,L`R!My}={Q˄δeH_t3k|KnN-7g4>{y wXy(O(ÇJ8-'h}% 6d^7,V 5}t̄4J_R9JS)(!U 5..BC+d[\W{hcL{b9z\8B0EUjGc2``Ŭ`g`͔ ԏ#im*(9d 8?^L1zT{!H-:DLt Ox3ЛUt,(Cf&l,Z hP>, fj  H?G@7R1'|)'X&ʹFl$F۝e |76Eߵ犀#`7 8,B^pʤ71M$@D=u9 g=Syno "`ʫp:o,A2aJ'GUUHQ(+:dVmg߀F@#:\(|`Ё28U6KKq5p) `{IOAl[òuj4jo%rYIQadԘhHYr`VI+Uy#04qCMD>:k#ͯ-52Y,';ˍ vSOOOj-  )NPwafGR=4OMLONV@Ύy r3澧+~"w8pKEu #|im5hg5Ŋ/^F2*ž7Bvn NJaDLhE2tgzmNܤNʯ7>uV6&+i\h;8ʈ HH"%M`X'|G(~LJ0uN\Bri-G@^XyIQ)˶Lx$긶iLM ZD[H5QU"W|sn EOKB?I+EV[eAI = 8\HT҂DO3vaVSAx |VW>:묷dx=+M?Pg[JHnvgKn.*Z5g+E^|YKg-J$J({^ϱdGG@? $HSML `Ly5yzߢȃY}L!L0tXsRKS,r`;։=F@4 %'Y*)߁PƅiBX 99]TY?-"" O]i*<@y<{9%,mgd-z\{@?tmrjL`GE N묿)o:pC"+r m/[p(#=OYAaNztmCB\7rPlb&a9p-P~ϼ/482iRρV삃%<ч` 㸱/h2d݈bxkV_2?&*(0hip5" .uK%"/CQ-ש(^g{ϣs/jO:7/~Nj5b[6@*EFґC(! ED0-vm('A(:2rs\eᣔz ""èF8 aR U;hRˎ (6.BB()uzN&mۺWk$P(mG^.ѱ,(GRtH g[ݖ ApϺ V d~O`p`^(loS\zp"Zoe˥K 4 o.] RZZ:LsyET-*2)j'UKGKOO5VWS8h]4kii滋hͲtͽ}ѽ():KOl)O~GZNIjUwoFG|tǿA7wνO?_]I)x ִIq1uEvꔘG/h˚zN;_Yw;aY R;r$~N=jL#`IEW I05wjtHH2>x?CӨ^Z`ri,l6k^u EGERF>L&=:u8fЭ_^Bm۰h85jYْ{?-&Qi;?ӓҼԗhtEY ^xz4cM_3zz?7QD8kŬ eH? 4 @Wl8>SPV(kGB9í67H1IJi rdhBDH88hŲ ؍SSp;ĔdܽV~$SnHpSc,bc4hd7dDwjP;ɠy_I/@Ub }:(HVNS׬BA_O^SJD;qMVvl'{4PJiZn||3})(j 9ڥySM6m_N6oI6m; i>ta=/ny: ٭' ǀ9X>5޵K9&%6“uQqEp ` O4iF#dK#D߾_ij(LmHOB*..f͚  5;F*~ fW1`obZ3$ɗG4z(gN=sEMsrxjI'JʙwH1ւA3đ[~S瀎T\TJA}weypڴv;mXUbJ`Z\ y͚uO^*h2-=:whC :1l䯧s&6:K{X-**mtTSHczkDy:y-6-@>c,Zx1=_II(@ @ƍڋ[bX|}fcCHxygSN&t;z~ ups/ e9?kżD-蠃QFu->ȠsOz*lْ`( T|2xe:867,\8s|?L/zLh.]CLgֵ.u;i\3'ߏO懏_ }J)W)co R@ilXb6[Р>=dN%0WlQ<[RiCS;JKIJ6[w[N b/?ƗwX߷NyLᷭԩKkI+>_mŧ 2w/_N*-Z i^SiرsݞWEWao1CڵiPӉ|Ϡ[W^Mґ3+T(R~>jhxN @,i+۷oo6mD;v:?餓oM7z 1>>O?~{VU4L#"Zfx؂mF?0͘1ƌC({&~glqPзg{D|`#e~mhg >=w?ϼpD[i!@PddӺU[GҩnCgO#lS)Fp4uO-g_:tAq9׽BW?:rtG>ڻy^F]F;Iqk m,Y2yLGhճ/~K3{ʣ!cӵ=J|Aiy;E;0dG~-OsKGtcU c]ahp|x-}cB( hΝE]D M4rssCa)?| F5hp4ʼnkĵ }q_cl1hҤwj _x  C?pgy}'L&Ll!V\)Cw=z{={aha]N(7Zl{3fUS'T;| [n P޽LB?.^xQP;x[֊+' nVYѯJ[lݻK|:lXH |,pVi#=r'!+pl߾=hBB4b6Mm1J6[ JӨɀX @@aҧOuV12`D` }ݗƏO}縇g"9r>H {LCjwl~h 0[ҥÀ;i@X1 ٛo)FԩG{.CT_?~'sz3jᆪ>HaO@Cp.AA9xz Ŵ};p@э7ϧ?.r0ԡ0eÆ tܱ:qmG䁇]A!@8eUbp;b񒗧VS$ m;v 0 ${9yOzꘂC07xz-ܹ3=t 7?.Cx`ֶ|=H Թ2xJmƜiӦ<{キx@#{PjvaOѻyg#}Fڈzų~iISc҅x#; {LŨO>nVzK.!L=(pEay2+~AXB?Jԣ {{6``a򕷢1F`IC =DB-e;$c!}}6fYȵOY9:2b'e8$3N J䃅lt)Pr3/),B [qQ ǫwΜ9&M$4:/tM7DO<͞=[<'.dmۖO.GGyw}WÓ7oaWC7"< Xwy"3^~e+B(8l;a 4˓F7 %ne1;yث+ b4F 9s+"9n 0!?pp?ĈVyڼcV(HDML@Ghx̞z):s؋]uU 0xt'x"9zhvZYlCVPu#S#|O: oF 43<ꃣŽJicvԳ]2`vGQSƻOC#"M_xPRݼx oX {7FF#I[ޅgt#̟Lz…q@GÌyXg7n;^R`[  rדּΒ )F@u ɋ@2<A @Y!@0 , چz܃g9ksH1omZz%> om{e-0r£ 81^4l :XX w֭PٶQM,ks7!ԓOI[ c e׿d9:1A( ϖ ?"};bc5݊bjbl`!L@z"unϰ4pdiV}9݌:| Ņ%T.^4hK۞_oa] 3 N$0HOm "OaHڶd7dl@? 9LB:[t L_‹]ty Ks۰ xS!/WX,WuxJ#1Sxư*YL֭ZG(q,d8¨\p'T,\~=?yȤ3:S xԠ׬Y# vN]Cxiž}³(0Art9-W)g\۽'9u0}Bz_׿I\7r8ڧKϤ⠙Gz6\s3;|<F7Lt F0_ (1U:̻"@#o} C_b5F pyz)^*oʒF77x"P>P:!s*8 8b$<$ r3준9;[ xFų_.=Ƴ< kETc0x0q=DiCQᡆ E]h6 QF n~ 3YUXiaoEG"3Wq ůPF˫nB8]@Bb^m4`jJ=n7cΦs>xn{~fyĦ#`p?e~w(|~WƄ1Ɯb4?vݫ>1`:Qt_^:~ވ v^\t kS6HqiX^n}-GU +C`xC=(ԩ5q1/?BU#/wA;-4AZ˅q4pFE,sX=Dh-"P#, I=ÏƵ!ZK,A%f@~]wBg1J:Im(_MU5~$ °#A-*jnvڀxݐWxտxbM.ێXqϯgr%7lCih5.1JƢM7N`D'fĩg+6Sl%S3eds*i'Q捑mM0=MpQlǽT*/elN#Ձ#ڦMiْpLC ANIR$B;_0 r:|Elܲbp7SShUO_R~˖0<Є\NxkSW}lSN|.x. 0$Y(0srrhժ_R6i#F+'oPV vb`%rOQJ:t^ -(>_|=t(#;-LL q6J+QTݜnJ~ ]wRa{it Bs[bp7zeA4 ,?vw=W' SS Ry~ iBG=}kkiNcF2%x4[#i;aւm*ywTj%_ e 08[x3%,[L;Ph![oUW]Mݺuco7FlźԍԦ*1N1+U;o6=?9:=e*;g\u(h8iX%rePANL&̳Mm?ijrA.5 ܸLbt.&zA6˂e!$rx܃sm*O(K`b 2# VTR! ck, C;C 03+bIJ1/#! ODC(syB \xE]@?3C^Y1ʳj kM1bg0fff97 | Ƒxk[mcA+oEǢݚI4 -fdM` wwn+Zv9Ms5pne4ꛞ:8tOY=dyt`%6 SOݶqqaQf V,3Wj/JI-@ 41w.T/A'4#qz""Ϻntm5C=aQfg py=kt1PҰ3;5] Q#^Hec<`{P5?`*>CguA0F# Ph' C-4-F!x"X 0[0`8}~h[vdA ,.g:bei̯³Jf=`Ĥ:^AVUFL7E^;V̩E`9Jj3ڀzG ,& !"_K!9@ bxKmBL0L |{7py9vlĦ v:2C`". CLA]Ƴg':Pr6Mus.ٴWCLf +: C^84+tڷ EZ{ѐƹBl;Q,[=ikzȱe&r xdy访@="^&lW1RuR6y tʟ4xssиs8Kd!700< ǣUiAַAGϲ]k~ xkmtm>=%x< xzgŒh{҅WSylw.qRfd:z2#3ó 1A7:̧oQoۖw=_MFwX *@) ?0[c[+,;m).V̔XhCoЀ)`+^LOٯ?}aԓwMW0=Ԧc>A FI|}0yюbz呹Ƀ,@geBՕL:PhcXa#p͂B0g8#͝)T6^:h0&p[&Sэ)*J+9ܷ4VɄw12r iQ.ôѳHI +gtǯ'luj"x^FOF7-ff>QVs86n[C4Z2N(WvÀ̓Uߛm.;^;z,zʟ wȴ(`Ӱ 7fF.YGA |ݩi~2nDfc>?cs 9d ˰ ? ς/-ϲ1SJGWA+Vh+nAιt-BfnzDT&jRG 9n+i/L)s k3oq<+*<"U ʋK8b$ {.ze!wy<:GFeLHEld#=ɽMC+qb% ڜ?Y-\;<B@B݆0LA'q,_ !6D\h ]8nf" qBrQ!B(YrK`ݼWcv@*y/DA9bn^HtԄgYA)bWó¼`[_\wB jVIC|SbNџX`?ƉGqiRYˎ1VPG#Z{ W2W:C@ڢx7 R_ܺ`Së8H=2ዺǹat; ! 搕0rԳ( a9 <=Ŭv9<CάF\ MP~Ժ}sH9vB@W;0kFgol{i D!5ijs8I(PELNl ]n@b gz0$)F򜏈]*Tk=ТOWR˚lƻLg<>ŅBhŵozʖW)"dC6\_G I_K6M^ _x,Ofm; ^XkiǶ"ĸ3U-o"4d^Yהa4ne) =9s{^QʛU+q9it[F%$Me@ל}Bjӡ9\:Cy/{ޙ%o%2Kgʞ7>w5k?S]JRdbVӠ("|@@x25D<Ϭƨi-x45|D3=[_:Kc%<j^c!5&jtGdOROHK}ޕZkʆ*?Fُr2h{z ?8}PR<29±y<:ƃEBjPE@HNE`nm0Mi1e8m7#vZ["/>WlxfFҸ M`gIG -<1"! !(%>v,hM2^qj=>T("`1("};c$ՐNkP#Fw1,m(*ylD˯L%""#dAB0Dx' 3E@PdE#2TUcF^G׊@P;F@'c6vCۘN䵹k{z("(@"!o s-"("(@!Fw5VWPE@PE {5GE@PE@P$C@$kp"("(@P;k"("(IIZ]E@PE@P#Fw1E@PE@P 5"("(G@c9*"("$jt'YkuE@PE@Pbݱ\sTE@PE@H2N*"("5c("("dѝd UE@PE@=jtsQPE@PE P;\("("{=暣"("(@!Fw5VWPE@PE {5GE@PE@P$C@$kp"("(@P;k"("(IIZ]E@PE@P#Fw1E@PE@P 5"("(G@c9*"("$jt'YkuE@PE@Pbݱ\sTE@PE@H2N*"("5c("("dѝd UE@PE@=jtsQPE@PE P;\("("{=暣"("(@!Fw5VWPE@PE {5GE@PE@P$C@$kp"("(@P;k"("(IIZ]E@PE@P#Fw1E@PE@P 5"("(G@c9*"("$jt'YkuE@PE@Pbݱ\sTE@PE@H2N*"("5c("("dѝd UE@PE@=jtsQPE@PE P;\("("{=暣"("(@!Fw5VWPE@PE {5GE@PE@P$C@$kp"("(@P;'U]Wח4"("(D@n6/Fv W$=%M>))|UKƶ}X J("(IIWT6acȧ iܫ Cw"F{TSVE@7@IDATPEP1N0$ ¢zů}oc_K>6mIi)zʩ4% VC׿N7l㴊 C"("5צ nfEM<0ʍq5("(A*SJ֯̋\DK``5"qFw:;,Y)ab5L͐c O()p-u.5("(u@ˇת}s:iƝbsꀢFjtG IMAMh^}ٻߡ( {yު]3|* PA_t%O= R(#T("`_gfeP&o>[ZUtxy&5낖ƭ̛~v7סw|\jզ9^HwE899FM܄v\ # 5~_8|Ν v*ny7_ }3UC')'|D"W 2:um7x6c.-CVR dzN.Zn 3sAs_?~ːcܵU=vW81͇_`^3R=W#5ݵ@,$ |DeA%EfH#ĕaP7@IulߙX*ϕQ?tsm_0d r~Cv90@`a%3'6<C2b:4RfaI <ûHKP=dgQg 43 rVdG8Z~`A6F! }.z4z~3t01%q=6W=PB "!& 9U\TBmO\H|>3~LS5Dם(߬G!`f:\OP/4fA!߈.Ԭe.w2GH%8&y d:<T3؏}-ŜߤE}3IE.(д'M:xO:;5o'۴8?zg8* ?Tem m9_C\~q=}4olbӷ< ?L&:w$zWd,GFC=9u\Z D'zz0jtW^!!dlC;V'zvћߢ'riԡS{b!jOG0$z NKKr.u"x.apOoT6ng;.zn֯]>x3Ğ+Nx@xg`H> צFϱ}rN gU7Ќ^^Jv֟Υs ̉i2ۯD QII | 3YB{e 38%dsRk5k@K&Jp;CUDz׈kILL={&yb?Y1200^U=}?.9}:ē㟹?38Q G"%@bmť<tz1/8:éO>Ԣy 8u`Xz鼩xn~k[K_|1M>r C:cD3, uCgE9S02owIQ)}6{${iuB';u&|+iӎ?XJc`dgxC T78 02)%59XVmמt(c [j嗪k=""qtޝ_f:4z (oG_x]/k^5$ 0(,v 3>+qkNRAOQVV>JL0Axw'|Iy~wZ !LMEH|^jI@6B@Ig}n/#Ńa%!" , g07-ޯ]_3V %29Lcr虥ura]^o%+`Մv໮~ޗkFlGm6$bB.% UT  {m_\×z#m+^g@ +?RXLyL( 6 8崇YqSL ˋ2˖BF@B`HE$#^.KBKy($UOAzd~b"`| ᆹo$f=Vj+J>% p,)e*䐆mwK$SDiNH}G:ʆgEy6Qڸ +7ӀWnrQz32+;@Xe#h@r#s۾1$T04Ww7,Ng~B }M;D akVYw<20)lkt?=1㉀pNr !usZLڜ`V UPU s D 6eV(JtG<Î vI%Vlp5Y EV~C;PYgF_WE~C꣞bk ;$zu։́FՀrv ^eEktY^G78m:"=|9QD9ź 70Hvt2TM{C0/I) t+ tSS^Fj]5iJWH(GKLGzkuG݀UR-G׃^v/Bd 2yR7xK`YU>Q#bhie%1潷pTD:#7WB{G~E ^M>zKwXo/bZz^Kd:zuQab2o/MgOwͧ{KA3q x9ҳwK_22yt!,=c_a!Z AC넀N/\Dv$eJb-?!M/ ,\)=/f/G]JwN{:.:nzϨnU^*]I'>p:+Bɬ#BN)~WRtΫQi?fQ^m鼛Pf7Fm 8"?{ h_ߩ!gdђswO}<=qm۲r_tjGx?uhJ0fwP oݶ.r/wv$H=%S؅ `y),0f XAө>úȨT8r%ghB%4$IP8TQdC L^_0r {c@G _iC%kigQeegZqBI1{暛xu)*;cFO mxdWoYsyRn< PN^5m#ۊJCyQ3.kxriI6UjwXW9nޜFٗjmgE~SH1薁 l'%T$h|)QOPAAr)Էo_w}iܹ[pm=θ'q;;^esi,ZZڵ+RƓO9Yf;[{^WV6 fcm ̂6؉!wiIK[%[r^>{zyw'jM[xׁ%Wڵpزe-/?8]O<G=mSʼaaӦmǕi63FUm6F`ʕtSNhȐ!ԫW/8i"˼ ;`+69D4iqljgWMG-M=[o.2g|y}3 Ї,@^d:ᑝRVs bDk|y]蟋fM _z#~ryzI˶\Zb|Zb--[Ji5x 0?#ҥ%x;wk/ׯ ~wߥ]wUʆ<.eB@Zgbcz4aG֧1@ `v{!/BG\\<{:zcÇSvh7ҩoo>NB:kҭtѧs,K$=(;=XNJb#{}K{/ltC''MEN7`iY%=Wl۶mt5?N_=3Fwc=FzC|0K縏;K6<ӌx][s+HϽʋ etHA~<xA6e<7b9Y,C)cox[3Jx8^%*P; 0Zf2_58 n>y㎣S0sΝo3XB>}C8(@8p O@ͣmҠAg`;>} H\xkpf?@#FO : 0F,%Ke[~={yJޘ&fxXtxe(qg|ϧuG}Bm6fr灯0&xޛ&9֭B-[WmQ uێy/Ԏ"5#3XКxؗӑA2]P}Φb붡4VlS!i:s6Rϧ.l\x̏LoAbyW;kp l/s(u~vfZD/9|]Jӌ.$쬃8nLCFTW:sEIsTIN//7Y˙4vҔv&a7|̧Q>{|rPMNJQ,3Ilo>sS7࠰5kF{. :Tx azjs=iڴiԻwozw%dPO>$+1CC yGӧO$!B 3\r ]p"hy!Ać3ϔN: ?<ݲU+o)x@:zx'0@<(A#ضEI1Yk@Vvx0o&]6TQJ[R=[I0߻S6 \x<- +O^vЯo7h3 p2wN>82OI#,JK7/;@i*U4@G>n81܁N;42e _|Q@(s|3BOb\3Ma/%̫0*<ë HbZ,3OC C?u]ԲeK;oct8 L_C\xtE~S[oL b;馛誫"+cy^ddvjLm P(>8[-}+';ihcC#\wuܘW ƆqCq QnʵӸ?3h}3;O'!J"i R􉨨<AQ>ał DT&H=$󝻳YbB&o{;93ߜ93w 0N:(Dڂy)q3KII6#!hP&,h9~mMy m8+1L "cf !a ^śӯ_?஻RExe/:)fyIS/ة 48];4ZgMg Tu:coFD,8|.҄A4ѐ= LQB$ΐ1m~ 6!WSL~Xꪫڐ!CT>&Nh /UOkkI[!gyX"5k֨-3Y|+ #'IfVL#%3F\8CfВHR|Z\0Wف<΁_AyGL` Y=l=Ny"qA.!T jժJ/byWU18={lf[u)"xghjyjKyWc܃ 6~CI6bܸqJ14<>c&qC ;„4,U3%zZ nouuVX ʡ=FK}e b,b44:ܚ!N:Hڲe[N5xG^Ш`j]S"0CoxxӧOn>SCtO :TӌzKA‘{n`jam#hnfD侙Izq.Wt `2;hͰJ5F Mw1h$hsKԵ  4lv3ytOLCNij5rrќΣ^ܵS]h.}gI=xbg`ǀoA'@vUФj]tH3CF]dL,fW d5XЛ6@w+0POgw-uqr$C~ p0D81E/g}h{c-llNp蓡@&f*\9J׷o_m#aVm@quP/<Ӆ6$^/;C5T@mce‡ĺcǎ=C'ơVm =e:0AƢa۪]p7 X iA1jG2Se81cƨm<D:A`pѦb{|$ @;E/# <_jVF ArjPZJ(%fnLcHC [ ?$0µ2؇oLjyôN1cǖ=FnrRzQ!+(bq$ȢېO?9CMC=aP7Gxv  vr ,T'}֌sd M7dGe TOt~8c]G_Ȍ/qk:oh$&&j=ԹꦕQw9SN+Ƣa0@!K 9h`ZOⴉ8̋L]@k6\`vΟBaύOF:*d5]we1=~#gK[fm:,>`yA0 c)# "Ec!Q0BIl(bY?P _5tJA`;>yѢEE6_6Rr;WVJihI,Y!Vp ݙ [O4  /P뫝1A lAbG;LؤA_2gB >~x hL&;`wh1%F 6d L0 $߅D =j*bCݍ6#{LMv޽5|sA s,D6XE,@\GW)nd}Uqe yfiͿˌD7Tyφ#x;r`t:D#:k`11O /, YFL bq4g?dstbPo)îUC=-7f{flJQ@yp5~c6Zd++*/kx L̺pQN'7{c> Ŷ H C[{t[s2\CH4ḇt""$ɠb|ӣ2fQf&59nƆӽſ]ub"])5^vSOdW& $5:s)*9ێ!(X b H#w#]h8?cċ8 Dlsԟ%R~Ahp :E pHBpȊԹB# pW:A<usbbFz8zssscPy;w%ER?0`/Ȧu.1?y r} =-)+spd}OX D]{%;ȁr޷rA-ųA=ϸ}3݁pYtRKIл:aLo! g6N\ПLrp  N6PnӋ#Hᣱ!qßon0s뙞´jG~2+M.56?oKL銋L̊LOYEVZ+ܲaeDgC=2gl#@4s;M'urlg=ܗ%ocD:m{6ݐdψGع÷rplQ\~ ®J!*tE۷x%B7"M v: ac~w݆1??]a>o.9mQ_ZrG^)4TlK+~vB[NJGt{^zH۰}NibڊE%cInLJSnsN%Q/2zY<0&-&b?wLIvB!`@),hRHLif0y?rڊ_ȏ{5|$G[~O]XriMaxFC#W̅E#*D[4=?Ӆ64/|aw$25Ӊ4i *0uN>@cHeW\%9K( -K+/0a'[`H(Ә/ sFN(OEUZd~)djyYSҮF:Lc>lQVp=L_>8mU%e<]vsM9IeHرxfxy IYC8 =P.>(VF Pby |_tEGBH2Vt͇Sƪ3r7OCw.oL hM!5ݾ@3 `X<tw#T`!Q܁0$X?RӼ±d,ط2nl@IDAT]䖓nce =z"&Zjc {aT, })gA H6Iwq U)mݴ_ @]A&^Ƒv ɹ HW+XR˨۲ H&%>(m<%  wGvj◎ZM6IJXOwr:YnO(-5 EIMrz.e*XZ<K#eޛ.@zlPg[|!5^B­$ = tИ`y.lTgnbrS h_PQ`做noåBJo $^k@;q/ |AsZ0_\c%K1y#(p+ʛ_ʐn_j| dXؾ*w#N!@i "a@Pn XP],r{7N%DCn4.mҿt 14Qv<܊μ10ST[8"LкQ톢'OWRPx `#$׶?htA%G},`2=Zj6%FUbcv`rU(2hekdm*X X:.Jhw/w%5W=Ox?mffdfeIdT'[zI x$Nhʊ=z(,#/YFnuOFnwe̥i +'XPfCZ\42-xYիj~'/sEҝܙdtٮJfu:+g/iiiS) ʕ˂#J ZI` R +-.*3 ^OeɷKeАK%!!A?Nq oLLLܹS-Y.-W؊12c1pڑ!OyuIʬS,(txDEE$LhA#""dݺuz P)Dρl\ΙcA!恐ӠQ88:ƴWQ12[ouk%22R$j4/NNNΕ]^l, ann[Gpͩ3#'ȍ#3K\Rvk ll322dW dϥcLylq%zs;4pӆegeKJgcS͛7 \܁7 mXK`F`1Uly<9tW[24Dafz:r壤i#Ngw uMce{V˂"@F Lms|ӤгI:dԇ0nhRw;Br6` K^pm>QrJ˚=}I.]%11? aHer{M&rzdžFf:o#3ĭ@dlm2ʥ)'gr Me{Ie_ɏ!}Rz 5ApweP̪iL}^λ C7 ̥`B[vm)O$'VSLMSeء^'^FW}Sn=[(CQ1G332KM6rޥmB\9-pK"@rG°@.L 7Ff⪔Qw|rϿ6-K@`=ŸfVc9EWO8!*t'\"Ȯ!*Ð_)=5C5!r|D]r@iX_L3UM^q~is䪊9iHH9[3HVyT6%̆ [_B  Iw`sS T:YEJmLLZk(5J֯!X _8Hj>!&y@иB2ɶRYݩ /fm!  n82Wql }'~C@: G? r4\+Hsi%xK^]j:s淄!`MB^;==S>"] vȑ#Ζ43)$#-S"M_6ߣS/BJJeֱ2, L!Ky-Jɒt-t&q*_DTDe`*)*}%FDB$z$$J*%LSm9Hi84 yF-AG@_0#ZnQEBnTfr`;Bc:uӨ$K7q\Vf>);2͔m9C@ Ae`eM107rHՔsF^|Ur*ϙ4!o]I>z\ZKvo; >]+Su}EێsUai**HZ I@}헜"wiT1b$۔sY#CBf?獼tj$$|n|2MCi74s L0 c zY{@@"-sS8u: Fh{l;Z;@@kg>h1rTQVn;:̘< j̾:[нQf&Υ)E,!hItڦV9t[qWUťWGϑYC 9˔H5'܃7d}uY1Ffll?(Y~]QKV:pnPhycྣKJ@2\*ǪET LJѰzq;9  vƐmȫrCu;:J8L8tEC;dFvhShT9cF!FannBk<)xtv1r5&v!ݦ26J9{t5F2n:,rʌB6#=K2̋STf 2SP]2Gz'ځ*=r93G[Wd0p3463NrIg*rDq;} חҭ;ڟ$mO^^*iFS }@+׸$1"M@ /A[`š` d2cwC@]u#x(]jNX(P:hP>09q9CBd%374 Ĕ, m?^XEpg,F-ӒJL+_4hz] mM7dV8w ܮNmMmE+',e;lLb4MV滜v0JL#-{!*Ndh;ɴ(-Pm-[TgMI˟;U$:/ӓgcZ҃8!ܦ7@m>fT;rBeo\rdrc4Q6+G8/Lq*.p2WvRbee`2CB%۬ė)c/d9˜ eufy1 ޑn)ofJU+5:7S! ?-Z+X]QTZQKA 0_({|~2W#h2d+n˟;II恤)@R[ MRNdNymIэZ!4DuRo4Fdnp-;2|̌ۑ\y4Ybdt֏#8Eh$N;p+7PRxoE̐ k8IT(m onmLC sD/S[$ 41%3MF'@LG@N%3N,1sstwގ\i;[p,|OXZHŒB3`2|vntmFZf]i4+% Qg5p~Hޜ@)_7YIuVr 6\4?A Y8BV]wn\V^n< Yף OxGH]@ QQ߅aԷpsev4AGZmعH6#uCv DNیrF)(4Nh2$ ծQgxӸc&И㶝#B̸ YFtܨ|xtQG >tSmBbee4KJL›L0Ty:[sD2&Ǔd?꥛ޙWɩg5v v`{@S;[+^/w;"W@ȥQ*iCN]2nǏ_ЃP: .$Q6l% !Ht (Dhc(2t81_:Aa=4P8q9\'E{f48 7DF(@Un0P2ct:!yˌʈʏ#C@,[g ]s,}K #`9\+ {+?Q]b[A좄yHu~Q^%mGhY.ۿ٣] `IIw!Jʩ΃T@0깹f'8\Ǘz_A6-JIBFw=GeO ;‘ \P11ZnugNt0X#PN|y1!bי>>hPshd7%^Q_E*ĕӝYcdǦRnhkd @;r`Ͷ[/yEȡҡP-!pqA/wpj:딷s6B#3FSfF+#z4 M)@}"OAbU~]L]:]sȷn^Z3% Z 9wK+ ܑH`Ю9b)3NlWvjm٣ʬB\Y?ڼU2))E|KYFyrjv#Mt\sآcVcn8MR`(56G^Sf+ E|~i<+-p 4lgipsT:g !Nk= LS`[S6Ř`_^ޞLzjif7{f51ʑC%[fhHPزwM&, a\!Cf3Ґ_&%n&/nfdg7gтytZ QAL.JնTB]3r"@ D;&y{y}NN34do|I7^O8ʗG5q \;(iѨy;ҷT(]"D"@!n/?ߔ{I4JH6-{w/TR%x_ȔzEkլ%=|\veR|94UtD"@=nV3KZɕjUSQ,jd2B"m+$e'7ߐJ?̗]3aknv:t :->#5wKfQwc@tLeTSx,njQK{FA@!z{4yɯd5`[~v ՞mO^Y*Ǐ~})DŽiEtFP'`,[l%KK;O"@ D xdRjU@ -t#OPn*;vlfZpdBtD?` vrr:tHbbb5ȽN0#UJU,4`I C%D/G&?"2x`pq&0A?/kTKdd /іR^nۓxR>-(dddhC?Hǎe׮]RV-0R4< uyvJBBB%) P^mhQOV %4=p?plI06"@|ㆃ2kI^{M 79.PrIc III(e#4lǣI\߁^"PЖ᭒IIIcŋ壏>-Z۹„gpG5KqH.R>>! n.h͓Sɓw,sC @; I$D"PVXbꫯo, ZZ<$A^L> DPCj7l ۷ƍˤIt xSj%^6C]6ʙ$D"Xd~'53f̐DGml@0"@4"@A,Ƣg-ӧOg}V=-T .k%"@P_|!#G4/ÇWsK4L(!D"@[۷iFF-+VIK"@ !`؇'==R~aJ~z!@ze"@,̒_|Qhߗ.]hB:"t@!2 D"@keܸq_ٱMC=$kV 74tD ^VJ$D"PXo-tR`TD p `J D gϖ[nEL" YB2eF@! .H|"@(< p? }NG2$ew"@ >Fv]wqcdDD_c\p"@T"@m۶ȑ#5]>TVM))&4 .M7 D Gf#x͑#G?KӦM w &OFk DNOO_~YfΜ)3f̐;* aB]6ks$ykD"@@f{ر2zh՜DNsC>t~3D"@|^vt]:u$w}qfIw("@ DPc{n5g}VԩÅ%X* bj D*vo|ׂg R-Ftn0eD"@ Kyw!NW7qȍIwnD"@8)x΀dذa2zhIM" BZ@ D}Ν;e„ F$I7 D"P0 p䥗^ | gq ހ@Dn"@ D L  NNN)Sƍo-Zh|2IwCp{jhkyy|f#37ߔ]-BD _H7@)"pg5N͚6ӷUTIobN5 Am;K ۤKȃ0[L2(UŒDoVicD"pN-{΃YPq"@ mK֬Y#z4iD,X 4wCD o^%%::ݻw̙3e^Ǐ 6+{E>쾇i g~u{x$Dl"Z={+ށoM]6scH} (#E:믿^vkGI7:ºu=R~'<D:p8/̛7O{=9묳B=("K$FB@==FDDxYz`ejSNrʹq}ҥ5ǛN?t[ja~5j$111Yn4mTу>i-ZNJcf,dʕ}饗ʄ A|"R04 dfBk-8{1dwQ?#rqjӧ_XճgOٹsԩSeٲeҡC9t>vXC" |UTqfHs!ƟDxt7"~CXĚk 6; ۷o,^X&$X\i 5l6loffj决 !w=O?ɜ9syokr 0sDYqUB2"@ @J i8{qcKtܹ\|K/ɑ#Gx?zgZnݺ)Ɇkڵ_ :YK52~"X͝;WۈiӦE]e;L"ܧgDl}RF 4i(p 7HFfL{bNZ7lKkׯ/K޽\C "q)/B $xF=v"6ry-ܢe*Tlqn3g5ݥ=#&'" 3q||j ƞ_}]N%˗/^/+V.]hxoJ _b|_N_2P#%n oV!2gJ/d)SHN$ի'},Įpus9GO{A}+ "ɯZz%ÇݻyJtt;S~=4lN++ "!%0E{g4x N% y!eD;lScBBԭ[WbccGM6͛Uz'r]w7lwء;ԩSGtƍe۶mz+V]F/U{eر:vWkG -8&;8Yw{?mVfYVG^/B 3..N,Y" T ҹjFO?T;[vo{jXǎSx˖-./1P"AfY"D",_\7ڽA###/  .@:,+q $o[ZM6ܠAѣ̜9SKMMCIrr$ME_~$&&*olN⍕Іּ<4 kցD89t%D"si40#qU]=<ӽyzK3ݺu:v(+Vs~"P0$c' D]K%㹏V $ٲe=zT5Xiܾ}_p7n(6lPSh?~ֶmdΝ^{M#l8ԮS[/ʃx#-pTF|yI D `It||JxL9fqmOg5{k +~UV=#-P" M8 0G%k=O?i4itDȌ3T+kVJ5{Qͼyc. 'GûD"@L#-Z$wq.zdےkHjդEja=҇yD g D YN_uK~u{7$Wl\g+ݳ͟uƋߞ=~s"@F;o\x"@@!P3x];lwd\G.rQo$؞`] "@@i!eV p?@IK+} ^%D"pTc$|GoѭZjiͭ 0AD #@] Y'DAS6ӦM tD.$݁[6L D!}J Ҿғ/"tD10D"@GM6(G}T_cwH"@  `I5^Wý=|2 IwB D76k2c ܹc<'D  Bb D "`ݍdW dԨQrwu7!/0D  Rc D o\jS."ۥ\r弚=A`@!@Bɬ"@@h `_3{5k:v,3D  "f D ;--MfΜ)gϖ~[ڵkL`ZȅIw.@"@@i!` 7ن )Sd$FK@ .nE Do,_\Jtt4  $%3c!#gsCfDvqݻeҤI'JH|"-$"HD$\7e1LG@h f#fqyɂ 3 F "P .k%'.?g}&E¯dSMBk%p?ryiu9Q-"tz 3Ah;vL~a۷矚/2io޼Y?|TIINqS[~2x(}p rW-7"DȈ $~!7ҡP_v[RWNOOWH_[>y^(a֭[/رwj.`tDtbF@|@۶mgɓ'7|S׭['w}w}ҺuBD;ʸqdΝSN9bΐ@!@]j3b"Px톽'_/.VWhᒚ*O?4hn=ABa}?5kҾ}Ic%DtbF@|4:z׎^~n% /ȴiNN|~: Cz.\(~r-r%h.6D@ Oc=zȭުUVMkNn߾}2}t=Kh~"PX_W^y?~TXQg,!/1B"@I_eDXS7߬?Sr!=&(nxk&g=ߦ"P{׮]w,TBBdq0("tg1D@4i"e̙*"+8 s=W"PX݄|Ms/ HJR"PlT۝-af؀~'ҿ]TᦦW_}صjժzN-waG"6ywp-!.&D ;Ki# }ѣ9s6܆ dԨQrgרVEJ"KʀdrM7Idd$KD)(y"&W2F"@|4g 4T[ЇzH;˗ڵk{sf('a"PxmټEqرCY4lؐYx$z"2 ЋTW^3H-dΝ;k $^]ƽa;SJ iii_+Vȧ~*gqe'JHrmmsL!zMӦMeGs5k66îpې)LY"6L ؽ>}hW"RkətNJJ?/?R3 3A=&oMw9:>D,"/N{Q2<]@]0[)Z7>Ćj{\"ϗ#Gʐ!C4$OBEJ@!fpjk,sY A5R!dde+A5& edfHkP3ÿHzVDã:/}ݛw| 0d¸ n"6E3:cMtwիW˺iİ(@Y"BP̬L3{_^m UWî0" ’-^3M Ų܁ s4jz O @T'G?w˚o %vjkcbbH f%Cox:$AVVˍF|򣓥r GGqJ*R>3Flh祗^W^yEy֭;' QH`mm6My~mf7-bS%D 0Ȓ,T}΍Ȏ;n?_4nBmw>f[Eɸqdرҷo_}ڶxe"@B^į Ms;f%</Lbcnw+WtZMWM?ҍ>zVZɿ/X")a C; &jS9#D ]C">QƵi9]h={oo$$$90iH6pA|& BM\80eß4;hF駟 {'vPr L*}@IDAT#$~A"@жz%{jHsp >碋. g-$ݾœ"@B50;$5X%##C)Hu?+BF-Z1Ā"^#@5 "`Ht\uY2#6xX]vrW\vܡ/!(2$E e ,2)c*HKk&#Eg 7g̘|Ҥ"b` >9>K <*,J^W>ԙfbb 7o&O,O>G D /HB׈ DDB/̩P4@f>5SŲv 'G!wq$e !DvJ5*IdKee?82j(#.s&EChxi"@@E&&/qb¯h5 8&%%ɴiӔdÎQF$ܡ^$>1(+=<50 a: 3M_R~|Y%f=pfZMjߔlzp 7G@_z%>}̚5Kzg H2_a4b;oC:s4L8:az yc6kv u":?w\}} 8#Iwh tZN#+tFc}s]#(ȧuWaCÍ4#,c.8Bm:*6&X#t9lG_`%ʢ|(m[5h(n[8ptAnF)W  eȲѲO%%廙oO^!UjWQi37a(!5.\UWwWȵ3F_6cCqڡF{8׸MA>$ɇF+>\&?*3P˹ҍ4V=7_s6=ad߸kͿl/R.}RPM2L9\a Oy<P;$jzE22%"<"h_Ž݆_]~9)J|2@*B7 2k,YJ_̂2j J=!aJU.%/.6?\\t]#̶Yx4uk/+g"xXI!QjyiݿCk5apHEq/-9MX,5kHᝤӰNnh;Yx[^Iڗ!mCG<7y?yXC"|̷yx~,]Ft޷_ uWΔ.״d;ee+Ǚsibxpm|p!"\.|$O,rZ6_l+FgҢq/~*UIכmo7jZbLr19hRCbbc4ADwSfkܻ^i|ZcÐTf7v`[MMݢTÔiIKOwJ )WTOܷuߪ j ۥjR>L8dKlX9hPC?.{6pW,ת$nThXCdۺmRA5P{yL2akXUj 鹒yk pt,*0|wgiڤֵO}5H裏dT3CҳgO ~DiHCA<ŐڔTyoȘ(OȦћeJn+s#ZԒ7k nH;iֹOJo^F;|ﻤFҪo+R-g<#(1_ޮYhըs#5Cɢɯ~ U*Ȏ_wȷGJ3}.g-ג}lpu,$]dRU*T [ח6-_{w''Ag~*]Gu;K[(L=ꘕ(4 p)R7(~ؤ$ok;4:ˊVȟKCIó.]pb `5J)2i$Ihi"|J-:%o.mQr8EO}Du@j)/4eѯTPA)Lp O7͋n[ȪVɎ?v%_"Ue뚭@dҲcK9v̾{tlRS{Il(l߮q2rJSd-2x`yI&뵲a+ Z72d;M/_8my4H*׮,]$c珕XzqI"‰:Z:ƕ+WJ%: lͽP*+Ve]&_|\jjy ؊ƄC%!VȎee%Sjh4m g\z"h)u~4yxy%NGcjthAtJ9c{tejb/z ,zeu./_R\&s䖊IseT^~M)k4陲k.- ؞cteۯ۴RFT"DPc@)Q귩/g^iԾ|1 9kYRi}6{/&(aٻaD~M'. Q&Ӡβ~PwEt bX, j){e尿fL>5m9Q#.7 pĸ ݎ49kMgL0`wޘYM|،a%xb<'UJ4I2m/k+排J5+)^jD76nelep[hN6vcv:L&l%_;^ k&Zo3ҤKHwvYdl0?,<)J*EA[_Z(ԯAA3 lEeӆ!LgS%+mֱ4L\zDpyyewqWV]>!|4h@~'51Iٗ"'jpːiCdʭjjsh?cߥ퐳1ڶ~4;Um$b~5Qep492dݔ3@:rLg>o֣?q\E5_A3mT_MpBiyjKm+,q z{Μ9SO-}`K@#@/ ^ %}D.{22,{cM:*QT3-@ʰ0h9=vƨg<*>~/eE.Ho"  5({4ѨaLh0hb(bAA E@o~y]X={mS3s?97YpHp *'򔌐 z 0OãBܫ/Ζ R[ߩ'LSC3`3ܢbLw[|";.G麘11y1ՐĐʘ5[rzHҠiw pL { G [vک56穚MZZ˄^z]8}𦠄]~Tp^u֕92q$/Hׯ\*'UjJ3Jt޸ :qgN1I3uݷ̸||z.>:ABEOpJvoGN="k y~IŞLM|X1jgRB{ȋx.owqrgg\#\wC 60\0(iu=iH'пϑn{u}&VSFtln" ^z顤ՔGzD~n+V'g8+=KIw[fBDX69:f=2Kf=_=Z+Q&~cT=cG29 RBaWA9zwuNeYk_V[ߚ!٨ŭQ_n^:k׬U?k7U2ܸ}ci٭eIWʲ$YAŏ.c.#9r3ԤrTIیh#ӯ.3%nZqA*MG\b“PngI'Z轢D:BT97mEnv Bqna.~!a[^ef7q[ ?I8#FݻE]$k;=N$n i7 jEK('DՑş//W|y&d+J.r nwa92Y|gr]HV $oC!SzJIg~&{O[FLd5N/̢ qç,;P%>e_Txi_/w:͵È7}n;6!MrsS* I9I "]"Y2QgY'xs"O$ HW|B' H^!l{AeQ$jtLx4jH|_zr3i䊖WHSɻw+VLH{˺-RyS5K?T{g \셟9rۘFkF+.HOiݹ^ZUhGsY%#~?BuuQˏs~-e!xҨe#}xo| e]SjTO~|D}7z73Q,Ճ_ -j$'?xs=z^x7'phm43sr]Vn!;P֩3 C`wHr4/@^x9eSqvW&]!.~ڹ:Z(%TB &ojҬS3[FHZ,owAzh"<Ԫ]KT?vI3թNME%NU;r ld^UT -fi؋_wIKu^ !QEڤUjtu4~\uq"O0@QF!zo@$PCY3$ aǮ53v`Ӌ~ݤ= @X[iӣ/ɎCtJn5 -&*| ~g6xG+#<"CGݼcs8v1s&uZY`ʬ|BmwIj~ݠoP:nڍjuRA(,E_&wR,~Tr:kO96̳gin}nZ66qMk.'/s;G.h}Lux)'3>asQg^҅\B7ϗ=p[_0[Ñ89q/a<|2Ҿo{YJjs>:x\|"@{}ի_O?Tzwd駟#ykV?=lkC (Ɉb:#=@rNBUL\XM 5o..p$A-'Z8|Z8 Q$>IW ^wz8qsα.zq{n=|m[vA.mUq '2Ͳι.(' xs=v˯[T wAQH3ɱ t"ĞWzIIWԯN<0Xùf}ɠP᫩>%SxB:_yWマ3} pr)J#jخJRM6X#fi˜^']P.}T]!~$=ۑa#Ývg_Fy?ߕ' c*^xYk7K#2? R,]Z#]|ME~z/dNX5;>o+Wz뮓=#6c,O@\#`;w ".:HG9$qc"[U05 PiTbNC"$y7XvpG$Ahۣd%UѡC.]-@"hyeW^&Gwt!fUL<ar0D0 h"U3Z\q %1Q1WPGԕToVv-XӎrrUD#0½k8ưo&OLY4딸ȷ/z!F~|x [dܸq;Oc$ CH t'PerQ`U N&,udUQ_ոYz#YL٤cSM_S;$6y͙3G?x=zs1XbGHwű!` H3f>#w\vkuҥrki֬ڝX˷0#݉QV C0 *sdՑ>crX!6u}Ν;$;Y]mZ†!0N!PZ$߬f7ܚj0zژd$_eȐ![hG!`T2F+`0 m,4LG{=ٸi}G3gZɤIN0=2 >b0 CMv֩+.ߛ.s~pL,X C}ʕW^)oS C9Fw0 C('lӥ/rVUV_}ҪU+9C0 {TҖ!` _Bc[//eƍ*ijӁ^@O˴idZ jkי0#Ud@b#TL i2~x[dyZ&j/,gq\r%2qD5oyS݀t@b#`,E؟A$kӲo$EEue/Z(.:U✵dC:H>lUVJmD&l~-C @Hw7ZiRh0s,!P Hu{eywd~$#=r.;kVZr7J-GJJJ)vjlD{=-GeRTHI>,aC@ʜ,yr_'999%WYiX!KMNy9$-=MZI!>RӤm۶eBIʘl_We!`!PnMψ} na "`B7i& 7@Ĥ1^q=C Q^T:*C͛zB9@>moɬ mK ix(k;I)I)_x h$ ׯck?Ղlm ~0wiҮ*'Q˥!`A@-HZJ\r۳=q(sjQî,[l@*)*|(r@]yY̆!`XpJMN 6=/?J,=Rn(i)i=8KeAcojyVA#NЉ{6UuYf C`@/̗w#oxudWG -EPc((.pP2!ZŋwiZP#ݕ oEn%Ud=ҷ+ H&וJav!]Қγ\+)w ^,{ŗP?}qlXA}[f7pSY"|Y299Y|>GP<%-=M Z <2Lf6[JztI*rx?ɝ0z^R7 ]CH7u&nV)#H-=ΗS|&8h)et8▚&'rAd ~]uQ!63rcn.pO7,%vc*%HRR$=z6)R(H͓|VS&L)z[^2VZtkm"EjUmjVGHwWqԬPU x%MlWOQţiˠAUV F+UV̙3Ƌn'-v44D,a^a{)cC+FJ rc&Ȏtd+pO>e=tG4h~ ǀGYR _(3^!]tiTGpI8vڃc?!xtxm ܪ^z?9OxB2UO紗f͚G˷|+YIZ:bKI IJb55-O*$nG reh39Czr;%, 4^={KAA\r%27ݠvex8m0#qSU` (gy 0@#CҖ*?I;>W*t%[H_ ]lxA\iWp[ *NK||l݇H课^㥌#R ߿6@QOC&tW𕖬{p \C} s<9hGNpc+s4ο^p? кrIPUuwŴg8ׯWAMvUy1 j@KJOR9$c;D⭯&(`W@5+$^H_}SVpg+L' ծ~­o,#&]v0%I~=sb7=v~'25l D smM#}4VmEѺZrs&J{=BzuP1KW=-"(7ĩ˝D {2x08m) TFJ2ۥJE T<&=GL*5y!6=9]Aឳ]my C|zIpJ0_+LlONqʏꍁą˱'ZVwU/Q΍lG{p^G9]0*tW1386l}E#c~Ã5xe; ࢆ~5lc15|<N0IN!2!PnN]T9FjX}\s7N㺒Y7#"+ {;OB4}eb_VJPI'u^^+%\"m۶%}r_)9s qgەr;54qRK:l/{/Ww_>ձGJvZ9eڴis)uƍr˦M$-Lz֛~Y1#!ԴP=qVZj*s=5dx(+ic}<&ć>RmH7|<3zouc>~cqIv/*sQ@݋B48"pBẏ娛ᓆKud?|H}\_^i"WBiؼ]~Jod98_ R_eӚM$[ԗ ٴy,L|D~B22C/&-8"),.TVi$#=CU]_qgIg?PN:$ʒ{L~J5_䧟~RݡC%ع$_ŋK E*%_z|ZoV5j$[lQ4_~$mڴѶĚnZIv$^l-uԑuɒ%K4_zL5kڟ˗U3R?% J&oI@%!`h\"瀴E? K w~Q&+Es7$7xS@=esd A J]Is^˷ȰӇɸ K_%l.tNG ?̕3I<|`9oR_֮\+)sD q{Kvl)pdJsH!HC ]\7쫑d|&'{oϚ5KƌV<:nr SR;{l+s.2r9/3қ2e#^.G$`ĉҰaC9SdO>]+kmݦ26{U,teيB `%^ K"v!U;NL ϋ@5]vR"FܰeC}poION\fI~>{W4T&?>Y%O^˪ūnrg&S=%sϑ[obLctEuƟyYB9r9SROg>8SԇNUkT=idقe2Z&`j{^zWwߤ@_~*S: 1cJH?&Gu$ΐe$ʼ6l^~޼yҷo_F 0ixI&4}رo+y's9GUZ.]y{gG)~/X1?)]?3TB ݃ *Rx+3E~ C 0Iw,J)r/z w >lyMrRQuDAQaoɗM42 D봫xz,x ?lXI G+9OIO^zcʲKig>wtۯf`ke!%d6ʔ}*id4qCJfYt4n,GtlٰE6IF hmdsH*~%2/u;ӥN*}@QAXwNBAGtǎe=Yu&{JB:UW]$H ~ ߐ=Z!}$=zƍ7|>| yO>JQCy5 Hz-7NL}]0+Fr':~*)VG1,MC!`]x2 Ā˞klU@uJ3r+0#]u`dd6"Tc׎ӀR/|Z0j]zHsW:i9?H}qvo\mܲtYJڌнA%eߓu*"gO}& Z5-^Nr6?%#Euˢ2i>~Okd#JLfpnR Xϱ=7/x]y?2ђ38G3%wcZ~VpUp@: AeYj믿H7FMcͪ G=rHy ]wI !y߇z-,ݽ{wUO> Kٹ`yk8 JCW]<3µc@b `;1qKm};x?Д'"YF[@T=)%Uy$/k:'8q\Xne ĝ.Ydeŷ+]vR}{ߨmn n Bk7#ta C;*F꛻1O5˒Qvu^rܿYmS9p[ &Fu#?B%٘1Qsj%.'XB^5/ VZ_Ct7SfL>Z3H9\27d{h9xkuV*R&T%;Ke#uO"Q&/lHb N8@BUN;M~i3BW_UՐrw _vm GܤO%/HY٩S'= ~?OO#M_?s!!`#)@N#?A~a'z]+WcW̞L2h||<Gks{?~ﯱ 9Χy_>7dETyKDk׮b8E=H~GEm޼M8keJ 7e!^4o|YAҨu#IJI/y\NDW(Xʂ& };7}#odxhn)K.~Ohsu2vj<_m?g_<Ǐ,>Y$\pLgZsiԲk^O>7ש*m:а@IDAT (*Sq6wՓG 'f?!3n!n >k7ؙE~~O\dݭ_5kaMbcrEi['7s̑Ν;+9G_3* dE>S+ s&(3}(`k.o '%99M3Y?ҢVG|cT5~h>@!`ʠ̈́ȒcA7ߔ?rJVd8Wt #"AbD}| h^}Fq1Prk' ]_d޹G|5{“5 ֭; e!NW8_6/7t^B/&)\'>_:'y |Xoυp<ۏd /G[wo-rvm2Y2!*~˞vtSdU?!O(>j%퇴>N1\Zvk)M$lXA]9N^EsH24Ln{~eMTR=V: !`TF+xy:R?^%,6b5/nmػ_H2z,bE 1Ųe^QIaQ{ '{+Wc ,b`|?`H8gƮ/HDO?]?,,.|W*#^ˆ0 z ]S҅&YHƂ1O^(:HɱS7 H(4Ri`lIίkJh"Q'=UM K߄~j$IKJ=)",DBNsG&O_$mVf)X(eR~-]8O G\w`",zAҬc3Rj|Ry';5WՏW̚ ԛzq.g@G2<{ų[zK'# ﴾3 Z =}v1` W6X.vJ7mVڮo%+$RjH3@?c C/|cw_&#z'C ;zCQm0?H9K|3[H~tv͞f@Hw@K n}>e*kp񃪿z y% >$ב#:Iwy>1w\[tڵ,a`S:>yM^y%02 O?\LDkeeak W\GGr9FR`bpB[eRB\fAޘ{Hw%H! &>>w͇Cq̠ .&;,D{GTt^j33̙3OFjZꬉŏ *ǐ;L>8NYhY1}]liש8\ P%5kLOl87i,MG6U"NJ]ٱW'Ы <~<[$w )!kZظO|\G#=%]::rjI޸r uvɼWK7$? '~1KW垶k0#݉S^nS22O0KG /R$t0 b FR \/=X?"ʒ.K4'ee婂3ؿQ~쬢׋Ǎs->=ǟ C%gd Gչp0W]F,%C*F gDeAnt!=T HTZl4kY5E@TWvH$ 8m$ !%#>T/DM ]vbfEBzgkyH|G0 WC\l#OlnH:vR9.I5l1!='H \vvmV!ӟ$#~U<auֲZ0 @BIU\MGpMM\l#Pq-Ӣl9D>mdFWOI~nΗ'~yk}^ܑe /Ix#>=zMSExC[g+510#Us|' tW>݅^(ujבc{& /K/j"HیY6#j2^H-d::zH1aQqWѨk@V!GqF^vG̀Lސ&[o% ',*do(`N{'wNGY@2WȄm(nUd 6ahu)3Fe4T%*tJ >j'H8#x+6KѥC= ]!Y右 }j]ĕ#0+!`1J`0@rE*!$#mt6o[?}{l!H$`!Y,P@-kяjpLڤ)G^`Bt5}B t#DG,2*$999*5\: 9yj2䝼qBܐc$E]G\H+q68fX5҃z qa QAR,$7/1b+LJ>hu4oC3WyQ}nR|^WO;z`Lfg%qj>ͺKk nU1|fWJ0W;1~jiNSټz^0 طي`C 2y6LmI6I>X%Hl9J?5$ H!8F)f'M%!.8X`:H!X8uw`:qDo>U?HY"M9 J;^?:`GYIH T ΂fɓd);xޟ,(j0#*$`vE]Trv @$q,|o鲕· U/;mgR4 (#`;ʀKt*r,r`<.](!jM_ 4iR!H( ,D}5z#4y'N!HYaFZ9DW^Q44 ,c-$;}aQ%q@[HO>dx!xYtI^ ȑD~ud#k옱:a`r@Z|,YI7+ D2D) ի;OH=kٸp y$p,pt>'6\JVϥs|ͣ=mZI>sye}lw}hiѭlu:# z^νt{OIcR3,f$:dg> pr65{0~8&.|8x"&Nǫ6g ].gS<Ӄg{\,028AF!$3ǍGj!GH${)5Ip~q_mH=iBq5'l{c'c0'Z8׈2&qP>pL9k_EL4Oȅ[KB@'.r7~#3n!_ZUHd}fSp_Ao&|8PS!鲖#;;[&}LYX[̂kh8''G-Qc}v'k;P颭smcKGXR⹁=|qy{KIdm e#ng3A u1߫'IZՒ%jF 9Z|F$5 F,0$z`?{%DZ'n\_!.'4}|sg0_<Qqc'.iFra`*o `D>ҿ/ǎ} "bU(7cvZ9dcK{K통iSkJ~w\; Ofk$rd邨r 78br7~ZޫH 3+2PInwܮo|7E|;UŽ>o'D{U9I3i(>Q 8U KI&v%> *8&'Ro>wY"yo4LD\+<<ʺr|!XA'hn/Vapiܪ~ BVu1Nh`F,ԣYų"iHQ;#<WfQ';CԜ' 3oF]fk%Pk#7XHY/䜴0bNZzbB"qgy$GpoxQ}V3`?!`T#-x3e?jfR>hö>QGI4j(|?|2>Rǭ@m90zUJ<;;[j'HH4eEeՅT1( ij%B$Y_Z*2:p^=mnL@$tVsunwXdWARU iH)wJ2 .F"͛@keu ^c8_cequج?餓T [,~\ Y&-xyA?uw{Hz{_mN}loI+Ը=C: A fKw_O](^Aud[ w%_~_h*!H!v>uf%H!騲`Eo<;cK&l_߶א FHw|_sN\KQ+Mq:!XwlVR9sn|SgI2;Snv%H!XjRk$ܐmbu!cR|嫮|4 ~nLtA }ȑJ1%ܞ{l#C%|sˇV|wu/8Waݵзd AHw?F<ÃMr+P+Aol SbX]}0*XņDB3)SSp9)bh{'$J?d25Wo6(&|sH6_sE'Ž7Rw:ᨰ[^vI3]딕?s!NzX)ܳ<ܫCjV[]E@owL|}VgBĚH !!t^ r?:88@8':..>V9a s I$/z 5& ij!o':7g ]/@y6s@~㺒p]:AX#addwQ5d{'sGX_9)vdk}!2,׫ӑ0Uٱ C 瞷ثCOTK# f6,ClZDZVJVyǏwY~߮N-mC .T!s Vq\W0?Wc?$‚o,xXd*!;W#य़>W:5 cq]_!q@i7j%Ev>JXkIcn*5gJoD-jC@9TՖK8 O6νE}?s!NzX)xG_UC i'\Yyn&NFw؋!y1{ b_Amiٷh[R3t#uqJ86 npӳC{ &܊s\"^ԛn}jzJZFd5˒:Hہm%S^۩ ;(ϖ[BR|W/-i7J~3d\5Ԩku;YlΨ!iu$vkx{|wa@ `;~9eup2@0PԂT}ɀ" ☪JAů71QfsGÒ`'' I\ص>K=iO U6t!iuB}֑Tge\i-% /o iءdfej㲂#2}N31̱ӇYHջ'9+"~;4 CHwIr=]Vp9UP%)!ZArh/MC"".[\7K8A] n[,TبbY4"X'J곅I&nWYPL>厗y g$!}p(;?Ji+Z|pt'` jd7h9JJ vbwiZQAH [VYbEBڅӁgPH˜Lp@iοS :@IbG*:gC<QlUgIC>:WU.?:O $ݩ|ʓnW?{u1N C 0{u91\̃IR0wM$fyW_n|Hʤ6ss@P%Hw@P-5zJ 9Cփ-> xT2:/TQP4%厀 2YP%Ϣ(. ݔQtI>⼬ƣ7N픾bL_w"J}>Ρ5# Z jDnwqr-U>! t n~Q"q.PP70fj ^J ^(  LD*+?g]L9BJg/3=|[tN{+m"9k_vCɡ~>[ 77賴Ԑ1PWGC y4㴸b0 Np*:W%Թ)el Zuwz[Iv^Dϓ>/;ZW$nIqtCgNP#e))napgnNfډg]9h^/%J+6*-C3uNypQϾi9C tE5z&y#:i}4;V^MԼuXjF88BP8e[D\7/i`Ӷ@81^̄͞Osu[*77Au9}VIj'@/9q+gP'Un'Ļ:EWǾ'>-_0ҝ5 Ī#}I$k!:pR.QFh:$ώg`A;bO|beUϩWW%O=>@EI#ؔ⍍tgo>Hwd=頿*אrsc0 #T[+itr)Ɂ.}`ID L҂UZšzuup_O+ӖniXZEX QRTgM>FRp;0{([TeF:1 #Vc_}pg֍j9)u"n?cDsL=DŽ y ) h.f(gUPg*8z)?fF(@ĩE E<}D:oy7 GHwc^-)f6pl! ⡃ ]E28c6=Ajp+#An=}SȠ:A0.#x4?=3]]"߾4TKzFmHs!#V{dQp C 0[Q ?I#ݽe>!`TF KG zucyWeb+)HnA+o+i'*bqEƝo{'|w2ij*{X_iգbXʬ[' Um͜!`Fcb*%l1Kˌ!@&,_#_/_R6;5(zH !ų_hOm P_ͻޔM׭o#^@/M<i,'V{: 19"D!Pm6-aC8Q4Ěؼw{퇵:RJ6\{+r+Z.7%Mr Ȗ4սs1q{ɂȚkNX%"X?D.+t-<͗o~]G 7L&m>Tb4˟!jL@ {뗯W~2G=n&,]0ݙHhQ(T &L\0ypg8nS-c8p+/猖/͕{\^<@êf+e#`;+Ȳgꍯ {ٷI#owl{T'X61-iiҦsiDXwϱ=UV_nPv0#Ua5,f]I랭o fĹA 4Y)ND5 [+i22sLY"%ʂ@0E0-*C0Bk!9RqU)QRv  2@z vu?IR/Ke1{Lj`n]sA/=󯒚*6I^QfVSJoɗ'՛VoT&w)R7IyNJ}\7 !`;zXZL!PSPr끷jiܦ1"X8rD#듛mN).t;I˝k?JQo#۾F!d:==]Q_N&ävڒHHJ0an,wl$~_^[/Akw!,"FEe5P-'OO?eL>eJ KHAҖ7g>C^ƛ__Rb˱ZDC̄.=%M=H/a&M7.yu|W\b_p#! mC0  1)(*u~'>M&EEm{Uy9~ҬC3)(.0½kd]/MZ7IWOǞyLydg3Ǐ?;qՠ^ݹvm0*#ݕj5%@RRæ&\}[C̷)-Dxǔ;΀H{Z C[uh/ul/9嫯Ҷ^w_|{ֹ2n@kqZ~moAHwtpX C"PIr :j"0zh{7|Sf7Cκ,iԺ;}rv#3M֖>Xx'%//O'r;kwOjO_dQkw5MYѫ#U%bDM73?Cz!y7#1^dr1HȎDʵJ0z@k r٥'~}'rUW GK~mƜ!`T.F+_0j^yit'pqZr9j%_~eYRA]%Q#L> e sx喛nݦMGCܣNf?@%#` C (?aq>Wq SOO[Zlť2 1@tcJS.̓!`T&VfUbCyr¿NiNGW.wnn<ࣚ&%ii Q䳒7I}´;WNq#GH~ä{ժU.2@.URGh `{;eC>ې~MXEyR=Ez\ǫeҲeKt欙r-#;Bs%>KM8|nKWT}#] K&ii {}Y馛N:ŗ^w>{G&?:Yl(yy70a\ hwmwztW=斢!P'UT^}s]ġ{ʄIV,9nL;<,q-iN諤0?dXRJҩ^*"ۛ4 P3iک8Dg=2f9|Y'%$Gu/9@JYp smM FXDs `;qJbns h cB[ФHmdryˁ(?-]*O=mH|o%>N kBnUV"۝"ySI3Lv*]_8eЀAoz7IY?S7k=TV+X;(e"h-(#`;ʀZt!a >+ͱ#Jtܾ]w+%5EܨO@Bv2KE^(t?tٿHrjgpCUmgWM4qGl&7ih3'n+*(mY%O}JN22Qv`ېęi[ lmw‚BINqLsmfq(F˃1 r#P(tCt;ő̂B_d0#ݠʅn?^T ;0Y:YxMfYyUVdk6$@i Hoҋ %" AQ Q(MEHJ"$HH ٚgrwޙ=o2{[{W-@"|{'Wy'7ΩgVZ>lwOZYlU%bNFM%6ko/oBaT;wp9 u7<:qUÀH06\l#zLӳ^~rțӟn9jv=+l5m+d1ZEƺ7I1>VNtwcõ9-??/w~GA'.?~9{nxJ,C7JvOBxgoE!hG_~FďKe4ѭb{H ,}e/'/~6ITڤNfuS`[٩ m>cΖKoTv=mWk[J}S a6lX1vN7I.4HS9*6+Dwnt-(7 ~5Dq|Z7?n&ꦡMi+:;v>r9ʓ{R9zh@ 8bsީs\λZk%ƻ\9uʤ)r-/M{ՉA*؄e@!#</}'xRN9?~|^$U^'b@Yg9ϔMDj2MʼX3ŞWpgEy~N'{Ɉ#m}GsB춲;O?-ﰺ6՚{Ѓw媯z3!>,)?#q4{;hQz;-&vВ{@ygwZaÆY(n0`+[zeлكCw5o\K`(;0\90D@ P=2yxI햏 xЍzƾ@)`jxxùfaDxI'B&][kmޜ|m ME#c_;O=-.Lpk_3-+IDAT;SW=z2zdՁ@jW^#`&:@}ܴDq@|w?l9p#o))Wo"YN+%Oo)Ŷ(S||us f@y#Z@x;G@ K|c"t|m/DM3#*IYJl*WmDU\KS4Ru `Hnsxyq&Η-zQ&Bt h&vlN&uII};?k.Lls Wnk)'W!Fn|uk17>*r]6ѝq E =&t|KI=Oӱ[eI k;Gx0&Xv[)ay^^4o:eb[]t܂MzJR]Eoʅ@t h'dtʋ6!&3_n2d l'u?wt>ۧ\pfw][W!ۈGF'D`J1fa{kfqdĉ2zhI BmQɏm?~ϧCT#$}GWf͚%|3F]vYۦzvc ɷs~1[O>y ?.f[-IimVL_8Sy9(W\QF-̝;!y]?nK)qq]q=  eoЋ@ Poɑ$}bzh.K/Tj+93W_GS?1y_cy?c>~'n~r7[o-pmaK~az-o _\ey[X[7EdAx;],vmmF>nriO<Ky8Nc|#qK,{ܩ*_rٵق/8v4sǷ}J{k'/tB Dw#L L&tۓIAMNw .O?-7;47@b!x(9:a,adggy:#_Wr9#<<#V>ca8v;#|mE9? ,ֶv+O4u{~okwN$PgZyb?NE*nGZ첋L1w/+y}G8 l@@3O$onPƅ0o''m"?|(os3gΔ ^grqQ~9^ L3r?;#%<| 0p؄a~en1vX[l1+-w}Wx#L?vm'k kV[o=S9j(keƌVi,QwM7 +`AvqG2ekp_~ټxp_}D[5d~<裲;ˏsFďyuAD?8jQX6N'"㏷p1xꩧU7~_|EɄCm喲K؃+cW8?bSN9Y:`kP4F)W_}뮳B_ Is9c?y[{6M1|((Bo͈:#T!MDO[S3D ' O>y3rߗc9'~6?&_uo&}橧Bيh:ĭuuaٗ?v;-7 db\;:&W#˚k)7mtw؂UW]UFAk><+0rRjf[r-6fZjs- Q XP뭷?SP?P{jkf88_tα`AC=dy9>Mqe_BW;XP;8&g3#_(O2@x33aH 091'hTL[O~󾮾겍 >i#U&vaGkrWgA=}h7 !@Ëǫ:c>n#l=XkEjڽu؎'c3_G?|+&FrvʓN+x y W#!<"`,: "'}Dm-X(2(yJw&Twd/騏a&':2lXHS]7\f3[N :Q=s #Dq>￿iF| ]1ͺYސ{μ' Ȃ'cE}8؄}-㏨ r977/~Q̻O[sa<  s=s=IOzR6@7Q tB =!dJx#F8R<|{<m̽ՄNx ov f@'g8+!a'8GmPu~ĉ-m&wVX|ߍ~zqgD2)/繳BYP(p6Ƶ)iVc|Wx?&(("S]&@ H@ |M*BZa-a9qǻ1QXp>`l`1@$^GqrlqCh$8?558 !4E]p {aB7`Lx ` 6ZE(l@ + Qrf0+pdBe.S̙gʨ:r[/[?Uj!_#ps^ V1-G2hy{ K"FCO.HOLA݈2nmCP/ "hGl!R31u<)Q%l'uP1,PaB8.%QQBNIrFHCS4,!C륮QW S5fHxh1][K|]V䂽/Eˮҍ%_>44(}(Vnjc6yε)5NqxӜ}R[sIs*QXJ01'}rC rqHӨμk?S~oDuC?F%\}>eOĉ@ o|BmOcF 2] l>D4<|XdǺ%a.[[r!un[ǭto;ۅ%-׽_=ΓBcxxn;}.cM;#APk {E)_JJ.mtBy=iclup'_r2>&)ύBўs>w9ڡp~v+K:nF @D `_"t^M{2ot͢La%^:']Q$/y kRᛈ/saZXYc/)]7X1yE#.}Ru/+ {JMy|'sct 6p0ag!c>ϟr/6,Ng¹¾Pwo8@^b᣶%q*| n E ?1`NNZĝO^18WPWzz:\O&^P1c_D|s )¼mH_Xί/<|n)o.)Av<)/Go0]]t=:u?8F osֿKw}N!,.e; ĂOѥ2<װʺ@wBjْSfJL* $p0DlĿ_Ŭ=M/@ @ n0 n0v[R\YW`LL}sEΟ@ ( BDBY *cl Dy@m;9S9҅!z@Y@7$K7>16{i86avvpy8Msq%(Dw%^T.~Hmu%L-@Qd>Ζ {9HqQ#`c|Ģ~#@NpA +8u2tg~E;'gIs-?vE}mƘOxߨt|AluE@ Fr;?)v] V2&|y Bl mc_okQ{ 33"w x/TVXoe3bϧ~dYvs4~x@{qfwMsه ]@/Hݝ&E=ec_4ud±7ո-RΑoo35ܛ)fa[0a'o hp)ͮ2͕R3D:ޕZPT(':])궶6U *sYJ\u52oʻ@|sl4YzHAj}eʓd's:OgW<&yo7^Y7ȐyA+IJμJu][ȑ#9x=566Bn譥~Xw;ל#0]@]ôхrzr7ʃ=(ƍ1þp{On& s= o/?WēϽ4.(~^{֟QV_ r%񮭫W_}U.R|՗|ȋd!fc? @:4&tCDHYveN}Nvc7YmŎlOfL.[v _ǐ̙/u_/W\~lōd:,ƖiM%sADa󤾩^~j|AˑG);OY&0Q;) ACG[1c\K{NFOm/,[Z0B ]]&061'/~T\!IC* rUWg:~! `y4Fj 穙OI69`Ys6pZyXN*J A%YBxx`YoݸZd)6$]M?"`ksD2cc)LچjuT wḧ@?F`EPpD oK-?m}Yq嵗^7fa{Dwljrg h6m"Wd*tkkjN6aSxiZ0<T$/K5naH;B hUɒF*"|Uf?;׊<E){t]2R>˽՘dJ8J])߲KXTA讠 SJ@aBԫk,22]eVcb6<DE jƺak3y)BPtF9jyb‰Q3[t|Q}20]}  d"HoEXA:Ele{ vr /~DE!۶#:&D&(bby4/*T&b؂/ iPQk 7 QnQAn =xw\KN;nwGS˺u) _w"/-Mxk[ `äE Dwm,(;Lb6%PL9d#yMp#~ďt۲", Z1b7՟@xMmZǤ3"k [K,w:xHds]"kIn̉6 SaYt0k&8aUѝ  Ae Ʉ61&ں˝6"؛*;?3@"Lt#lU-v{!6?LPTPi=4%1~_tr image/svg+xml Passenger+Nginx Management Inspection Node.js Node.js High-performanceUnix channel Filesystem Static file acceleration You only deal with onething. Much easier. Web Taken care for you:Process spawningProcess scalingLoad balancingMulti-core supportSupervision...and more! passenger-5.0.30/doc/images/phusion_banner.png000644 000765 000024 00000141456 12233035540 021753 0ustar00honglistaff000000 000000 PNG  IHDRlsRGBbKGD pHYs  tIME 6]'D IDATxw|ƿgf{@^B#]jCErzxb{Az+-HHe̼lBH,>s̙əx]p-?{Q;.\" )ԉȄhӁ^˄ZA#<$`h b#\ ~~NB )B_,Ӆ󳕩 |.7);w"OִՕ.Lw{jAq̔cR4m߁ed妮vٴ7[}NU܈8-.)WqɅXhq(5p 0 7bڊf )B )7IhZ+SE0XG}.7Gc dQ(GH$חc1FE++ooVp$eо8vowfX"5uoQ~puqMn4v# Jx)0x`?B )%n!9.T}~oK) W YIQ x}qʐ#&if ̚5Uiy:0V%B0nD^}ʢw^z!3O&]{"@:bC'RH!R3 3=履8÷A3I:t2$U BUtE/gn~1"[Ik69afp^ 'AGU< A~ ~@`"h꬀](^n3##mzTjjcnq}Tfq=QY|p/x 5]kq4""XW60*!RH!j! 3 ^t.#X T;sBM¬؁\n7QV$I$"<﹏Ғz./!_2 -÷}zW}~bW>K1/e<@焱UMh|M oFѴ8{t4N!:*p CX&݆9<SX&Ōl2Eb_u=&őW^FnF&'_yrWC>RWE#2cUQ4$/8mBGH!RH!Um[]u:ti- NrֽzױɓgXyaMן-70}|L a9tG;σ܁$%eej#:.6>kNE4m媧p@1۬a;yO\`DA˅UVEU x͏oٻZqs9ݿ$QKM8F> 2 }X )~!T $ 5,v|EDIJ͚jF4+,$;{Dڷ[%q7p`af3~q^"Vfe Xr323?Nepm{,vgBtB:I"dTbf =*Kr?X?z%y҄ϞeoWY&1YT |#@uL& OezwnWi˰Ӥ }­H/(D MQ_`򽬂BIxwo|%IX~K n_i՘6iNOobHJ9od'5 h*X$]ۥѢzAH鿍D)Ba=j(6lpkN^1MizYQT?IWx:vXeϙ=0Qx*C~K 32M?N;ǟx=n5a_GAx%gh뮥︱[4'%*HLF4VGW]AM&.75+>H=\}Ehr8#<0m3{~6"La/hZǶ! \60c6ߏV{ؑE8<*4ЪY3Z8}4dO^gݺ|!r`~}Gl x8 2`0glgggVz_ќzr2\3-Epɕ6>kuM}8MӾuϵbt9~ 9$jY:cXEZUoud="-"=5Wnٻhs/,&%StaF梉^M<,$,Ҹᓕb /E!y(,$kx1^/?<_\K%3 h"dc敗oE/-E))Fe&I~Ӓ{yx+ݶgp6$dNF9u 2򤢌yz3z:$3dG d\tY 7ym DAү/Z)̂=6? SvW8Z#EXhn ৫yp鋟vD^\ oDh3#e58Vlw2LK0i^4#"V_Wѫ!`C%@#i%n!% dI"f8;ueۧ][7j5q,:L拗_og^n@4?[{grOο>e+ܙO-T+)BUUbf4}hnI̸Jzs|6],Oh"y#*"Z/$b WWuӭN[Lq}$Il*+,;=߬7޲O?"rnҬY+gAH:+Z$t Oe׽VR9ԬY)3g2d1DQpYN_5&O$jՔR5 I'2"Ïv:s螤{ʯ~zet;YEۘHC|'+&KӯCYpo[:?cS`]$ K(e^PNgUSkN{FIAc~߹Y]5hDMt^k?RRHGBO;?]%%scbb.\yshGJnn0ۡ#gDl)r޽ҳ' -Q-D ;0'.8˷:|[9'}{e?r=s.})銟t"Y3n)P<^6fOqހi`1iN-<#;?iSZVs AL5nQ Uz3-"XR&֖wB: (|f>yM$QSgX^?F@Zc]@jS#ͨNu)`'?ZOeQ1So׎{p$אz4'ƍ_^bZ"q#!L:q|k4oт)-t5|{h݊Lˎ9s^}ӵkܽo_ETXqi*%N'N(>z, 픔d:n,GֲHߏdW~ʬ;nh~.K̝hZ0u`$e<$%ee̻ϔlru_}}%Lü:@8D!(--%X;'2 ޣڬM\Z:\Co'*ԛ9X$3bnMIу(pK.a«gSP "a|gyx 97m3?IҼ}{<=.la.N@I2j, zz=f,cT5rObǾ>Vwk^̻&֯^~\£"O;ƫ`Oz+_~iu>aw=/T Rw+Щ!QeXlvmK%?-C02_"RUy]z`ܹdrreeg^ύwMݹu|ƻMxd4==)N9k6rxnxQTӉ'h^'lF--mܸ&9yٙ0`к}{[UzoU^~s% 8N5fDc~ ? xro8V}S‹R=.S}XuN+͞={^,7%%۷W+Km#t^}Сý:n`~{+VxZ{!rfFI:(@H0 ٩'(ɱ~1v41d$MN|.&2i6S-Sk>82ln߻gQ>UQDMס#Kr2Sl h߱#Wu] $1xhnٷi>6SR\%,-9qx,ɨF _j*NH|VypSgDDFro4orN}=m;b͚?kzyB!0،FyyݼbZoϢ)IdEIa! Dp2Bf2c`u\4^y6l%Yl32NddfR\Xx\Nb4B:vVRoMCx~ۨcT6۫oOgJv+ˉGh؅CӡėKkhDg$I0e%~?CՊ !h[cؑ??:6$_z{c4~ھn3bTIUծڶG!].d!0 d\99=rM:ln!qހ73 < 0yiiܹlf޽oقK,scmӲ*ISs[ _=o[u2*^9 Gt9{ї[^>#sޯ#F<6[FE CE6Y/ﻉj!ڭ;?':LCr%1jmi4|p5M6sv6שjᯚ:tqǎ5=M:[.ҍ~:f!^I3e>mkּƠܜ>{dj6l(FMBM;`1iM< l{Z瞧9ʮZ6L@%dS?@Պn'[n=Ў$IC]Ě wy[ 9Gg/YwkG6g7B\kBd'9u,W.~<xɓ6nmT 9t$cFV_2DZ~! Ce_ghHz#qmas]lۼԣG(,,Dzj7VL&1`0 JtT²r AӂUOi?q PZzY0[(RT<@FhK%YHjŹ$<.:[KN2vYэ쒺c륰܉@:>'7قfǪ(۸msx~8$:~k?Y7wn/0x>?Ukj'8(*lبEomQNY /O_:XԳͣ9m{r<4뺗_ܷB6Wנ6Q|1~&@xc+K@i,rMk̍bh|gbbCTԺ5:Iu9‡`|eԛOUU{i:W߾FhVV6 )_d F#cX7b.0h۫&MA!C#(v8,*BQk6wy{K2r8."Z&xP$agpv3gqRػ};۷n%q\.',ӾsgFC!ķk_)u*+GU*պF&@<.& nnwɨKf]RJ#(U?y7LvqIU]@SU4MYVf(-[nra28r i'N4,[)S>g+^;L)mڶEnŠu='q+sa wi t\v}qC(7>~jlzTFaX/ wY!_]m-M]"{zo'}#yc1ۭ~hJGwX+Vn~6pGn&*2%PTr= q l5.k轳^ !Q 8Ui4!@1@w穅@0~Atlv٧~e%ԱQa0|DuGrraT!i2wR:DvH4b̜\dI ˜V{R(*~CMiԃ6={s5גiOQF^%dBTlBUYg S߇j?L}hLl,2t80rcTnk,^ n4bN.7n;J2&^QSo:,V>K *F2YWX6OOl9)6 z {ۘl fð-F\yڃw&RG&`6o '~tk8G%c(}RRRJkh`RJMq'\`!/vc=c&]a0߫eIuEsu 6BK܅/))<i'fed؉ӧ3`hlMQU\P`$Nü2_Ģ[ĄK.*MC/e e B`nڜAm)wj V } !0MX-$M%p`0A;JL_kV埽FXwũA-.>xD QQ_Qz<5W$!oב|zIQ+F4pcwa01 ҋ+n篴HD=xGɩ |j%MgUW#ŘؽnCRr~?:FR_+/X(۶g=:&.ڭ{aDXw݇ns{o ;PmmI"ɽ[EFL{|SrrKp8F̀7AUU[u}UJJJʹ=BQ(Jc[[J6'plٺJ/68^g%^r3fhv߼v͍&Ot?(UWK Qrjnkg`)H$V~ >ɰɥ'UHa3tgZ*{}2?E6GLJh5Kc3;eʱcTMPff {sﳑ`]nXm3sZG6llڶ rj5VZuĉƉO9֭TvI< @`cW4휪u_C 9Ufc4O6Y&M[9blV45 EU *zYOx78E\& FÁf ̠*VU]rjZ-Um+». Nl2b \d?[I9|R?Q-GҴ`eIp MU '=̹Jf/r܁\mST‚e7:t Lp8AևUNQUY&/;6mѱ[2%NW0n'DeT$IWQXQ\Y&i\ônݚIgn,"[S=nca(C$&0(>I_ ήO 4DFq^\]f3m֥\t%G7c&X{iX=6cNz#Ã%6&vb{jJ?wUO7S쳓[7?kGltiçf/ဩgf;] 3 ~zh蕷>-+ z]R ضf͚a#>>֦M6*N׿ӧOҎ;*.(4ݻ'x!&GΝ;j@BmcAX, .+}˖-i8g֭͘[D&M)R1rP"7oTY@ Py}5`Z[޽;&K>|6Ѷj=٘΀^z%X5ƾjghB@t0U()*⧢`ʴ錟2\^_?t!:Nhh4a40͘fLf3VH66zi֦.Ja1'QWk۴k NbTLD v):@z1Pdۄ=}&4̐!:dY!*U/~=ضG0c:w(ϧi4Ŭ,O\̐(+sOb ߨ5aZ=~g}ĩәZZRXRJn~f#vˮbu%[ɓۻ7-ەK @T,[vI޳m&dbRZRrF.W_>NaQq*eu(*ȧSd\22'h!/8FMհByYs8@%͎m[|0}s6 0c 5[r2q-[QX sFAZ1iXLf&MƝ=Dqq >poi\s~ џz-Fn <N[Q4^yu9vaw'uRÜ*>m\ľ}JNN!X/t4hjVShBiҤɜѣG?nݺ%uS͛7QӴjwHo6Y-ץ!0,,lɹ%Isȑ6l3ǍU$I׶O! Z j :;wwܹ&9!Nfh|{С7oܸqEcedY^<^;c޽{w|Ntm|Ν{cݻ4mRmGEE}u^6lrC J͚ŻGyWqNg̾;k9 ]vc΂5v%EE|2bggB/WI N*&wHըU×?g x<|>?خR} hʟ'M!*:-?3|[9|/:*M߬p0ql ߧU$ŨFEzx/wNCU@5׃?ngl.dMEyЛkDE;鄄KA(/uTiq>|uJ­UF4 Ɉd"jKӂfⅧa˦<ORdJi޼?ΰq9nxx8wz A$bb rpO""")(q{oʼnQ@ۍ3[+M\ݥ\6eSN&I"qWq"J-GD ٥'}$5b6W4A o߾kM5@.]R4III78q1:&>}1nd:]xhg*54T?Uxҷo? Li w:rѦ}~L $$"< >)3 k!$&ёlݸy/;ufɓqaNgU2h-`2'TNB9~QQt׋ WY)*fJhJ0EC٢+ A $:WC]7Pb2qtca0' <_| NiЊJHڃoYKN]i.@@o3{v$]vv#18k%\ Zvr\('w9q"m.2n@nk[ܑ]PO:0Z}ə#͡B[w'X8v`~GXՌEPDj %qxVQӴt~&`KM4L۟hL Z9#< Tg^u1L i2&K?ÙZo"f4>>OfffIMX4-8F.`S5C@RHÆu_[ O+h3l(NI)))'c[Pb 'x ?8MQZZՖS0aؿ{VO9#;$$Gr/zOQp"j1;C}ihȒLd/V =M  3#WY)zY>cpK5OVwxTe?3ɤ @ @Ф7P,XX˪k^ݵguw.D@BʤL9cBHB}s_\I&y9=߻[qAT-Qb>`ڴYryG4x5 |~?e IDAT^HXAЛbꚆ(IL|&@8bE?3[6aZQ5A1KF <䣑FjI2LDG/0h{P\hE q8=3P(H|\^|%g^фE a|CT&1MQ#‰wAdWMKʫ~GǏ؅zcO l/^4j0 >|EdtL #pQyoGgTxk7hֳE)k  `L蓗W*"t2枫H'N2 `=4-4INqR޽{z}G+}~4ϡC tn閟 ~O>fzH>}* FXV dvļA30͔A4U"  L òm&/))}CbbcƎ㬳g2(s87Q |ПD&Zx8DVogzr'7t>47gΜ_N  :Ao=p\PUY㨭ق옎먊J#4{rۊ'v/\:* _۬VkdޞdMMXHT58+LU-$M-sСC'߿yH.@]݊|Ni]Dۺ[@OÇ xr=>j4&3}e!ָZ,bZWF6Qq8wUUҷo?&MĉSqjxŧ)).gI rODfּ^s9}^vw]5vn' .?۶m2@@$'[n0Gdx1b r<9n'91PgY9MξH>Z԰d( s3f*'I(d L0҅ $Jz(!!bY A0#w>ӧObq+qsT>(JX-؊IB˜ fR[[$I3KY|1GOLc>g1^p8U[׽GИD8pB3mXfH4CT]r]SPPuLOOL!\֊*xj7m٩0UUݶ{׫WIII$i ѺK[QXl6ڄWJKKs\ δL&:b8.@]ݵW7O9uMuKtOFIFc`0C xަ] 0hDG")) Wk{?"Qv$IDFƍsg1|KeK^wZ:Fp'PBalh+,;dY7^&@4y:,uU,^5??=};y'kjmo.1Q?e"P[όs/d,_Ç0y9> rхB u+ ա*` !DHMmE.4ePH@"`Y1I"4nŊb!>&vRq(7Ͻ0`P\z%˖-0>[n ːYL4xO$5uĊ3s޳z^ރ(L:;u]Sheyxkӛ"""C>7m; >nv~eyyy9MY*((ȡ=jHLL|nAC[.0Oʵ-Ʀos:.33s9EYDodZ3AӴvqN`VGrss4gUr^r':Y1ǁcW,_6*.uu=3t[~. fUU{FbQr.fM (,㜳g 4ʕ(..igG^-@_9rhXV6 ;Xf%ψ8?=8w-[624s8#g QG?-Ar H c6yIHHÏ#׿>__zdDDTUl2qW3 2x/[_35lv 0ה wwțoj'>6ZwWvmD' b$6`ch(PQQTc{^YQrF U A$:ItB AD())Cj͏y=_2:L֮[}z*ªU?>^cƕnzy=DAx(qf_p1rc2xذa-jHidLz ;D_2/!$`Wϝ569Vε`Jb pג-›O_?K8|Y8x#GvuYRe9`0L9{qذaznmS`"IyKnni'KNN?>SvU\.Fo_t/ ?NNLHVT&:uuu 79 aHNLE9;)efh6P\ZoV쇥=4Ga4~*}ipٳw'{E6n^8fPuI(3496dJz cp7491JF5w_GFQa 11}rUOIq! ^ qɫ6.Ms:v,JGw Ǟxk!#r"JU )(g<ȳ (!T~%E2~6E$&$uFΛ~1_8M7DRXTCulr%3swl$4ΝpF9 v]~>GVYjcq!$]hip=g V;A}2uY<~D"}GĢ%4%F&'H+.)⅗t""`Mә}E\1j ʄIK VÅ0Sǟ I㦒@ 1:vض} {wq92_/<sJpk1$4U ef!<ԃZ>s4RҨsEbJp҉E8&_.Pс-"Ta51iO@ 7'-IGj6< VPP)33s(0GFSSSOHHx9//1]PDwTvon֩D@wݛ`M wZ׵ݼ-3 V=zbب5Dpc$w'/OK|\[`1p~RS0 l+GXG~XJzo#ٿ?fպޗ,dq??a%@>l> Ҳz Οy!'P>M),g|d!II\|?2^~ :.O\K3矘c>̑훹x15K\pU7'{cG'j jm6[)-/b՚@A0|0p֔Y44LzH1M<#ݷg"dGn Q7v5D M&PSSͬ3/ gdKZحvtEEVC6nfk||n.4***/82-ģO=Ć\cڸrՄ}y$jraP/\U>˘1l+Z8 fgl؏>y/N7u=XQQQҚ#FF Y[K***nNNN^ .NY$IO8rw_yyyN~ʲVL\k#m,&&u>NӺ[g)I)E&шd`0ÇjISFq0mA^}INH%GlHP Ғzw3{+eb$dUF"`RLFfɈ|cٴbcGfzl۱ݵ1/)ces"+2jm歛':8%˞}'2͹2D}mc=!vf:>mx:ߨmKL&.Sɟ~Շ Ojt9ޠiڻ/v^&F6[k ijk>m |uaР Kt)LPպ 55SI#6nЕ{'yZ׵uK^ -5 M (^\55QT\H=\< Nd@&_pO|̪+ B1 yFMZx}^ Ye=]ؔqSЂ2RдS9+~M>3`(8g!7܇[a 8k1ypسv52z$9<(9lݾg]В|>2G YBD߯XrSU$$MGJVߡC!ˏ1k,jy8wڹ6| nTAeQ D+_(5f#0-惦3$}0u 5;m ;(f3(CӳCWm6]Udfdr۵ XaDblزWiM^I^}a<#S㭻+&)ỚPAA9C^y_\PMjv/ie`UgN N٭6O~SP7d=A)Z &x<w3U&R?Z;z=O=mg!***.'\nn9 n"_79SR;: xq23272eu"V_E΁:"<Fgq w?_0n }/><.܌f~q՛07C z#v o 0X.w:YtPxA8 p E =RY}-M AͲFWX~=_ν;9u:O؃P?h)Q1<^B!,u.RzaPD|^fMՐC2fIlBSTTE#)6}vo$Yp=?/ *z[{(ӿEݑQbH2 ?[,+(0uՊP)o눘jU>4,`g$G5K; '96I{8ٞL0 ; wlY>/yZllJ!^!b6B͝]euufP'&&N.P()nW׵uKP[^|l~/c~ifm`؀̘tㆍ%.:OAy~7a߁}hp0 FQ8csRR^BB}0 :)*᭷+8c8p j W.gޝlI~6#v45r%,P49bDFJIģ W\ IDATQS&IFqAIf@m"Am; /;0wEkߨEAl32ﭿkﮦ>_y>yGؓ$0)8ºTf Lþ#9aZlP4,#q$~De-62μqCF:X0Jҗ5Wv\~[y_ꄧFx9CgeNN\;VI@"J:莗o| TwGeӤ$ Menu\qkm18Jſr={ V|Ԝ.|I'*f `W m Szgb3MѣffoXV(s;,ՑkI^p:DGG/\m뮫;`2>$]{O\ch4O0vNeqX.mԡw-  9*]g:caY))iعŌ} `[ =ǀUV:k:e6_[_kGoDz[TT²X,Oey$I \]{uQt:{J2LRSSE Wz{㋏i9ov46DH%ĊM+[~Yf= I0 U`gO `BV[|kE E4tƏ{Gy0h4Z¼,a3G9?-'͠lP+eV;9ygk">|Ot3XE|~/V;7_\`b%TMDQX)eF{p ȀtE >t+>]Eg\YWH48- >ky_(Oa GCoY,LRF.:;67A\\|?$coS)Q1a=Ψ"$0.>|/*wӘȟzu=fPQKu2NJi ip&O  -:'YWL_HtG`HŹfyn'SחX,x/xptZi[}ytBjLIf}xllgsާf@H4ѐ˵O)K'~,BRC-w4k*%%w E5Il{_8 &3>05r6W;fT|G4n *[1u pcz~lmܺzj|tڏ#V[i r=>~30t*着,|ъ:qǁz:{]ٔEkOH&Ϗ쫎$L=U[!k2kǗBh4 c?KiҢ b4$ Ɍgֵ\sTb"MŨA> WeӆL-bW$$`YvHhFYc]rUZ (Ac#w`M 웑RIJ}(J3Μ zIIdD7El;[ Ջ.d8/R M8|Q8{ ZZ ^4lDamv͝dvHB Dw+??iDrA΁֯C-65M+IVA ׷+++祤 pA9f@MMexi544le E湉|4%%%4cǎ xĺvAnSD!)}_hހ O e)mKOspOm<ʢ  C$FŢjD-}B^$rրq=t"+ v,^:W:^7H^YGʹk\^T'EE԰:`[y70L3*T,}~V GJٶؼ^]_.gLCA v 0q]ilJjk9gT98j@f\(;jj躎l")ԇݼk;=75I$G3c186 тsa2_z8*tdo -e,[c2rsXh+b1B-8~صF9p 7$N!u>D63 Ahĝj[^UUdY&E^BB5>ADxl%{t&gEfD`F&d>;1/j[-!F+BǕV^wu]="'oY P씔 L9 uh~P`@رcw4pO{k pyk \SXEs竧mGwDQ|\{0`Џ;vwRRFImDv(=VYYv Lp\@NnnY Q#6 s[ۜ՘h"dA!f`~D;G e(]Uz#]S5{wq͌_՚G`mCPG4@b&-Mbl47zFZVpbnZ$dgb{cCD֭܄/t~K ya9g.g% *@%TIƋ)3ٶo'/}*wnbM$%2aX3;=xg%9`,վ:& "";;/3"Q0F IdDt¡0*** FрAL'oi;g_j%{[j}rc( l,VTd6֑Q$o}Of4eiLJV3>-vj]oW/%g5 &O'-J1t /^M",C!dBXEr Ԅd7۽]׉r%1&$J'-!X̢$1(/[AT$"4Y!Emw떵9޽G~>~-&79 F|zgg:% 4<5[}@AjywqOxu`88>Qd's(Ϛ\4~Ze}XS`= t^).M>cEGK(*NR&LP=C:jP!/`=%@vرۓ8-ɦm?x :kjjC nw}'|XZ|4j} g@kjjbbbL) ft]JEr~c6OWUU}|?`0Lz  6躞(6n`ylhFuM=|xMR ˋ),/("DqU1.h Ɉ0rX&^rp=`/OcABl7Ͼ#*^l2|jVQψ^{=u QL~$P8pfYkU:?X S`uJ{"2Ơ<HOSIq(ޓ=D1{7>Ƚry>y ަWΥ=aj;kʶҷg:OzVE@bs_~ `@ؓ'm=b3yoh~k6TWUA)ѧi ֶ3&yZ:qr>ܶ3&ThՆr|jkk v=^=i^~,>qk9`]v^.kW?)B7 -haW7PQՇ>ɽؓDg<1Dj誆D'UMAQ(g&5L-VUne"MgZ?zzC8" 14/_H RU!@hp XF&_\1m6 u|zIsVrpި31(lՔ)w˶fKvj^|y%w\|g حV>ٸԸ$0doZNG4SGg-,޾6@Um5.o=uzFqѱ'18?bRЂ A8kx޴׾l'=~/ fq<чԄd&¡0gpv+Q٘5f0YkՂ;^[+m {mUUvZ~{siWD}T=ȼ?d^£w+oqQ8D""_9g/=Ui Tng{xy䟸q V2wş 7gzw9LzE^9!Omz-e 6kxWsiΡ Y~mUNkhSе6:b7bY_t>h64Q]81YboCWo~v>{]7(ШQe̻q2dsC[sC $N$jM۝ڝD;5MV#=8&HPCi߻Ɋ( FD7FGEEմ-W 5tK'P u;1Kٱf^)6 /&bpk3&3Pܴ-(qչpDžGXo9u/ؖG+f}i㛪?973hZlQqA<>`:"w+m2dDE\:X"e2{$% 7zW{=c`&$ cB0:z8:6OS4ܹIATx,b=9#!Tjn/y/ <Г%tmڊ*P~?.w3y}Rb_ KQp2$mGLgU\9=Y*Y8 q:oDJG 'I$ 4˲Fj IDAT5uP(wwy3Eԕ!<Uej Dā*i 97˵0 ͨ)#Rk 'ҋ"812tooLo8D< uL l(ł:Pj8M7mhoB qPCm)Ypv3:S`;U.+/ ~|(qp}ͧU2N֎/,+irhUIIIKWQ}! AFm[up4 nFAxC"Ci'PcH8x{y[ ?94BP4 nPy#%>JpV`0)y9b+Bh`#tZTTU  if(AB(b˷;31 ak24 }:=}z" * aL#xq9޷Ҫr:~ }et m~C0}Sn`D,iBzz΀ơG/G)aq0+g9**tIKYȹ}F/ɡ_: 0ܭK=_x-]C׈zbUpz=ﳫK;t$@WI7ZWcI&@,7 ДU\Zjp5U=$|Dao"3eWo %N@pƐmt »KqBk)ԔaEvK Gէ]?ŢU%n Q-DYPH~c(m%] '*\MM@8aG;`EҔځ_>pJy7@d}ß+*|1=~׵7/Pf 1/,Bxg*Tw8wBZ:TEfhMx)2w{Kʕ(la4nSD;DGTx{47tqP8BTnxk:kEb,<bxTE|s'<Ԧ3G wګ&D0FPVB&ջGJ> uE% zoAՠgdw}d S6&fm*ʰ@*83/?[xWѵUG<޾>ٹSJM6HŜl,1 "?"&X΀5?Dv5ݺ}RB'X$=z1:kXqY)v~;߇#з3ڲ#z(:bO8X7pPp3惯X D@& Θb0n8*WpW b1 zou.󟭪޵r-?TA;:*YRS})h 6{"q ~~|S=0gjQmg.[iNnPوs| qR<c?]Xn"-=g>EUiա5K:yl{5J39+\ tGpmlǯD$ErKޝnyw ש` j⹨'xhZ^g\݅{\Au<?+o=?l:Þ)#F_eH޽/8ro/ @Bϲ0D%⇿~Ƿ'– TˇPfT񸅘})^1a9n`d[(+*wdGⅸWA0p=ZFB_XzqLܔ-P0dX$~>6#uj9n̗CFxCWlϲn\È[= },txu5Yr!JS[!C 7ԩ(70s(տQۂ?܅N>~nK@Q%_m5vTXRni=(7EŏѿU/,s﯇zah* rJPVUr"JwXsSd3M x9d}ߝ:wǜƠcp8+RRz g:GE3`hjSw4Lk R0`{6}"%JBVWS[G'!~O ]u>W#>8B$((!")t}7ĬKQGà/ݛvΉˑz|%^v=."D * hԬEcQ~taDH߃(cA{Jb94ۋ%;ʆ(v`8> F.j'|)W >i˧K.!ژGxGy? F)/z_7m:2MGr(F=]З=7_7ͼV|~4 wz VJQUEhbl:9͞/G(g0k愨T6?/o1/u5Ҏ^=it鵠͒;<* 5@q^*|Tn+lgd_y(i===B8~ 5.'?`س1PZT* CЮY}-B§?C!:JigBH+Ji y:eѥYkjN8ʻy% /CǿC@x.8Eݰ&'qа:y\m~Q҆?H3=ב:Ft]H"N ԥ H̫ @g\?I=ﲭnG>OD=s[YJ7L8[GK(A-B@kH~=q zE< V@H|}}q2".)Qlm8#ȅ~7z<YEnml89&KQQYae2z/ "(wAio怂^`:t 뀄4t9^ 3돵?}SZTVU!JJ øUrLƋU,aF?H*vzɭ60   UVcӡ]>tW2uroDeU92y_C@%8"+GVb<,C,aڢҸ;vܮrK,X4vCbBhnnnff:NdɒzP!OK$(t͛EϤawW ܔs➖H$ DqgZmZ\\.2 rG}+͗QF† ^zMɲlz^^ާׯW ?ٰ6:G)ѭ[7:u>דz63Ј'䷴I^  zy/F CIr=*_ʊ Ebu:pB$B%gaލ493 kϼ;4 kOОN``+u'|||p%#d]fm2Ha^Y D_TC'pE<QXC9{Wl"nAaD-m]^9?/u4DZ0U| lC.߹>g[ kX(NG3Y#BY|$^x`Z3.& >)ʩ,_;3w_zގӠmL$JYF5 }#!m=& ?(P\B 0` ,B}O^tF%aꎥ؞qHu륷?Ͼw; >aJpƭ;(x~qK4;l4E>L]pRm[UYBYYY},Y[TT!DP(;q{abx6.y{ #q]paZ~_ 1bհ̘1c j/u|m]LL\$-YfaYvI!yf7ݍ3Gy9W▸ y.Z  K\.2CY9Owt"=~ W&},Q^ꯣƭ&]=F ~ptA1w@n^zm\zbV}A3Y\k VGpDh}p&:AM1iޓC*j\srX %p"PW=RN]gMk]".yniR33Xvr#Hϟ-"}efr/L*ʒ† 3OY=gΟ??m…j?66V8"8.i|"TV12 z>3>>rj̘1#J(-'***.\i7{4!Dq\իWlݺUeÇZn-RR}4>>>|ӻk@ 'G7[>'00p?p&s9dG,X0gԩQ:t8@q c;vx_ LnH$pKSNj'sбcK 5n*ޘ۲=&H#&LK@4acu9O|w+3EЕmtt絘';|o{?p횄Cgƭb5Å똼w{Uu_pq,mbo]!G6R5Lku-!mGtSmKV7XXX8˗c-"I[T C;T0ٲ+VuXlyk[ǹzK$C-A&F, 6F~նIh?ֻ4 m!pT|uhR<Ĉ <!@!U8 ~x)hV@WJ=t80 3TVO[`A9-MvP&};qĵ+VN:8Nv'+%kt鲫uKg_ D,m6%Ϻ̙3@(n,..tL e(%00ٳg%&&#Z~!Ri2qfz|~ԨQk[hq~֬YݐJj:ҍA%EEEj߾%z2q\u=wXT*g,Y$|mTTlJرcO߿_80GK.K ~~~+**^KLLt4.\HСCΝ;KU/v8?L&~zkV{+75癀^^~ݺuiemό3ьi!k0-zPjs8Szv+lR wލV^i˵0P D".mzڌG!@OوPx(8,.X` e3 #GT0 3* IDATtW_}Uf+vU`رchT!ųu:] e˖ej$D2|eY)//q8 CP(4qz/&7_T鄐111qgS]bYvA"9o+]RR2c~Ⱦ}rrrFB9RavRYVV~bbb;v233_TI^,XP9sƘܭW^B(ʘkY%>>>eh|wޱ@߯v!"q XhQ˲>>>&EQNRbRRRj%...eܠhI& Cuz%$$Ԡَ8^wuQv2e̸|R*Wni# ,H8Z6h"67o(kQ?q|֬YC)S8N} C@ TB0T:e֭Sy@B/'%hֽ"8L[/{lxiU 7ƙtdtF8X[zkhCrUOo]VǠM31&|{J*x{yW #a9 CQ7K !fՓ@PzvR-s6|>FxaF(SWbƗo%9 ^5&'u{B,ہ9'rBs /OƚpK_XV^D ¥!6 Tz[nwRQƇRZ9N;h YU1(('˲y˖-˰S֭[Dzl/{=KF*ʃ 4w 3V=hjRg eAVg2Œ%K2XU4+;Z^o\55ɲllr`0 i /;B`ݪ;w\t @0EϞ={m۶ϲBa딐bb0yaJK`om۶fYVl/,XZaKPi^E;/H|Fj4s>i׮]s7 SYqYo^L2l{z}iB,]sY#HyV4Y?uAYQ?_zYnYxU$oݛw@3 $bȤW гYg(nV9 IӿZk{}4k~PUhoqC-QVg E=:xYte8Kcq=GASCA!fq25WH+އ{明>,kg_ UW !DqlΜ9Sq {~3 Etc#&&F4,)))G,V\i^:s޼ySȳQ'[V07՞fƺ\T˸ 4fݓ˗/ i>>>;MJQ> 0" '/q`Y6Oq\H$=gΜ!̔757e\@ pӧ˗/8NQUUi_!nrFm?jGq`|^=qwRiӦCBaOBH)~aYVe.4!:.U DøJqL'r+} 1= Z4smፇ{#˸6렠AAGdin3BObKIo}Z} @[9ot$E:= V 5vvU).rg|ҕoZ:?'gu` Ĩhq8k&X*/Q@8l,؄>&֤vyik014YR!lEK !՟9ʭRL M0!հat_S[ EIm)ܰnhu[O,RpPca*jT_SVVk 5'N^e˖M>=E j',6:m|-!**IG/=&kZ>guK[y`0 `Dǽ Å|b3/8of0q3o8(//_{Nfi)-Z8[o)mۖ+H4x@>]ebv6f: >0h¤P\>wxZxK7sk!K_Th#m) AjvBx?V\UOzNy)F}YaSo bhZDBҳ]@<"8: KORU!xwNY5]I&`QZz=@{RoTj?4M5kdJءԤpֺurf̘5kNTFڵkg;vq\z=]cǎŲ,\ ZF#Gm5 D"iRl)Z=5My҄iLk5VVgNIIIUv&N8Ϗ8:F5f̘ȔL;y,˪i=^F:ub-[b`[E'N8ߟu-&V{ˑ_9]lZɲ,LyF|aC-Խ{VK#ƍ\zu|9ya,";;;ͬb|Bm^$@BSIBHO^rL^099yjGN@|'yӧO0i";w)FlĈ PQ)m4LnWUU-8n@ll0{~2eF䅅k CT*cH!믿VZ)l5RJm4Vl9V#;a„!,˪3L*)aׯRZٱR\K2X5)geӽ6 4Hf+_5*R ,kۧNFڿ{4ieU/_NU(1)=|02dRڳ"tLZoN8hR2a„,nٲ%΋#:h5M E@}Hw%:7b/vyq05QAWH6!ͱ*z<$>3+oWru 몖oh2F!oM#Iwl{RoϺQ./Wiw!>g~qG^݁LTJl`nnB[TC 26N-ǪQjoHјتyG šC:.w}Ųlui(jժtN7aM'O=f̘111 jSnݚk}b)!-k)7,=ϾQVX;iݭ˱+V> \ C$}_/*PsDw._{y^zQ,qdK#*LkzHEAݽ; ~1`UkXsȊ+ƏOe2١I&PBz,7 }ѹdD"9kٵR3Omڴ{wIȅiRyLn36 U8-9p$//;w>߱cLBtkVZh?555wر%&MeNKdFa_N( ccc+++nذ!}ʕ ǏrI&RJU dY6-;;}ܝb]O ck׮={H$ݺukX7˲EEEfHUVm=zR",4iR2\BHlvvqT V\# 8j>,V>f)noU6mEJY B\VJIIA|tAnڴx@$ @nW*^zʕ5v IKKS0OVvmIBf5k[Ue38 mĉ0LO5|mVV֚v {ĉYiiޑ$CCh4= a oK.Q*݋_]}p |@wB ]G7&ggfmH111Ν;gݫtPW_DQJhrmۖs^@ ,ܴiS3̅'NiӦ~ɚ7o 7nLw9N6lؐn=7oMKKSSuo|cԨQOɏ}"\V~'U<_ tw 6L8Ni"J|y띦#½6w5oڝl7a„,˦Y&'= !4!C(|||AѦ[ƍ"[j{8 :lMpM-%V$];6 _^<=cR_96g#bo{p uy7`x˳iгz';y3Ob\+FYs]̭+L'>3~9%D?R {"C@h/cu4dblTX0R^ @4o)J ;03l'\,k_ E[X7@6.+R}.?~ZViӦt뎆 u=P7{-6~Mw%ƍw㸌'nQU{8~[浍7 {;99p3w2q_u8.au 7PwU@_^|1qgo_ EJM`ɯХq6G""[H0zkTX;7 RSq 4T*`s :VOQeo?V\RjW7B6 ,= 1\S>  azT(1T *-5BUr<-?}m rR\bak]  s,\w;g۔Rx|4bĈpD0i|xj^u qBqe_CsW lU?@7`kXh[ZT) ;_Vw1?]K7.Az(Wtp|Vw(V ZOo5W`/ ~] GJ~8:j\Ʋ{AĜj`X~Vp`;SD(랳>u|-tJacL}Ve@i=Z wI¦I!͗g)9ӓaroݺ5Ucu m|7W[9i=tTVP^]TU63#M;!X g+[$ōbJps!s azm)uQ9|fQUOL*WV`YQ%7ѨI;.c qzL~c\ѴK}5.5uq<@{h\ ܝUָw5G@jnэշ@~̓Eu鯺T\u@Tm~afy9ܴ+y꡺ G`k{F]@xn[b +ש:u (04.]S / yyCVI:(\<[97ޯv?~MFO[㿋U0\Dƽ^om`Og*'h6FGE]l?$mrC#| ]Rƥ]Hp74OR\iP\}jp | H¿ C)?ޟ]K_],lekt69Ͱ{Ʊ%ܷ~= 4:6!mn9w3@uUCQ}TJBZcnZ]`ܝg&p]˸+n\pYc/_A lwjVL}9{ݿ|&pu(Ҫ*:豺[OBYoe3e^\YWAAwZWޜ^0.7҂N(;8>{l3 ތYƭJ*76xi<>?V~@[ Oq}uuu+ KyDz}lqwaʞB C8;W=1ycnal-c*4.`˦ |6mA u8[w.Ib<1z6@,M-[_o>0k]J+{]Ά@@)~t<<1y fۚar/׻^})kŪY3]@]?w+ߏPWk!,Ba<1=]?| un~<1=.Cnv-?fڎt\OWyc<:@8ڂ+WPJ5;*gm<1ycRJ]]ju=C]t]fwh2|Az<]U\=wv~jC[k^v<vj,J]GjK(rh~<1Y jZ]pq܅ڌOh}x=1ycS;@C`]F. ;U]*sm^ycF,97U@Wԃu aϵt6֕?<1ycl[!IENDB`passenger-5.0.30/doc/images/phusion_banner_small.png000644 000765 000024 00000070356 12233035540 023143 0ustar00honglistaff000000 000000 PNG  IHDR^;Db CiCCPICC profilexڝSwX>eVBl"#Ya@Ņ VHUĂ H(gAZU\8ܧ}zy&j9R<:OHɽH gyx~t?op.$P&W " R.TSd ly|B" I>ةآ(G$@`UR,@".Y2GvX@`B, 8C L0ҿ_pH˕͗K3w!lBa)f "#HL 8?flŢko">!N_puk[Vh]3 Z zy8@P< %b0>3o~@zq@qanvRB1n#Dž)4\,XP"MyRD!ɕ2 w ONl~Xv@~- g42y@+͗\LD*A aD@ $<B AT:18 \p` Aa!:b""aH4 Q"rBj]H#-r9\@ 2G1Qu@Ơst4]k=Kut}c1fa\E`X&cX5V5cX7va$^lGXLXC%#W 1'"O%zxb:XF&!!%^'_H$ɒN !%2I IkHH-S>iL&m O:ňL $RJ5e?2BQͩ:ZImvP/S4u%͛Cˤ-Кigih/t ݃EЗkw Hb(k{/LӗT02goUX**|:V~TUsU?y TU^V}FUP թU6RwRPQ__c FHTc!2eXBrV,kMb[Lvv/{LSCsfffqƱ9ٜJ! {--?-jf~7zھbrup@,:m:u 6Qu>cy Gm7046l18c̐ckihhI'&g5x>fob4ekVyVV׬I\,mWlPW :˶vm))Sn1 9a%m;t;|rtuvlp4éĩWggs5KvSmnz˕ҵܭm=}M.]=AXq㝧/^v^Y^O&0m[{`:>=e>>z"=#~~~;yN`k5/ >B Yroc3g,Z0&L~oL̶Gli})*2.QStqt,֬Yg񏩌;jrvgjlRlc웸xEt$ =sl3Ttcܢ˞w|/9%bKGD pHYs  tIMEz IDATx]wxE{'!J((EUD!(&HGRDzA)Azo!=7=/*<ߜ9bf g10Slrl4LDn -Kr``|&x~`];_=:wEHiJ!GA8cw3!A(+/~ mڴjݾcǎv B-VZ;v{8lݰvzܾU{n_'v=eT`7'(S̖8(NQrl֛̼tEX\ʸ0cZEȁ\ۇD`fRtlBo5h|$lC%_|4a!'[{7 Hv|y||Wrr7)))[˘~PP1| z 3CRi;}ř2-Bq_@<|nԨ iFd0J*dτ5+eua mr'Zbzwu9{~1`3Wj>?=[w"k4)dܤuk._.%ϘO൧Os\\$7 13~L&MR܂Ɔԭ[/1wݻwgQM \y饗㇎ O &2?h,>=`(SjÇziwVtB[׮WfmQ' ?*ڃT^pƫf͚Ǐ{׬YSk6A2 (nܸ]t-&itdXT'NQF}pBtbbl;vLF?#xŤEuW,cG(ɇfAxb"$X50]KE]$S_C wуGЕ5ߪWw!|̫bD,bppĐ-r0,,CCC%j Y)?$eJJdXX+;-Gv}9.2f9%nd%,gW}h7y brrr]4DZ0u/LӧOHTTp:W?Jy:]IO`=|9-~=ӯG.{ha7V@A"Q}Y<Rٳ3g""R$%%]1cF|>}^Yd)f?d޼y %"*g>;wIII޽NFX*[8p wРAg͚U766VѤID>cƌך6m;Vn*T:wܙRffsr{Q-I$iXf:#N 9m.QCfއMm^w̹a'";iDgKm[m޹rQ\zIv (VNHNmr+G$>/Ԡ+9啫ސ7M8bEp(|9^y7'bSWr ڵk#9@/ދ}~ܺToRbA`fDx޽{wѤI]tg)!ETL ZoׯbdfAdX^up5jd vfhц>x /ܵ $`yH߳laէ^2 4xDpuo޼٫u7o3[HV"R3YbeE yrt'n&JKG t-z5|%^ EƐ[n"M 7~CQj/t2gР|F@:+JDFIP2p)V/UuR!:==7o.AUTy?==5kּ[fM+Zj1ZjqJ:ժUk֬PFR͚5MTjՒj֬iU$N:}('&TҹU0s=(ӛQ\ }#' z;Ong7qbi̜8 ~Ip]{n6Q4*}A@\\t;wnYFF? @jjƌ PPFF222BN<qڵWr322p̙Ȍ Yÿq/_R3 wm̬L\tT؟dCk*Èzu4u?n*tE3?DmKl;yp;`.\v_Z-_~ڵk f6hZ`j`j ٪j@^jl2(??~(Tj$ɪj&[2,8?gfucY;cܡ:{ރ_^~'7<{p+&_{r!026g tLNDy"tڊN*^^^q*[//XAApUjB3,3V ӿ ewՉߞP=8wך5"8V'3PLhM~ѹ59EDD(GY)ݥ0~jXق+18*@Ms '5߫@t6hxܒK8=%R(o&ĻWiݡH=<^-PJJI"dX$"eժUUgΜ11=%%e ן={m6ϯTRa0zqj-~o&''e۩[nƎ;IaZMgIvJ%GEr.BCбL U/KߙVVNgUMe՝vIшFѢhTfV&,F$+3۟/ D$23VؓxyS=x`i/ѣЦkj]~$C Hjۆ&7KϦ{->vOΟonTBՒC~֫hXd ֬CNR+;L`摎 X]w$dVݹ6^}zlf&_c^mMYԩS'삈H ڹsVZ QՃvwgPv:g)TuE?Ea:TDɿ0/VeQXM^ՊykFCӺ1_wG^[lqA`s|<{Ma'?L*+.(f_ j(6l%"۷/͍5zl0 h>$(eeݻAAg"":uth4KϜ9kذE"rzN@ըQ#j]z4h  |n݊qI7fI 븟ԩSD6ߺqv =_o+P0YuhۡOh(/',1= Yfr\x,> Cz bM>6wfZ^hܫv.N>A gs"D)Ҡ7z`1xh\233!Jۗ|~1G0Y͈QEżO~g.G>hfZ^|,6+oiMF $ EQ9>c"k*TfGrbCBss>OK./I?Y:5IO5s;ozָR-JcΘyfaPB,W}fv @04B0(= b C\EmIFJfɓ03&Mk׮͛7VP$%%pB%J2qʔ)7]@ppP03U#tҚye$qpST)٧QFM4*D0X춁 Cdmt7 |7'N{Rա-3hРݒ$]}\nUcro߾/_rB௿:sСKئN:~ё&XDd~gڴiC iÆ ݚ4iٳ zuUʄ![͹E8QΩ9Pw6;.eC%[ʄHEbY6Ȇ'#|hǤcL)E4.!c!>3 ,~K~>3wu.]z̕u2$a[ P ,MarŻ _~)˿75k6uܹ7%RQ(]&\U1F%EV e hP6.Na/rns"^[d—\f6~R(~I3΋NZ#+sm/@[n. b~~] ?\?Go ;Ugܽ?©f1</ ^<>vPwy+*j"/^s!93o]A;-[EO, }NωlM''`2#v+C7n♣z3}Ml 0rraFDDrK ТQVX< uYw8Tk?]ǫo^7{ˣO*fCd4Ǎ18ʈR!2 `y-݉hÆOW9l6[@U@ hD'2*b7>k:%]q(' r?=jϑ^|^Z"سέF6`ʁU/o,)U|eLJ*u!3gp k՗TaêUw.Ǔ&v*#e  D 7lBn]pqWyS>uFI_›>'rr@o?JHxEb:Ib"' =%OWy 8*} *S6ٓcyx1H/o/\ ^p!^}8zVFSn>@p<8}ݴ)LKaj5L&`U(^E0_:LĚ#Za\" )gZ"Ç) @٬Y<Xr F;?@޽M.Se@.g (˖K82eWXv (p(_Hέ'pv .7CsH.<0h\:f IDATō.]Tt)sԹsgYFE6a(b%"Vcgk!`bܹ3X+U&"|YQ{ kb%g#7b5_ؿ'G5D{)5pDX68c_/?6߾mZf``bCH, O}&Ξ:e 22Xaګç.\T{}8H8g 'qXp;pr} 3 gkHuM,Y@@a2&L&Gш7x# h4d2a6BW18O,INsX9.׮]sܹ.]l'v$L3EQtG.WyfgۆXkbp՗S6oƵb1d&ٌ\^j^F6}śAFzܾBʲMbG7~DbhVU*,\* ]_C|J,QDDױ%  ط7o=-[`оx*:j۠&{t3?(ԙݠf@SLpaB VCUwۯbQFZfwvۉUCCC7ݻ׽QF +,`7>|bmܸ13}U`mԨ S\;L!Ǐ_^^DܻwGtt||m`߾}E պYTvٻw/gbJ |n}ig:104q"L5ߟ Dx]r7>HQޢJAi#AVte2e0RLpDJh8" K*УU}77ZF#GO snþi!ג%]`"{-sԿGބխ&bf#H#͊'@۠1KWww6z=SP(Uz=z hlnZ1:z>vfN'ZJ,yGq~~~NnjA$xNFDDtN\^D͚5* wXWl6K/ )W`~|s?A^2c^HddX-f$rrsPF<5XF% ѫ-q`)h4*Dv R 6-v";7oܸ#tyy5+"00 } .mX 6#؇ٍv6Wÿp-ɒ*g;0U?U,In">w/ʕ+? *UvZZUP*(Pr}Y˗/G:ebb$=pB fή\f3z&"h7oGչ\p,O_W`7C/)u!?K>] qp]Wb`ɗxb 6V,6YmlXa5[8ccO ߹sիWիtp1Y:yt-ݻwoӵk׼gF Q`0\6P(DQX\o4L nX$6_ G/$b LO(}(SS<jb;l6ͩ@׌w)Ǥp[':rLy4ޅvN|VMi7N-cV+V+:jNMBme׷% K'둞|5["/7%'yJs0ET}s7}_yUƥH#GݟPAceأ!fA)H^po x%FP"^ؾ: P+SRco%դ1sNwYeA8C Z,n*f}JY%23 Іjre9Y ؘy GS=zDdٲ(M&~f-ߣaS+Tk[“-&z% $$ꕩ6ъ5`T~F㫈 @ Vtjl6>YTR}CN^V+^v pԌw.,jw_#G486W="z &dOQPkէĮzPK^R^H8^9*|OW,TDTP &;8l Yn f&"Q$,u\l6h4>A(JFjA˷a\B.#d2l6,ۖhEQ-I$(IUEnsŪ[PmCMT|V/7@b.EDm*匜L(_D'aJ Y%w,[Sm+Լ'n@4'M{sGb-Ji^љHT$bAB6TY"EDm AH @`1,*V)c i@fΔݙ;W RQun1 P.Ў9[pv.1Šus^ɿesRoQvǻ/8k-Oa?n݋>ʾW&m קݝ4`N y77RKDvSi!nԁk 8 ǪowQ|euE/+cbb4%KtUõ ƏߌP(\mmQܖAAA9M=PB>~ok_QZ8;P $gN N&kؑ ='E'[[lvJ1vΛuN) Gv J@PA^>p8) b5ǹr,"J>!F*Jm8 T,)SjۘtQO R߼ysRJijRPL2L}FGGk5u-tұcG_GzHoݺWppANR,={ pkΗѶm[X7fF|Ǔzyǻ13^{5ri2o6Wc`]t@G;8'3cСZl7n̈ 3ET B2d녻dٷYf!3Fٗ1|z*1bDgǦ?~%G?brr:D9#F5hР̌)))??~ZJJ (SRRvHII"w;Ǐcf4h hܸqKFz?99yCرcgnr4jHWPB%+pb'_{alh75>iI>A1iGFV]ſȻ}xa8!z|X$.İHcy͠rŰSO^|E5hڴҥKӧO>@ٲej5i4)S'"|-`0M&')&Nb(h4hj5222rss8ܔj4Vcǎnݺ{^57ou٬Q(}W=z4`00 yz*ݿS~~~70#F8n4<7qqq^z~?T*͓'Oxl&)|}}5uցÇ߮߻w_Zpqp&.03/9puNqBHIirx]n>uw@B\9eda%bRPI&V 6f%$t8?YI*jRlэyƌ =<<*~~~ ˗/LJJ:4Ϗ6 8؜9sffyɤ3NJJڲrʫIIIv{($%%0^b1HLOO_tzРA-Zt.))dRRl2eJ_77*ɤEn2/^=`ϟa0.Ta? @6m ӻw&L>iҤݺul64d2ibX =zbҤIoL,y뭷>Z*SN]wȗzqL(Qx_e\ִӑ8(qMg#L};9+gͧsM ;a.R/tEM.zYeŊ?l߿UVj5'LįziwO +jz[rSt:VW&4!|џ[ՓxN@o+;}Y;\4)s8CE:9p~WwJ 35eSw&.\-"7WȫO~ʜeW3~-B6ݐwH3ڋ3:H_%|J Y(("S'ۄ]!nQ?ek_eE9;snLu۶jsK1P̱8ĥb8f(ݪl'{O!sw/ H PzDs/f13Igy<ʋg<|N嫹n<|}z8lG19Hlxy9U@Y8* (d rST!4BUV$#jh'Bh~JN^y05p='yxj,Q!pW^ۓǓd8gffT쒄p:[̫֮*U(CABrOp븚rV?Mk-'My+{'3 A! *ՒұenHk5Ng#GC5_AhD ,8z70/\F68g?\[WbTg9rUp]v,I`E āOgoXtY;" _|q};cp-`0'=eßG[y^eo+$$}Ǩݵ=U瘹 }6k6n$mHxgt!+3bx\tmg=9?3ganxRΝ\( _C~~>I^:Ũ3?@YYp1<"yvo-9+/wx/ؠdR2,ĞZ ¯vM FH`!ezv_k [Q [ɀpr:6׆u},Up@3s ]) sQOW;>7z^%8Jƾ}Ƿ<~ sz$MݛuFJ"2th?نFZC!|9݇]FXBw}&$9~?u>J"<&sMǎh޻Px{]>=RÖ-?;sr&dCY/FC`Bٚ5^ɅM6G R jſ^+233s7|s]T*VE ?@PTb4hР\PPP_In]@BCq yJ~_YmdDPZ-4ݹK /OvC"D*I.P#+/ ]RhDHL [.t:v}f[Gd?#G;+A@ 8o/Ĕ={,Rh{p}K*4FnJ2RSQ8}daK7l={N}R/|y8p-^{F'wlȕ3 o8]cS?gLIjʣ eyfUIXS3MuORfN@ aq hѢg55AnnNu)$lҶWoGE23D@/=VN|#DObBR G#2جqw;y՜ ${+k4ԫq}L& a3Ā 2^1t Af =*7j^R(3ص+S><||tȸ~>ѥI 1LJw,5zqbvK萅Ƨ`y||=c?2/U57l؞gt>8by&V-mLQy3=Ú&#i)o ū-UN+pX5S" kN>Z;7o҄[۸2s"I+N{*$Zfl3V*0gQv]zۮ "w6| j _)d)ٶqqp|*/^x%:j_ǣ4*ە4*ݻs]n_36%qFZ!i[Ĥf͚}?A$icFFFF~ZV?CDǎ?~ JQ`8} [yڶm;, m12EQVTT ?GXX 0ĉ CJŠ(ٳg,IwPJoI2Y*8]6|c=GOD9"c`C) ؋݆' #GFQj;6P(Js*`h@D7m5-!{˸x];uJƮȱ5'Lǃ)8RPt,&fMCtոXfᮎ=LɬV+:=?0ᑸPTmJi)Iqr!V+ڴjmm‹Sc pȡy3 ?' :߃espO}wa9F=.5#IOX}M+ *780]X' itP<2$Nm^!"%^֦M Guu=/^$%%پɲԩS&(`oݺYdV렢czڿ .l6VngXG_.kfAA!!!QQQ'1dz).Yff^IDPZn7 3glR||P^HVFhjr38}tsdQZ3P H]#Aӿ?_{!Ãsc'y\Tzח/}^&<5řfAB򷗳v~cf^Tc1d.@?M5?8a}pÍ7+.HMB!jXՅؽpGK?F}#q";vAȿ|cڀo1i4+u2X\LZ?Ѯu;ذ ~ڋ_~=8?vӃ`?i)lP TY[eUFy%ѕ}jZy8D!(v8HЍ-F魢/{.XLO6{n|?ZZ.QF<8*IҞ/c(gB{|oKEoۧ# @t:TUUUTVV&2mGEGG?bgAQ˽O]r:u Zw Ts{PrT7i'V/~G+2~Cq6 SykS g " #Gf3piDײl˨B0 @dTY_3CƢf<5c0rc8{X,shn9SmDZO<`/9zɼ0h$Wp`ԣC %%?Eqqlm|~܍vEh[0yt헶A68/q٥mO|jQ䪗m.T`Wܦn$|#7͹6.;yuGosCݘAPa)E.Pj~.\.fSCt< G`ld8Z뷋31Ky`'{(e6,C'3ϣsv3n,l燐ŕ2TVMdUּb8L͢A'DA;AKq](jZ0fExdOhG#30lbp.FgO"&f"&8o4ؖ@e{>?ddzPSfP7IpޞS#jZ`M-"cL?bͧEq`_W㚤(7^i bVVS&FEEM1i)&Z41M&<EZ#(b- ~s+))~VR-QQQ;|&rfiiiq&"Q;>W/-{q:I9D_ k֐g<0qq(B=:3I޽ŹfV!gkxv<9dV%dȒzNd4 kwуsFGN]:{@VxuR&CcVŊ,% 5o.'Yh0i\Z<]!/_#Ia 3Bn)D7$3n;SQL"H}sϤa]f6xᱸy'n=w/zZ0 ~RBQp<$r.49qxQBw+d2vSϼ^ +ۃj+$8^`,We+W3L8&˲-$$.V @PV5L< PFM->й=Ew l nַ%MlKć7C9 j2<$(ܴl}nAO #@L AVVH؍YDx8ƿ2QcyPh,n0zvYI^Ȼ,~.v~1}QG8d>=3,` OhEӆ>n1a2|[s3n@]:7=ݟs ϖ.-c€1mOr9|%FƢyX3>mOŜw`OFf  ρtķxC242G6 Ww;1{،Ki@a!ZIzմ5Z-=v&u7#Px6y87M|''Z ז:=s猘@e.{RG?1m+P-[y``LyAkW0:mq>.M} v|Sx~;pRqP# X(<ϼ@-TTz";~zwqu3yɲLc\Ti QS346B`F Լ Y*W {ԩ͸wzxxGZK,3Fl={~}\"zٯox(sT:tWrlȂ* 8m]p`O"XX68 J5)k\\/ZtBĮkw]3CN jbR15|yqͤ5(v%s <pi <˻Hp%4LM,.h^"ŋKd"1r^h4_zG[l+))Vk-`&%%q_d_ėp$IRvvpFT7cǍxⳌ1`@RRgX\M:mڴ;w+fNM6eAAAɓ$IhT&“9wܴX<F#E(vQTTTK\hѢ)*)dz֌./RGIo&M͝={VrrrQUJJFƘ:))eΜ9@xx8vqvΜ9S"!!x"~X~~79++kV]#Iی1 cl6c 4oޜ+//'PYYsTTTDM6e,//Oq\jE\\3,77]v8qB l6$%%1ɓ' dh%Zx:uJxu5Dn@ugrgDTpM[ { \ͩE%ˤ{wlAi{U@drE$R@`SQ(މJK5Fׯ1u15x:z))J'Rp0 66r* 2}_+c#R@fkPKרFT\M!cͨsd2uu`ONNs٪U+SӦM}@DV"*8Kjjj"222W8E9=o޼:Nr<-ZmZo~?vϫBNIIȑ#޽ۺhѢ]DTq\G՚s +++1c3ZlF)ƘDDԱ0L< LII:cƌ'RSS@ZZ*F.Iҧ>N#*"4o޼9#8j4n{VzzƘ;'WXѡCͤI֬Ysfڴi-"##Vcʕ+>dYvdgg`X(dff~kBq\ݾ`ѢE4ܣGE㸮%%%O\ %% ^O:YOΟ??;33s*c,4,,$$d`ZZګfY3qDxJJȾ[g X՟wziMo#:`D:q!J=PRվoĖ_X1h@]T;{:0sP8q;LZvq],"0w~G #:1wƒoE-;ss,t1 2Y7o\oT9 *Ŋ@ j؟wc~BV*nDHJOO?qg֭Mvk׮ke&Ml6x<>U*UcΞ;==}ױcU^^.ziiiddd$zs̙"bh||Aa׮] ,337.ZۍFc7UbŊZV'=--@qqئMfeeٳg?|TNNβVg~N$\cj8SKt8ZQL /<`4Ax]łaq29s$I%^O AoL?.]$UTTiժ[T*6fϞ/V;rJRn7_\reڛoYpb1cDlܸ4--HFFF&MKJJ쩩+-˖;vx=z5͛ɓYm۶M޼f͚aaa.]v~fff~Eu)ƘQՎlmZ?[zA"̟?ѣGgղev3((HeXQkt9?d` WCԺC ݖ7@!_3 WD^CfVث~ttOؤK؉v He*TWdzji@ddD8j'7 cܱڽ:Vv8'fWcs?C*5^83 O'B26<ļ}S).yܘZ:TzMJݺEÒNSN'q'Xs@$9tvQ,Yfů `l1xUV'ڶmCD."O>)((U""qpʔ)CH&bŊ= 0!X$B,"v D$h:u~ //\~['&Jtj 77w(K.]dɒWXIDATAZ?EV-BDٳ JDʲevï~-amg͚x<myy˥[dKEQ{ȑC(jBBBjFAP>裋 0 L$gBB?{Jň%KYdAv}o&I1400p>7SNVAVLD2{˖-VUU?s51111/^xy S$I{DQT:wl^p ؄Yf} HNN6X,η^$*P`w>`xX+uC<Tw3tޜ^qyyȷi o "qh;ش+Oӧ){[09iQSd,'K3jKLo%ZinsϦba9P3~sQ+0V% /ADvcgwK }\nn*((P$$$Ν;EQ$ӝ?*7nM]TT+,,J!-,,T`8N7cW9sczHnӦbbL4i9nݺ/fbjX,i9cccu"k\.-["DEE%%%[V3NǬVUA(rRTp0myyUV|>a JKK=&I.\^|-))!o9w%$$W%OJJҞ>}ZILL_ꨨ(v%7lVWV`ʔ)kv 6kXXj*n/-->WFQƝ?S4yl˵moq 6 ׿x,hĒܘE!pmW[t*DE3}+ΈDjE:*u@!7]gul}U]g1 O⛪xDc=6%S아Xsg:'챈qVv_yX_z,֬> -PYa49&dv{#$7}?Ulτz< ߐ>MrŒPIt3fd UUVV* I:7 u׀Qctfcʦ˲l~лoH7K 5zܺ[* _aq0p ]q~k&@/{:Q/Q1pHnc 7&6I HӲAvL ׈6T|낰R+94Dos*GNj:*oncz]v` X7&pnC0T5nr^mY=skLr6fP_p|| ho[(^ @ތzF 7bhLolq@|}ͱ[`{*w!@vt IENDB`passenger-5.0.30/doc/images/rack.png000644 000765 000024 00000141307 12233035540 017654 0ustar00honglistaff000000 000000 PNG  IHDR.\ iCCPICC Profile(UoT?o\?US[IB*unS6mUo xB ISA$=t@hpS]Ƹ9w>5@WI`]5;V! A'@{N\..ƅG_!7suV$BlW=}i; F)A<.&Xax,38S(b׵*%31l #O-zQvaXOP5o6Zz&⻏^wkI/#&\%x/@{7S މjPh͔&mry>k7=ߪB#@fs_{덱п0-LZ~%Gpˈ{YXf^+_s-T>D@קƸ-9!r[2]3BcnCs?>*ԮeD|%4` :X2 pQSLPRaeyqĘ י5Fit )CdL$o$rpӶb>4+̹F_{Яki+x.+B.{L<۩ =UHcnf<>F ^e||pyv%b:iX'%8Iߔ? rw[vITVQN^dpYI"|#\cz[2M^S0[zIJ/HHȟ- Ic}}ҚY}Tj^;7ZAAmP:Ρ)&M t=\ VO}P ^Oi6FAZVdYbPS[fʕ?~ٕx̞=ӧc4tx{{PE3jHeYV+uuu[7\I~+̙3ӧ'Ȳ"ICEFjjbXX,YEiycccf!55U{̙3tz#F/Aƹ_Pd~a…Znƚ mh_|Ef͚NӒT5\\-$!IߟPy衇0L-?C1ͼ$$$PTTDAA=z`0`0-jhk X,aV$ g31~XMMT+4V'EiqNbE>X&U TfMM _,^z%Νhl9 ܌hkߟ/ VϹ AbPWWG]]ׯ'33Ǔf2y]@&kKc4KjìiӦ{nJKK0Ύ;0Z2 bH $QWWGmm-u<ٵkim?**˫{Dٽ{֗hJNkұ.%lT.F@@~;۶m;T% {_Pjkj7UO7ȱ㛴鶴^jumMU9Zm Zatjkk z,Zk씫.j` <O]]zOX,Z؍rAy.bd1?ZM3 ض}@}W], Ep`+pD9gX$ ^&}mps=$IAVed!#*a"#j!I>>>jge)M*r<'IEr(HuQ Uv[ t/jbZpӳ*bql/*~Shpq^HVdd>.vF&C{rCEgŎ<7:H{-u*sk}YO8 Wk9]"Ψ>.GVf/Mq-|VUk\@{" 38YrMS:r]UV)-{c@Sq8⪥(r: @"|\δ,ťBӽ[ دcfqwx{nf 7h*jx n5po3=bHw# E )*sNǥ!5U 4iZ  .2\bz3|\s˫U}I(."@ to;""  {h)f .I=" #ZUxyQvJEp!Zc.|9\)%%'gϞv+cĉl߾EQؾ};'NlN<ĉ9yd\&@ h*񽐻L|y xp$5)ƃ|~(Iqqt0U%լe7n8.Qhz,̙33fFLLCޯ6C{e\s5ZҧO>#f̘={< ˲8Ȳ޽{gƍ Y$,^BJJ iiiQ\ReӦMỴ̄>֭[G ''ǡɓYz5%2~xV\)?wO?4⋔W<@ 4զCIT1+~?ߝdpgpm>L@?qP?p˿Wpj֮]˞={x7EFFjF ͷ )jh;HOO`1 z˖-ٻw/cǎu(1KqI1qqq6NvvL8ӧO7,@ h/d]* R"B(ڄO#zoJN(dPL82Vgޏ \ 2~QK`_A1ygϻ-=yt`}fQ;C3'999vWy6d~Kϐuz*I&L ;;#FpͦM|gW_ ؆q@ h*]={pb a-a9 r@u}2~+Z;V'7zK]GOVZ:nmOOOcժUrrs(8^ꐿW^3yrrsݫ7&I;s{g%I҆sJ߾}띃w<ȈHa0۹0L۝#IcD)1Ft::/X,ojjjj0aEEE,}msL |z a_mDjwʵ'0R̔RA- 2o___0 z=zQ%c}^pdrUؠXm  `03f:L&۝c0=W@ H" x $6e ބc[ΰz?^H?!Q]ځʀ8zsS)uLABXjjj+S;@~Sh:h}uڊ1~uhW-My>.nd張-m!IOۋ'|'|{Y+_ MBB<=@YEg~,U#:\Oɬl:}s륶yZEMuuu,_ &pWxbmeh5xbn233CQN:Ŕ)S8ueeeL2yxz2evܲ2z-n/^LYYwdff( o7oW#F_os-]4FƎ;mf#:9999deeM7OQvO[f7e>*Tͣy("gYzˬ})GQd3ul_[f8d޿;~:ywI-CQd2k8e>-7C=N(an~CQdWԿsy*Te>/%{9#{rgѤc6:u*W_}5TVV /7HII !!![٬w!++rss:t(< [C{_~zu=z4<<oJV\ &P NCw_-yK(^(B6>ˎ͞Uyl]PnN||ϜxOGQ ޻k㻊 2Prj9})O7~d c>_J`]gZ]o)$wLMECAAF{ל>PFĨP^8)Ϋ@F&|b;oWgD|CM襃]^ږPCQC˲/{O?,l޼6nw{o_&eeeO:u*Z7oft:yǶ믿'|BFFÇ;,( 7or-r::ɞ3j(233i#:)999dff2zh'(` `BQD9#; r@&aԳR4;TVs{lwiv*9QC#Y6S">Np#r_) %Jmy-%'+b~-%YFΉϮ冧w4=߿B|_%y) ( Sϗ?= /rkw'Fc !Ŗ"å DGEOpI}] @vv6`S8mƆmldd_sڴi6GڵkΦ?͠ wyݻw)/5Ј-WA2yd7D 7Ӷ9(.!ISe^ڶ>};㆓=K6TrpȊ¥7\ YQ(!aT㆓Ta gjP [ ėȊϟ!$,}88hbo^z/n !3+D5bsOfDimu=rWiMesCjj*O=7oԑg}(.>p;3xg(//?&""#llj`5j.f̘1dffm6Ə* f۶m;v @uMu#nFyǶVƆ Xx1F"&&'$G>\&N3}m۶xMײ!^xzx aݐ>xy{9).3[ETs~hFۯMٱsydEvW}y0Az~*Jt~^Z?9+i`6cȊLԸP+,YB˃{kurc˃|4y_yf^zk^+-JǠ^SvK8vZM$I lI`ڑӧyg>|8񔔔p}i8kӧڑȑ#Yvm.;`f͚E^^6D';vٳg3p)!Iuh}mDUi RUTGI^%kNW,uT W]4R9- ~4٫NPq_S}/Y)7JOTY&P~p.ң2]ރ|ԖK9s/X\ECedž~C({:mUR8QJ~N;UR(?YKFtwYj\8 ձTemMC{\N.槟~kh^G5:|&˱c$fΝ .\|ńRXX(T #I}_???åIc<]elj:|.]Eо;c2Xd EEEtpAGBqq1K,d29s9iܐH"h,,ᢄ"tA$I"!!={h"} QD pBD=D͛Dze˴ul2x1 Xc+ Eg٣ȝc&}#!!ϟ_OFLBB5sm94 ը "):/w:11ѡ`ǡ"p/g%P\4IPKW95..WBrr2K.@0$''}AբyQP.YKCm`0b @.AWA.XIADrH z[m|\"Ŏ#:*dfϙ͐C]%@B9-bǹcA9W(.m*t Mi`2]@Ўa;b>)H"OڍwL+.NXW]RSS9|P]N#IMMڢ*U$pFT"|]?!1lݺDU[>& »0Ms R%m! E 脨jKlllBt"rQZګ\62pGsۡV=c;·GTcqYsMƋ5uklMѼV[mk^Ɗ  Uu7gP]N9󀦩-u>yGIIIivOJJ\h|nj@zD꾩`[ӧOZfhPi@SY$:jkks JJJ fܹy2SQQ'|oAii)}@@@b0f IkA$KQQeee.jZZX,$ IX,r͐ _iH2'Ҫ9v9ue2^z=tF}\Oت_7:o#~zy=c[y0ZRۂL&$$$ܹs۱9PTTDbbbWu:vB6 ,^K}r'W%K`\]+ *.p޲$J˵8p@p\1cb2 00PS[zk,Xp@!{iZełb:,V UFeظq#޵Q~G^^הYEjtz@^hD$=z4YQ}%ի0 F|}}1Զ 7o˖-cɒ%tF,["VX oooEWNjA!222^^^0eNj9fg"Ȋro/[yS]ڷ\PqQE%I*++* å`? ZFH=ߢ3:e5w,] -=SAuQ[4?s8 )L$Ahqn:o:=z)M1\xyy9;,kjAFj2t sMuupĵjj,{UŋkY Xx1Ty[^u),Ze"+xy{0j$}Ahy^?TEQE5PԱ̺:m V(W  <7\쇋TO>R z;#I yG" UmC6\TE~,ESd\(Bm4tUU[s7QUuvjS *.j(vpj*9G$͙W uxq*/KKƩ +V`Bu܄Xtg;ޱ+z KB:ƇKUNwXnAEbXFeYv0\OHY8Ky`@a/-qеg@Cm^\ 4Gsِˈnb.ꋶMȎ cVw~bҒ0MqQ-!Uα/Hocco`̡sƉoh'uֶ8@?1}[},˚mvpt?gjK+hb[*"h9),į!"t,mbZgžp~ݒ&.P LP=稅ΊZk"6#+T7nm;0 ~kG8+-R.ຑۅҹpn0ۖ 1T ԧg*fv?Tz>/vË-ن=V3{g1- 2\Z騡Y_p5L$a::㬸4di :4wii^^^ڽle!ƒԭeݝ[9wv3ZUw+FQW_uݚU>.X"bnYaq>.*BuڗT[\ ξj>AYaKZ P\pתorJ?JrXS[k Wk9]"Ψ>.GVf/Mq>jj =QUOg֭^@Y|h\u}bȨk`J=#PUT9,T&55]W?'|\j)f}A{rِ˄"4|ny4T0b} BllP]jKll[#|\^[CEW_{ˆ\ h[ x"wL}G:reEb7XԚEz[l7P{ek<[9wmj˅CEy](.N̛3O.0o<=j3^^^OV+p->. !!OrrP]Um(E}CEhK ^LOpwXt)̛3CuHtPӐ$IS[.]ڸqιgT//V%m8.d2iKJJ suw'%%ES[d9h:ګx{{ǥ=),,$;;9s`21b!Ϸ~KRR#G $$ɓ'$ImH3p@bƌٳGˣ( ,k,ݻ|6nHLL Eb%FPP%~\6mڄ,`ڵٳ7|!_ddf|bQ; AݯӠ7h111\s5q-7w^ƎPncݻb ؠ7p2'NM..mڦ6+XTp˥=EsQ @Gd#6SQRɠpd.m|dLFg ֗>ZYbw[&{Z0).)`2220( w^gHOrrrm~o!=.U$&L@vv6#Fo'::M694???@; 1t?8ra&55Wt}. g({i8(&ePLAVd -jX=#LTWk 6:\/[.u>‡GZ uܿ>? @yy9%Ōw~`J p=ϯ-H0dbcc5E ̨jKllG+3D4\(: ljma]msE0/aƆOa}H_[r.į4iKEI%gQ]^bX\snˤttF94I.3nwhz?@RROscǎ)L&~i239s&=Cz``ᢅmf%\̙3{h+Uo2\r%,\[coekSꨮn<kpDŽ;\umXmӞ⨸(].El 2wQx^:/ Q:|f\µ d p뭷:իcƌ`zdҎ\}{)-)YIعs'Co߾A}߻Wo?OdDF۹"$I.VNPpA h4b4tt6/57Tؚ֭[] _X,ZPSSCUU7|Ò܇]6*ɔ>=VHT&8,~\[!yc.p(Ϭ(L/DrI;0-g9% IDATM=ш`@ףUb<6߾}/d2 >pok\}Mm`'l8{Wu0Ls=.;`04zݑW_{ˆ\Ƽ9=fޜyg-<+e(۔e3xmn;}x!QDwk+:x=7RĆ x]&3dINNfkV:[Sr4( g\=Xť9+ٵkPǻ޽{y;5\t)̛3CydQI4eҥnMqTiΏnsT"7G޽Gttty!##[o{ٳgJb24%%%s纻JAhjKg\AWgtYE+˗/'88%K?!>&$$8(kK?IHHH: wGi*Ro N^ok"#g$[neZ~ǻ c2HLL$!!U;nntN@3cJJ EEE$&&vZZ)Bq8a:tk>.ӧOW_QVVưaô0:Q% ~c…y<_c}H-ZDrr2 ŵi=j`eYQ-,ZM5Tdd~xDt17_!z߬-;rNfZ%U}]b H3͊=⇹e۷;-3'%DDDc0>('N_rG΀0 $&&2o<-[ƒ%K上r~q5YV>AOC9-hxQm]bG|@s2v7!+8h2w/v?؏jvg1bg4;%7!Ia"""ؚ?_?yصkcƌqw5\ٷo_vK/s=eC.#11Eyo\\ $$$0Va'2%255k=o&Ǐo3H̞ӵ`t:Eьb65~Άjiι4ܹ̏jDI44vf+e|2'Wpv@-ϣuE$VXAZZL8?O 0@˓;Czz:?<7x#Bz)Ϝ9s !$${>,>, l6GfnV׿j+ˆ#=z4ׯ'88[;V~~>%<9Xr%Ceڵ=c.\+0 ,Y|n>>c.s"?ګ./^kժ-5k8smQ}z6,X HJJb̙F &ŋHLL8%={ޗt'sG3[f#&2}t Og879'ьG]fU65T⦧b #s!K%ʎW1p|V~];-DFX%^=,H+@B[y׋?dq2~ 1pG N=a/4C֕gkH8_.wVw e(2 d0zg ߖ%' y>/2ӧOgر[tK͛l63uTBBBضmdee /Exx8QQQ[yw"""\͸>|8֭cݺuXロ{w xKe߾}L<X1d 6m>^z)+V)qqqYtiTWFKmm-EEʣeSod ,gSYnf㟯>… yѣ,˭62$''ݹ[KKK)**A [B);^10[B@)eǫ97̿T3627rOꍟwC(8x[nٳvcdذa_֭[{n3=0Çq-8W&!%%2 ǡ=`ҤIA=0 6mڄ;V,fT1aeez gX,섥Âu*X9yQ@--*F9yx``vGe0<em}A=" Q}E`b$yfMs?.-?!qx_] +|c>,tn-wdv:{svíŗcLN>_upK⮷/]q8GHd<~`iI>ݡILLĹsa۷)))8~0Irr}o|ᇸq ޽qqq~UO& h40HIIA14d!5%uZ۹s'Avt{'W_E/uR87@|VįTHὙUc1j,וdb<.LEW&B"y)mkD8;ц+O܉6Gp_f|HNNƵ=ǏCV;̙30aZ[[]b ,^&L޽{brZjd2!;+g$peI7P$ZaX.bzRU3qIX:*k+.$H$u2iinAqq1nvxbJ_"뽣;~Ɔ ??}eG/#/:c2(eNci$#hnj>؄A1tmMh'?;QS ?(SPC8zwokw Et ʲ=VcfL~z4&}kՅ"1.Nܖ{a,Ja [`℉~~!%.3f7ڵ >͛'|>a455 CF}Nĸ<3x7`0e&ؿeOH%-Œ30}tl޼AO&hDm]el8V,ۥ', r9MnT2.u0aHRVׯDzeÇGss3y\-G= e8CmypO?+V ??z1c ??gϞ̙3?(--O?O|q^s{!B^y5 6 ^dBFzV\Tj{Tfj|oF qM, CHEEFƲeː"TUU q1rzzmifԁUh3ulC+z _a E# 9m ^>:p3`Η8 +f_-XʓHrWD#:A?} |eŷ ?ӄ7w-X\BK51> }mh;mhʼn~CBm vOa|3fpjINNƜ9sԕY?R LH#ط ك;8"&&JReY!m@ҦkjԔT?玎ΝlƲe_s lwo vZ ZXDGGCPardQPP#Gq8x `…xǡP(V֊ `߾}zs5XNތ!bM) 3`8m6$oWi:ց ]<& Jr_-ccy4 b%dL 8ϝS[aٺX_NOe7->oRJRtܙ)b&\k\s 劉'rܧ&L\>丮ߴ?@Ҧgϙ9juV;]XNT{3p^9p<' 4CQrYj7ԇ؈ xW0dX,X,Qq>@OD 95A:9+^x12D1q$r`dfsG"'L'jHl]UؿKOuY _ &I{M74PiT]Q&P7Jq6xE5RyZf`0O5j+.rA'LڠwdJd@Ҧ׮] Vl`ݺu+=-0 }..S$Ȅ\b;"rdQ[CZmaYjNVjJ*ːr2!))Oi7"",ZNرcyf :ԫ aժU=aw3Ľl`gPss ,+-΅":t(Z-PVV#GRBI#DF 68-qLbt6Vե£@aW "{BP[.jhogXޣCEgTq $mH$%%AahllNj~7H4gēXNskBm]--8VΝ˽rZxj,$Ai73 u jjz:cř@FSƒ L&Q[f%%^޸Z4>R!z<lP\\XUtaa2/DmIJJBaa_f >>^XrHTWW%`1͕Fx|oG"jJiajQ\\˗{uǤ)..dBYYY@2zd t:L&L!_ϠK; ص(!X[VToMl6 jKnnW3x))s>]&H=sEv\hV[iDu"K-HAQ[: csi,ZȯHǷ~MjԺ|9y\h %XjӹHMI`Ju 2@Lx⺉l`@jJjg 4dijԈy|#9#40(+/CvV6PZZ.vD Dm)+\l3YPBPUHtNN8/k׮Y,Aut(*CԂ?TT[W+-X{3qj{U?/h@ 'mJ[JYr~RƒݻC{Z WtzZ:4 jjjPWW>ŠOƵhFPM2B"8"FVFOaĈuƛN{H|}Dclق`9BͿeiӺ]iq!3WtIi O- |;t3l6#++ StJKK;I'jAUUVVP(pM7~ٳg)S6 V6 ,i m/R0n {}>0w~:qrr``9dnՍ^T 㺆dR$J6 6&a,K0B< ( ɡT*ETBP@PCF2A 7IVCyy96oތvTW=p/8^׸ q8uqFXh<ڷK S䠼<(A ndR8)' IDATX99zD?bE켒!@boKd29T$evOIP8"Z @BǫP(K *^xpfѢE1cO͛7{?ۣܿ\XjL>66T42},Mzto#UYų< Xb 5|E.7L 9ooR !Jv u]<4HeR{#;v~=Ur\d2 pB!&aLD =!v\Ey!K8I...NCFzFR:NB_ń7Cw UlJ7{?ea:݀ƣ0hHXVCqQUH9<76?Jyqu]3Xl[*lFQQ  5%ڌq y^\_[fh1}QIXfVk֬A^ur'* N M F-2Lxϥ*' 0{໕UL䊞Ҧb==e˖a{nU ~9-/K<.:im){/HN_3>–xۅaw9-J"8{,~^h՘3;2AܩO+9)g9DD.k,L"Fŕ)n FTJ|rFU!8.s(Dž(+Y!mOp6eTVV"** =+S:nxf^ ݹ v0p<ŗՌ`:]l@rXL]h7 m7q=>,__gh1uax*-ֆr$&&bΜ9.ٿ?r2zwX j& LV_qmۊs[p\89l.EPy9<%\Ξq$ŷjӗ]vx L8 `Xr%֭[apkǺs?z /߈C&g08k>ڠG'V3wj}6_wa;g`ҥpgE1pV\mt^ٳW̞2iE }g-tVy\_!ieeeXx1?{ի1sL[f̘!nU>*n_;~PSP}=PY0ck7?_LnWZ&ni5|4]"r~58?py&hmmŻヒÇO뭷g7h88>6mDž@\II3`Z"Eaڴi뮻b]\=o ۆc}w.uSbe` ⒆@ ~w4?"Gۑ9Y;0dX ^'ĸ}&F^= vm\)Px6Iټy3Yg,bxo/v8.@xq">Iڨǵ᪉9b[xvp4 }`Ep| T G% O߇9+DT`-[p%`ܸqظq#J֔34h6n܈)SC S^PX-66efd l?ŗچ8k׍'?+qI9I$MEL¯'B۹߄Ln MMM8W#<JvN mJkQ(YȚ]mmm8{,}Qرa[66H'uw5PT̝;O=T*/P(O 2Pa+WĄ SO@g2l:::  qJ8@ ŏ[ =.MqPd8.QTAMM0TDc[(yS(ѣGqM7aǎBp.LF-Dwtt 77 X~=*TTTeil(B ˲xgp-ٳX``)J$_1> ,(`l߾vʕ+>(F!̖KPP(n؈[o8;w+@cc#N4f_Ph:UK[sPӦMÏ?cΜ9#<3g;QؿKKD6qP`ڵHNNq=З0bz-C9U=C56)fbF}ݢ3JW+o{2Yp&|_h塼7nD^^^ǍGQm0 ؼy3z=rssuPŅB9Om]-͛7cر8u-Z ˲Xj$&&bܹݎ 8qՏ@q]5oҷY\k2m `65bkì>hp>;M8pmv?K߶;s=dҷYW< ֛PVRmeh3e0w44 j×xNhqײmPck o>CHLLij<۫ӧQVf_xRbС6V{SE) qyZ@eY\0Lطo m^xgΞŋ!w, [AÐ0, ch~0/pqk0c69K=Nl„y/C̠aݎDⶇ P(5~~> h9{v3Μn 1bL&!qC6o\c2@ImoKXt)Μ=]0 E0LpH~Q6ŧ"R0\O /+x{PӧOǁEzZmf!z̙hkkM>;!&qZ$l~n#ty _q+m3mh'6[AE)h:⇦Z6A}#yAOuΜ9xWvZF\31$[c?2k2qA.|#U.? ǿ܎?3v4w7oC;pv_4 Ǿ<\~W_JM^'迯3 ]w݅"!M4Y8ZXx1t: CXd vfb`ݺuxr;Fۅȑ#CR 퉳c  WWl6l6XVXV(,,h4~-J(!^o駟Ƃ ps^\9;g đ#G0zh\z8t҂sl6 z_}n \;V߁+3 0L|近6_Rv3vW8 'OT10;p&(5I _|$kp8\?i|h1 fgN~UQga *`:v8ïĨ/kPUUxjj@RkACC)VeYTTT@d2!5%ee}*] Y.<¶3`+g#<L&Xq>WN ˲`YK^9s,Ŏlƍ' a0 rW-odرc F|8qb˂tHz8~geϞ=;hP]{lFQQ  5%U۫B* XV3˲,ڐAXٓ vq)A܊v:49FDEEaҡ,,ˢx7+V@^^₞~E7C^^VXX~=,ZV u6=gO=%!^Cgg':;;E*?~ >523} $l6Xmn"*t^7H'Ҷ3K7<σ9ظ./ZidٸJL۱ڲj*~*#%uGu+#Ip|s% _:}f s)\mG:+(;͹'***'o/G aj5z=t:]U1fw-j5~YIܡq8⪦^E 'DJ wsO%))aPO 2@PXX(P]$oGvV6F%OA-_~GKO'N"׻Ҝ{& eee>P"azhZcA-Od UVB鎽JL\Pe`Ҝ{l6 jKnnOǢN<ř\t:t:,YaHB2jj`cժUn4@q8‹KwS] ]Ls"^ynC "|.* ; x\硭`KP =#uB ;͹'f3 RSR}V[9JheŚ`0xh D#hp. u}h:Oj᭺wsopYo-z|\AY}]PS]Ĉ@2L^DzlԨ93ƺx4%gTsO j _diᘅy {Jok mڊ.>b\hp.|@"ԖeBjc5f̆dF;p|IzΜ벖!;+K :0+9{ @z1|F=~;^~e N창sC$iPN1.4瞨hD&Du1 YḚaÄ 9G!5%<}Q!VUD >`dP@9Q[WW aIOK_l߾//_~V´iӄ14p|7a~+3%42e 2)$Rpgt`0"/ (B(NOA*?ƅB 4`YVP[VZq$D"L&T*ŤIah%xC7*/aҌ_lBG+'OLoJT IDAT*L*6Lj* h8R{$C"H hp.Aqqu "3䬮H%RaJP*͛{潂 @R \& KQV KEEEH9}GE *t\O!BaYO=VH, ؾ-`\cv:ٳ~-=9~E*⪫¤Ia#RS]Ns{APE*bt^fUhqHoӸqIs L&zm"BJ͆I&aZlmJz TP@T"** J  aVNuCpHe{O~_>d{DmIJJBaaǑJ8!E&7RpRۈߣKhB-q\ bbbRR #TˆP]CEn<.A( DEE [ZZh"ٳ ›old wa2PVVsc "!* ke i+kq$t!U< HJ(T*k!E0 ^Vb,_܏߮qoP= eىvʕ+ԄI&aӦMi00P)N PJP(t*r`NLq~hB!#arssdɒF7z"8Z)Έ7BիWc+Wb…P*A.U CmJy^p^)}x83`Y6 69ƅ2)dRFB\Xu)**Bii &Tq8' U\(}{n=/"RSS]00 HMIB CAbGD"@&RaX*&9y&$GB\9#JK_-5/`UBu UDq_4z<3g~a$%%(nCԖrc[bE#H u":Q!Ns'&%BbET8< *֭[#GbҤIk7o?Ʊcpw#.K h4"F$idɕBBb༈ Q]F#jjCZu1.g~hiCGa0] K #[N!ax F<$ AE\Y#'l2L4 'N^ܹs*>ctM R)"Dm^vD"D\H@.uR a8Pq9ٯ! 7z߮Eh܎V~!ա}!Z駟:8$~?AW\Dj /ą^^$niꔩҒʳ SBdg`ꔩ8Z48TJ{M{H !@C:+gU\(}*X-J"V]+ NRؽ*.@\Bӆ2c\(ÇPLڒOJđ| U]*0Bq&* F&ᠤ,+-V ri(aժU0 iqf' <9iR|]˫`DS(Ԗp^ׅB Z|7ԣ"pnWFMl=).[B 0,Bj %!u\Ӂe oxبQs0g%`3:Q8 )P|C* =& z-GVCCӡ- jy!RIq!GГJ.48%)) . a0鐛."~9v\v %d2,?azZX|yx'fQ{ z?fYP[rss] %")) :f9(epg0QsJKd׵BpU"=$H%RBq _gc OB)0l6`0 5%-Knn.RSRa0q5z֔AԖrB0 2dgeEuSODŗJ:OYEVP[Bqs %diea /wA!!8fQJ1.\hBP"%K AuYR.DqP‚ںZFP("b4Q[W2 ` u%jK B -W!#=#Eh#&#Dž.H F jF zB 5ҡhBBuqLs %4׉* 9%:e*$%PBdg`ꔩ8Z4`ODK;xĄ'jK TmPz. 6/Ha'F7n,kŅaQ6O -J_E *4fQ! <.4օڒO ґjcu$R `zMɠt?uΜK`*i, j˪UV %Xj yZ=٥O 4ƅ'P***EVJ8VzTTT ]dpԨ9ݙ Ey&& ˲tBx gt:X 9RWkԨ9Y)Tq0LTmPoÈ#7`H%cǎlٲ%5ږ-[ӦMrZvyLa6%77ׯǦPHJJNl1ݹ'M8Q5jy{}a!J%+T L Lɓ'֮]JD---ڵkRz2***P(~.@׽_ٙ3g7-ේԂfl z<]yNe ffbb ---hnnl˖-X~7u<Ø1cX ** Jrr/ʋlF||,K rbPՈXDEGAT N&q)((`BdF~~>JKK}>qZ Ɓ8tvvwGg^lH/ĉ9s& dr2׎T*qP)ڊV LGG:::qX Jh#xe2RJBtt4bbb,::JCAT[W t e0*uqg<*tl-XY$ Q"yLux>σ3N9##gE&-JqP2pRiWd8P(a,u\RR +JyQ*P(P(G,ZRPV^(Jwː%KP]գL*'v+7pRc&%;-bU"@*RT{["ɄmܥWEYՅĺ#ttt!q\1(rU(v%JhDGGCR!**G| U[(/ՅE,˂Yf/0TtF<뺃"; 8 y:L1BWVB!&0 )4z "." Q]9c\2Lq Cm1h4>S(IOKFhDm]qZ@.wlJ0Roms,5Id{bE #er fOgcuY$Y$\+v\%{!D3\&N!⮤ P(}ϡY҉%}6s;Qi( ȤIe! 񒞴)n O4O$gPXBuC ]T"✐87NK TmPXu6V<}H$8NP[6ж``✵*(/>8- AlgEPq9yoj  #&j <q7aWvy_zˤ5W$v`` jK~~>uZ(| Uq@BdӢ)4KxӦq'Gvޞz8Wgs? P(KlH]͞J3Ls8qmB 'dBvΞ/WI촐***:-JpPՂRQQE|L2@i8g B޳lJ>89+.ze1tPL&P(A.III8}ϪXu'ߣ ,;ĵHq-U qk_+[EEL&z=uZ( V|R]\ GᣞA Ozj 6#mmWYE}ՊK\&qq A F bP*)Ҿd_zw w|/NsbuK8v #G"//χJ67΋[s*4ljW_EaaFQB(OcqyYr%t:2NPڵkj|rs5E]x'mg돢S€}5y<#\a ̹i!嶶K/ERRz=̙Cٌ7Bd2ĉz1,rLS(m  ڐAXٓoA UZͭرi'^ѯCәshllDTTT=iSd+VXFD $ׯ'|g}-R;RRu]ر꫻-tؿ?{9p Vv 92 111ؾ};ڐ>ZՊNtvvb㏱;o%6@Q2}]T1*ޏDfLǥOE,ގ̛7CSSUZ" ٌ1{VV*`!/BOW d:ł̝;{GL\t%imn30b۷ B0 kVsBcccBFll,233QSSvahOX,**QB QAYyQTTu.Naj U6@ qTJ<w@GGGzr?-nC 6{X,X,Y <<*]Y:!FIBXpf7@t}80Ό#θq\s#FD@;dGSN'tBBSOO:Tա{ΩwZ~ӐҥK47s}nu IҥK}8v54f9y|qG_v#TU\s^/^Wתkkk-:'t/ZWWf#,, ͦk|A:?䣏>j2_Z^nOE* lԓK}]=6Ma\6w)-.Mû n;7Ǟ{"]gV((nqQM$I RY=<($WQsnW}]{?S x9OiuH=Hhoh`Ф'*x-HKkɚCL8~rA:{'NlꢡbY\EB F"Ap0N9H:Q; O~ج'yxx[A8}^|E?|nt# Z#Hh73MUZb,},^޹F|GE0FGt8`Nj(.B 8EQYB+Z;`c2)9"]29~V#Ms"Jˍ !+.S,.B eܹbm.yܹsYtiHV@$EY\TEvuk˓O>y$B;OtR7Xd13"1.Bc|l mPSU$lRRuk[%݃꒞}w^@Eb\T(ԩhbbqٸn-ZEeѢE̜93d#")ھwF"6?~ŋE111,^Eٳ6Jp@ĸ4X\'WҧO}3*A4>nu{]];'"T6֥EK(M ݛ>3pq`:U '6ŋ3g RŸHpb2NKh3bɒ%ӇYfutsAhGf͚… Yd snD"4m37'I<,^X-Ѭ.-f!PHqU8N-[FRRX[0k,^x^~ed^EW+n؉*Z6??"=ҥKyꩧXd Ҫ(҄ĸq:KGzz:SLaǎVǔ)Sؼy3yfLnu:t)SpС3Z{#_>˗/'))'E0qDߗwo}/ns|,FDz߮v?رkO;щ\E晧rۥ,zOVVV=uTrj: [K/-#x[XVZY\ޒOّ z YǗ~ ]GH9PϿv'>6g{K>v4Z]BtT[RV^mΝ;9|p9sL]hNEEÇo:ڃԩS9|^޽{7o^ziIOOK/e޼y~y΄Hi[- ]wy@iҍ⢳!PO۝YH%ܱ|ِ 9zZɾٰ0/~RρQg^M]ϫaK)iRl6w|K{rv}GLL #Gd;k|嗤qWǴi4 @ZaÆ FaahȑL6r3f [d#dĉ,232:bz $++ ㏙8q"鯦+Z{*֬YĉyWs?Uuz:\#('Z@;$5z:ﻭZǥ1.Q:h `AIDAT-47AQ>fΜIFF)))~y ]feeqm7pyv̀xw9s&;v󨪊(AQrss)**bڵYBȠW^ǺuP{w>"^[ZMƫ %yuyGg6~ h?Hs駟nAL&~ifϞƍwO?DU UGqqC1(Ƴgk玉 {pE18Dl~K 0|l+뛍s}}ͶMj˙yYqyyرc_D?+HLLԕn f۠ʴYmR)))\~?~_]+rss?~|\)bژ0a;wԭ2SLi$Z/sAƌСC۽>A:?Ce̘18p?/^;kcgP{}YCb77_oKyIjģx1dBE!"Ξ"|M5<~v-#ZS/@IYy;EUUѨaXכ|==X𹚌遟KzjWͤIعs'#Gd„ 2u4@. ˌ VF{c?< .lzA,Z~K7)@n)îA佷)Hjp@%?VP]V;!X sdӱn5ҭ[|9E233>}:˗/ ;;ۭs:qu}Wf;mwAvv6NSwdff|rϟOEE;wg]Av[_]vY֮]K 6m۶\^[O?ĝwɐ!cA :ロ7x۷Ud 'ߖ0=)VJߎz:\s\ts^֮M ӯ?:ϧ O9=NWQ{h$;;3gbZΥ#U鱗|f̘I}:\tEdddyY~$i3@eUe# 11擑A}}=YY:V :4=AXX[NqA0<>zs8o;誾z\\ΰs0A \6Y˿xqʫYjWƇOɿ{nmKh3:5[ʊ+*+rq}PޤŴILL$--f#ӦMcݺu~;v?O[!ICf$ioӦMܹsC~fmM}}=555am Ajj*o&8.v !H~58"{4YLsq~c]X@,s.w;+>e77.-ڽVZ~Im"8BUU^/n[`. IUU,Z<=4ő#GpU64۪*Jҿ_fΜ oM|`ܸq~Grrryӿ_bbb:F׺nݝs0pF}߯?b׿&&&/==n;hmfUؼy3pnncXX,m^ M.x<]6r |ѣFcZx<,wOG7*= \ˍh Xlr;C M>xa^**~r1_tk_6`ΝDFFNDDv͆jji-.l|LL`6tXΩӚ*.o{ 111zAcNym]wEdT3 cqD95kftL6KMY?L# .M0sM]k,/.)ndvPYڕ6mZG7A.=V\[[ Nkqi /)HF+i7 maRFxxov\T 6(<ԿSCmA讘L&n \ǥ*A:?M !WʹBbXZEhDNWE; RHMM%..^xQ ǎh6Q9YF\Gk)(7uvޛKJ$W}Oc|o9soj:ZKƂ]!uztňX\Z>)(_}[TkG/BLe˷:krO!ql_z2oj sPs1\-*t0j̋X\:{oS|:x 30LYE߈^ܴ|3JHlt}#55 6]aRSSsԨQ\ROWUT>L}}=?Cu>H3f ..s6: BkQUWi fJdQUM8g((唒?Ŭ l:5NG{ ,>;ulޝ s޺u;%yZ)wR񠠰kA^6\nV%|PRRBqq1Q+V~z֭[;w.7ndH #++Kw5nf֮]˂ 0a ;RG="B'r-`ߓpm.}^_QQ~;8[((&0zo-^f>ێ1~c|lyo7(;u%^{ܔ|WNjbG ܶ2Qr)|vm;ƐPM_1~A (q^ɠqT??o¯_,KƨHޛ\G|^txIpy|ԓqAjM]Ug-f˅^իYm[75\Æ HJJ`z9ٔZ&t:>sx '33S/cƌ!ǭf ]@gqQv?dlq$vn6YMKG(+&=|2;j+5 bPTkɇa7ptB35pcR EUvC?~| }IW?`[nDƆsX>b# gR&xy?yopCM>'}PQJ*>{_-)h̘1^x^uRRRغu+ 77͛7ǘ1cqstt4cƌaϲ2elVz>cy$&&t(yꩧcƍ ozTn'wy/wcpq пkNƁPMowMu}8QQK~ێmq2P~Iu}PRm7s9۩'&ި'*jٲf_$vM9W\ʹ*HBII | SNeӦM\.{1.o)ٰa/Wf͚55> w3=wCly饗PU!CǸqx;wd׮]X-GJ1]wڵkYp!111$&&v*W-t\AɌl WWe+}@S+}rdD@/8uUUᬋ" g犃Tt1hNzOgM i1 v@AW}`Fm#-QQQ+PW!vgU>KˠQ}A nɶk܆7* &ْbݫ`>5CnM% Ր6lR?(_"Y=?oeԝDڹ|vg4!>VtEۯqьKD;LjQUU^/nzjjjp\\.bQTT.K_q+BF 8x G֗w\GYZZJ|||eYYY\}Z=چPp8nʇ}HlXq8DEEa۱X,,KS3&EvSWWux~3#voOEQ-gsX~8]5C>7p]^N&l;Cu"-D5Y׭FQ&b55i&0e"qBu6\pX%iܙK:eH` ts =YN/A45CD Y&s# ]K phBHa ЉWAWHu\ZX\A6U$Z!N`qMA%^A`IH9Kδ1H)GGzSdd.0+!ZH2bm9|H}¨B Aw֐YEB YEBUA#& e 1`V2'bI%%^8HpJ&B4,!ZM,I#ʋ Ma6QLFeE4glAAS q A5d2x = ©0Z"SNrp8!W*t~\.999 88k t?L&-<'{!P)~}50M~M&W\q6mBUU9ѱi&n쳴)B r0c u FW; 2͘-f, Ǐ`ٲeYŚ5kXl'Nlo".#Aӧê* svy;^ja=?`IKK&GRWWG]]'Nrt:cӦMZ5}]wcʔ)DGG **v;V*AAx^Evz<Ea<TpBnV+60l66  MvĉTtQ]]fee_+Y] %eȑ?8""#b"=xū( ]l8hwرL>0,V VKpl6()kjj˥[`jkkv+!tn&^łfnNdd$QQQ~Gdd$vOC"A9ۺ:{׬-Vզ+.%d ~f3l1vEFꍲlnQ\Zg2ͺ&lunFXXy$=|-(fE'F (fT$t}JQy5L- 'K,'TBm2;蚔t). .ΎrR ).ᄇVlFQ.h}ŌU]xQ$ucAmV1෥Ţ|E!,,Lu\槸~f")F.Ҕ"!A UUul X[7᝛h=d6ub%-jVq1vPm47Un[WQod1V5ͲfTZ4FASZVQѽޱdӂZ,V]-;!<<\sbT\oH\wjٰX-zb7 \Z_-R1+x/ou׬4BZ,f,f_HQii)!4!uJ=lj `9B4EhʊY;t5A?F`2PEf],Z5| U6жs3%D3"B,<<@.6fG!04!BA&XTVΨoձ@ G& VQXG\9 Qv eTLg#͉}@ vD̨U5[q/m],p*]N ЅF;Ӡ~y*IdZcr#d`xBdBID[{vR̬6khojcںAճj8[X%WDЅFI/08@ر<+9;"-i:׳Lt"Eʪ hFy6"qet>+#g>MPh +3mD l^¶Xmel.L]C]Hyvvh.>[;"7Pf6b B]".X#"af*jQA,R]E (?c*WW||Gjd/(4!$,O/k k'Vquu#s| DSA_"E4s;_:K>y.t7>^h-?.~>ĐQSn3QYQF.]Yꈍh4{D]G]Xc_R\%pN^oJ0?]g1ZS_WlE2%]CDa(\vL6Oee9e%l6a\xf9ƲڜLG˴v^]0D]hOC`y}|e'\y;/=r =3tVгOA8& @aPHUe9)PZ\@ Xᡧ<ʙ{@@Ps7d#{ǟα=қcwE\~7>{#{  K\/܍7!Wk8_~޻wO/b1oeZk} .XDYx(̌L)i~(_1h4.|nG hO eƢ7n}_1Q ِzdo?wcdWBpB"ciyYFǃ;v⿯O>ԕVGsgP3|yhfQ驼,ڤOe;30.7>©~ >!&z{/"E,NkCU{Ul\n}M))ya sNɪ>2zFoT5Y tvECmm輮~4vdf>(ϰ z>y?5sE"{ iB&_RO~Gpï3쒫8s(@޼>}/  mo=s^G]:?)PQ^7Z48w;Txq߼X@!hӗq*s{|4o~}`F=/~)_{,\sOV8`$˿\@viN 1y_$‰VVǬgg#:C,bLjTC ۾WolkG| K$ #/.5zyΞ:Κers)ġJ}/ B3ػmGok`PQVj >ys#iϡPQ^ 70b,zs^? ߾Ë?͝p&^s7{ \̩S  il&58PfcT**J >~)WI!m )ɺu,eֵ+()̣7wOq<֤>ѧ9ytq=P^Z̎+ٲnNQ~u5pLXơƓ4-)?_?_]Uܬ3&7 0Rb!"EtÇͣ8$p wט@4臥֠-yv5/ȫb/_1tMX덧䅻'qo_<*J TX`ԱqqCTV;镕so)+ݤnپdΆk}9l,#".X9Seb`PR7S C/-e׮bz{dǩL8u h4;bK2hT.> xxg8.=?1iC$ [אrh5D&0jMw)yp-O'z0H6Q! FxBì; OgoIY#1j@Xy-#L첫 mT:[ c)]1MglxYγ}ZDX噵%2"E٩A A,ݙ$n !Vޝ],".X:@ص<+9FCLl8"!"EޮhvyV"qZS`` uGky6r@ˆemk!"Et/@¶OFC-:"  i$Q/D8FC Y.".X~۱ 2s0OUFCYk3 bM_v[>Hގ5K& iC7D YA$no{y6b,w;%DX[{u6dvI{+n fciE\p'k]M#"C!"E4s(3j3+he8x(i*-A A,cCP4Lcmr A,b.]C!n L%HnU 1˽%gJ$8;ZtCDޢqa[ŵ9U_]0D]}-!  Ύ=pSg A,FlAѱ,C A, iC5\ݖn!"ELއN m*-474e6 1[r igTɗߍ``å8-YKӨw#".XĢa2z+MV 1X)|CVL 17)Ϊl(5O`T(9YڌȩnR>:Mg׶3є]tA A,bn5 XK7Ln2-\SSVSjtNPrcT%㋆/dI ٲ Nu؞;. C7Nil%! FJW$rc`/\w3/`cȮ_;+^m-2ֽ`sXՍ]>$+W{VS?+o;_XGC3,!3L^PeB{5BS`M\0D]HsKRƬ2yž'QkL70 &ENБl~݉ '!9Tx"룼2W+[r6qY[T I$Z]ͮ*<`.9x U*=ɘ(Q67kOw/>J~ez30d o{FpaH䵣+@+Џ%>ͣBPܿv}bhb 1:Swܷ6V^ǚՖqMuiƛ=9x ?]QWsaHGj 3bf7QSju5o'>ӧUT߹)s*w-8k5'ηܺkn"/ X]/.Sِ^,ĽQWӉc Qg,Jnx _ xu\,Oz7l7kÉc\g/RٵL]0Dx|FCoZӴOKK]/wiX!Cx{xyܭq]ף0>ܷ6N0'6f֧vaؿӻ%.$-g1% _-#09r9:y>j^M5MeѬI_IoW֍<Ǜ3A<~T S}~ٙ/F e 8ܤO.a1k ˖K3>R#W C׌wg\uߤ.fȯ\}[oG(^Gw?I*wϧ~}ޡ,:ZM}&G^_,j5uOc3M]w%8 rуhRAv՟P0kȪl4Sv ;SvLc - to70SGgow1>q\eSSwsn<--SKN3(ݘez{G6Fclb}_0p$JO2mxkli->MgR@E?9ZtyrYtzEBˣb\$fƭxɮ䫋1L^;ZNP> #;~XŸI$&}O3bfSaiWV?G[1[`g8̚~]H=k?-`{GJvQZUkԬ<ؚu$b4F:tK.krˬCFO#* ~O_edi g4]|1mI@>#10`sssR|\/:RKRP)T0F+NDJqua4=g\>[e 3\h)?=f|,vAxE;3ʃ uqt?]fg^U.^*/@I`ۥM cР[VXeTc5h4 f5 vsb}^@ʜ=s*=n~=8Vt+:BO1>q.:ht>L^e.WF]R(Vn=o{ioMb!"E]vE vזs0]׽8zπ8F1 )PyPTUhֳԑQf܉(*kDסu`kfn2~חilr/oTu<wjgSbɯ=3\~;K؃:#.".XDCo65`- ~G6\`:W+|Ju%e vMnyRƁZ BٻY>ʻ 9 />uˣti }F!!qm y  wgp-+5춯o]NUnU36ٝ#>fvO9C O؜=M$)&e1O|fsna%'xqFxkf]MiM %'N3~pE]Ƴ=o3ɯ[s6g[`<Ѱ|[s6SjF̊>AqaᱏXvvN}T4ۜl|t-:lUg^B:" FmYv']?4ḆOyK{/?og~a[\W2;v[(t 8vАgdם쐷~A,H ^ -/K#=7? IDAT  CS}׹Uw# .XDe}9hM^,4 YgĆj*m&.".X5+AMwJ[rg?DX{ -!8;:ܚ] /?W&ٱwhʚn A,bC. LC0٣k13 0˽9;Ù$Ipr ;Z[ 17;ÙIpr =;˴".4JF%6ʐIpvԚZF~8"E c-Q4Lc[ߍ``>-G&ٱ7K ]0D]ΚcLC\e%9  #mE{f!``~/47r 6{][{-Y䬴+?*3kny jK== A<6le#{kwDۺƎG1[^8A,Yv,lwCyfmhƵDkVo&a[1hή%l(Ph  0˽6h +hZFmKujZk`='Tmg#<i:fֆVvmF&BC]f;@ZI͸GCA;"mLv``4:8:" QAvLh re29m@Xm q授SFBA]hݠO'moabm/ Oq0 F'5n[ %_ӜL[huke}sٱ^ !!.X͎4x[1ʶο]CmAr(E|hݎCܯa7 9DЅ&^]2O6rFm}xnvf 4` rf20gK %DЅfttM"-u[[\@TRxd>itl^6֝pJ `H,$B.X`w4@Ye̼rkof Ӓow}Y.Ip,<]r_ElkAAh>" AAv  DA . tAAh B;رc?cժU:~ X #شiM/}:555f B;d׮]OBÃr1jkk1lΜ9CDDD*}AvJDDgϞ B0oÿ }a׮]mb G2a#wNAvJSZ]v;р]ޙ XGaa!;S[[k1Bח\.P(@hk+]zJJ+Z+MA81UPG7ۺOر/4 /,뛃+] \!T:.N@q2pys˽:kFMlTZmM-tڀԮؑ.΁VCyp, RČG6c0s|F/Y B떫9̽;WyXbFJG_VzU ɂڦx-JEi 0x!4 YuO$CD{(k}`Tl!xAWb-N-G26{7ʎ4ʭ F 99Ff4ʭIiҦ؝\qWU[[i= rJlǍr daQnm(_pф]wI\VLrȨ^DzU5^]aQqZA/,oeiSvBF9yT..Ԙ%!yKN(;2焜{ޅ喓&N- 'Sn(ˏ ٵOs] &{QCj{iSݍWhoS zKx((2m(hYAxv(42X9ʫ,h p nO=egX[TZ nwnxPr*0>ϻhOڞ1ZkTϭ+>=MWcy3ViV4DU"ٛ@妠eAtg ɠ;|=/;_KI&Vr֯a-^Co#1fRCWԛrh^uR)ͭ[C7/[߾vm7=y6弣Ĕ4pB|o8;I9w?f؃x>O%'JǾsa-[pQ1+ӋhY;=t{ xcGVwtE,Yú2=>^wwzyХ&ԄZm V//2U*{w&=X{ϞJ<8w={zXCug;gY:[;UAl OզES]fg:PFqzU' QܝVЭk4o _ ػ}7ΟӸePQ̌A|y4I>}x(aۼ{b0NL$--Ww.,Q۾*dm3'޽(*Ǘ_С˞=(- &RV̇vW+t}xRQL.nHfw_G**98yE>lڃ"mزy2E&Og޼N%qPyyIlޓ-r l`|Kб='&zzuWJJo_/ 8p cM㼞Jx3;֛$~1N@H KRRҏˑ#7y1h#1ND/z_̐{958&(΃΃}q߇;2Px/ҍ΃}qUQ]&h㧁1ڲ O&ƎE*ywp%bFt.J:ůڠxJ r״`Jxw6{76 @B\W\gzUcM%@22YEryHn5?c^R`(pァHK⪫0c }z77^ݕ+ 4BC]yp:.:֭ -pCyHΜϐ]W0sfqq ~j ^O?sLΝe ep-!TUi422yߥKc4ɟuytUYva^'9][DǎsOGVʜ9,Zk>BCFQV{0tbׄӖTހ='ysQi={uϞ2ʻ\NXWֲT0K~WMzPr·{ݪ=aQ\}u{%DGӗᮼB'kIN>@V6繬]ۍ/eD~G CYR,sس_w]odq(*x#򽼔,ZFaĈl߮m$.ٺE(/b{?ǎU뙬]ۍQ|3Ə+׉Z~E.kte?.ԟ,cV2} N8><{{+K!ՌˤIG1\rT,^ xz üyp;Ҍ&~eG&0~$˗oC|[nLz3}M_ًx<~#SS@.|C~>E76-ogca6tbn2?ڭdqZ1OXPYd&1Vݬ#^Bp?ƽw9RAʺB;2ky<]X@ Gɠ;|O8k&֋;H#9ً; seon-5sӧx,z*Sxl `LUNN o3D菹(3'jUyy5|}„ |UƾvyOj &0ܔwvYϟy]ٍ׋uQ9G;5ķIB_j5z$5SA&^&zٝf-9[Mjj4( 2vZ—S3 ?tڼ Etd2tUe>MīC`僩lz]MTL\pWa})8Y ESY\ˢq(ȸ}.U[B~z*>эğy7_/'k2mrtd}2MogO0(.~6_OџsUYT] {(q/w!e]!'Բ+0v^Ե;@s۷4u-\}wFʺBϏƻ++M៷2y7WO%oe=lz=Wo %xK.?oqix|OC (ރuϝbA]^?4Q -9l$ z+aly[!ޤWӭݺ SS?;`V VQY!)xDm"keb6GVrhh8P$%yѷ'}zүII^̎<4Fw(3{~2f {w#AOI4I{ܰ{6}FǎU; #G6ja~}zYm|,: J5Iuj~j$$$xqŘS\\KxUi oOo ,Ɨ$$xxqQ^fR}7[o ϯֆ;d]\l-R,sN{nmez/+"B$IL SCfNNkW=U~>ǛϼQC0?Mxd3ep_zL Jj9r9cUabGŀ[B EX_oB'vugQvj@ &tMi .[3p^AW[m]\̞DmOZ5낹 n={1 cN=<~}qi"5AKj憩]\^+ɍs]|v]|Mɓ;u2?S'ˢj ђA{aa9Sd` o""\nȩSNٖ-wwch#* kYqo \pw84Wpď L N+"}Ur OUtH˃61W`2xA RMGlдs}R W/펫Zl-a+m,b1t7m+,ċku;SGjd2 R+:>h7O ?;ڟwDRBy2c IDATpm wѠaB Gd,eS^,_O?lAlmVhԂ˹9*n>gYSaJcT_A;bT]Pymx@7I ueժ"-3FFF5\ucg(-XCz[/زDO5#1-4țŋ+̙A,Zodwtʕ3;urU*>~Vk]O+}y chgm?p,dz+@ю(LoΝepC0#G˔N\xҏ` /ذxB[?'O\N n>?7pxE>?9FivhҠ;[+TMu*e߷96\x' セfߗw%W} œrteAۑS(!dce,x#\ǐ{ v'f?j_[X vta}.Sslu]{doܼU&qnR9X}oU%,H"!:uydWPYMʟ`f*p7Tn J͇͓b r1Yw;-`D:G|]j `@;S!v.g2T&밯*#&x$M;Gav@FeÆP5XQtS}y |Pҥzhə?1NGvv ~~*lZʆ l7lt5ʗG+*j+Wrh%7l4?]k}|rrj\y)ۈZ{k+ )*婧v݃2|E7_ł{dg0^MfE]MF֨ Ӎˎ\=o:\=ܺ9v'r[~tde>; @H~޸8ÿyH7O>ף7}ek4t4d}R5 ܽ'uOEv_ʼn? ӷPQPC tȱF^pNoTn R*29Phג;QAeq-1#L!U_wc\W~ҵߖ挝ZAO:$.^nHRJH4}!<ҟXshs-}EѝVЍMe:ĉ6SmKKrrjX+KQ݋??2$/votfٲ86jndlpX-=> go_O^=MJؾݻˉvcǎ̛׉?u׏-TP㉬ZյA;>,U2~$+WveѢhrsk1]ݳZ 7wgɒ> }ϋ 3{ ǏWl؝W_bTWk=;EJO?Ax+۷pa4۶0;2^޽xs<;wS㏟я`8;KvR"ہ-qF>ƬT)N$yzu25a}Hpk=crѹ{kD3מ{z͡8-ůz)|gtUgV* jHI F 'e*d< P3vn:>Lx-Z_e֐_>W S>0?HGm;wW!s(wƞrOc6$\Ͷ OUsQ2((aX/5uϝJø7nzD7K@;ih9 8;ئM/KX㫯>=tz+ ah7V* z䥗" Ҫxt>$3g꼢֯/njﯠCb5ݫWt瞋`@oBC]ؿ/Au 2wn'F;o_Ms2n-}=I;FNPhgSXh굟g gp_f2~:O?;99{2Г^Ť[k#0@{3h$ø?n%*??/5YMfMce &O''G&oy†Wΐ8#pEY 獺ZêRJ4AMrIg:3Hrӡ'JeǧM}il87!<9<]$\L`ىe4cI"9\NHwO\<)F^5,5!d+9X[@˃Pj8Vǘ^7M jk4tD];흤?;MiDk;{q/gF$@˃Ocot>d"ST΋=Id*SI`ŵ, n=N@wF_$o $g,Mw)tQ(Pf){A '[T܊8@DYLQ{2:(ҽwH&m4Ir'{{{WcGoSuYly.OAxr'+[K>$ gj˾T!HrsYon̂i+M"˨QUش)ߏC.j刕'35%XfX՚Ez8Hv4TYc_G%ϖ3| t +9\ZCF|gbjő"˗8NY\ltGҽl.xޭpŹTnUx,_$VZzk'vD2c5bg7}PkdPŚ3}ɵ qVSpeĤ:pp%yA{j8 ȁ\JҪ 󉺔˵-m)ٙ[\li> +`2ն͵}goS N/UQfBr\i4R^nTm@Rx67_ݨЁ&c܉<%/Y-h4;+pay4]4Y x7uي[\ZхN~A^|-VXk"?WƲ)B ,B#S52g*tMYzXs"-mK_Xnmkϭ[YZ'9@P1nrh.~nU2IQe',`( ldXeL UEgbt@ц E%U2bT0AB xR4CL*N3t@ K[,J:O `5HDŏn*>*t -~4G Or,90y4n.wsڶ&c4G&$Iqi&rg% ] ( ,@`hkkf-WL~c.5,pHB/TJ b]em[3J 8rnلq% MHΌ&;CwRHtU^s>)R:yXg E~!Dwr׎'{+D+&]<\7&/XB JOT4hA f"IA9͐$$\UFʿ{5 \,P2em4*^pP1*\h6YenL1(ܿ@^fʵpZ=,$TVixbcGԑ*f5tWAAB)c|Njt~:#J3:bzc_;U$DXʦ wj~s'SyP.N11@YLuwݺ .U3ajreP6bP@9(e$r{wN?t4Y'3 ,pLH`N_3#-jF莓-|: S*Z22qv+nЀ,7#T߷Ylw^2B ̊| Γ3DAj$CZ6d)ML+mFY0dyRHƸ8؀M ˍ:GJV4.M3qt,3VZ+6Q&P 8U@eI 2mhedg䂗NTF#wGv1]2BʅXo(*cenвtDF.[9l=&'*mB%q]315v/( =_(,yjXkŒlAPz*ܼ+\t TR"10kYi~d߾6Wg\C2Spսh,Gcz>g1R&iժEY6 |*g 2/d_PGr*5ԿI~Cºe_gCڏ)fƁ]~+ٱqhFzgSqmCJe[_iݩ/ѬcP)HCbP{q֔3W\Ξ-wpB&ɾmll>G B*ZLlsǪWBܜ߫mKB\NFz NnZ+'';;{G/HOKUu ;{Gk7HN-.*2}ڮV(:BFc<#9sduqlY>ZuQ^^.pqٕLn^:;7FgRHC"U 6u4Wu13#xzUƖXn_?GuzTX#@.a'ᷯFfHUcnq{G'j ͣcSSrxzUnXYiV YݾBFZ 7Q3@b"JZJ".nԨj>%ICyU=аZ:Mu{AOAp C|j_6#0BAbQWуP:E^nGv#%16m3*dgʰ@z=5=j\p DB`NL|kU>y7';7.DFz-~{K#=2b"׏_խ*䅹ߩ(_`joʨsjxRhչ?/ڊD"!7'W|͞ͿD*ŷf]u¤)` pV!7LΞV1ԪD%}D ~9[WOo-to~r8l "!//D_~RGowxj2vLO1y|ZXll:5^cqJkf  <{5+m[W*=lǾʥ9 ќ]=ύ<1SL69MU0u4m]/"Zێ;|[O_-ewsl\z1p^>y}9ٙjik횓kfg);{2sb"1%I 1ڑ\90+89j|BN3SnXg IDATf~ @羣xMDZ4DSwލ?bL+Ζ(/[A.ҐDچ?DqQ>-'-:_MJO<We0\JS`n+~-N7>/;lL4 n~XTjR'ow IRznQȝgquƋo;o~d%b\1gu8vQSQaIݺ+ˌErAZw[TBn^`熥*!0r\z LaSoT*^~L%>YIM^#e"qqʳ3hQϽi6Ji WbkÔ)3?ǽaLSpXʲ("ӷ}EN}ǐEvvU^?m{zxרƎfz+GE)]lepw[t: [Rbu?DLY>*ur2iv=aZ! gCo_F_JٗTkѡZ/:ݻs ")lT_FˑH:a J0q?c-<@)4bEjeC>|| Mdge*5trQ`^oMn|γDԩ\=Qƽ>ƾZ~m-ϐtQ\qtT{^?ý9t7](,]<^U3.UTvu>9!/W%*jVqQd%TPAMݫt݋J~Lru0KP}Nآ1mBl^5xmŌ5*qQ;{NCyHV4hڑ7>[HQN^nNҩlmk P6Rt)SbueInb&糆iLé[wZT9bgxa[VVz;H?m)PX )4ȋkckG^n99YjJV9_vFna\ WܾvFc\NB#dڜQ~"]e{wj w,mq r/bط已tPEHl=(!&Rj ? &TH\UkeEVW t(PE[jD"ӷ,A-(1 UN}X%`|Sc lQ3]DG5NjU gE)tԬ|j#)>,xHO1/ƫEd4ٙح8چ}TyX]2yƒ"c#|zM[ڮVJ P4"~U|w?LNMƱ`+'HITY_cK$Tm'7'3WU(o} lL~7*ܓ9/qQ8dG~u+̳{oJ:wp&>!gmax0^t2Yeflu>pEʀ{s5#x4i xM}Giժ  Sz)J*$Bnޝ+/6G/ 7'kgqT2~z;е t!n]A]^ܜlvo\WAs׃ӯRVr9H% i>XYېˁm+6h2pasU<\ft+dbmctr9k5-ߤ= ?i]Ӓt <}k؊ScߢyL{nHѯvl\)iӗNJb,_9KCi}('a!Vj}dQdemFb#e5>*NjPF=XFfϖm{֓wϠ5fv"G`_y*YCT5c_)j`6Ks41hܹr;dzEk>=%#2dl2Ғٽgcgh0$jy9}$m̌Tf O!Spb%$):Z Z Cѯc"2^;C^#9{e&m{aB#!!d7>xmg/PQ+V6v~9ٙXYYF}t;E.GE1l\pv^P[:ZHgu5^KJWY^ MhS7>w8Z ZhL׶NY\.geLxK6{ٹG\xrv}qz< MOs^ 5ѹR\LOMbӁ5[Dml:ie\2`tb 4hY~>wi~ƶ%?bgڰ3Ұyg.Cvc^C.6s]{'nܾv*^~Z |zٙDE!??- &(&vNTjMnn`t4D©r<hFGw,->3' *_yǫo>SQk‚ϳl* 6 nT0k~|Kz΃hgASa7 x4vmI~=xx`,r:y\9W9?)\ֶ6=Ԥ8v]Ďg!NUxA"ٽ/M!??%LSsghҮ/29+\;MTNYω=U:z0q:uҢiJ2]1֚tz9]L({9yNQR1/Q.dpiz Fʹ3Gȡm9*57o ,/zˀ\^A*K/RQX@K$'D[0t\\x)=lyX@hy"nMP^ <ru,5 yH\ܪ__SqJ9ܫ3M|}dxԶ&Nns[@̀RB*U 9lt}T[(16A^5'W<=7nӛ1|J~ g qTwo ݓҺPEf̠)ܻب{q-UqI>yn[Nݫ KoR' ' aAt_xw' t\j7hAO]">V]ٱ;WHOM»F{Qˮ*b7dӜڿ D?w ZvDnC5iׇп+shTzbhAZ{v ǶsNcmm_qe6 ,9.6.i(Q`h`'Ņӕ(!2#}'5ˎ̦_㟕?Zt\%wVU59C}1:e`kܲMIhi\lݟ3paMUW,YǾ*5o-o_z9ݟsOJ/_c$y)bjgq YEWϫCU,-!|[Vc [Ʋ|Vr q3Қ}@%X;)]ӵl <)mL ~4d3#wQ\9bn<ڼo)^7:5.R32X>,0"RTMqX } c>mO,sE0)Bi~byx$ H񌃋8&u YCUڔG2̠h]R)ХX'j ~VԿ~ ]ywO'.aHA56өD׳-Hﰡb跣 V$ŢL^2t{L5r }9H.Ab6n<,[z:~Y4%"qE"g1\χ fXh㚲љ'[z)|Q$&df at.WzcU ڗe$ه$qK>mR*t(򲕨cXnXJz'WdUr|Βڸ(8ҀBr7+LTRFЋ'a`.*7-T.6nزT9#zn.n"#1>}9ZP3u]gYgD/-%ʰ|goH?a 10'2Hh{h+Gcz>g)cVe[Ql::ZDew,Xie|mg袍Y6^v胳>yDd*Z "`&ʶpĖ0>4c ]N6@T,E (/jM!(㦯? 9?04맂y"Oc4UZmٺA%%T$m 4+߭mD].g.A.M#;zQJ7fr/+b.tۘy0/SOh>c .tMsdL1~l:hVXschd&1©X1o؛п$fKT'fZ%,e6bҳu}NNO@^ҚS"c&OH4L2C5b}Ih\^'{  E/Ag\:oU˃@ t4r0t5y{u%ItҠu{yO=3tfуY W9L峝^>Bc+@9]mx0Z݂ۗ-ڀhn&]#yʿUZcSM )u`_[+Uo1 'cIK SwYmGÛ^SVjkDVkTU9ΈZb͛MeNX#GN\V,cH/~w.&WBm P^ to+ gr7Px>}BZv)ɵ)|h>ƠI<_rpd2xIIbZWiFr<\G5jT[6nx{,O][٫]&VRwv"-+ʳXQߵ|jXI*ދXKʶ=$Vv(3))ަm -,MȨcL6M?"*!F7mg#!<-n}J ' (xJQ|/ ЋvvΌb fJbWWbhZ KU,@9IDAT'6qY 1Tv:rnH0wGFseh(GF\X1D<ȵa\FjSĪ.Xތrg#.>ukC-&UsNk<>R_%3u&3..=O{:sCt[| !w1$L ʍ͙!7. /[/"f\ݛ)|-rCq32mUv9̑~g ͪd|v1Q_eڿmeFd]ȑ38 fpb2= ]B&/5&(HZlUcC0JtŃ>4UtgZn\3ū;[z!'?/}4t dVx|P(vR;[Ԗ,Jb̈́)j<O{/Fϙz'Oo~h)e#h6P束ElV =}21`*eG 2#`'cEH%RE֝wF< ?Mm ro;#) h.y>JƐڑ[~9o}Mȋ3/IfͲ?ls^'R7#>;Vm!l-:<]ViNh]=ϑ۟=pGXZУ}*iܛү`6G+?3lXOlV|c ZDW>O;/bKF2wѴږH)jy&xua/1pzACfdrM4#t "$Kn-"&+Z@ [ q\J8φ5d˲iy,ǟQf$4IRN"=JW$$Rk2.$DL .ӆ*V \ht'4.>vbɜ=UHmXvgդK|j!}+p ¶3hڰl챝1':wFTS˹+.e֙Rb_\|Ck3;ʴv^tMC\J8Ěo @}]xYxav9W`>*eVF]m{:8kmaB\z^]'+_1~|fܧ6Afӽ\Nm~ rlJR[St$H#hAyk#m\g+lUW^*jqXy){T 8r&$Nl)']!Ƚs/=ApvAA|SEχ->ڙTFA0~g2$<-~1[ ]ɬ3/)ǖm8X;0'=)ckORt쌻m540~C'l鵛vڑL]tE}v\0!` 3g8sTvV\1)F,/Op]6(![->GU!ABH콙VU7sq#ZA:Q3tݼ{*CR}zң Q{9Mc~Ч'z,0h SЫUډTgg/Q8._4pm6 |qc2eP$xr-Cpu2M+2 \Ic :xv&-(s1Gz>urooNNw2)Je wH@Cf Y½0QKW?CV8( k=Ý[;UBCL.&by1( ׄ*#bx/- NA<鳧JA5&t%uf/&L.6?b'S=֞Ⱥ?7&wY΄)?5Y+o{L30@jlt&HS5Gpi67TxV>mê.J ם&Ԧ?WaPg2UHj踣)70p_7sxɻ,.O/G-~oeͤ+\coͤk4V65&wv: \F:ˊeAwU屵' alDg=bR민ԮG ݟ C/C93pEHID&lんsk8c%"!;ai!eP{jɗPzJn2-HMa#(gD2ᰞ$¹Br|$^BD̻o{ԞE@Uj&am\(ZB {B/-TGx9hTq>9 rfǟJbEK=|3#MBS24ȕ2 Ҙt7ξc,V,G&q3:=} +e-$b^qq M;vg1>pZyQ9>scч a]j~ qde+&fAoeL;1A9.ȑ۝\0)D*28DamjBto-ٲlQU蝼Ta/ת$#Wi~>Ug5\3xy3*4B~,;)puS^0_ AHȉx?1'jv*c2vylՕ1Gx$dr(SCPO^{``^VJ.Z>}:Pǥ.7ӴJ l\J8qGF\?wY}8^BǝQ|y>֏6zG[W1hVbeHS5G `Hm.Ɵfu6l5뽠(&+Z@KIdǃ?Uq>^c okM>+ڟ=Ch2_Ԋ1B0-&}^%֭^Ma};֠㎦jbKBCA!?G z5Q;*\w'T7kBV04 .0SOdK_o'&p0>r\g,,pkƀUA쑵2Xu  (4t #W3rwT-V[/XfL` e.NwWRcWYǟʞpAA06EsikIԒ`-G76 `_y 2kV+tКHMQ $fQZIN s]:yuS[ٴJ ^i8GN˧Fapv䇛_P^eλyRq tUۑIH;w\s8Gb'PӸA])ª7^2xK6Ő%a#e TbY{"w omֳ 2Ҙqdgdjke~y =Df:14wSndj2n7gNa"J^n2'3\6[k0K2heJG0Y3/a-aQ<ž~I#˘~9ֆVy6"r<_ fh?h?YL{SNΒ+Ya7F'Ƚ){#wz]#gW͊va>o}NJx$.FIȎgױco,6[-PLד7ts}i *gvRWVEV>]7Ͻ9>}5[} ~L.݋+33Xi8ΓH1xܫfA :^͝mj xjj׆Ce+/[4t o? v?ܮuQw#z8!Ѷ?Gw^TP c{&?ԻQϵ!)ڧ/?CW=Sq:V+"ӱ'8yɱ\H8rg̑a\H8K>4We5<&ҿ{ǸظO}郫=}z<|oa"c͵f0\k 5jQө5젷o Z;f#Gk?7[w\sńss_IwSos:b_&r:c (N^-җ "y5+b7,1}=4^`;l+ItN .WNMx}mj5o(9>.soNFT.G^,_\W]|]M,^pH%RVM_n ƔǛr;9[|΄)ʣ t/ѸWykFM?"3?N:swUghK AQ) ]PD:Efs$f32cƨlE5.Tah)9#`)E( wr={{=?}}y`T5<7.;RXg m>0hS ^sǽs]7p?O.@n1tVRxܶ姮Ӽs%j.BsIG _xQr E#$,9&]-j[ hـ&ԏ?c'|z{^j|<7̭'$Q/Bk{3v~ak}+ ga?8w\=->y!b_7xr9N7zymGKk˱YܶT5b$*cslmu (+)z3Ak{mo[hi N,# VP*m"W^[Wk$J@S/y]')1B{ Uesq8xCBxI, ެ7cvIN0'ų%z= ^=/ba"tn1'FrhB!^Z1 cX݄[YzHNʎ[rbW^OhAyiѬ\ϭ*^ F x&s*c%Xɷwv_GWkc t=ޫ ~ ҘWopA9j᣾O޵y&BTsn@"tWQ#:Ae| U.U/B8pmȶ]CޣZۚ(b#x:#s#jD>ۭ 21Q9ts=zx,IԉY4i|KӺ 3bދiI240 PIN6BGŏWOBGYŲܕ540^#t7k$r3B'QGڣ7E3*p_z}|'֊f{DꉆdFT+q-)퉮nx;!QɎ Z^*95!$ք PCWGp=@Z#Qܣw\YF*_B$W ֑F*yӿ8ـN:ZS/_e(7Wn%-c:,u>F52߅m{ NoqK` `Vy3\kL=oUYQK܊YB8ũ(s_S ·^X:qV|{y%y:ҙ+~}1CA+6KxZT=أw-ّųϪz哽/C^sM!9!K8[=k5pbxxC=Ѻ)!؍wt>6<r`LlVrp9qi0끻ZdEAOذ an.l,P':n$7XF=lqι3YN^/N4e̠g2p ? <8iZ:!A*fR(1VIӽ\|hm:壁Ke\b !1꾯-'}[cUD:R:2Ifތ :!J!BCN!tB!$РB!1B4BH A'Bb :!h !@N!tB!$@nT.a_IENDB`passenger-5.0.30/doc/images/smart_spawning.svg000644 000765 000024 00000027567 12233035540 022016 0ustar00honglistaff000000 000000 image/svg+xml Application code Rails framework code Other memory App process 1 Other memory App process 2 Other memory Preloader passenger-5.0.30/doc/images/spawn_server_architecture.png000644 000765 000024 00000242135 12233035540 024215 0ustar00honglistaff000000 000000 PNG  IHDRPsBIT|d pHYs 7˭tEXtSoftwarewww.inkscape.org< IDATxuxW( AHRJ~u-Uҏz ^(-\k !Xv2;;Bd}yvvv{sdY@ TcM@  J J J J J J J 1tQ[0 5]@PKJ 8+ _ԢQ+ 0\cC G(.Ĕ@P(MzI`=v?!@ U'wAxTWsFxB 'zUCO^AAYeYWM 7B@ ac4D!(c!œhUS%QA#@p.9=_$Idz|rpQRw"80QV44ICÙ36O`0`2~T9D.xܪL$!IeR<_; /)$AnA{ d#@0M&SVd_A?\ B@ "P]uj^2ye Κ.H}3I6h/f9hVH-NJ nD4ēxxE?) B -@P;J1h't+crLÄ4QB*ܾ`%a$Az9Dvr |0}$IR@ >W9.C5K.4OUekqk)++ N˥x*KW%qh,6 LBMz%Ih4zJ #DGK]@9N+ǚ&bl5UtA`U{(w(I/KQURESAEt "mLmUl߬oA4a`|cݧt9žb?E:j7%(r+LjLeuX q:J&rfr0Ǣ_'#^~ֿ>v&Uª~Mc7gQAD(Ap.->IFe|ޛ݇ؽWY%4jKy=6vV{`o9,ZǘV~yv@,7ExE@X+׬`׆WmۛNʱ?e?'UQ9.啃|#PAmE(AĠ :6׷p] !=yf]8ƀԈ%6nzA4ȟ֭YnSx\^V#rzIoM?Z9+<ˢIb@%gU <?UWNܞbч7oɢGӺ[Gf>$~Tzv+u&xn)Q[>.S]owP7oQx>ŘrT^Tyki5ZDžP.lS'""Q,='?Ӗ@L? pY<{Ro/ ڿ鉊x6(3F3]ʶ " FCmDV1]Pl:8g%VYi-3ϯO,_Sri֗2Ӈc]1'c40[XlUKBUY^QPMf#noJr 8l1rכM1?#i>n )VzO`|,uMIogƷ{e5rZC(Ox]wAx!YtL?tB3Nkqyhm:'0#StER~$ hW5~.+B `z}mqf٥Rىg 1?kؾ(0D@;ο~AV^b5GsQw|".eB@ @ "Pd5b [0&Y؏ nNQ?f9}]?{VӼcj:G.[b;(+vsK}zOxt®LbW@`KokX).Lײ];?W6AhJ =,KYGraKqFY+f VLϣi8վZ6( ;>sk%۸t2k-v] S=I ?D<׉zhlgN9yLr7f͢Ŷgj=MyfCIΌ>7>ˍ41v=ʟ~e) Fڔ Kʠu/ka: Lh NAI=mCLFzpν 3-s E\4'WT%5OrCޥߨ+iɂ?ri-Tk>okJ0*ZO-:SV:.I O 8QP)5\. )))" D9er#l'p}"W=֚[^nwMw880>ZKll,$$$@||<Mtt46 ͆ll6c41"@P 1PFa2@VM9" xJA$ Jh-[0BEh6$zȲ[ٍ*RTKh?၈DU5\zYL䂈`0 rJ+dbp\x8SYNTH| IGbT,VS>^YYN~y!Ŏ$q (ޜl?Wz($B(ADqq 'ۍ)YGs%_GF5ɌddNpDeܒvQFȷƸcKv=/)R (I D Z/n76R8yy<V"/ ~_[$tOt F UqZ_ȁix,~_8AisB( |Jq$ łfC$e 'Hs݄ܭ-ZӢKKKIDOWwmLđl2a/:I-DO LS ND'{* :v[qeGSr>#7gb .2,ֺ%(jL'|ԍt:XV_}*fScbbއ+S'z]x r 5NxB(AĢ %vuDmzE0fqL,M9a'@S:6}^ @%HԍH&ɤT7~T.#j+QQQz|||^Ѻ"JB7NxA(AġNiQzjmOz$I b)>>*Ob/|fNR[Ax  "<~qu(x }Wׂ*..,sAjN3G(AĢD/Tz:ѦV@%)JU }X*ngܮk'ui`I oT8P "=/UNq-fsewT DTu9Ax#@AotZH!R]:Q';TRPF.{ծ%?LJDUU.Ÿ_ A(!Lv."Pzu!jP-h=?z.P©B4 P1Rݧ*OR]:W׿@PqTްFRI*B#@ply@PaFII ,b"M6)3g$&&K#@FF;wb"\40Vk׮5] @ U\y啌?! "g@  Bt1F5] A?2zhONlll H\uUڵ!@ cڴiC޽kdzILL"^ED( O J J J J J J J 4HL-OY#0 5]@P) hƫ}]"Mޅ>a+"O] Wﻪߡ.FB=Qb*RHo!ka%Dh9ф)M$֩S  auj %J"ɿ&/},z zx,ͩEk[ձsX">% AurJ4^*Y#SxƎ]{Ψ"IR g3mdY: }hKE+""R'Z)DHk6^UٞBjj;$)0iP}LTN;[k觮=PV׳T (ϧ@Z%+"R7T"K+mExNv;ۍ?T&ⷳPgj9*uM@ 0_W =!N,F@ƫ:GT0'`=.vz!?ӳ_9Q7TuA@C ڟxܫ[煣U=x?ZRo#ԉV(xU$4\Ps:v_ Bn\.W R.h4C]S!R^O8Zue9.Pz]+e' IDAT&ڜRF PIOj/r!s)sґdz^SRڄ?;sG{A[llIdIԷ>;p5^z ewg^"|y ,|Bm7ܴr6$[[%Ź93M7Hٝdd2zmQ+ G%sKH~UT%=*70XFb&Vk-ߐGC[LwT6d6N{F2E0eQMh-yAu6!TU^xᅓ}5Q 2𰽼(Jt*tMv#ÎymviCqLAm~4q]e(3HIܓ h27eĢ%X҉vh=ja:#[*=M=lBd$g5Xe_`Umr_N)'`zu.( T(%ShNYVHOXOK=@J&\xAۋa4Odߖ3ؿuy{wW/2*cgyeEIh;(G=1[|q<ݱc 8g[Ҩ wq ++.(`CWrٻeh.4e_IR7kVвKO館0%$7lRcJ0(ݺ@<goh5OG˧od⹬3.buw0Ѭ_%4los%^I^Z,3vN~̾/Y9}eړKxx$xf-s \ˀ~}n}XQHn7Ǿ1 dywdUOY3'c 9yi  o=GfAqINJUdíAMo֔WvgZj]Ͽð[b@+7{ڥ;Q$IG^;zk!*9N|g;ƱwzCnx뉼zPvo\CrݼF~-:I?[ 7x% =Q3V1X9}ΊrRym2R(<˘{chޚ6s wq`0wѠe[{mxn1 ߼5eEg3^cOM-s%ߺr3Gع6g?^x^yFM mr#/po?m"Jqw?sIOՄy)#O\~oV*uX%jc?x?xMy=q3_Wʳf\HQ^.3}:7g7͚9Ә;| &W-ټd>b׆s?m%eS~׈dq\jngâٜ>ϟEf?G+{"VNēMQV]{}rZtz^͖y qJYJ vl2IknReb7-M&Lmڸg]~#mwuXlv̙Ƌ++uк؛κ&VϞʻ]ʹ?e 9+Y/sŌگ|gq9;ׯbUƴ>b<;a&1񉬝75s(/?x6N0yxz!ʈFRVxx=qc 1NocAkx tRjxk ln,RZ,xT~Թ(:Qj]*O ZS29|`?9۲)/)Av,׋A}t὏ct8?xՆ%F~CX3g.|;3z hԦ==ν'*j SR^ν~ΛЛaY}F9lZ2;&r ,}|:(IcOU^' e1R~$3y4XWۍh\tbk 5nxHywxndwwΊrJ ILO>eD+g&]ټlbkRV iټ^#.#`f~)y3f5sm6,͝~vYʹbeڕ`ON g3Oc6OLQivh2$)(s\ Vlp܋#H5ό{Q<Ʉ?eKݱY}(ޙYW-m3k%yqk$vo\CYN7YfW:gdg  fݼ:͛I^ۻmҦ"~Id/_kS)ָ]&qҚ|,ޮԗjΝw]O8ޯSFJ$#"oz9DB攌 $W(DZę Z ԴWU~3қ$E\Hn7t$e4R@rF#jKT\0FlX;m]eW_\Rv%n,# :?G=WX`Cw`Á];woX֕Kxkx򣞕^ Z0x^k &!QWJ%gâ9zP.y]v=1OY7?KcXd&,-/*ls˽:׮W?\NV-eݼ\,z>vˢ}x;MTl%פn%n2[<ʋ uڀ╽A]&'(K%0qe#+nRޫSt,WLܟ= r1 vJ]< 4hٖe iھ3[V.&&>S`?b7{= $`~x) ѱd͜sd-_|uY\R ;vSΕgˎuYA˶i׸:xx˓v*$9 aC%9.O4UmC5k)!/M\xq-gOEBŒ竨?z`0gϤ g/݇]DmGDzr$n^$wآ/i΁h֤}'z h̐e)PMJbI+`ގ5+-PQZBh֡>XZwł~`Mֻ?;;kgv%&giMSw䴎ٴÇhi۳W?:ox>.GqFHrcqxĤ"I_{ۥ\k 6R61o(o~lfqrbق 7f͛ЛۙZBQVJBJZ@XŊC-5)Jn7ź$gU'D@Jx*98zAQ+ᏳF6Qq7I 4f_}6zT6.K#bCA8+9[R7#>%3&)a׆CW<_3 g;IQzFӺk/m{w:glZr2\=֕^/Jef?_7^nE`GѺIT!?ot8YR5vi:~{z;m+PX1OyΡaضjq7e碫'Ck4l}Ӿgy-:NfAl]tOkd/["su<>OU~ xx mWē)P|OU)N>@Bj:z ?}gE*~b2/Y2g::~~E/`">9Uyb3=ѡ e?uϾ'9;vc7W\n_xQ7yj.x%s!P߶)\7DZbXyIx%gFӒOgE_fx.%<`[mhwmCPl摯~bέٟg1}'Y=41%ZuŮ);>y}3.#\ g+@ěqо@&)i4lՎ~2@q1|ңeߖ49##=s?ӿ{xO-B(84Evvٞϼ*F^EȞMp-tz3Dhم?p\n1kg Uéc!5k-=ai۳/Ͽ\'q3ywzAr烞_~Zd҇QF&7he#~ жgkn#!5kWrZ,56}#`}P6CLyqǭ:]K>@n5jR7pRzpq?zc%>%>t3zA6ǀ/1%PY x߬C>]{0hF [sլ>{{6lТKz?;ey1~V8+q)/)"Y31"CA޸V _ȯ~-$:t~ E~Cx)Zם앃u`65A>IߐGAg&w8Ey(9¡P% rvތ w^A.=پf+Ob1p#/*c~zyf~)sE鬡GJuG^x8sO[` g{6]}/O\DbZ}Y3{*]^ g/@2ya̙F]c''(+:7/<̊RQZLm\T ~Y?|kog¨gݹ_+K۲sHvmX-*#.ƗFc ~<f-eŴ?:h8"+(+~Jq۟!2j<_KRʰ[禗|鎎iD'khޱTJޞp 0lF3(y~5~Ϩ8k?_>AlbsQϣJ1߾8D'$2ߔi\>t3 )l_¼\/~ ['>9ќuM;x,V,cXx_{C=вKVϞV*בedrm(+G`?Рe[rvlېC={=(߽{7|+WMyq!Oޝm5p=Wft9wlнɖy)|kSߺ ҦG^fd_QZ–勘Xnz>Xg'+MT$XIP{Z^|]{s71_ٶje^m.cWG_ go_Kp #_~龩v F#/6oi+tAe%IҠe[Νޤ*^C ~VNߕ{qb_>y5cWedG]3\x]OW<K#^K.ӵ3wY8«kYWUyf κ&vbƵ>͡ wҀl] yw&>\=G6q>XΊyd`&lqeGi c1^ǸGrh>x3o\7\0+ٞQԯp߶28˗uǺ,>x?R>yoV{/o&>~z>eWc +gwe?+K|/9|'#op~]ϪQ=P{6%>9&ulAS~75ɶU˘t.&:r0H#b0;ɟ;+_&nPŷydB$/+{HAlĆ&OMl\5'\ ҃SǏ=6dzZ&̸}0صz%爋9m(sLq 2܌4Zu_}LA#X3YslΜ[Ƚo~ʀ U|x{q[Wd |m[R}SS8o7qb묏}'?cytl~XB%T'S\>y{{DF_;AP*}zJzāҦt8P(l{R7PplKQ{?!}r3Fߋ&*`*p竤ޏZyT=cWroxOq7%6 8rZ9FtV6,E%2y~s_3dCs wXՂ 7#M4hL :K|܌̞<\|]nN݆:ːwyye$BbnĶiGNz*Bq^#DZw_˝Z^ƾkmV uNgCdgz;7sK[jS">lL湯q63ïFKvj ɇ*]GR:S$P\gͫOq/a5eշb8/b/|ֻwhHއM+&U']^ǀ'tB=9w_1A8/-+!/+FCV^ƱZt֯e7SmoߙԔJ^QW,6t4œD'fךypN+*T OiFOHmݵ'+}B1E]ҡ N8\Dr8}:w-< Q|HZ݊ qdF?Wvc^zL _wrt&6X'w_+fVBP΂ncˁ<ۀ6mGb۰oZ_5|nt|z&ijAG Fo+}脊7$*h{ gךUli9O7ɭDT*5=\~wp#{EVK6l Wv;ǾM& fݕAI^SFT\"/\Jq?&){SS9v\uǤ{;1GxCWc)-*`ey,e,{izc(Fg F*ƈ(z_ut0'2>QJ G1Gҡ ]994v,;=JQn6:c}/p ;_^Xel)bڃO݂_>|OfO2|ZzЦ[o ϸd=ȜC4`z'CR{O6#6po Z{/^ aޢvp;8l6~|[;kAK|gߏĨoǟ'42 OgQuN{[JVN 0k濼K}u&O<U68AoMs^9Oޛy-Z'& v3k/ڲCFր; D~> aVٿXC{whե׬f18s3_=;[]]B7%n]kVUNQ/3} mXi7^'wjkipxy)wnc{a vAPn*ۧd9_.l.xaD>}A~z^%w>쵏\6 ^`΄2yMo٥%xdd?{M+ȫofi<9uU _eob D-K]Zn[!3c} WLb^Z/fKsNl͐)^v3@C;'ػn57Elvb Ms^&+;7s w0E2КWe㊯Jg *Go:knA1kmW)өPq[hT p2+6'6fyCtHF-QL.,v'K-)_f)3*U)ha!֨E mv']J!U*ЪJ4J9Ӝ :/#Ȍ4xnbYbsB .TGhz(r4r21t1CvKrLVK,d,X>ҩ ђT3@'Pe((ֹtQTNVH= eR-eʠBJQ@Ydsb{=Ủ2z %Bᅦ7Y9gVCi|pA~rL2.€J)߯M͚@lveQjWLR>\CAEZQ@',6&r)Jm3Tʼn2NNN6^`ws3&vB4t)BKF)ti\`%B!~Vb5!(>lNN]jF!UQVI<6'%gX e6Yd4f4[Sjeϩ"lpePDYGB)r]9-v'[N#.VaAfSC"JKBɡЪT &Tø6!)GzH.K(5g IDAT*GbgWf&7 0100--Zl[N,}N1FY%:c+k5SbmB36˟!-*.12$RgN{VpH=R.vdzp+ڇ"{]"tвY&?YPFS~[f"쒉V)0$rZ*g@Rdru)y&J-v%˞&f*ٓUE깹kLQ:%v gl/%q(5 .};t2yP0"u"I<zYBYM{{E3"(sDBFry0]v%82͊@YNvdx+Kچ21)TvO_@ 7pup7u`"LVG ȕxk;cP5۱>\M]#HnK<67XNvfy-ZR ]"1e9殑i*~rNd\H4;h ˆH0ҷZ t2Mpq<*98.QaN-3ZAADpni`$9OC Lh#O$/4*n =[JL *(*w2*Q·M _BPsMf;kk< ac- R+Xl^:GjUH=i#JN+]p(G&MFsɭH(U)Ʒ WD.jE4cQfspJ3F'<F$KByzM܃ZP9&T|3JFh1XfJ A/-%q##iն=JediI15_gb9]bi6aaBjh>@Ӧ}^{Q>ҏWKXfZu̹h,]7w6rOq3i߹kcvlZϚ~ Q շ^ܚjѻپJC_k4ޞ:H/rR mY%A-9Fuut#,"^S(Y}ҝ oa;ȋG-?wZbZhyӭS.fdxFvg@\:&7jtoSzNuŷ\5?B!#oZ>n8rLәK=A{0w'Ƈ3S^Qt3t][N$1:GԼ2bߎmhj_bbNdVcڍ3j|myصy>әUЍWS'+O'31Ņ8a$?>0hWT Zhn7U\p"k]>7%rECwq"HgY_ɾk<ksҁ=έxz}=p¿g2KkFuLaϋ3]3Ѯ%zg=+Fj+RG\Oʅ;i҃Α;Ww f=Y|m?>=z1:r^Ё1$?"yƫr17LH$3-YM{nM<.u}G i#w~MVVY5,:w879֧~Zᝯa\W^{nc".OWo b !{rp?|9f}n߁_ TRpgX$ZڪEh} wQc|EC4+8:޺g0l$o?NGF :v#O=Ϥiא{&R%C 2 |x3ABQ2iȡA8UOy,~ N4wcY|z=R07js?ٷs;ֱj^_.{Q&\圡=۶0u4wN,zm9]SwEFډj mE&P J6 =';7S*^racǡwǫ<տоSncuov>ze^_\{FR@*G(_k|-HV.}'/a۵c7EBB8/YrHhŅR&NcH(1<֪๹پo})_O[Ҧ}G>x7CF5֧="L\>~0IZ1=zwP($r W/'_Vyo>]K~Vq9Gĝ ,Am^h̩獈Ϝ"Cj1Dۜ.NWEzF]:7U,_(}gs,e&rOzb. ƊC{w3ҿ#; p:3Zx`SdlfSH>½\?f _Yk$Z !($\Yv^{RN팿l?5rSEM7уY;|%U{ye[EȌ.x/Ĥ:sҴkhש Jknѱ-b0۽Fm9^*%}ͺc,fWH 7x9ɬ[s*#j*^8wJK5`3l6^gB=@(aWhy&$ }g '&<2*hZђ2rdyk!Osb6s`%aYzu[51Ŕx照k헎n<2ӫue DN3uz=1- /}v^zp<">7|-]_{y!~ܼJC7_SFUяҽoFFFHhExHP`4`|I:VBЩ^~!Gy> }].pln׀AUkHK9͓pՈٶ%12j*aŷAK'p8().9vL%{Lx牟dH|u}dBUZ%P7ʓ D y9gxhW<>=[\uwݭvo)^(EBZ R/ ^xNeG}:L%%rn&c'O峺~x|-NdӾK7z?5sﵗ!/|]R{&q۾{=Kޔe};a UR}Rok*-ohy@)T cow6wF|6,yM=/=WW\.~H>~d؋ΦNgfXG~bתQrѻL̘r Ň0?A J_,fזXi*T A@̪jU($Jhru7ؚx3`-1\ԵʧEюGoGo33`{[ q3woO?ygEYr8<=>\}=i]{][6{l*1[q딋qH6LR wM1׆b|=hu(\Ydt w<("N{P^f"d*.?~^ɦ߫n]=ZCHH\<]33ƻ搙JfZ8(8Nq^zSi 2<'^B|6;8~gPޚ7BQA>^ϴg׬ ANcH69nH$rA.|d{z6rgn۾Ҷ}k`1{JACyxŊbW@eB"Q|;l"c:v7xUvfp"䄴LH+v[n6ʭ ~r׹L`@Rѵw_B+jߵUgW7PISאYjj/2T5b#F$Ԭc/F]ST?l=7سm k6I:q7!^No9Ϡӹ =|&r! !!wiն?X\i{vׯ}5Q1-ıJK!̓?r#cڼ<:o9U3;- y1ƌ>>R`a9ve-ye|{yG"~ZZjOFxJKص;YwL6^>gOd"sy9kWȵ%~[sAڻ/.I0?~䑧rz*[Ga9~zjFŠQƹA&SnZb:. ˅tp8p8vV+Vłld n^z{aWYٖ^Q.$5Nm(8X7O,$+.VS!1\m".@+B,[&r5U)&S2' R[T(2EXϵ p8]|\H6VܠV,bp8]|ky 9l=]QHV6[Iz ".X1E{SY5Ky%/YzkekF8\|R䥔cG3.pBuxN>;XkdN^/%6B``bxl(PZ-e_L7.9ގ6hhV;hd-ePϖ^KG3Km|r0_] @R~B@:vLv1VQfwB6ddy"G mz9F t9qytRNZ{|6;gd1!h1ƖoF``:i+; (Km^LjE*ܢ:Q+ mɁ%dWX8Zh}Z:Fj ifkűB nh~ aulwbAg ȷY`=ڗC +.0gJD_fs[Z kKiS(ZAZqލ`v'%6'&,+~4J$R'[p4*ɼ`cԂn-CQVٛkPzz#/NNjl>e[C4* - N3 ({2 rgi EQW+㔂{VSs$T4.ZI)"Q`T+ (Ց!pQbsRjsRju u 2)XabVzE< é;]>HRù*p8QdDUS(H=u$̈́<@ pՅ.NF4*.~9]l=]ekUQVU!ev66'%6Nz [hWw^xbK.9QqL@Pvӥ6BsRP-)&N#mN~۷f )ƈASS l% !;B QY}JAh}p!"-k\;(0 W9^3Aǀ *ͩäZF8k"앋Zdq\0ht63~{)2>@I,?grߗS^g 59dž.@F3%-¨Q%1LW'I -JQaAPj*)(`9nr3Cv{ACL &R gJleU)1j5j1htRED>XP.%R.g $X(;ԫC<gJ8POZI(pfǘψ'DJFU "J3;M{˙.ǣ(=NʬXNas: B@TU)ЩD@QBaj~NvgJ;t-TZ1@۳lN)N תrؐS8eb'D<bN~130nhUKtWfL"͝cp$P ZJI -zK^l%_FnSwX{6Q*} P'35JT R{%'2VFKjJ~<@+p(LzC Vͳ`͢J21|"g1 IDAT4J Y}=rZ8KQK l,sR!]rᤖz7Rh56gV䛨vZe h~<2%y/>6̗v !L.Ktǎe4>X.)]FxyFD)M.k !䟎uh\e4M+*a=k_+( Ӭ=K-Q5rlΨ PSPK|=I&NUB@.P/ʟi%A$C+aqT̈{ݯϰU0Y)+ƏɅ.+C| Wi$y%)JT**)J;0*V^/aʍʹP2F{%K)ZoCNǔٜ,~^F3vU?_\p|BJc+6ϱmvn$#%Z'q`ߙrؖt%[jZ|ʟt?(hۅUQ#^~2d.=^\NFX[@UR|駟].p:8NN6B VHu;]?&ZrEf2;ϫRwl%EǽF OxЂ2tN3xYٕ]FVUd4.~I)FzG|A2NJYIִZO|g. B k.խ<3y:Ei 0u/)|wK}i.Ȝʢ0s \Yr:vv;6 ͆bb`6)//r+[9*چ24Hɨ*d)3K<@ ~VѶ<ˣ$t:ã840AriE]nsg7>V[iowhuFɁ3{ϔLy%/dS[蓵B=K*RRWpO8V(;-~dV0}ZT7ߔb/'@?镻SRg0^:^_PeuF<$p( ,(f/흰MM1zZTik pQhqPlqYbc_N9~Z% =-1b~1XR%@INoZlf, %KBAE^E~ai(9)8(49UjU; H[C#^!9J*'R=gX8f3}p ´JzSY5MEV)gwvY6?*WT*ʟz!Pzދ`zyGeZ'PgOpTBYJ$J4 hkE\m UF#CڌiJjZ,˟x#CVfނ5ʎ .ә]:$KM<1HIN*{f8K;& QUbT+zMev'%V'l/Ѷ/ DτgȫոG1H_jiqg;fns"z߄Nbjs2(.+k4r'R :栀Kr1\ȡ;TƑn˩ɨ'DҺ8T)Q(~ePJ䥲GjFae5iPAKCtrp묘͉OԲcbҏ eSzsA{O* v: Q JmXKj|xIixdߊ(_HVrO^S KW3Cm۶ff͚NxCB AnAjggw)WvT=w#٪HUazT,J.O _EwG? jk1eQ _Q{MaBUU J7ꤙ< BT-mktyTv;ZH{)NGi/=z0jV΂zͅ ~1ZRV+XwDIy_/o2_fZ ! 4EVWpR4f3*2AJѷtGv'vD{ q 9Mo~&CeСKN{M囸o%9D2x#P4PDy5d޻P@ʤc "!n9NRşK'g5&PՍ^?T%{ՙ@@NeSPo哥 ㅂ':7 @]BJƦB/)q$M{ MIiF9v=z| T&WXW\Ѩn #%3PJ|KxC=zKjĪ [{<͉@l6<)`7D W d-%{:́7+{s3/_SS5^T( 2^ *&R/Bn$lϟ z!}&ݻw[oe" 0LNF@$>!'_c9/Ձ/O] ,_+V[3_cBBBڵk]F:@D*^J|$:P$J,򠹅ۃ@$JPۤ#P) 1'E*mJUzC8VAu֍ٳgӻwo b yFG&`DʟN!<7xJu_cCPz-xJNjZ-[xb-Z(sq>:5~Fz@MFը($xU@:sm*x@. hu{ Yk,28qD-Zsdd$}m7ثCz.y&z#kAǎ+L&n]V3iҤ?٬^J@x|5@A5^qW7eJ).WMF0ye.#F@`6+}VKlVhC@rO|| D#M>5Н:ݻ~;NƏ T0T"IHLN pl1,ɭ xS9ʤt{0#TZf̘1^Z{rKlJO=y }@:P{x)}wƊSbRdԨQW5 Mzs˨>|xUTț 2^<ry.r.?dN'<q &O!ȓ#?_MSS?)&MK/Ti 9zՅGUU~+fh0 d RoF*jx?Yw)b<4zQ#Uu҉w")ESAaavFԩS tuIN"eT RdURonJ0^չ^uB̾vLcERR۷yGU| ݒ {G^DX] XE~T齉"*"{{I !}cٛ[,}}vウwf93gFC9(8Jn\ " s[hK֭_` ڦM,@J=%Lt^Q9#;j -.tԉӧc0Kծ]lU\oLKl3e rd}2hѢY}AZx֙xg ue(+Set^VRYNrضm[N*Pj ,#rͳ:wov={Zl)ST*:uʖ̴o2<;YB":/=;:Ng JmPe./3xt5hM`` ժUĉe}۶meOZheKN뼄9Yd4 eʔfNɳ s :u(RIƍssyyAe(g)Sy1bC ATodtqr?'89m۶?efiCN3S ;m۶l6Sn]rΝX+5k֔\h 6,K73fˡhѢ+WN:etQi$v vڱrT93PxhӦMFC }-[%JdtQ91%SR.@@˖-9zhFC3Pʓ Q2P@)\pFA @ 5P)k׮ƍ4nܘW_}Ufn޼ @&M_tmժUдiSի -/ 1%pBƌØ1cؾ}k;w]7otm/ȂlʺujhZ*T j*Z*Udז/_.]^,  f@1t:y ϋ@ p1%uVMz R _~%JjդkkիW˗%TP#Q 2%;vZjt{GܑYN&=;u!)c_rJ=HmvɾmH*-MTS*6JI=Os%)ɷ UNl6 ^6nɸ`\R۴ʼc#9Ud+dٝ>]^N%-\ˎ>ј4l[ٜUg\ܑg{{[ɷgB;匴(Ni%pVa2L9odJ,0*-# RE8;c[sRJ F*Ɲ%5ұwgTL&!UqJ?Czj]W Ƕ7 9UqJM]Yf^B(JZn,3 RX\U0g!bֹ4OMS{gD7L .dݙ;%pZe;s2 Tvɺ+)m[D֑d2ϙR/Ȝi\ь.Z(P g#ETݹWv(r iT?DreKj{yd]{v%ꎜ fL:n YO}BkB@aFv_qr&JT*PPg"欙'`VbYR/k7O5]Lӌ* 6UɈ +m%~NkS(L&%%Nݤ7cxX,n>{dC;ӾCm&Zm5eBrb/JC6vJZdVY.p~GAiH@X1Z1N9SXۊMeFdiKd#Ȕj;nl6;V ϪD)u4n}`"b+  *z|CtsV6 sIiJ=5 7wFA9I3G8̙Mz P4w.N2o@S. M=꼎3wp/%IRLfJtwBYFkmGIJr5j@29h&n4wAtÌW|&ⲒVR3s0Y_1DnUt7/U=^b,s)*gWyjd$hsD TQLY0yv1Ѹd"W'gW؃X(Wo0ECc'.: :lWe=W~5z.x(eOޠT&Dv^Ɲ}z) =F-LƪTPX$xhsP{* (Q s'I%: N$ۊth$j'&VR,^^J "pxydrn̓` 愂mɲ姢X]*a<|$řHy:ƨ3a*TyJeMc3vj~*d'x -'A'1&J@ 7׼0h48S3$=1^VP0e^*^Jھ8Lz.Jlse3Xjtv SDmIuFA:<- WBg"!ʢl(S  9޴w»[㮛=|kbx5LqWĄDt::$률z^y ɄazsRkzʓU\K:VTO*z^zYe`0tf;HɲF< 2TJް`RJ1̑R=Z Ԕ) flK`9`$Ds!D ۟Hԇ×<nOOd6 ] Y~?EyÕOwS_! sU`{r0kʺU?' *Ӄr؄èޕ{pNè/,O+--lxx)e{~xoJǛGw/g5Ο~M| ظHҢxERRCep/^.̌1\;ߧȯ/4?t_Jo߯ߤf3O0ٹylZond%le3.(39wH&|g #61 rß6KNE'DsI.M|>jiPDkz"浏rXօ.,U%>m8/W!jC}!<}$.PaORzpA|w*ŪI\ OV7HBa s K]AY?KNdf=Y{'7ӠBcZGǼ|@tgؓ\wϦk>Y @&%h%zFF%Uw5Ј[RnY7gvJFmfx@QL# |q'e~\p⡱v:j~Qjlyf7?co΂l4ЄA%yQOVZ,(:Eyr[.߻HfCT\$DmG:NzW27^s 9h0'E| W`]WSt]4Hn-ұ^Y92^JS,_ ~;Z q1ysGK:Mۥ]$_ =?!Zh-3r|f3.âW0( _3r)ZCJ ׃NYQ@)QZnllL{*PYCYk1e V7-\waҧ@&;ʓ7u|d4 z~cHNE+?a՞eL?W5 ":\ǟÖQ,o;;&[iU##e G|aץyt#fowY8|%Zτ5L܉ЮV.(_t$V.=Z`0VeFOY-kGpKF{>^>&1ht#=CЦf{֓Ӫz"ޱޛ|s6E!Xw9} L,kd]rū=+qߖ[fҨbUUv#B ,J-*AB]Sx\ @չyH Ƈ9P([>s;e+* ֵ V9mF#&c֒o{VZL5\NGۡ_Ѥ,mSvzGd m۞߯+sڑ4)P{h`1|c߷[7IHf <՞.;zZVk#+0V)A%(_;Ym.p"rwLns5eee^69}y@`|ͰI5Nc71)3je1󕰔nAKk]FN:Q,?bb %lDũ48([_Gޠw8gٌz#lè15 ` ̼ΞIjMfޡ|mIZyY* 3f3.ʹ,ܶyt#q4"oۛWm%m˩'x]lcdWLf|;$1xZxad6z2z*'jЍ\T)VJ#J|BWޢ+oɾרb?(C.Uw[ MS|?|9[NE١}GE턟/߶dn(G4A/S2]ӼMDL/!V:h<}vY!ezzZd.ۿ RҾNG٫Wvhߎ3j2,ݱgѯ =O^?μfۡ.7?S$ tc?XI$FSvz(J8ĮY=|rQd-6#v+ʞs;XmD~;_c`nʊA;gl?qmv; N*JU{J (&j#KSʌ]FfS(/DKxlmN6ӠB4FZL= k?`zLBJez5gAZ~ِZVn{R UZo[_6QqxqM0հ,8Y1˳Ҕd?be$C/Bvu;SпlǝѭqoN\;W]'Ѩbzc@|R9jU#_*XF:8ٺԋ쿰P"$+5, ̼Ξs;6! W Dn<= ]ʏ~͓_r+pS9;n3/iT1"Y^Z;{7;2˪VZkp:;長tvh==`_?AϠ~i̸I2$]ql4mF-Y=`0DB*7Y'?e!x=5h>~X>2y̢G ,.67[Z4O~Π[^{45Z8\"Z-~e;Q1A R)U,pe{E:|~3yծ}gSt=ZE<~'E,7IAuFCێ]9| Of7BJ\P Ұb Iܷ{==(\s, _I~R5^-}ȓ(flCTBla}+d͊ʷ+#~ qz]Z3Wn]\8_r, %QNRJ(;%s3r{aJL`e9]HuEc5W[U׷C뚯I\Qt=634L³ >Z_lPyFI0;u,**ȸ-1kR~J"H2 i^  7/ +6yI%<iUR:N2b &؏ 4c!,FZLxЦ O Ȋ9”!؛Kmm6*5E7<3p7ݳ|/e&etj3<. K~f2 IDAT׳3]QӫOm7Wf(_ڇ2~Քz7O7~9ӻRd?aSdylj@@O 朿}V1^QsTy6\+vRQsTy:E/ꓯuFWrF)1(}}ymb3޿lfB}4[_9~x&`\2'B~bs C C 6o w.֨ uW 4SK9,C>'yD{QrP~Yܴgg59r !Kˮr/wy3{~gn򉊋$6!+.q6O8v0/p+f6y@dS!`iF=`4dH#0fnХMFF=h28~#|f9xqt wqY,&!(&i;ﳭfyH2$qQY=(t9}$gv{A>CQsn'm'5>,ǟG7ɒTV{73{]{m';j*;s=v,w.=|?Fѕ^>ߴ3ibbwQƟ?|Ԑ/']_4S|(;0_+kɬ?ŪO0(|R6Ny}z /9߶p? ; ݛU{Qgt%eQ4^:fq{ӓl@y:gCOuܚڏ  Av~s^!aKϗxyx1ulμzjyoHfeppGl/=f0ZO%q%F>M]W~3IL2$FD>f%lxmb3)\@LB4oҏ}Ss ;^ߥE62']a%h+,PMch*n]{? #Xk_3]!rs.߻pᶹT)V JhMx/䟯RпMlFtbb8zk^|*Ū=?sM!zKF7-heCzϩoQKթI5Y[]{R 0e5$'n7|o,y#)= LaR+(Űyr\!d\],E:S|qt\%{=ѧܙ[vÕ98 YHNli_#{'SXo}!+w&ru]‚+,V )]>R{|Q80ͲqOM?":obᶹ|e<'[ֿ8K.k2 /$當a)T)'p%f=M ;ѫi)m)UF-[&U1/sPhUvvxϖ\{&O=[v̛_2.]rѪ޳[a7=sΟ[HAA߿Z*ިNBr. 1 ѼjW 8t}KAL8WڦG/s)IyWtۙбޛ9xqYf_}V)A (^~[)1|n\.{,1A텑.,۹)VCrԇeghP1~C\~9wguרQM8,Va ,&%vYyl6BRuYof"MFzN}˲jo/֓:PDMY81[hP37`b$OH' ~İ0Gh<4(4őRp&#M*7c%whU{ѢZd6{ ,Y(OokO5ik;m4jX#W2wb)RC*-ҵݑ XϽv1:|"}^ ~| ^CCHS_F=qyDz6}vRLrA?Kn<Ƙ壤oDB8/pIXv+%8pq/Uv*sEs:̧- S"x+?a;Hh>X's Y`9VYƘ7`M$utSb:h6o*U(?]n2;J]Ȟ;^RVnpȯMFTJcQ£0M&nߤn ,Fuv;yv_Ŝ-38,gn" W35KDާrѪw0~էjsqUXTѼjAZ:?,Kۚ۴A׎V2{JkH T"hҗN0 zrJ(+ߵ̈́~/\բ|Y1=y()e )WmGbDD;+c6y(U4 njE *s;*P;ڇ|n<t쿸-٤4xyAwF/~S7V::YZ)xoh\))P=9Qi0N&":Qʃ<>~xzh8iD?QL}I7͌z3 գI_>X8ƛH6+K7cХoQ5ͪkd Tƽ%&~ e*5} ߌn,4u:ҭQ/|t!U;yR:~z+6'*.Rj>MiRI rG1-Tw`>W7C]t2eFZ:"׎V:*kH TBST}a"KϠnYK$CgCO"c#I'r=oYWMh|e< U{ҾNG),׏dUj=")P%1d3{ބTnβ Y5=a>M)h}-SxV-[كRP9J5EoKiZl'oJ%*6΅qj.ڈZfwըbԨ[a S[Xwԛ\x1[6LoCu;¡l:+]dÁh}%s( {QQNfK1 o1,pl&^Jx^ghה*X3NƷ4yZ:nQLMLr+uv;.ڶ?5{(Xih+[{ P6\ˆkhu^)׀؁NdBJ-{bk^|f/ll;4S EߑV Xnug]ݯqw i;Buf;vHyvET{r Gn%-zʔ't*C wB*7m;c*sʬ߿UZJAPЪz[ZUoK.#Kz2F1+stkxbJn{jRj[tk[;V^JJM; eݜ =ï1uw,3$x-_l],T8KdϤRSqx1B˦UZσ8xi~>-TyfO^RVnVZ_:Iگqht84!rs_giRwRbpeN\;L347Mƽ|"?Ⱦ iT1['&İlBiSN$[@ܽQNHRu4YO;dH Itoܛ_h_v6Ldnq.Z:W_l W W *˺U8>X0o^EQ@iV}[ {sM.h4 jQdHfm}LڸW*w}\]ς(È|M,.*)[*Wab E*͑׸CxAwX)]c7I{~łJNigyl^Ӊypy]ʹst?3?toֆ'||6r ($gbpeYEңI_Z3kҩޛz]MEn33+x>=߬{?! W :FChY1]ޜ0LL>Ej6cjL&#rMlyW\C %J$А4YZ_xfo&koiӭ{}3b6Q<(_I~-#^τ3f(|'vQh_## 7ٵjkfF>ZND>f:Buc- s*lF7 +)) NN#Bwkz[bbE|lE}ݠ;n;Ӡ&R<&21w&_gɚf>C.^8~=DP$ 8M#rwvDsĆ[|\<)-hZZ-^^^xyyI5 ZZ*h{SeTQ5uJi_q筳ݿB@@ZTkMf5s'6ze՞ez6g]KWlO`TOq_*'-y+%7s>򧤐EEN6C.+5v]MFl!?J7Pxu<ͻ{dĚgGcO1"b©SZVoàVä)&!ɿ|͎3rmq% V;ǡðd|_I&Ѥd筳Yo%_!wjͨ7>FZuR(Ags3ьQ&kf{d&>҈o| 3c6Zg#eD f<},JN(fy`"q'4PBP)ƛ93oV2f+*&҂W^A (=3|9 f ZL ]`1,$Fyr׵ҀJ@ Dwb0D3lےg 3ܖEw"CMxxSͯ+BdqA#I@^2B<3$ uhq𦥿!Gyi Źpx M˄'N! LTP}~S/ 5Z|}bfx#+J@r~?$ΓDY Dr_'{T㮻2U' Z@WJJ|LX#LTk$/cO*݊Uᙛ,򋶆C7$M-P^ (KI##I$l]iIxWjw? IDAT&ecZ#Oƈ94<$ʟvMX#@Jp1,)&0rO!ۼ]0j-4Tq5|o8b.U(辪Kٟ_UEkQ<ʼsW<(h+ FqoT.6d{XK_H5%E5X MBwɐUd2:jAm7HS5b;V} _vUdl'XmNv܃EnZ>\qddlXND;VjzRm{lͯ K,^{hk[h5bڇ~>yj+K ٹG:ٸO~X=ݨ[e5nU-EQp9y9dd^7qS~Yg2vx7,Cq8;f[3W aZaV;YVm{mq}$W 7x9^ٲWܿ|8%h H>C50Hmն*1%QmJ]O?NģxHJ x OTg>xg|XFͅ_3[fAa3Ol[m2*[9z f;3kLn!_4|AI\rHMws8 "9S}Ny.͏]O)\ޅb"_/|,.D 5i8yy,۰nwn=i7{Foٳ^aԤ\ξ,r^3_ ?O Ğ|78]N|6R$2Ӛe]m]'td}i}q3UV0a\uMeؙH2>K?TcGyo;$$T2s2uq_dd rHΟ#Ⱦk]%ϲKfEf_+`څٰ}V?OfޭeΫ*lZɗsr~}22OX}ekN盹XzC5u e}|9g*lZY[l?38q]Q~] c/osjX>#E{4׿~U8t G3p*Β-I/ZVPR~l];հo7Rʢ5 Xv!gwVz".އ{new~e=욵V3x={?_5wpMv{U_L\&glsST\ȫ_ײi+vǖw|9+^nV~o/Ngߑ}ޏ&=-ncNs|K/ﷱgvm9p{up,gg돻عJSTܵ_ھ1FI0gUl\8_o?qW_gdz*HOwizuspt~\0OGqظO2s2nt߹p!O5vg?}-^3c_.]׿~U}GiLwoٷmVN˱^ "R 3{;& gɺEt1_;é|6}o͖մIiC}k9z'=w;]v;aM}tuA&JlY~ 7[/bׁZ2. Ѱ~l rgSDTs],X=0Czޗ ygŻPk2P~^>Ǧ(¹c5` wE}0r⛰`4Za],ZiHM`?y26"D#j5L)[*jBV=~]>M&͙wmގF[atчW.rg=K=φi߄SiӼ-WSa{\`h|T笻Ygru K2;Gh߄zmkrlMBNg$g_^~W&HfvRZsϋw1o7L,=:˟@ⅯY2)3?~=_qVO,ьشs#G2pyi\;z{;0鹿efwٹYc/sdI欶!˛<ƃx1gwFfN&=0PX\cn/՛J;{SоUJV?qqM΍.Yޥ3q80t$Ith}&+7.gx,^/aڅ0&]ȿo}tÔ缂ꮫfȭaFœ~O7ɺo?oznu;"9)wɁyGi،ڎl r0v9  uqeǯp|qe+6( #O㓧y/~YQ:oϜlNiÆo7] ?u wOҷ{?ڵ#\IqIն]|թ]gNUc,X=K.G3sO љSΜ) &&4Y:yB?q|1k }4w S?Ό7f$ fl׏[~1?8^[={iVx{N#-*۾^:1/`oՁssW¸i_\x }//( *;mQ5` |$%D#ذ}=sWŠ)*.bz8UPv9Oobӽ7CNޱ)YmWHg3Ρ\=r"G]mh;yMiyO,Yt s-$k~UYu 'OWb2,<̫ﬥwaޏצa`ÙӧddsA>m'OOg>w\y~.㰇Ga:ZqɌ4\;zOM\cxͫhۼ:'/wosjtd4x/k+7ٱo;Ņ}j֚;#Ѳ=mRڐ_Ϧ-+)Ok="VeP!ü=6t} st,\ʍr*lR"2E^y+"|F?5p ЇȶK>Ϙ/j 7@gb*2< -`Mh҆f͖Մ3 1Ug05$tb}  H;qGUYGw̐CYvNE{uX~ ; >&|'<-uJ}Ÿ2K%2ky?߽#5b}Lb|&.ݿ|j,s]'tSCxqӵپ)=W|t%F_ӳy?qNs)qz˼5u <~#}{?>LU,M9\U=r0Z,U3rPΚ°S\0п@lVo͗JAQ>|~c'n]p!^;~Ȉ(\8JZ2ycl߷Lm6_9SavxQvŗs_w3"#i܂Fsm~ŝ'>6pnhۢ?o?̟;60/>ww\5?nU]1`<մ6֬%3}! e,]!2Xy&cAQMt6=uϥuLPy8J8=&x+xs& z^uy<)ܱsk z}+^8JNu6pW޼|>hۼ]7dToΏ6>9~7"|m]Gu.- Đodb' >&_ͦ{sqt؃Gz EQX2(u`m~ʕ_w'OOsf/)QX~bhI?̟;6]зG?bYeH^nt.v^Nv}4D}BSexK5sfZAVWcsW<HLTL/aڅz8o~\x &N?I|VL]{+,ɜө>$бuG%R2aNiͯ~?3?eDQoso/x2s2;uB[\:2nxd".z \k8q}{ӧ_C8e{bKxy뷽9* '?Ӎgs">:'zkG__߬ bpxY\n VO~ddq؛ kyyz Y琕Mn xsoKUJk~_5-(-ekFr ru|zv6E"n]ã_ɲ?xK-'n[ÜeM[/Y.0ql\x5A!ߩ U₷V,_@aπ~g[! F۞%HCډ<~Z5kMmO~L& 39h %$$pfz]avCPf4He5S'T<%˅Z~n%0[}qZ/~czLW?@=0h^0a}.17pݘlޗ|c/?v㐣QE#1|'A] >V@ژ162;~:6,[zx#z`њLy 3<ɭ/ }̵'P\RٳSەx6)mxmaXwͫS^".&/fM)<?[&Ə fp؛QQo W^|51Ͱ>N^2#"?OaNj֒~e/fM! 'xz]r0՛|Gds0؄` dE?:PD;p!}#6*=K a2yY6|_>͛xbUhڒ?Y7sŔdsi w׌#73Zgњ\6 !;ݥ}W~_5?FF= 83$ĝOs؛xƇ2_6o??/Xe5O4Ef3SGZ6k~Qрw~`ş*lUV4H+ PtL%挊 #2}_v>*Ptru.EIxgjf-GL0ol6|]ĵ'Tz2jf-ݛxޗaMW|/i|Ivn6=7,ѳ|7o:‡O|BTD ?gsW#xƇ$M[,^Iq͌0l^Wg爰GOq1y槜87~;+}嫗hW.ğJ7R\P|05|:dǔ:ɅGU:o bcs2/1t;{4oӓ3=]V?=fn}Xm*s̈T_;22ut??X <'8|< ,:IʔWYo_r~^ߵ+wI ),a^媭G!Vgk$$??ֱ{r+gyB@yHHh` uwn!>>3ti85R4jqHNҳӹkfLz9^-:2̱[lG+P]?0xeQ=PdAtxwRS)<܀ٟw=ѷGg(.o_-5IB R@D%J1ҽcwQ0%w@!{VCV7n7 S+{CHRcvAJj̱(T.f 6qtBtRQҹ$z' ,.SXAI29N9oX6-jY PoB={[K;-nz:c^gRJVk*TNhUP3W%s/h$:O!E+]%5kH&5ƒ+Ot, U- y{ \n]:JV^$ VjO\l8'6(d [oP^TSE&JkF@-9u{`7$ 5w1&w q-e<ܝ*;7JU2]i&cՊ[E1ػb~SS酊*TU\2bSdw!_PpHTҗ+>|˰e\BbDQv|b UVk|/kDlJ9 FtXBr.If~p1VR;6g ;o\ŻPjTZgQIAI\@xq*)%/Q()~6(었,ѬᏵ6<%OJ$Xxop>_*B>q9om d3uKqAj3%HG>j$[z"~⠺1ڥÒ_n?*$ %{6g-aeNR!t=t=S #P}{ojFM.ۤV\хʈ* (B-Jz (nW$oCPYyÇ*a-$›=^6KY IDAT޼SPkxJI £ϽvHhcqf[I=*,˾T+F%ƽ6|TBQ$ Oz^Jaqdϐ͒ g&&OK (ĝPH)YPaO#^&簢Tqyzj%L"x2 D$<}=.,7QMjME-MF@f; DD=QLGUӼm?hoDgfJm?Z蓪z=p"H<"S&,Up\/%ywCUA)ޛSx˜at#TICx$aIrCrYb'jG@#, E*J7]$FX5iVFPU7ԍ$ c>$f{J=W@!Dd\;@EVɻM51"!'(r.?ڪmװX,r5)E:ǼNC'QZM% "´/]<ǶP``FrPET0!U儍@_{D|7Ix6u_ Lqj[B۩#\,˺x:k^Ȗme3qci(Ct1 X,>QU304DdY&^ӛ ya ۂ]rp]ϲ4[b Y%}y"*a }My>TkihؖbH I<.k(*M="d}^==^g.pdcqB PU8g8co: YZVVk Aۦ+Ź:w~ VQ6+@P8.U\=Aucm61*6# fL S(*2$_aB r ڿFP=NLpi|V^p&Kss`uPQT9g]cEQ,E@@IL;*K" j 7{WR_*=nqF̄TQT~YƊͣTmrؽ8CN wʱR J̷bst(OƇO wd~,oE/ނ(wEO@aUw6\;7 'X뼗VkjrBT;<qpcnڄXviقP5: Jg Z|f&Y|tv 3Ils+)6"(n 7yin]eG],OcubV.ͦW6sYGU.-GDUUEj*!{FV;[Ywy!q-l4lrAYcSY,"V΃u,I䪪juV"iz}EYm{3av䥹Ks&h챯eS<6 6kX:"QcEREx<n7n˅tp8())pP\\Lqq1l ݈8dz "a %,]GBаPo].QM# Z>k[*RXvNDDnlkm^wgu݉jSI lD'[EXjq(AVq)dzKwQ)ciTV̀060Ў5v0m=ǴmZXӲ[Z.r&Tc)W<Fmg3[t]kߍ,<,`'c(vx8v$Գ /T*o,Ae/6Qh99+:bP !w#`GdzX(PޒkV6Y({W#n]jv*ٶލ(k VEQl(숞C3hu'6wOxp+|;V܄Y˶l,=x^H(wx%B lY(]='L۹bj {]4rOj}Y__g>מ˲]֍^a@eR?(zH"*P'cDTªcyfPk EyT%kfSZv*;7eqpT΍4~u*VIF}q `CxZ(Xo{ymVN4;l]J`#\lݿ]&̮jT /PKbiMk\.ie+/%*[aF#""9v4^]^el'o,?P: ;7[wϿS185sRyofq\>9f@"*е zyJP[ "T8U? ET\.WHފP6#@Cxu,Z4? ,\{& T@(N@ 4Ema@V6ٹQЮm kOZ.YuT-efF{7qLZcax<;ظ L@ױٺqɬCф0QgoqCB )>A"P*w_j˫lS2By:fykh6(cyn<ǧc17+IC(y0g!"P_>c9<; f߉ T[/OD{;D<2慘kehK\B+/$*_a@$r.XX;BiƈfN:F7>ٹ"";v=\/#)Cg}fߖHelf tR BpDs ѩ qa?7;Orٺ`m:P` Ԧj-Gel=Rcay8PDū=$ɻ*w}!]u 6?Q^yku&^[ؿCmOU"KT&)UU`$"w}QX ^Eغ'[sO/GxP*X(4 Eغ1QT0D{o 4`Awaȑtԉ/ /7pwУGj} 8Ν;3j(-[4i7nСC5˗x;we˖XaÆpB.\?zذa5N'.>wy'M6eժUzHHHUVL0V@ 4|Ǐ碋.{ݛɓ'[jgx7ڵ+{/SLp!CXv-,]222:M۵k__d$''suױnݺ6@ИhPI䂆Exx8o6 ;w\{޽[?#GD^^/ׯm۶Wh$''өS'>ZhAbb"Ceԩ=z}Kzٳ' ,L:/ǏQ߄@ "%1كn3vX:uDvvgΝ(OӦM9s)))0TU嫯>6l/m:t(/2$,,,m޼͛sM7( @И(A1k,^z%ڵkG~~>;vo_q5 EQgƌvѣ111t҅QF{ذa[6l>wq뭷NJJ <$''WχAFRꔂrܹ3vkazônݚp}UW]ŠA9x -[gip8hҤI|3ͥ}5v @ 4>DJPӱcǀ%Io2Mttt KRRIII5z @ 4> \r :tb@P)vZC7q-T=@ ,<'O T.@ !>$''3rHrssʐ!Cٳg-L PFɺu2dH@ʀpt޽K(A#&Mн{w6nh*4񔑑رc먔@ -B@ peaضmC AQ.ӀFe.:.@ uXHSP5kЯ_?TU%,, Y[(bCLLL]W ZGDeݻ.N'%%%\.\.}I -B@ 2Gb)p&LPgJ`رcu`ѵ\"@ "iH_uff&)))enڴ)iiiuP$Iu]@ T3"UTUУ!Lnll7Jp44[q/ Skl߄ سgCߦ*cƌitR"% 1@_ME7T֬Y}>bĉb^5LbJ 2_:жS8󉎎&??_ֻwoy)Ohv$I>67&E(l[ס\3rHf̘xOƍ⩂"rb(_L !%PBvΨQPUÈ#&&B+8@ T/}T(qFsm+'N}w͌:.Q Ԩs@ m*d:@ v݆@||<]taǎ\q.R!8sd&ی7FD4J jR@OUyav@o=;v0l0^x(O@4,ntn!f9Rq1YDmذ+-[]iL0' $ 8"PfaLCUUJ1,!WڌCNAN) E;iC/T}BFE=yb]TH8”ؕfDz:L AEsYQUgoJ#P"%5i*O<&EQ(n1cϜ8 IDAT,CQ~#}/4Pץh|HXu"d$)%Wa!hX,>/<\ i'2!.Pv*2"P"P @ TF+O"˺θAd=R#2)_Ѕ1 e&'@ TF';`I閹qm}kzvTgfl8V9U>w vl`U>O~n czNDza5~ !d=fJ\>e`4us=KHu2(S2lܙ|7y3W\GcI]rb6`x; oW-7Wyn2pj9_}!v&i`+ 9ܠwxyc- 0|:ftJl?s2SsX0Sg`4|ۏck?4CN,Wq+9eso>/k1Y7=z6}~ԯ?d=υ 1=bTϘ\iGs2⪁_1ÇJϓwSgpǏ8'm pxx% 1Sk)ku-7՟ ߙ +(@ V@7WLpQ]#enf^ӅF]щ'oٿss&s!~_/G~ؓ͋s?nsdn1؜țknan?1wm|::vmQnVf늟xT:;[ELv ődRq?v^2צaGyҜ=NË.L[x-iGyc;RUk.z1yΕ[~in#we塻~ ? _eo0=/_IݑaǦ ܅=mЛg̻#p9=iw䍵܆HJZh:El_ڑC{s8z0M&&Ύ&ErJy6{?ߐ<@|bQWtҾS"7s>"\s9 {&{4[z}7)?p"l4o/&4ISo#l\lGΉb8+]>׻oH[ۂ1Wu֯4V->o cȘD4wQ_e& ! I$! CZت.*KVVkj-z]PDTّ}߷$,B=f2393 $0<9sfȃ{_©(m≿aEͶuhй;+v'N.2BtLP<1j[Nn.,xŻ;E;1p8ZZF:<3}XcC9[Ph9NS9_VqOFkj[Wpt_ IDFh540`PT>sCaDZM@Q^5}!&szg0(s46Xݡ-k*}7}Ddw?K0z䝣BK{߆(u~љzsP[+ʫ&!1ʠ1aUQt^38 <%Ek;CZssP=K<֯<wL~ oMCoZ?mʲV!-*ƄVaŮ?|K(VsWju_”B\iS;1;BTW9y{JX;нv+pXY.v3̭,Saco^szgg#eVyUJq@֬p~eTV{ϯcX'շ)^}>})$FylXoP0̾!"xVP½ԉ ""D]hա?4^; cH[/juEQT+O}5IBtLVN^'Gם@y YIÆ ex0>]:,܌HæUyqu^Q'8FKh>:]ew`4x|GuQ]LEizջ=LO|^xH^hϯ^7/g|4 s*yuͭKu 0uN :-kNG'ap=#y`r{?ŷ?ISɑ%Lj;DF7>7%71* rqKϲ.&EOB(A?cvJZX,E7(YD1E9hj*d'#9z-'2r|Z.Wy=vQdhWTtNZs =[0tt<!سS'*JTw85~)™8- k)̫f~Xvs*3Pzvn*<~A!*678ɪMeyج]MGhTMU3+?5CġRN.C2ev q 4Y8~~$Db+l]Os(m Ĩ#y`fw3qz2 ͆e% cz[PWkfؾ^A̮g(9SGBR%yΰ5F Z #44PL&Fш`@ףjt:Z-ƽ!hP_6M5@\jdq\^#ݑ+@JW'ע `+D !h_WGn#"4yGpS(wMx-Bq5}|Wp]`o2(f` Bt\PV\]y]*PB\ٳɺI”Bt\P(h}呜Ⲓ1:⧷s_xU5RU.ʗN{Ms)vkXl淓BѵzLZO K/jy6 dj ΄/qSrkew2&}fKαr+OqKی"Y)䢯hl?/yO2ߝ;[du'~3f#Rɵ?8^_]̓b1.,6d WQ_2JUEh%S3+Uylݽ~̤h6ʻ(!=n؁Q=Y7n@tP}~ p]Wg;CƧ#}sO?w;Ģ[{]2[i,z'"X-ΐa4xyl-XO[V>.ed~yT9jvd Ww~=yݔo?`x附[Fp}y}FVtz-xp 䅷sx9q NgW7 ^mZFm pV6c޷uN'.p,f;˖ao5/<|$e% ɯa˚|~,Fgct{d~$ќ8Ա]Ŏ Ŧk5K%` !D ځ4bǹ[ʨ1˧sUyܟQΩgI{VRUĶu<,N)g8ԹUc9]yPBa^9^(9_E4Ecz4e{NiI&LK?yw^G'E_1̼~wl׎ &~T5m&вYj5d#ĨcDiR17(9SGV,wmWVJ!Dժt~΢4\CkS>*niqBbİzE69*7%)l]ϱCNgWlG8=՟dsw=hӅog8[PámdbSz NU{m|!#bn,J# !l*Pj(N!:WP70<}tGRx$`9)IƸ)L8眻AƨxziWUCūԦUyME.SUc-M64͹l[W@Cٕ+g}=TB}]'3|\(VSL\i}ϳݣ$$Faąrá9>k*WT%Hj{ P)!A_r%bGoIRB5+|͝sfbL(%zԹ)>24dD6Aޯ}o;˘  _z3KŬ1vrV͢N%%9D?g7غ6Yƍ1/ɬYí?͸)-f7?[3xX,(dCRJoEq:jng$G` 3xXП_ :^!D>g'9TU4QY[_|߽}UEy'+}CEKu [.CCd Nkh @1<vl6Պlf h +)Z{/II'Ck/~F+w#cT:qS?5kB ^A`ؘ^n`7o*&.!ի=Iregu%%-]R7 m|L\s秷z~ Vi]vIL$1%vgnRk/Q2 Vwq"`ֱ\BB\P7\'q蛓(4oa:JQ|y; e2/io#.:UypX >@ N QB!.M(F(@|CV'x!eĪ4Q` Ӭ+wW7>^=3H)eScw4_m Js:Ϋ)N RD'/hw }ߓLjPdA-*Ulrnk0Q=Up|+PBO!DM DպO:;;Ron:9슙#aS^Z RmU$H !ĥ 0YӉ8X8W#ʪ4rq);%Sb;4 VA3L/Bt P.“/in@;[O^=M} ~9q;8“m{*P W !Dg N^s]H ~ՇUu;رri9q gBtUiмb^{QQm5ߩ&]LjQDc0*VE9whOR< ,݆C_a@nJPP/YL4hP 0P/h0h4Bu^Wz^uV Q t|B!.N(EAӡ( zޫjxXt:l6͆ntVGgsŐս\jqWL-}+Dyz6VƁgBʳ z9*bIDATu(W]|z#_gӟoܧ0$!)x@4 Pk(߫ڪ@y'!踠 P.j(mƋr(JV{CNh 4ߛg ʷ՞i\$D !Dm ulS OʳMx\U||GW Mہ\!Dmri+H9oxj' ]K@\ ۜo><3(!踠P=|I8 ^j!_Zj^U%)[|o=Džku۔zW_ӝjU*QawQ88PTk}RU(U*N*OI 5y)P[q;!|>@ABZxZũ;> \4Mp`“B#Qvr$]jIBTϤ6 [ajopa C)+\*߰|xt'*x ,mj+L&ϩZ$< !D(=,r|U S '^<T$< !ѣ*P.j(_n*Uj+8ɰߙJz_!#+P.jU@U@SODn Uj_!D Z*JjU@"xf5]m{O!Dʥ sjh=DўN徏@Bt= P>֩=֩=B$@N+. ;j$݋vt1}B! PI.q9I`BGNhĥ$W PHNB!DϠ Bq%Bq?o^IENDB`passenger-5.0.30/doc/images/spawn_server_architecture.svg000644 000765 000024 00000071625 12233035540 024234 0ustar00honglistaff000000 000000 image/svg+xml Spawn server Frameworkspawner server Rails 1.2.1 Frameworkspawner server Rails 1.2.6 Frameworkspawner server Rails 2.0.0 Applicationspawner server /webapps/foo Applicationspawner server /webapps/bar Applicationspawner server /webapps/blaat ... ... Application instance(/webapps/foo) spawns passenger-5.0.30/doc/images/spawning_preparation_work.png000644 000765 000024 00000111003 12233035540 024216 0ustar00honglistaff000000 000000 PNG  IHDRF zBMiCCPICC Profile8U]hU>3;yCmK+u!aR&MMخ6Ut'3ٴ } oZWE|-jžT (XA ~gv;pg9;{eF4"_=}GiRpfg)ƮZS-7q޹-25+0Zj9G҉a!Q및 }?ЬVr/ =\KئƂV^vn519= r>W3?iLLJN3ٶ==TGfbFx(ب {t,毘AcmXEHf*0|%]x#Y/5Ia'r0?vȕ>c-orGysxuJ.ߴOFCQeG`n)CC Q`;|':OO/Vk!&rgXC`ERY)$?/:&||[xQۑޠVO>9C?EoDhF̄v|ky?pm͸ή> _:T;ҿY/ۙ5o(UYYTD{2jY}QVlxaui_2;;Kr5a}Xg |ǬV^]#w}M7] kt4iEZ>簮J[ԵOY 94)`6 `)+YK=VUՇ)uP=Rw1FԂ3;*qO6e7ΦHpV '݉83Grxvwۈc A6 oIdz'˖#r1|]8ӏܥʄ2dIS1eD98;TSbv ωd(Oh( >oҊ9<ES&Vm~D[Sۯ-1߶Q*u_{} pHYs  @IDATx]`TEW_zґ{]E@Q],(XPE"t^CɵW}wK.yw$ Wofg,CIP0RCE #a҂cHVZpF"F#_Czb)oe?D1CȰ6R"xy~e\Oul@چ&m\m٭m k^f;3>oxG~)2grK_2^Is||aL!?}'5NARcGt?Z[jڏkv;OU>TxhkOK<*j]Z}Շx`9{nF{\+@T#?jdioxޡ̰_N#B˩s84У.@ZW*` }[fhsK_RސFxibHPA``{+446_8Zn`x}㈿m{wiU UriG?Hb7hvӴz!9M3QMn r˕ joXA8fєgIk P1&]xrr)8@i>~8r;b[>>\S*)@\*p0LD\ә~qQD򛩋8jcHJ2K̻Q#aPb`"Q}ͽĄ ;X|H`$ }ͽĄ%*R?8KǨ/QJߴmP,hR3 P0ړA0,jq~`0#*R9& 0+8 u 824k-1@0Tmа])}.@02$l%hF{='}AD@CyÈ tGC9@P 6F 2CIARu6bTTu.5`0RcY$$d֑[+`P27-K"'􉫉&CV0";3*Oؐ\B$I@/kh8#JnEI.!1w\p0Q#x8r\00a7V_4)xF=*k¬>,LfY4<`$Tl#{jQ@"\70b( S_ p˗Ε]<e)W;Fp<|YFQD BO 4FID5"[#ɭѓ)tD)EWà#+4H`ik BRdz‘v0HfFfE"x^Di2_A_ Nҹѕ-eJ1R $yW:< (Fb᫂ҬGk&#:e Q:J)c-Ԩ4^jψ?h&kr!B LLz݃.~,C }C_ѦƊv wDYZ˚*7aS%[T{jXAZ$oCsff [nZ{= q 3_PԙޖVyQ⽂b#c&V Y17OJC#xeE{zw9`_Q&j&z%T(v^_/XXp3ߙɶ-ZIA2a rggv @4uzRJi _{m70!ټtjaFkQwXs&уl #YXj@(G6T-A͒-???`7}v(F =)6 >r%m,J4 9rxxFU Ip5-ԘoOY&JaO`wj 3T,lfAQM\*#WjęBJa5ǩ0ɫ(xepJ$+0J^0Rph93 m,=a?sӼ(O_\v"x48T Z^ӑ|SK10J^3*j1;8A䨯TYrҗS:wcPcٚza Ԣc"L: r6 QGnLOKKe|Q%v͉;ejH.*DQ! 'SfHhdDW3&Fkr4Z`j4UE+<`gp3:UP ,)zѠӆ~\ !*; +l4QjaD1'^F,QH:\K 16l4!^s~ HZk4bnj)/<Ҧj4#-R#ZF;XCsԈ"M#t<SH0"52M#g0'!!b ,C}p.dj.(B~7u^* S F3#Y)*AsS#R %{P0:NC]5)FVg3z , *!cn%Yd7KhQX+z|}i."uYB&X `yN049ydyHu #l!  sf]D0zp5Ka iHgMeUʑG B誓`VMQ`St;ѣfY$6gDFS9 I;[zG_Jw,C.mR[fCힽSg ZaژgˠFi !),8u#3VzHan;Sg~8 tqG\]_dzQ\]QK=mʺڛ+6iQ[g?K]Mu;Щ1ǔGI1jt+AHKj9OZ]Wdg6 G&ouJ3 b+ JE//A%ϟvuIn}F9-F##sV2L (`|C'Qh7Tڹ>^489ޱ=s6muu̡W.JO{WN0XYS D.+б+_>V`laj_x ]Dyis֓n{?УmuO%UG"ew!Lͺ]3xċCX7)\Aݮp)q)rf4W "F~wnLwKܗ >:+"&{VW_}䫸<ю[U%jhu7u"ʔ'aeGY^ak^ml;!;gZ5+DL^gȁOw^Fdo@_(`^Tɝ520SUN"_}&{ ?N+YE+>`|Ƕ!ÇټBcl황ɶ-:ѡӦ'E ]e}Xͼ&  T0 O@zHi:uFϥaKZP-,{ 5枚Emeƀ)O2DH;Y$a3a5 C'[Q02ݕpƪj|!!y\EִOhZkǩ^BNEʯ/%Flr>ÉNvqGrS3)~`JWB;^7t P' GpoL,4Qc`&POA`0US\(bQ~#ˏ%/Qexw;t8Oa m,=aTp_E"͸{ 2 &GVH>7s2YtE󀑦Iw>G* Hv9!iZxwȓb9g:[l:,:-c(nhZPA5zƟ9Wz|t1ań:FbyT0("$˙zq-0~.Aojp;e(&I>¨ײF:)0Zdi#8CKʪ@dR #flu:E`9^4 S&FF5Z^[B\FA~CUjl6TQ{-3Mci[*c E+RbT"f#F${# sÈ[euECaF>zľVO tP-˹bZ6X|Eeta0bۏc izH FoU\C#BՓ*@<> Fi 3{|iM>ume0W!Pc~+Q5T,1?tT:`T U~`~e0*e0懎j2UC_2CG_`aLuB&``D{JwL="yߞ>  q7qBw(p-W9_uH"0pm|P(aj\g=%*_ 5b=5ʗ' wQΥC>^a]pe6L 2 FaNk2ţnA,EE Hat-2,%XUa IpeOhh"!Ršr+CS2 OO a/Tmz9jx. c,7#C>Ɍvq݂}a`DedhݟGj/_~K`jDEhYt%$.]O|W+5QLz,AEfK-wE-(m\q)َav_c;[Bm ;|bCVY v 2Mͳ"Jf|!tJ = #EzBOH\

O3v&RYRFKmĿD2[#bo"IE`'FD :B/((*@:a-ٍG\R?]p u?/xb0s×ucoW_&=ȳT)d+aZí0<бQ]]H95._gT撙(qe`x|a}AB>F4Fa{6\b~m4M>G4`R,;N AEX3hyF%;Q/*u4,nGu?4FZ 8qBwݰp˲@0cHE!eRYeUO1 t.M R]PxkIڟ^GJJnMQ``_Z.PؖNm8g8[%ɬ6y(D,WN6_b sֈ[q";Vn 븲v%C2 viHx̕k Ik˳ D~vqVfʲK32b)Tsma֬]ߵf}۴ǀ؄Տ K\yxpmZJWQ׏ GUP`޸Gr~5W1<̤ǶRC7՗ȗW$·'V2~p\( o=a/NLg  }mZCX9 :Tۋ$ c'Bw}c*ix3̆eS.2ýgdG͹K PwVxOj ʒ]d_wǿS (g}^GͩFQфCv.t-hb()5 Oæ0r.Zi6ú9wϚd^Eg@Un@kl˻Fa5OM|Hylxfw#L&|wzGڥ;4xv7+q㳑s4وTn5;@Tn;W?Y݇lԧZG<|gM;ΌSӶ&șO_4O=$d Poc9jGçy@; ! O H?͘J'WNу;1n H:АNUuW>ەA-6|Fw'd\oȺж|[Xii=[%M+wG?ڣK}kcӭi}T[{ߖzœмU_Չ)`ǹ@uL ]Ukͯ.Onǵ]>Y.Rz\ ;:xE?s:o6ӹ3|7J7^veojNL ?-tD4Zp^9y0SڀUπdR۪#IRv| >}t`cb`8e-WItհR1x8@i[ l WtHz{Rf6]T5T4H1 jLƻtr SHd Ѿ5O;NC*VD"[!p2(>gqa]]10 G%8 #D'22O ߀~P<&8`Lce// ҹ?Y/h4lWxt|3n6q_-\Θ]+H8Qh6C5 @ʅV9L%W"bpp(Ca7hW+}>P^}wˆZZk>Mh *CUN}d~Z 1 7:kL8Ԯo9bx5MAcz`RB(ogxKV;~35B:}>5R#A/0F1GkߋX (iūׅFjۆоS9բ7|#|_Ϫ3U\F0hP/(^wW(tqҨꒄtB$.Z<h 4{^FoB57˨ # C4'pN.טGb#+xlFJ[uAB;`!`nG/TdBlAFJ9G0RF'Ŏll$83UA2i@mS: #E&Bq)AZJLa`D:~QDf1UXNcHV35vi? Z$mH-5h_t`8ҦQH&>PCNXe CqhFKa:.LbAJ٦CkP8S)A$Dd-! #$t:˳:v˱/TjD BHDLh_dN>0"Pn :it7툗Ji#uXhp H&n=Tr!rЯwoAC]/ϩl{?+#Y~VY;z!L(* l 󊞕'x.'],qe= FYuR֞0/")qE_3vewX9Wl! \LQ5qa%5:ˡR̮7 7qWss|2 ߆2>2`M:|%,&9Y"f<,Oxv.'ӻ0,v[k?2a\}J4? (Nf0/)'=Kp^L})LN 5pG$y GCdm>tc ޅx& ]61wiIj0 "d'd9a\WI$ґM3ar=ݐ~ VȽ3-O0:(ÐT'%T=ZC2=o@fz.IZ4HZ9ӊE uOLTGu# 1'HC0ʠ$o?!y!\"jk/(ɚ+ݧg8~42/_v cꮑanwL}h~Z*Y(~Qe% {jVCm?w*\@`6~\ߝzy|/xJ8+PsfRc^"KU_4c3}Gʹrt u11d[ȳ3 9䃗[]H a!%HVo]mjI^#ƱL 2- bA:s KT)ltd7sb4pJ;o?׀8V໿E7I]-1(wT 9K4%KtfyI~0++ wvM=11\ bU$+;1_2Ê (<'P?WHʧ89NyC~)?ˬ 0Cp%i:5 KLOЫ{6|su2sGq/ 7 :I@T),T|C}Cǹ)w 䉣n "`qMudmQ_EEyJXh2`,1 $2j,1 $2j,1 $2j,1 $2j,1 $2j,1 $Q#*-+)V%8 XZV.XVEԮF-4UdQ#]RuQ4a`K˸ևktŏuPop*c_ IKFܟJfT񘾢˴Sۣu|0u}xRU? h22#'̚/cc./?%|;/+o~kG?`D=L̤3u~]?KwI!7Ԟ@4:$EpnbumDkvRw v\SpP>MJLz8‹o9~0<쾔/'D2fr;2L>}gj#'Y.u+9 mUDRr$\_YشwnUxH£~teV궼uwAZ $\ڲ{]}h˼JC/p 4zמCJJ-w_퐥Wq=jٸLjV쐼a떯]!ňL-ݷ7]]4vLػ}ot#"1}]'K_m}V_pG!Z9b_mDF\(n?Cabp 3>*/h:KU._0:Qn* `|UJs`,1 $2j,1 $2j,1 $2j,1 $2j,1 $2j,1 $Q# Ҡio̙ãeƜ!JS`rU,X ]`f]*QtY>),0$ի;¦bCX; u1`06CmٰT,@(0P/WE~ڗ>5Q>tjCPi7t}zw. @ƒa(/6 5ӇuD7_vICP_Z%O Z<`T  ;$R]T֦)! =u+q #!/MOd16^ GGبMd F(kgm=LJ|1O< RiFL>H_9AsS, I /?3r$Bw" x #E\}hrU9xH(c"=(US`E^,=`R#N25lFa|F$ #BtYҦyi˜W0r+ yHk0vF"6_d8z¨P%4ԴlYp.1SÂS1Kpr*Z0uSGybVB]&^0jlj%# lsF.O[X{E189%>٣?2 #RYzlk4UBLjDXױ.Ih՝>ݣ5w{{hU;JWLqʏ GDl|X͆h e4`˴#ONʩbU qxb`~0Ios_}y[/ 4lNaַe:R+Gك>*Ô }p6+Q(jN|1ʋbvEex(Ye/ohS&sߞ_M<=Ŝӟ!znye/J߂{),$GmGER6sev)"UxTuXP)>f44bgS+lZ_KC|Y1>wW0i[ D_P5ti<2 XGvƸ?+_Aak#٫?xu+`_~x!?_&|,0%Ț^M gڱz$A]X 5pX~9C;6~qV?|'2ݫ8}ж|z_"{+EAO»ei8)Fc/?%3kۥDVZrDiTijvh[+?<]r*{D[{3Mvm-(u`!h+)ƌdԢL15\s<4s\;h:0qxr,x8[(7>l /?Ԕp5j+ )qpO÷*\X1D3~i#5fRt=m s ȲIp(UvU Ob65aW{!SocM=ETvi'zY~YƋV[ԸDfjuzE6/{tApGݕ)O軕9:,I榤jyqb;N[uztJIA0%^f՜){ct.ʇ18/9Ps`5 w85txt^%51T!Os"QG"akȱřy:SR4GEՂiD6=\08{V<ϗAFxe c¨4.b“ s˪/lұ&\_Uh^jaƹaD2\jE=*au8Ů˩R|#"nBÃzXjY"fmF G'jsGޙj̋w<]djJF#㖮AQ@̕|YbF#%8Yֿ2X>Տ!*#"qB'5*!*XwY%;`;%Z!5+$Iӆ%#bA "jXI x<(4@U!*0q-XzԒ_k:}GrUrcգ?<Î/V7L>P|7e5ʰWۛ2aa疽D4y7p}Qd48Ց w_m[U' p k]fQ쁯:n{qF^ߟX5'ܩ\mdw Qҝ¤%T >N*c@F;?Fb1oU/ 9QV@«W'7TlekKO{+7¯_¥j]Udm$[Ժ+06Ó_4tM_};>Ę~[fז!c4k3iU7όzKOķ\h;ߙ6wUX6.L [\oo /;R=-u*ҝ,PB"49jBS=*!\D8A@7^E]7 7ےS# H{! +SKb&׏~ݤy76eW(5_ Tc@>le0$RFe0$8jރ HB)R ˂R#I OtQH%HKoLa32\N->`ћ7_:ǀXB% /^ x@Hoy($'b|uAQC ;?b Ks B=3d^0Ap(15#0zAИU XFȲ$kI3^& "}<xz/RC憑کD􉫉&#V]u Ȅ:`DZCXw[xnL mFY99o([E@q+shvH #(9?<zjz0zd{6Q._uEм-{:Çٺ=#eh0bGzI,-cu"wϗ4⭣U0)XGيx+K&thG`9&ĨPMI;XS:<"Ú( NK5EހeatejJ!ȭ+C)1xL-3AIFXQƦejzB!5e'.ˠDVt F%+JȃKAv C苼atr;#p #:iHݑ]a(swuxTk{t 0|fBNPV{D⤽D7CɥVЊ;Q 7!|9=8,%1*_)1Y>0RiCM[&uZЂLP3ơ-燬hQ'0Z d ˹F.ohbXp#5aue%V׷K^xo]{VsEzEgcۆ}mW\2FʊmQ+.=QuTC,ѷJagh]Z 'UCGKru꡷5VI |9}ɖ@IDAT{G"66{}|旟yJݶSKo~B|t#ɉ#—>ә?+S3hB9 zFq$򃷭t' ߕzڿpNZ&ݹ;3Fvыm{-)k*jnXṃ  6CIN->}b%YG]Ɨp G]HoYx3cI#ޯ?T}m6&tڕc$O<Q;"nה' &UtmlM6nyoF; VkDvB{ӽ"xQ9I^ʏ\ޮTАE'wIC6*u¹*}&hϦ{ʓh([>qƩƴ,c(8/4Wla/]Hzvc\#{C3 5JH},zyG6TIvG&&IKHH5 6zjFLo5*o?>glDc,; ժYb%/0 zDyu{HXPW%vt7Y.3UImR;gyޚ(x*.7q!I'J*>iЩ [760<׫*&K'HPOtfqt}Ewj J[niz(X2Pj `JQT*PL ,F$(0Q WbG=a•?C~]ƺf(ۂK8yUbaU(ٶlZ'!0ōSD=8}[5|pj=ݷvu nQVG 1lMOKMM=eM ]apraa&5, q=nDlVzʜbO5>e^FȆa&US ?< JFףloj;m9<R7j5.ԙFjeԋzOxzG8S3.brh2([<$XBypzLsFOGҜt:==ʖn m=aDyYKx-1(է^"5:ųlKQD ӨeHS&GjtzY3IYH%?|@HöcC')zLY¤N=xhҝ 3-߶Qu3 Du!0PL{*Gc*q6}b&%+1 e c|-l  kK tnk5x`R$Q5ܸDTk (8B4NDg*KBjRH :lHM(0Aq,e@nnub,br! Fi]Dy6tzc} K=QyE r)7R?>I׮ 5gM" #Ɣt*-cjdhQl:"H8L]F. rgD?ѲI? A61\zotX%e&r@Sbr0RdiI/Y4S8cL۽uJpQjX҉ee[0Q.k/(_i_Gt8ҖH:**87T/ |g*tG p$͚PkT4CAcO-0ўg(j]MTxse..]z8t`7`]0cv_a'T#[OH.ZUPGV }_/0;iFg$^婄G?a싂Z8%tĵ&@O"x`]4!N;>3=Br ).0tFs3R 1>; +10{Ξ/s锯A #֒!$]fv:sݙͦƠ GcNi' ?y2n75,O\N:Y~:k/5\[>_gV4W$͇=.]<)?﫾Z#MZb'2\g.^>[¼5]Zp6+N|n۫&{]di]) Vou_ټKymu[M|b߿x'kg}Տ]ۚ<=xqZ?#XL6JƥH@dŻ Sc{ۢ#qgYz'+uJy:'b LFt*t0J &.hy@hylKmm+^:|<1MiӦ/ F b[ BƊEXFzka7_ʆzU~X&4_o\L;י4&m+!bZ =wpcNurƻ?o[ NZm=RY2=%BS|zm^,Dv}iVς&#o80TR_-Ru#8#)31= _P)X_CU# ET\dO~O'kϧd}ʊ|Nϴ$A>]q6N5.G~li{uM|Ev6||sEZ+֣2+@ L/OMLv&n")Pk:F$ 9rӟ갫Ql, WHtǗtIFRU>CSR+Ypq=~{^i8w]u%KRVNbdZAJhq^ZERJe Wౡq+vB԰cp#37Es,ĴDrˆbL6ѻN-v\WB5:|,޲u'QС6 ғ5D ]"_uODykIG*GqG;4 &7d1$iP]IWC2[=XeHnx/TFD4gjnt.cVѠJwrxaDu.WۍS-XNÙ2Q%C/(Iqw;sU:oht[3F# QD4(ñ S0ZU)GHP 390EUR!0Ԉt%% Y*IHGҥH(P8L nǃ@tZV^@KF #Pkw;;`җƬ=<2QX0D#0FGUe|EokQ|TLhD_3.ň523$wĨCvsM Eִ-&v@L>K=\ѕ ˗>8.t5d;;'_y w >X9h!#N'Θ.Q9Lmc_k8A_|1› R+\KHbBCeΡ,"S;y\b/t+:n hTU1p8#ɨ3G^2XQ8B]fDT 1A<O1PQ0KZƵQ 0 8",j/L:0ƻ7L;?\m̿&T\_MkɪH'VY_WEU?w`C:T˞T톳'>1ed|J-]憩ikF$sUoz}뤓/o)m煉b]MҶǣck c]F83dFfnؾQ'&mÂҿ~fx;FSd,x lU lǡq1+Q2OT>7MC7!e L\j+'B,4maJ_q2[RuvQw_4$Ԃ-m>o(PGQ"53_7%dr6Kձ:0A,;`T>pyD :c2걥ǩuaٝu 84[W`HWы%ES^:S_se*=Qޕ7dY0">o ŭ7Yw 1↲ 5k~),,L9Z Y^|G[tᚁ)8*TQvq FTԆJLeo;(gFB * Eņ lU U*HC%@zeo.{$ȏQrS̛7MA[硈4:D(c@w` I4ށ1 $;xƀ DH-#L.x9:"<-w'a{y; 4_**:^a$[J &M0(M&J#̴9e!׿JM@`r1NN1 5RB:y!G^ɟ|oMHd,=8m+኏* dU6<v ٮ4a!Wq|QU+Ƿ[۴~G3voMUhQV7d̛'5xyʡ)0fL3xn3;p)AΓY0͸0!lu"ُJ-==䔞?j0wZ:wx3y0d _d}f/EQx'4 ̨ufS_6N[]9- ͳ<3[*^h,yLg_^1ģ,DGWv>t2> ",c_v|F84kLa*i!..K ϣVCn نrCC"BrA  xǍDh<c5f1>Qy>tе?Qmjg#qpc:(9~߆j_D/i?}ډ*>=%Ke4XZk0*Z(q9w@[ 7L?J*3~\C_̧0޽R#8qOʶ: ?a|揳a<jm)ԸO?c{wdk̺ۭ0?[㴵o|۰Ǜ\8[콷GOLaR)_m/;}R>CqViqծL?X^ Y:d֭ "@8fs!\Q5A#e<)@bxqqyu[#3Y&O ny0m*+*wҬ֪l!5d˩ @%_z*``:Ed BO AN!{] $J+ƦAI%b0 c x܀f~b @+/WB bxKp'~:yWx V._e8d΀ "WЃbtЈPXSHIA_XA]hd:@$sF?@rFOLyF'&~܁<܁?|hQ艉>,œ~ZfQJ0TS˥Sg N9ZVD3IJ)Z85ꔂ" Wg$C&)puK[ae< 'AWB$JyՅ:G47F@6wwhXw'_Eha v=·޺xށEuMvтFdHvo+&E.4*=E yprQA. kGl`T8e;qo/̅ (Z O]SRo]Ԕgh6{EZ \?R‡tFrw1(A}Y1bKPS =#h[BOf>ޒI/ 3iadXS%(8@KzR:*M$ˁt9rrN`Y\+%_ڢ Hp0\%p6Җs)Cvr7a")UnyPCp<̥d Vj5lj+A)A%EDZZRV24vP=YB8Tkhʃp$Ip9l,FiʞpN\Ʉ\8*2J .QA!3m̐iGB- [g˧RCVpksUwP!HEJRX 䧴"ODj "!T 8+τ@p"\ʟ#%Uw䐁W䁒I yĻ.G%bW&m*@J.j0AFQl^+D] ,6^On^sW]?|g/PSZ͌f6׳&WXPD- ވ-ܱ/uΛbP"٨E¹?>N&goijw9Qs܈/ p%4>b*`a82wtUݿP2cc~/}6eg3>sofIJK q05EGUiO 뤧7}ݝ_2^|}W>l-[bj, E5IBy<:9>'z9+}o+Yx=wE=q4t0PLUE? dxդ^~J~re8Бnx{6Wg_Y$)?MO 4=t_|#6껅|tri'i-{%s^+4^Wupi3C\ cs:٘b ʼnJ^OUcG$ tu F'.Y3y3.L|Tr\tHgs%[ۺp+G}9def7z3)o Xorm7v;f5H/y4" /E|KW=+oLiMu]O_}1K{]Ȅ }_Hi$3ދ|I'}jP6fPչ+c7:cHjWV_HV}g-F~\a"rpBȤ禞ko/Ug]D^&iU"Y113M ^!fX,RGJ#}89YYi#~@E9l`,NTJ)vq`Vjoi[Mh V|xG}@ bV5(G*TOc6I()*'/ɑ*dܝCC84]C 0:5N 8@;p$=ªF||[.VJ2NCo_cYavC'GR HqnRG"rٙ'n v)IņҨKU^0Xk`Ÿ A?fsSi3#EAS7Ö.GF I#aFr,i5V)7 N=/JA CSFw#.TJc:uQ5H;Frnf }8dL`AALaJ']srUyy\^'07A4MkD…#$`TaX\}lA{P'{E!\WYJA*h 3qP|W2'pcLdD7Y/s1n|B EF;S=!G42 |0ddrD3#ot4o MѐYqw3,d b9yqJ A DY(T7fAxdS1cn{U3$9GGn2Lkʋ Qi!4w D8;ԯcG1jDYh2+0^EEaOԁ x㏿ǵfJZ2*]i=8lzR42̑P] ^4ϸmW`0LɻjFLѼA5`d{I.$ tFEqa2:Q*ߟ^`p(b)䉫f>k '/g,OIzsSV _NS7H2ΧE%ބpB}I'>Do Anǫm6)pި9oVeVO(Ƹݚ-l]W}g$tƅT!Ga']?`G?o^*p}Qά{+U@c2UOѫ(r^tFAڎx2ُq(P塚O׉tK8?NC@;(9e.IJG7 laaO;Or(#^q.Ҿ5f̓lnBPW0G ʕJb2-3Zr"PIL&QY/YTxV,*ўz`LPK/"11@kxk.DY#횂CtPrN}*yW8qs蚐Ho3h2^?.$g7*kҿ97ZPr㹻2kUF]W 6(3.&cBHLzT1&o)|p2vN$/w<`#RK s_3DrR$g`j_Yu2WL @gOى$\cs9|&}YBG< t k`eQI)?{SԵ9"ti{?cr{6, ֦s/ty4:W4<,j9qG,A4yxr Ud?qͩym$0yώ={/1gF:X[&S 2VڗKǗ=W~b~̨ o 3~3>JLė4AwYޭħ5oqMYoŠ zd[{"rf’iHm[cp%RZ;G E7}o8NDoR6.pϒssVvs'N H~gt_~KZ2qš.A_YCk6ZeNONa i;fǾ$Ե3M_%Q}embeq4CW6L0J`b[EemG?%؄P6Jʎ@YA PȈf %.ӕbQ`YM4PM'#*La}{6e5k-ЮNdY)&RsPvdnʹ"Ea*ܵ٪ 2/-ʡR-wl(Z܃]ߞxв2~QޯB.a+hPK?岤厄eĦ,#erH{Tj2{%>>Jղtq:Hܳ.(&?_Jfwle"ףv*B?UnKqJ"pu~ֽD w>7ͣ3{s\y!UHe+كh #?h%ʡ3@+C FR.lh7專Ժ;'/UI^[`ʶ׬4S!q%!Wd%h=G7äQRb:'z(- ~hב8F^8Ҽ:b`zzS(Bؤ=+cEVj2^QGpC ƦVȥ(#0 ^ N%} ܋`_Z^e ٹ8hCʄ :J8X9vrHY\4UhaTC1Ux|],d*{hz x+04"KWqjgT hEK#rFAg[w&!,}k _V阑~|ťr6aIf6 K|{2cIɟNC0aC tWWsyCRk耿$bp4-d{2xǨÂB|fȼ|^L B6r=bHo*5h1 oy>I7uhl+Fs]+J9ehbOz ѥ[bxޞ>eJ;6e#S|H:=&6J \iYD1mHQhˁC,q d[jPa/MMW .Ӈ%5Z7fnXQ@GiƒO},ʑ敵QbSKW0ʲs_W2A[=pw#drјB޴Et6cb0ѝ+#? zޥWҖv9?6G)*&~U4[YS?JnZ@ozȭz=NRx}GUu{jbe+4+PA6b(_<0XLqpP&w/^$_EFޜ%̕C苓)sTY: <܍r%;Q7- ͙d5) zlذr ;aLL8s%ojUO~6^p}7XDuaߘalB΍6e`)+;td0pȦ18l7 œXax HQK1ddPО_pd1bT542瘟,얥6Z!eӡd~!`?`z.΀u#;۳YQ]3D8)ϦU9MU~8?_(cöߩhO\7ń_T;cva)o(%%2g/1노sfr6[sWƘۗ-컘K'\+bLߖd XGy żZnZyVojqS:Hzėδٸl5ɘ5s/ܓO^sްO%Ձο Ouadv9)DIk:L&?W=mOf҉gưM5ٴѳ+X䉹ԟ{Coڻ/_X=Z3ydh w|Oxgk'|@|>OPśFwwO:٥Wzi/>w`ۭz+=Dhf7b[A3\gZ{j;lH}tKw֠%_>!NXir6DWOi:ӧ/׼댹1%) e\)䰨G [p;ܯލu‡[E^J8jYq.uCl,B#"Vh즧ZZv=.Aqì,ׄ#:33ڲQl͙CFw+,Wr/Qbe4JG Ie>F>[(q8{*ovAO}WeF2BϘ3is-V0 z9jΚVXQSrlYX),[ٸ ьR#h$h 52QE6&7OM2ZDN+ J# yF&cC3X|cٓ •<FR^ȟ^А>133hѾ}?׋I8%wNz`ٱ묄Ƌ7sUQ#1 G滩,DIr# xWlkNԒ2p /R$Ym[Gw龪;ۻ/Lo8l`~p=F,_^9gah\,X󾃑N{2= Ao10;;协}4}IAFJW szxx,<(vXs2SsYduwTAieF7'LjI f DIDAT&>=@e`A=#<( ˳fʖΉ8JaP.Φ\[8mɋdaq.; Z߀!C9l8[N'g[bӌA8 ,%?&VHcdG1RIEgKQ99U۞\#&)nz>@Ώ"BC̼]<~H#)/I9 8n7!;GeZhI[ rL%DǕe !*Id%S$$rfB?A"\kg"e e,ld?xhG$vP$d98D E9笝mH"`*R2-ͫyFU]dR_IBj(dKddA̰<4PAdJ"@UhDo\(yI_?`{r76> ?9?HSCw" Aרko/X-^vE'_Yls:z4b#~Ti@ř>+JJZJYk?M^*$j?`at" Gj"9lR{?g!5P(Ccl鿔oX$|Q$ xB }36f|S.| S4Y$3%;mJ%(M?x%\@S XM9 a62A8d@E27I8Lq̩R xKT;pȏA Izs"qmY&/]d2 KQ&:4k]U|(*!3bexѶ;{X) cՆp'R9Y ~8?(RWA8{kW)*/Z:֗ƣC?责Cw[]xv{M̟ci 8ƒuC#_6Yvyw;$8%6kIgNh(=BPn:œUYe @d67ܓ"r4i$5D__*5V'hV|,ԧQb6ۀ1WB#;g̚^Smit=nbGOCcsmӽ_y{? Df wr>_*ss77aw$؜}tХ2wrўO UtܝY{}p*!cjRT](㮨kGQMtZsV>Khf\䫝5_Bf"&T:ԕ#U>ڲ+1Omd1:1u,D_ ٘6><-Ew=Āo#UY2m9FOgN0ۤ^ y$P/&R&T <=Uz'F~]Q( 0sW`bSh+S eojܲm/0B`9Mޤw7;2!dZa.jo3X0>ddA"z޹+ 9ՙvĨ5K>žF*0j$)֜}m^H۪2rC]L_N6voq3.L4I"tީz?Z#+?.q.)R FFITO'2~[޶uv]+`:"j-CvL-GnyO+˷Gaӄ Ti՛FPM:,f'|/mB)ԏM{X{&v<[(?mX! woڞlF51,h,xԅ^wD=)㐍C&azK/|memW6_ɂmYOj(Ks=&4wQQB|a\BlP5AQfS,%`!KVG0%X">i ͘EvLvLp4 A/wag_~'We&xF(s镀F&6 nzPYBv8~ fEk7 />)OM,u\0BeTLf _IA2~ȅ1z^ bl&lG&8gaP# k[$bF ziE*HlS9p L[׭L$;"]$Q&w`fBnSջ`H͕4~.l&2_/GLgbdGa.Lݺ>g'}AU7h4[R1a Fُ 8Z;ɈU Q脰xFT(]a?E^#v4h* a AE1!i@V'ja!2u#-GWRI6|p:,5 GC͛$͏وIBWPs5hpłEliz}E7윜)Jg z!X~(WE|. N~⿾IK~RmNgKןwѭۺR/i,+Ԗ/ V,JWyrxTz#Gv/k=T-]~x 0u{gZH}m#I?X?6uؘWS\: #IL"~>X9-?>`8 #Q`k+54r\/NٿCHt7ºNhd}L*K%+;3?>VcE 9/U8-{LXǘcϱ:Q;Ы,L!&\$n/%S,5s N:`(P$pv1l2IV=Wu`Ab H.3~+7 F `dRuI#Q 4DN8Qz*5gab6p "8ءCi$.Փ",geYat6*RO`YP$4@E7*N&0Wv{HtcIkbz =D=E$<āv}0 muatfpJ^\nhz1~Z sIENDB`passenger-5.0.30/doc/images/startup_sequence.png000644 000765 000024 00000266657 12233035540 022346 0ustar00honglistaff000000 000000 PNG  IHDRXhP AiCCPICC ProfileH wTSϽ7" %z ;HQIP&vDF)VdTG"cE b PQDE݌k 5ޚYg}׺PtX4X\XffGD=HƳ.d,P&s"7C$ E6<~&S2)212 "įl+ɘ&Y4Pޚ%ᣌ\%g|eTI(L0_&l2E9r9hxgIbטifSb1+MxL 0oE%YmhYh~S=zU&ϞAYl/$ZUm@O ޜl^ ' lsk.+7oʿ9V;?#I3eE妧KD d9i,UQ h A1vjpԁzN6p\W p G@ K0ށiABZyCAP8C@&*CP=#t] 4}a ٰ;GDxJ>,_“@FXDBX$!k"EHqaYbVabJ0՘cVL6f3bձX'?v 6-V``[a;p~\2n5׌ &x*sb|! ߏƿ' Zk! $l$T4QOt"y\b)AI&NI$R$)TIj"]&=&!:dGrY@^O$ _%?P(&OJEBN9J@y@yCR nXZOD}J}/G3ɭk{%Oחw_.'_!JQ@SVF=IEbbbb5Q%O@%!BӥyҸM:e0G7ӓ e%e[(R0`3R46i^)*n*|"fLUo՝mO0j&jajj.ϧwϝ_4갺zj=U45nɚ4ǴhZ ZZ^0Tf%9->ݫ=cXgN].[7A\SwBOK/X/_Q>QG[ `Aaac#*Z;8cq>[&IIMST`ϴ kh&45ǢYYF֠9<|y+ =X_,,S-,Y)YXmĚk]c}džjcΦ浭-v};]N"&1=xtv(}'{'IߝY) Σ -rqr.d._xpUەZM׍vm=+KGǔ ^WWbj>:>>>v}/avO8 FV> 2 u/_$\BCv< 5 ]s.,4&yUx~xw-bEDCĻHGKwFGEGME{EEKX,YFZ ={$vrK .3\rϮ_Yq*©L_wד+]eD]cIIIOAu_䩔)3ѩiB%a+]3='/40CiU@ёL(sYfLH$%Y jgGeQn~5f5wugv5k֮\۹Nw]m mHFˍenQQ`hBBQ-[lllfjۗ"^bO%ܒY}WwvwXbY^Ю]WVa[q`id2JjGէ{׿m>PkAma꺿g_DHGGu;776ƱqoC{P38!9 ҝˁ^r۽Ug9];}}_~imp㭎}]/}.{^=}^?z8hc' O*?f`ϳgC/Oϩ+FFGGόzˌㅿ)ѫ~wgbk?Jި9mdwi獵ޫ?cǑOO?w| x&mf2:Y~ pHYs  @IDATx\Z67B$@p E4X)RK)5?EJhqmq-] @B3s&odmf߼n27w'/AHHHHHL 90     #@qHHHHu3d6$@$@$@$@$@qsHHHHu3d6$@$@$@$@$@qsHHHH@a3lH 83e$1gIH P\t!*׵"d$@s9(DE7uw2: 4uÙ1Epѱe6&$վN K& @ P\7!3A}}䶷H ijubpg[[Y_ + lB:(L` >ce,󼢢B ~"}η$ h<<Tǔ!'iPCl`<={̙3G\l{xi8 .@P FO>ҿ<6: @ raݚD (A "zԩo#<"oFbb+w];05jtݮ&M믿^y 巿jsl`S`Y# %@q}ǚ@j Kr}1BN9˥cǎkɍH { ,ZȄ5De&]T_m)kN$@ @pk kl+g}f%Q94TY@ҥ~f~'䬳βkm\E+IH "H6c ʾ kXYt%>|-FmF8 7n,Y8= $@$@M#@u1uH XpN!2,EEE $ DܢZnm/3KrJi׮%,8A:M!h1-@R`hpkjFp5E{pCTX Du{7р!- ,6xg7 #Q\hdzqȄF UL 9Qk'C$@a"@q`]2J20Mq!,H P\tAV1@6 lnN$@@>Kc蠰Nbf6--,H 'rF 1f~S\gs/$@B:[zL U/˜) ?)[X, @N[HNu7JU/IHHH(JA SuhNKbmkrnZsL`I2En!"rBE O\])PU0 *B-(޼k;qv2ǚ% 5ρ&RƯڎ3{FkF>iƬ @ fp{oxX>9SdܹRPPP?/eժc`u u-'̱fI!$5/vևDҥK YqXrmJ)**֭[ۈ 8}t  [HZ2l"°vn+a3yE̲|ٓ'g| 6]JeeG>㫯4h[֭L6͎zRUImu&`  Fn4:& +(Ǻ{4+jKL`>}Y,X z|GˀSOv~s.j>Cdѣ ٳb?I~iٳY!M O<͋1HH(J鳚`RfuK2[yl CxsZJ|%Ȼ+Æ GyDFx1cY;8oXwm7wrg3fr5n(myv}gIH ]67z4h{&1aq,c]dsKH7 HHH hN#\f]0jښH,R"juSWVU[n)rBpQGw2yd#~iKfŋJKlEr8(Y7+  n*AjGW/6mXI&&lb pBye7}poe…/Kye;#3uii.]-:uCP& Eup^\gF  h4 G,Y79,x13%éSʒ%K//N8Ix9rmV^x9- ~htH ^+L"`߬ojcHH hN/_r#XRxԿ Y; 9%ţ>^R|娣]w|q1ˊ`2x`{ᓽr iizn@2F% h&cX#К6{v_j{c=s=,ڝ;w+W8qMh" W^VK>#}MX"ÇK%> ƌ66qNdHuVt+.[XeʊbC5KFC0Kh3>}@Ư,xEeF 2/Qƪ~Qd6 $@$@%@q^=K@|Px4 S聝4mq|D{O/1 @΂NbGB΃.\6,19Ӷ=G6՞w 5qRgM 47& >!1(klOMk5rmA!HHa(ƋFB/aeԚm$@$9:sYR P|sXFyrhLH$@&@uQ1b G6 dufyp/CXEVD|IHEn!HQ%ڗQm'ە\X'[N$1\g 5 #a։HH-g1 @H rҎa2G־\,2Cpf)$@Mo j_<֑kD$@!%@uH; יRZ--˟ gr-\Xr-Qy"4 ddYK/7wF-O (in` HO1[A񱎨@z)9묳u"z]0 4/Zgl_!C._~Yf޽ ZJ;˗/_;ʎ;hL\ӯ,HHO2 ]Tz7j(QZZ*Vʕ+lϛ7O+[l;MbH: kd 74k ۶mkI 2 G|˜HH@hIIn%DCaAQTTdGҮ];:t,]T*++r HP n+VnI9ի`89x ~s???6t$ zh0k\^`K.&}M?-%KXf}%RC 8?X@mLcuaaMqKk”$@$@ @q b@twnh<,}uL^1E!Ĕp&U\Ar$?\XNu"a#L H봡e-EBbKZhpNT%R}~Bqݴ?0.++ ~k< <\@<3xQퟠq@>_#$ 9I.t!8#87iN j_FTv.R]MQ5_\w5ĵ[K..iZmHH@d `pK e"XVuuUuR{\_FTwW`Ƌ\7 >c~NnF`6qpI$@$4M!%BBÃWEe ?Xݲk'kLk38|s/3\|$ hqc," .@ .).2j.}LWb*>kX7ܙ…exmˠ$I$@$tMgBJ (E5u:5D5`ߤu[^n \"M# ,oa.:P,؃5υ5,j g@X5?N (  $LfY$kHZtYga8אPzԙQHHH(KH \̙3eҪUzb Y=gYO>RRRbp:-*`YI ˳'ǛqǏ?(/ِV9^kِK  HhsH`He ƽ+Gq,[*Xp=Ƞ ɥ^*~ :TnFYdY̙77Du:モ;Sf̘̣>u56+For&Hc5S!Nr_<k5.ɴH * @D P\Gc٬"PVV&O=qr'ˈ#"qm-q{UW]%ƍx@~mOϖ[+W33,C _Ž-]4\T`)؃{]vvmۙ}孷޲x ,0˨QL`=m4[GyG[؏xGu Lwe_|aB""믿E-**lv2|s8h@ ¢ŋdw6뮻ʃ>(w!+1  9Cdӭ,x@:pl߾ ,?pk'/{ 7FȆnh 4HHrޜHBD7ߌz1V+®v*8c>,}FcoFLі^]9'b͋Hu5رccj?íZrcj厩}vOUUVVW<ӎ*JcS1ةcLv|#cjiu袋bGydL|L-ȖorQZmߔ)S,ZeWvL-vL-1hTǪ:;N16pk-&viYH}cկ,sm[^51}֑߻ލ 8#  H(̡qJ&YC6  e`3|z_bSO=f AE,w7p V~isfA@**eȐ!KtPq7wAdXq9 wyFý^'˗/`1 ZF3֑mo7^քeعKg~O~xr)5np; \ab&y:?@]ZPG;^/6{K:sexXpl;:oSK?$@$@YKn!Yux=DA ?1\Akԩ ?^nKdٲ櫌tjEM&W˶ Eʞ{i.ezWUYe.ɝ쳏 WM[ɘǘ/3vD.CC[&J3mԧMI x2 F 7ǜ-rXlrml&7X;0x4h?68?bۘeJ2m,.IaT$ @un3[e`A VH[&OCF@~.H}ӧ;7b=B<^TE(O~rϽȪL@#ݦnj>c(EEа˅<#f=vq1 @{DP>,|9"Wƛ5b&/9rYjk[EQ8餓l? vV 0 &Zstyn9p5lq R1#,x)U/e& Y]k>s/K/b6  1Y2UVoYz!sA\X{1[ ,c=fsߵwmtauhk̴/cšꩧ \SLˡ*xy?؃Qn&׹ko+^"aK^ϋK  !r,!+ wu7ǧ>Q W -{pKFmd~o&c4@bZ9?aSaGigСMGlcw@= t!.!f}Kl>aoyYG\>\~q]w "t1%,*ێ%7C\X1u k.y&1HІȇS)8B]яYS0[f)x'^K  hϟGٺ,$Aę 3XB/[n/#B-`?EGdT3ho2Z!Gr!q bEH~ 18p! :StRp+ܹ/]v~%A1eQ ,'^W{}5ciȸ{o{l gE U+Q"@g⩂vm5~MWk]|6Ȯ5w $\da'1lOw׌?5ƷRƅ:x<̫'3-~Ξ֗<SחH߇KK,+Hד! +` Dg n߲eYL` u}m;SƯ!@s2Mǽ2&3^,W]yU  :˦ dufy4    pi$@$@$@$@%@qY,HHHH (#ܹl @f P\g7K#   0w.F$@$@$@$YיHHHH"L:˦ dufy4    pi$@$@$@$@%@qY,HHHH (#ܹl @f P\g7K#$f$@$@G:-&bR2\  rY^VH 򤠠jٺuK&z$@$@$PoFň$@ %0ey7%??_dѢEŃ>(&L%K&l";SohO$@$@a#d ^a}H k3gJkfחy%}r1ǚ0 @6u6N!'н{w3fӧl@80 @dI  }תtR1c.~ 4Ȏ{0u"  /)#h !}t .mX۵kg`0 @dI \,K/Rڵzjv^mI"E$@$P %$@"q uIIl3o7tSn# @T|SgZZf@d,"N;$ƍ/^l`?y\?&O $Oj2ߟ  N VfVAW:<ֶdKd{x㍥Ь-m k=!nm}amE$@H:{un0eo78s&X+p-..CZ<؈^bmi>.z̜HH(yDf,XKS @1vm';{Vm qM<K|Oc?(#@$@$|(%s !:tp tSMeS%,(ȗ.]6[omB{*+%/ =jRr\@W9]`[3 4\BH UXeee,YD>?^^zUqd+LVQ\i8t*ʕ`<̳R*MIO;S8@ Nme{`l\x}\ 4@~<8vLr.MLZ xWCVӏ=PKf]RO uI;}HUJ骕jMrchohqU"C'yч5[nIJJg$@$(s&W Q ^\B9ݷ].7*={t4/^p,R 镐 }.Gv@:Jn6С瘩"EIzj\IH 7-$79'[Aq 1ayD!">kw$̨apJ`NuQK߀޲|J9޻d=2q?V($@$@ A-Xz\_uSM>bՕR]3VBCĪեRPJٶ2S>/;I0׸[(N3 h)-E妍 kw kTVIl=opSN[1 GJL{˱'#0S_],D l I$UQoWv5qBU ڀOĝ;uA[Ȏ\A$@-@pkԕqqB5G\xp KiTq~ qB@$@$<(#s  .} !Q;kpIk8^3@@Xkr!`4xDl b3C (!cc215~4&z(S1*į=;uba$@$Q\mVHp`K\D@/jrjN\d0k\'~?#B N[܂ R~^;.B ߌf x:S A绉jgWO/`{HH (3e U廠0) aBL֎e@Ď{p'v d>,HM:֥0 !4Rr"q'i}4u\TGl uH:܈h;S*k\X ksY l6o% Dh)Z6H5! pG?-E⺥ȳ܌[P- @.Υf[k!p =ϭ|.jՍ`IH ;P\gG?"A 5E(8-,H (lZ ?)D֚I k9NU5Ɗ d5>Vj Osc$2-u> Y:$@Q$@q^eO|Qժg̩1kk0Ocma"Y|STT2i,UxCFE IX  (50@ZOJ0_^}l~y;*cs~uAMumg'p Ոp<1nk3_ |̜@' ?ƧSJNj!ѦD]1&7?lqy2VGO'yr͏M{Yn]ZFdŪՖm D=&gqB^ɑ2 u2 fL $R+VF6G+ݬy*t BVVJ[SAnʄ Alcb< E|u@՚q!N5n.fRRYYu>޶yk1ɲ`Y#Vhm[6W׎m:Gd<=$]pl~qהTZd~27Z3% $50@.P#,{,d|ԫBnT#@9 VFyyW2⸱]폿.F%O.ҺPf̙/GB6]^\|˓|2sxon+~N*+e.Kn}J-櫥LQ; ZZ㯼ZW@=GKUUrco!A_H]p/QoeW{I;FJKKUN{^xs'ǜ\uïʢ˥Ȭ'Wzߝ7{.:^%yIf1KS Iiu;;e/?^i&u1P_O+Gg&ʳo}*|Rخm]$:y)Vm)g\vߵYC{%oӓHH hN+^fzfVap֬j!~B{r9GJZw7UtZZw\1ף~Է9j{5Vjb2i[嗜qtͬC>_~meW^r#w|2UIwG(M9RN9dWY|U${la\y+ Cf̓G\,/7c4jjް1w2b!Q%ڶhskiq9_&=| Pq\`lK /QʍZ}μNf?wC'5{uddsI]c 5Np!I@y?. @:G(+7טK8HE<ï˄ΓחZQꅪQ[wX' :b4Y <*o0EUjnF۫/'P"<7R}g.Sq]`3[TڨUZvmZJ uf3kramoyQ*)|{u;:uRV%Q9;?pA`Ȫ @P\G7ٖFJ|T+drZ{0}ߜZ|ߝ{: w&|+'_\>Zf?a_A-YJf[(#Zo"W,^.'\|\x*6[NZg\*JԕbGjqg/GFe:̾Q/nzكraPAI͵۫Z0^2d^_T nO!rĹwXmN-ꋊ~Įj_*ԕw'e҇ {y";bCꪰ?uڿ"CT+Wj}(7?NW"79t-l6sк[<47>nZ}q޲N,Xy$:pbf/6ZeQeusfa@IDAT<^#6>WgyEQ_e'miߦH u֗*少z mҷWGt Olˁ*+_vѲf˛M=;ߦF! ~j D`` h,xABz1j>nʕ|rO-WS&?wn1oau(Q |`]uj8l|oɠ1(+սk*Q sjlF*D/iu@Uj]65]Ȯ\U&:_tC򷏪+Η{}Y-[sv-(?N$@ $@u1z@%,AfBu&~uBuǻvm/*HTŋ:@tCC,3ǰ?JVqZSCz2c E:u*b,k/@#JakڨE Uv2 ]jP >0(BbmxmN눀vzemT]-HW'\VW2ck`x1P.i֨ƃ; a@Z[-j:$@$QE :\xHk|n͞v#WH ICCt:bnU*q,.4BmqLk\OOiLFƷۮ UU&(\Gc, 0cN##fI&xHzd?#E׹l3H@ƒgF ^%E=6 ux5Z^>t^ȗjyz {R*J !9lNTl,ʪBC`&(J(^Ņ_`CJk[}f=$@$([:(JΓ6E69i?J^$V""Fc|Sg.*;m.}9$@$b([ = N'r/_}Ne" _Pvظt\3G ft#P࣏p]^a({_RfiKfK)((B:n@W3* ZP@G0%XNrwJE3r쨭dzE@ncP'3Kz޺?ȮqUi e2/?~u b˟`?HH(ώ)CJŁ a[F] G^V$Ҷ|aq 9D+(YlYTI*˖Iu WUpʞt7=$[wӾ*]T/R~ʤ2.3QR_K=vَuuf2QGA$@Q'/'V/GGU/rUHii^ZV\)V%dĉݼeuYj~4)iBr+@,]ODdp٠sV.Hql\Y9Ix+th?ꧽtt#кXڗӐ4{✟Oz>A0JndMsҺuk@6mHIImjJ%H@$@$x\7SD[射j| & ѳtM]R*u7amVLC+ԝ@CLgx@8E bg͚%fζ/-SמBԹ==i` .HilVkR~kPEHH I:+Q"Kn0tB<º6qwH/!A!`G.[:so ӧlu/ѣ̟?_L"l`1-㨃\'xP'|\Pc.= $@$@M'@qt!T'@`#@46@ 6 ]\C8 k,Bqm8B}W W?V7X"jFsB0_5Յ%#lqӆ 0+C$@YL:;U_; XBH 6>DAamE?Tڰ\ĵYU4ejZ4752٣_-Zd//v@8"K,}u֐yz~7uM]Dc ha-,>\|@\p!X"25z@hC`# .!oU հXE6h#yWOaÆʆ6)3ӫWcyw9Z)z-ҡCs=sܛgyJc.$@$@fY\j M&|]Kt/QԩSH&Nh" >D !ިyoٳ<2j(O?>/(۟x7Ub$@$hZfZT{nAw+up˖'GXqET"s ؟)|uoP)g-Nɚ$@$(  l$5//Ϫjŏ'S7ڟ!ۍγz7\% l @q : D TվVUUڍ}?X+̝;W&O8i;C>z0/S-N}K]zc_)SdΜ95q,Hrusu:Zj䑸3D@~5\K:'$9رc堃?P=P7oAH=l ^z8?k,6m ӏٲğ={vhSlF=O2{Z\☧x6 ^p}}駟j2/? Duwoꗵm'njV~rdy&nlɃXA~5lD MZ?akk>X>S9ꫮ78i!|߾k"Յŋ矷˗/ Z \3gΔaÆ%oϥ\ VkX?붢Ϙ1#2֭o^VZ%~rP*m۶RRRRCtLuXS[l`SNMZe pW_}%˖-K_[`||Ǐ/ ԨC `6iҤxľիW˗_~)SZZjǿ Q~m~ޗ% @F9՚oƬ$-^F)ڵPD q 7_|Q>͹Hva{DݡCKdYnžM,cK܌q#G6|rEYg%mQr?7& 'ȠA,͊+|<@),,Kp3Ms<\,i/?ҧOq%ב7ڰtRˈ#d-Hv!x] +x܎&?!?s93e7ץK+uxVj#'Cn pM q曛k'sw}׮mFzk]v^xC"[?xN=s-{'> Ws=W8CTz ڶ馛ʄ dv Y{cǎ;/~ ʃ }"?laүo?wݘ1c ,ol&ry ,;.3y䑲zY+ N~_YUG=Â:t=]$nc`o>zJ3ΐzKn7Wve^C|H ΐSTL^1]r%1XkTUz#4]zYLo_<<112bk϶Fوcz4y7?+󪫮ȎWW~~}ĝ,[LOo}rԨQ1ve=Mee|r燒|׷ϟS!S!jcQ+\:~exص~{׭p.ȋtM1u8?SQYg<@L`1Lngq7? "~Ya[϶\U->IQc(31m|!$fV\s)fmX+2< ` W#| ?+!q<`Q]bj}aZW+"i 2!lq}`7kuk)>X;crşk6vQG8Ä< d|(6@@|Hk qWZ(F:]u/D_:3;{tq m]weBX1$iS 0P .A-|>x=;b 7T/ĉo}޽{[>BA$t ol z֙ ց܅a=fMy8ƍGx${!MKrۣRpcwQԚ-jց0mf;n‚B{47s1,ځ}w]Tk>YWxqya`r_~:D^2(,k~4e/pqa-pB@D$r GSW/>nV ֶq Ý/6b\?*o3'4e ו;ywo߾v}`?\z(^,BFH7:G$fHQxW^nٵK)\hڴicuY߯ x["\h/CmF: XC@P?wql%g:~vm->z5 : @4ĝ`іȷ}yq}F[e+N[ !D? CKQ)nͶ) Éb| >!S%vIF;u,׉? p.nhe&Rߺre٦9/9|1QOLO'ldw~j3&xƛnj2!޻Y{ B7M&JDs.O>#w  8q= |x2oB\ ?IJZ-O j6 Gz`9# 0pF>xBWSuj| du-(Xs=k yـ#1O#=^d"Y܄qÆ7XVrspDKHe!CML z3 uq4mD7AB|! +2}1C(nAp@ߥvix [*^KUx+4k2r>}li_hX(\\?ܳ%د6r_ha?~~|&F1qu Z" pqmFIXU^-Yc$GNN{p=ꈀ݂} x'R^Xdww8xZx5%O% @P\gag@ŗ,d72Rq<^&eya&Ga?D4"J!kn-nPxE W .bf `MT!p8{ѱ%{Y!ƫ㏶qC Np0`mc?/c3:8Ȏ? 2"lXn L`AG c<چ(@[,[()Q P Pxp]wُ¨mw-1=["xpAxŌS %`Vg^ 2aw=A>#ſk b0puA:pBqN|` O ;d<] ՗a׬e=te0'䠃A>  O~  aԺ`<7f gfb0~tH (mɉVƄ/dQ|q BPۗ?^nrxL,S)b:)#'_: K ,f e="Hn.Xp 3,L:[=6uM{LB& nf(7M<V9,Q?Xqmii̼!qB(*t`k"[x| |'x>\Ap #<=x8<s=e96߿<-$@!@qe}/m?XxSbvn^Vf}x>TqCM5qs@ij6!@* 1 NPD竿$n>ƽY_K!0ho?Lt&! a)nz6Az>n.f!B<LXR! <^Bz#~? aV.!<D0 eYY3 0!\yñ"$>σǃukS|<#yl\tXb@  ^_.=8c$M9O{4 ,'CApǰwkR 8AF{m*:3~> @ϝfK __Ǎm<| q kY?1:*n 1A47/7c eh[}2|%pc{XSհ#@#7a4(ʂ`&kǀ7T!D]k\p\pV=w2|i_KM-uv8~E|=ۯ0ŀzy|f"gm`?1~{~ܗߗu8%o[[ t\']"o-)Oں~3D:|4គA%uK1 +>} 1AzAigڎ#wQ["M``ςް>{yW *gRelBdI΁{3sX9Gujњ=b\~K@EXIσ $WBXnt#7s-StMy64\cgD\up[!stu=a]WWkr/ſm k/p!zAx{ku01 TWʸ<'[8]v\/9ذSHEˏɳkyFõM29ONszЫW/`ZMIDg慛Z&ǏW\heaw"˙HĖ^ߊze?8%b5.\tN#-_L}&0Kzåq=8- 2ʰt˱nq8/%8˹OryeR˥Weqgy@dg$;kUW])y.J@sKky-FWN+͐˯8'PVFtô{f3 )˵!*Mr0|84‰e2el&4fʤ $7%+|b,|X^;CHL¨[cV( lZҀoyHC>Y>0tȮ5H">ou0Ά4X]G~"n!_U>!R̈S3ؠUg2.e֭[)IUVa:CO?č!+t^!,RLw7,FSl8Xe3+!, .JCL5RΤ'9Q++sۀ6@ Y /э*XzL8e vg}ʎ#hy,G^V4 2DYhhH\k`EXmpV/X f|gzt6 2,%>@+XDj#ar];UweBtvA*%|$KAZ!<2X=q/= 1+@h!#F9>|T'A~t;vg/^{5gpl6Ť\vGD_#dIOR6kT#[lN;!СCu}lzׯƱ S7U=^ M]lg'͵c0xh` SO=UbQ1;?Ұ;+6ӎBڻv?p86bf}|xÀn:cAB_@A-+!#Pͷ{]gC~k3$ Bꄳ.;5ձc Tb=4Hz!~29y]w]]k+,AʁƂ+@R_~ıSeBΨD+7_Z௨Ŗv>z4V,vDk,6R>i3HaK۬qs0} 4 pǟNзGyn$9Vn3o&0顇ҍ_~۴.7.~8u'u\sG_5h,mdFXs 7$ Uqo_ |ز'u])b-Eif#ٸ3+ ~LE XȧoH $.ʸຂ;>]ve#B Y;̢]ݬ~1wnKVpR q.{f'A_UtV0c=n;[F00[=Ǝl Z,X! (don}R>8uw Z;@E)/tIR 5UlHj,V`I$.C2 ƽsV= ېDb6kpPN:M]\R6D|igkm>|qYy'zqa*QŚ%uS+>> >LCYp ;ꨣȑ#㷍$oڊ˙gi~i͆+.#PpuOo#8|NTTc!fB^߾}mbd9,X WL$<i!$ɆS& nb#D 7ۉc)Fba 0 ʐh&?BIt՝ .sqKu7\}!M}:nݺ)ħڄm\&YgEg`@;{FUW]Ug|_k>z8py 2|뭷DI6pk0c'}CV_1;Vt,07e1݌ۛ[# r]/8Y*wĿj%bW$5,w6 .${>1CZnVZ) kB3پ}r&/GɃYn+] >X 5Ziue[s` @1BIVvAt!,@ͽ F|Ahb J8enKF>m0ӗsF$csùs|RN\Ynɖξo fGʥͦw%eƶYnx]'9.P?xڦ2Xp W zPeƺp's]?[8ˈ5"C6&x1&byRW-: 0@crlj1W":dazyLt-ovO^qSo8]ܮ!|z|?`p#o٫~p #dH|q)$Rnp84d/Mv%QF\aV>q gUn0'Xq9`STCvL݂ee1;_;@F-{q#PH$/>qyTtO\@+3*&WXg>}j0º*ұNKkepnn,dz= 9#89#P&bj%R>^Zz펀#8#P\W%O rXŒbȤɓBNu&H+>Ԡ:^#8#,yVG:u [GuYeuv6,dYue8#8@ fpR"`~l- Z~ 8#8׬ Ȉy ;A[#ɴ]ƛ^#8#N K\DK4l 6bSaUR*/%#8#'Fs"ݡC[cfvUC:uꤻɩ秎#8@!5j@d=ЖiFozUVi@xSGp/Wϵla H+..+6A^zVʲL2<lvp|jz.GpNNh"= 0`@x'Bnݴj&5BISU}|(u(38\?9y>:Ivu>uGhH8nHm͐lc|A1÷gϞJY-YCR&ZՆ^j]ȵKKKgڮ9g|]1wG(d\rnՆHGW[ Y:z s#A9sNs{k,ڵ!1Fdڏ#8K(pbmY"#Ivgc`V[^z)O׺nHt|Hl FicA>z M;S:#\/ @|cd#&MRMZ$uAkԸQ5sVh2C{YO? -[ s a`*0gX6qiW_}~ƌyŃ/#۵k:v7VuH6:gW~zT#ilDʕkpk5|aǏFvxxࡇZ\_GJ>aǝv} ,6+6;&U*9#TJȺrk,w|n-{rVNZ5ƛ֘ڱ%ʥڛʄ+KKqM_8L(*|>:\2ʫ |Z!ոpfsqGX,\/|PHyH5,i?C=,kpyW۴!Ϝ[j^ޯMƗaү[8'{ѣaƕ Y͂]@V#k",qn:i~vaY*<#PP1~J#<"cP{¹#8#P;D-^#P!F9>X\Ϟ='C\A$H_WXG8u~3gЫaAoN ~ۈ3u#8 W:0`&2uӦ: /ݺEX~Ô cßNI GGpGBC/@0HuH6B#PHJu"oRopR7; f=> 6GNQ+dRY+إV q"Q+]֬f@IDATs̔Oj&2ڛ:ƫtzzץ ,шCi\c@l2jm ;$;K8#,:N;Y@% i"oBWH[ Sjًs 4=@L4{`E%ٖ2x#8"!z`Luh6"Zc&[XX=#8 F1#yr-]Gpj'58@ 4P0"#?\׿>9#8#Nk xpGpG!pGpGpr]K{#8#8'OE#8#8ZޫuGpG8}-rGpG%\^#8#8@CuSo#w|C:#(N c\-G&(_|!pQiᜰ⬴oG5n\T.:Rwv]aK_玀#8@ fz8 $eIgS ~II3d#wnhiX)++#ͯ߅Kt&q\|S:-WzsGp'׋w +n{Si*=?°'\~V8 ?.;=Yv9tC4)^jH1Dw̿Nt Ch< ŢMz%J{xkܹsBS!o Lɳa91}z_+Cֵ&Ey9//Qi.ŒpGA\ ^#P(/Ɯ9!Rp闅7^zFk هlnSM]:u. M0sƌØo MMH9;~;`OhlEnJwThSH7Qa#MҲ0nBʃB 񗼢[šyb-D>&Z1y0_4ߦ?Zcy8#T\ue8@A#BrKsm7kDGz|xgºDaM?6<./?0L21[n|mg6zp[$oW r拏C-v :lYPfO>pgQa/–;;OXCu3֫χO?|WmZ\J-TD_>|0WoNaM6+0EH5uWf<0|Wa=I}Bj(Q9#Qr]G;v" йJJ7FA [Gz9̞=+̚5#qa-w(g0}0G?R+C˥ZS\5ėD-ɿQC;+xg tDI:u.rX}$vws;8`p7?|/Z_~ɒğuӞCOlظf _1v9ᚿ+lrׄZ_<_#8@u":FHL2/id]WҭŢ$sgC廄-w3,t+L0)JnX,X ?:vɇ3* %+VYme!!oiN-˷Lvg/?(,F>kV/Z뇞K”I-[w0~((l:H5ozoO:;#NkX/(Dp(vqj5hY$${8Uj]1-{~a/VMZ %7QsrڤI#q?9:7>xhg͚5k )l4$X4Yo- ˵$+7iBǙ3w|TXgoS#VSL(;i8#\M:@! .GpGc%8@ r{A*&fiύupGI\$^#s-e [kM1pG^#^w7hĮ% o#8@Md8#8#Popr]o8#8#o\qpGpG"v7pGpG 87^#8#8@EuZo#8#8@proĽ>GpGpzz۵0GpGp|#:߈{}#8#8'ka#8#8FuGpGp-NmzGpGp򍀓|#9#P $RgQQQ#8@! #!h7n V:#P8EA@w"VQ#׵ ^kHD,!9s~ZOjGh08n0]p @ʓ naIJ< .@}C IBFEᏩ3? K͛5fGpj GX[{5@6a^}{֫a5{&MÜ9b'1N4jTh;}+ {pGf͛k7YV#N ?\Dȳõ}x-Χ,t[[ج&s+wzAhzШ1rQ3G#G/;[bM;߈V֎#N \DG#-[l}xͷQ ;y`yЁy˖LG *G4u[Mlp9jї td{(ե8@DH^Sa}m5tii~ٳg3gK?ӧOaܸ';C klawkFh)-ZdI&a%7'pG`pc9 ZmF2HŊ+ڴnzN*~sBD\GXU!-A@L8Q;?489,բyXݵM6aRw˷5䆕#0{uVaeh{Ig~#.#8ՃK)@`0Z7w\4 ^:,Fzc:v,& {UôiF&~͏:`|%ˢ!` 9>oԸQX:xiڴkJm~'v\4-<#8!ڐcB &#X h&6"XVX@+"X}wMl`~iꫯ3f蛂N:e2g WCL W}uqXe{3K4GOL&jGp'ՏX@2|(u%j]$cFU7$Kj߈a5ħ&,N2O:묣,.N% 0o6Tb-5̧IS\BX;.BMvGpr]mPzAD!l@( .G!km .]bwQ`?^-FB!`Xwij"FbDž;#̇ He=J@&6DF!.bsEC r&MR2ݡCuZk)V^F65=3x4mrmGpGzpr]=8z)@L88 6asRcbmT'֋ޱF'OFߎzDR}հEa̾׍\[ǹ$;C74FpGc%0@ckkw Q ,YjO>.rn)Sh_,oѠ7܌,8넃G܆>rGH!zȹ H@!}|6e7fJXy7u>U% 7|<s,-GU9#P%\W &OTWB3B5$ȴhu7'd~ =®fRrǚl| O{]W (o7I4EBckSvGpr]h=T/ʭM0C(83"mGe?~*!j7|{!F5ҿ9xVO8#P%!,JPy@|snq;%U|UA@ 3믿5ٰdO0!lv_hڬihѼE())x5b}J o),Jі`|;.LZ˳G++kAuPb2>:,#ֆTVnEz#Pf np̓9ϖ8>;ί+F\~CΝB&:A3޻w`k\[z&;f6bOxs>nԤƙ}rGxUjaE]`bdrA*9;ڧ;}[Xvښ6+AYzڽ{~G(L\fV5=N&`k?V CnBV0&J7(Q&k +V\qEU6Eyǎp3YwuCV2/7޸hD5Er5r$k-ّlA_&v~GZlP+W?;q>]&TA' H" X,{&#i&ӏ?u[7l2#,-};oYFis+Îq\s{ݻV~D^mq$}p@_&G`8e*1e]]P8&+dMB>}fmFATξrÖ[n)y\6 wޙ!:8-kȍ]cV?$vn:( }'.3rb@qmn3{'SO=g)wNو,ܮ| k9cq,ɸ馛f܄2iS1l2V]犷4!N;lnB]z:v bm8p`:R[쳀/ , :thAH[ʡ>H́đəli"yC^t3!ؐJ?b1>gyVƺ_kYuW\@j>p̱(;FvcW_}Uq|2*&ݺu բL;y`p,^4i~ڵep>@ />gPշo_TAx0C?prqGF Q%]tQ"dGÈBt+U˄DhB9lM"VU KW_6-W,D^{i+M+^ b Ki"\,/D,XZ^5^XN5LQ"5뮻,9!v=ׯ_"DKg9hFG>k#b9OT%b!kt XXzX!z- .s~B;.E%]w]"DE</d393c9&w"^u+ qLhyĚaVm۶/3]i?#"W1Zڇ1DsF'!$OY,ʪ2sү*_|Ahe]t)7 }!6˱^ˀOⶡ=DD Hd'|I9眣>AF|y38y@B/#.澁 .P^c;٬Y3p!>ڐ+2J?UBbԽ &>Dz*$EnrU!* VZ!Ϭ|A>`Dxuom"2?Q\T&%#(,P+6R|ұtZ^.pLŽpk;nB^C(xXO'ZӸ?`Ūk8 B|/n'`UXR˫_Xh7:a Go,D=rg3N?# վJ kaMpޭVY`yTXZ_,>-0iF)o~J9'z &M(%1l]7 2PID?:A61ӈ'Ö3,mBڊ<} 0ydcl Z\`8nv:s\&Sӟ#d~00>tm7THA,쫏;n-1+131P^cyXڸ裿OAGGN8 @BUa>fXiEEq4(H yEjen`ib5Pa-Eƒa -BY!=:NʁqjQČX)n& k,6 Bx.Ȩ=&sl,k,XLC ˷rU1Ԋ&2Ie[Hf5l2Š4q FLFĊ+2=Bhy<׃o&BN1$⣵}'p >GVrK{:PbGC{7ZZZApySqkp\/rh׾]2i_:PJ~85Kv*"`Ts#!?d\{M۝SrJLҥZ0"K$&L) 65H1pꪫ4Z@3WX&!VGy yDmU7,GuNbBO>V}b &AʣZ_e6K&17&A Ijh D aqycag)ĪDIڊˋ b9DIqtz}auepU,b\b,ં /q0qo 0O39 Æ:sÌMo¸?㔓OQ4XTJ}0b[?ndYt~X9t\c52 'OѷlSZVuBйWl&<\Gfh$.*tGpR 1D t32NJSN9E d$ K,HG-[lt·U?~ YƎHt :wH 5Rwꩧ*Q тx?Y̢k#GcZfEkBޮZu a|͝$Tb/nC6N,VohqiuY=1=-=7&'N[>;VwFK'^J;z a yXe㦛nb=SCu{5qM~fVx!DZoaOO%t,=JÛ \lpEəWcR_CqybH6o)oIl`O W=a٘Ej#RCz#0<-d^frK i,B]Yau4eED1j;u\юsGp0rWF+^e.tٖ /hW 磬*/li˸]p&Nf֎#P@TDPpm/Ė4U+PGC$h8Ƌp|IdWaq¨oe0Z2pj'׵ W8(vNy,k\Wo1oTMYaC&Ɇ!Ӌʶc."nGsvͬmMq|2 K1k* n[P|\*/W8KZ,{xAʕÜe tM\!g?^58C[L,!,7 \[zp8 pqcĮZE[x|UeEN&!.ֹ6g x&!FF_VM"EY8njHat#at49N,M7;yv-?UғiZVۊ-|V6aiGۼ4dg)<(Nc}2x{NM8X:;ZkF2ry6.龊#N ? JI*G{(U%C5.m].(7>//<-ƆlvbĄF$X4e咭<&+8PG,6 FX> /5/ 2hbmuّMWSN&^zWȡ-Cw66ױW}pV ȜLu ]Y'N <+Cv.F,?CVu  yꩧxB(Yx T,}H8dKM QM)m9;",o@M6'%O<v;bYtawHvadrt79[»ォȎ|! kmcj>xouwC<FO,H}one»[ #_~atkw#yA vxd9=A^!^0gk6`MZ ozlNt30~ ?:@ ?NG 'Iꒈe%xδq<1c$_= W"VrI!Ds]e)Fc$⓪V^|VB`ИQZAk-KHK"}$X27RXvKzbELIJokTj7x^ ^^5U8!X? N!/:█;{D{kklxM$5:"u!n c=VFnGt$=e"⮢ה8LJa>M N'bQ䕭5!Xy⪸\x^{p!MAּ/or'!k!UNj=KkFB[ M˗X! ?$芀a#|kC7} d9uQu'W]=d74cAUIOmߞeZon"Klܯ5GGG(,r-\# FZvaaa# 9syHe;}UW$YZ,DXI)^brkb,QXnV\qEeҵآk|뭷Wnge#6;0[x#\:[\uX\}8A,XOC\pKܹopObeE4 7PϱbMK(. 7pCvm5~РA^RY%%%$:w,y^uE&♵]~ws]: YdbD8-S;❭T#%$UÂv)3죻-DVL_{WԽl2KYuDQsa\zh#kNӛs^`9Æqm`݆bNlM{Z>n7=u17/!ِYs=2xXڭ.{9&#Nk BƼ2;O&ǫMF: 9j=ϙ f-ed e DBlf|q\|N]e-(moyⲳ-/.+[߮rK[_Oi*BHp>@ ý 9k ?\駟5|,qf e;m-Vjvm I)2 PM6D*+K#_R >u {gd~cxalO׮]_G.!`F}~rk?2;2sKywPn<##` O.gׯiFpN5+P6V/iD잰-TkB[8m۶K/_qoqqAuEhD's XP1C"~(T< X Õ2xp3<o]M<k`]#dxM6UrLbB|GycDX N<端z`}j;W&-amǪ$3 PLlv:Z5G?,eCXRRXG EyͬVXFlaL+-/ڃXB:g͇a>iкMkGXZ!`f ,!~~W6ۨ] >{!F\jIbw%Oes1=IMp)wСK]9FJ}=">}h6. M?eKȾL@xÎ1.iy͆j*nڅU:ŽDs~wׁӐT~LĒnuQX#5 /d⬿7|8CEh0 o5"1@ G߲#3c1v i49@ KT0"O> j=32K[ a_&A, A䡊 h@D < L}jj&A\y@0 Ѓ1cLYX-y~:l=Ї&K x,~U'|Byo<V@XNy%E =t@k!d6@aU ̬}a`x駵Lt{@-X;v[r>&f~{L^D @l k3d=;:vUB\ IUa g" 1Ɗo &Έ4:k \S 1׈t. Xс zW {+t~ЗG.?}E^=3~OM(HLk8]4aܞXu Ţ339<6k&Nlҙi!ZM*D&<&+2L^'BJ5N<D^52BzZ:Q,gh8ȃnur.q Y}؋u\ D@<؄ALhJ"|g$kqsc,._Gӡ:ȵEF>a M\@tUJ e=ʀr)p9S&e驗h8E}M b>f1xC>o0^%3taP yرWb`RW+nā%-!!G-[sKH/zP>3rk/e5E?%\\!q?i%E:mCr=e7}B,~sq15Ÿ)C/騏r⍮* i#k0Su`ۏ#PxϵrܢWL5;y˪!&Q[a^ ,hYlA:!a"%qk!=ʃ5 2D_}^ʃKy.J}lh\UĺJk{ne sL}at2̙ eS&nSq$NG`f|X \N<&ق3Guq~M(RA'~1ġ?qH-arc,v%]JcqVal!n -s窷Q8/8'Rǵ ~&̅%cEP.|L6~&eyl2[ˊ g\~p3A+8鋂Є?; Y#IUQ2Io \[q哎I&gBXTɄ5_m;rnu쭷ݪ2u0rqHb5S b _Lc%~z|o!(I]buRRy @Y"mWڃ_|jY遇(D|s!< Jcd;0)Z%4A|ي"`o_}@\SDT,x$>f <*J]2_\v?+KDBx#C(O^=.lLIJ.oM4H! ٘lD^:@HURRn0aI\AtI.&Mad!2-#CA :L2Fg+˛a&PMꖉd: s]](}7EW;5_fӈo6֡ eB_ob cݤ+qiX*ol#|9UE@ <_3n'rU-9@"P$!OZmrV.V#J `E'$!fadb)&O.I\/u FBncAb>ǔONX!v⸦nDGê,7#:\p۞ UaIZXz=:R?6hH۱$y u/,< eJ=F\h"4kL NY 7e=Dv;AmsfIMe:Ձ߯Ձ'Ǽ\d2~llR-'DKвWUQxeuW5>J,aȼ1K>[sƁKUۓ%GpG`1ppQ vKFbͧ` B%qmŔ0־j#0oS}neͬ F'4]^rG[8̧7&=h7pGȉ[s’@H2d+hEn= M ӫ”Q vW:&U]EGpG`pbxM +yrOWF83\qX-Y"ʊsWaLDf͜n:lPſo=rh繮5QK6a\+Joر4n~8#85wAE|3B̶&h禷-LdoGpG/s_DmFPx Y tt΅SLʋ ˾s=Zk?pdS͎kv\<#5rX% YpԨQCխلO=TtMnᆱTׯnbcm;nز蒈|N2%݈#t=CZ͗^znZ3lذΓq.~i`FzkRjǢl1lgִS飺1 du-P\K.JwpGp򈀐 <# WQ$B!y֢Lk&ܹ&|TC9$ҝtMz-DX$X1c$BқF*O=DHs2sdvH'ƍKDHs"V2eɫ YP4'1{Oev[m"DT>D,Ob}Nv}wM#۠'B 赐WMw'W^q7v"Q!ɹ睛l/[kzCwB=]buNgd Vt5."ZZn ^=AGJ>yG^k^xAӈ%]zQK_Pd`[ѯ`jGp!n%%%in C Q)^BhXj-.<ꖁ%2h$ .Xf yWK3|aРA7xK2r!g.B r-jguZYgu{ep-8Z>cvtl#n."e9e!jesn,}Yٳgts5EG,o-$9poTk:tVh,@#s\vŽum{xM_#8#\Yw,=H׮]ȶ+h& F ܐs/r˅ 70=:\uU:ab-䐔%J=ܣ~XyC8#8GW ?uFd8 ɉ<07&۱j$ EpXQdo&X4:h_"BB联 qO?T'SBp!иr NR7 *!BpSꫯL\ĭM6:m`INK#8#8Nk[ F za03~XWXaL {OH9hy,r{2 0B m:KߧRjJ\pA_\sM%S\LXe-ԲȗNV9%-~ emk |BV^y0tPu="C Mw}յ=X]HlՖ;PŽ 鸩sky'|r8tN bJS.6lXG svGp P$yz^%@)B:Y6XC祬3ǺՐdH fEx(VtNU#Y$>H5`,X)~{}Ѕz!%Ē}Y5.&X~ u|{-g6G9VuL:d'ubl kyc'~ن/Žՙ2g~m]ˍq,Xi LV[m~;r}B ɀI ,tv}GpEAyL*'6-Ύq +w TT|Q8J \rȺA)`@!`;qro>Gp|#n!F1[}Ky27pNd)deJV!\[ksngjes+oAGtqtMۖKZ +?Z .9iL\~:GpG j!y!T MW/$:gt+F%.3[\i\isysҖ5=XXZfڬo*ҫW/u_sGpG/NT1iveg}vgvscΝs=G#K<%>-G``Jx<@ u$/G ХqP嗒bX;G#<.(wҗ#(YdI ?)SĉIEy<mo.{r<] 5 vo߾NCʂ ʒ{{<@{+4~>G#Cڶm+C Z#|r)IvsF%1!QӬn;XTq'kb,"_#E ~9֭[D˕W^)III;@AD}ʶpB裏?#FV;-- ۗ)N~-ZTVX!˖-J]n]SHsPpu~ѢEoY|I9TR^spwsx>",=yZjUW]%ׯիWG;y"# _˽+w}uYeOw$&&ʦMd/.] t\ #WޘB8-[ֶ|,"P^=iٲ}Uy뭷cۯ"\(]tM4h w*nZʕ+UbUFo+$w͝;W^|Eٰa`O.۷Zs9G.N^GzG !@D_ȉ'(_}lSG@!pBbP_xKd„ rz.7k=~#FF~wk} X1It69*q<ӳ cO䟭; 'awn_˼2 {)\8A[tWF4 fGm[|3ܹToHKu=ϼ6l( VBiҤ]oԨ.?~é!4껡va\7{{ظtr~iL2ft7p\I$ۗ2LB^eJ/NPืB6>I>Ge}.P 5ythy1@7s̨H"2}t3aDw{6n(ӣI_z&Md ܦkN0F;FⳠc 믿bga-r'ע?xG{Gt]γ{燽Y'?e:4:rʕwޖ+dJ|zf'#P@p_B/^l%YH?,={ 7h,z˞x.Os쁣i=Zc>ƍ#L:\.  u ]zGz j.\w=@!u1AƀE7Ξυ7( 8Vs.XјO۠`YfieS)YΑ6sW\)ϗmD8lg63dswqVax1s7;Ȁ&=SL^]@ˋ#,rϥCiiqLw60dsN%KXpȓ\#_0yjg59wi##aIGQ؝ByYi ޽^,Ԩa_ P~X>SGn֬YYСi1 ܝ:uq F/hYdrm!R7o܈oa{fb"+?ƍ O?Yp9)dēyEĎwA83C[{Ŕ__4k,NFAG 暗IvdQmj3/6^|թS=nA}-pbF;?FvV†/l!g@0SYeg!Փ?XjԨav\#wyG9#h1ęE@!<;LP#\9JӚsep_~^G{q&vhqYO>1L ʇvoW4Sv袋CoV K/doYza2'"$s16om͚y?~u!>c/,O_pէ:oesg^BG"MAF.f;g~d4,GH:|hGAfb5:=aqGv) !,#QF~t0nFÚ8w-z믷/h 31l0_xf978`~tnN;4Ӭ醔C:(˃2<쳦q;nIEܽOx<8B`ȵ#;kza'By8-5)Sa# v'BA&Zɰ%lYqЎbz&}CT_Jݽ{Č3ذFs)7-9$sL# 8gVk%.~Ȁ}11acѺu+%NNM}wHӦMk4ȱyf3@Yӥ͞2A^_a[ѿ6m8i ®t,1ΘPF4ԫC2f#n\!@G~6mmc2̇fhiWsPvmйËsWfx<@5/J6CÝؽ ֝!ˌOW]urAN `q *K֗-PpwdkYsͻĉM_~YMNI6L2 Θd8;$3gS_ժUJ'hrȱK !CX^/MRǐ m8g6R<>yV;9#7ĔrC+uc-sL1# Qd${L\0:a;ڵkeJ=@c ęMW*5+UVn>4FG0aBܳIY ßkl0~xky(r54h+#qn`R~^Dhph}X3 @tvXfi9"eBpNC+vg~p{bĎґ Qs!W\rK \# Z?:Pkf7CIA8aJ&xLuf A =p@Vc*YKʗ/oW^G4ie!s &(o4hs{LY05aFS uŦGZyFuŴ5Bdž{v`FpnD&pg)'|ꫯ?1r%/P8w=#9Mi5 )jyn@lw/F Omly󹝗#j ‹HqkϮ[-a!s;H`AM0Ø|`B&;!{hUH|#ufI{̱ҾC{FKԫW1ov5QyӦHȏ2g l!e˕5bOXXQhqN@Z8s@`u)d <e1bg:b@#6ՐhhѺ1l`EI 3dD1vuofCxӑp8S&^ve'MիToΘ1C6nh0١idž{D0f# E]̞F Rđk>yk?v@p\bI >9'&rz`VXx>I/[TnVAy%O`C0\]%A;>eꛀ?-(CF& f@sf NC٬-2ʔuJ` b9FbE:BI|#!>D7mT*9ٜU ͪ&]LX%kֈ<&8lײeKGȀ"7HptH*y۴ic~t`v0TpAVt$H:Ssܘ00@F:-'?|^CN# $׮dO#\PkV /̤C@ԗ#PoժUU[96/}S/!z˱w G(]}pR@8>1a7y+#vؖ;s8Džtzț+_&LGوsN0ﱜJ{<}.5r9>+ZrruGd.<ݸKĒ:Ъvm^Q\c˚z]F $ՉѮq=;@qG9y%Jx<2N<G`-rMR4Y5|줦(Ac)|^c xS[iNK Qs_Q IG(ޑ;vVFua.룒]Ztwš)o~xC GF6: 7h im!nvh 1@կdrĀ+d)I)r0CsXuhv=I8Ln360B{D \uI9(=x<`tN v Gq&-5Xu+Ck4~`q^CuYWowRW>F`Wg>"#n:VLMnj;tvEe ]l/lA\j@q؏r (24Υ1\1~q.crfpzù0~x *9\F֑۫9H`G*L$ *l\ꗫg R&_."l5G;۱ D4L Ysqaײua!h0 fԨYΝ0c΂L0蜕(awtli(;d5ß{2u`#\S GHLz|z+ghx<)PI|5ocr5Pc]6>fQpٷ3B<,63gLZo W0!y?s3qwW^x<Nɵk)kR[-<h '26GW Z3Š~PfY¥gϞ2aF@Yz۰~x5yܱ$G[R>x`[pW?|G=_~imVitĹj{カ 8+/27;i]|ңG;w8ap%Y~~̈nr9ʈ#1 <F!`9<@aB`f!eގ#/@rk{kyb,s:p]u>GrO=mU[had)B8PVƼSN18`AESӏ,Ť&hoY޲tA!h׭_gd矗>4ȅs O|S4/} &-^y)'0oAc9!)+EK);ڶŋ[u= 2PM5tx=m!m"&IŊLdBؑxG°Gݻy4^첱FLH4d`Rp9e vRvpǤ19(  :$%0 qE \C>:Ȍ)oe}gfwaЪwm m) af]Y(9ꙫc;}^ls\ᲒGH+洊V# |t♝e5Q8Gx8"'-7S1d@M{@0/A{46nXf!G9c}wZ `#lVp裏NO?mq H~Xw=+^nsqBM;eTsp3f̰)w:u|mgsx7^A=  tc6$P3= quv@5@|hއp=i\>^؄ه#~| 5.sq@r Bꫯ $dhIb/lKs9Et-=yD5c0qgiu2GGw|`xc/Ztu -rai4W;LUȆDn Cy1ugYg|sѡnYf!'mXx<9A ]IL,f!,Ho|A~wa4\ďc1k3P^nv5H )#9HL90i!-ijFNʽ^`r 7"8͚5/؍3_I޽_/v:πjUv3/j1~b„ 2zh[D}%V$l;ྌw|@D 4Zl5/-6H7c.-&=H bM;9rϻȊL9Jv̄ĉÞyA* h5Arq  $ WiOfL:OGb}qEwq N{R5{䰱#qx& XO.m}@!`d:[YDLiњ4|9rtFFV8DБdg#:2j{\oѢA;@ǛAʘC1ׯji=&( ,,~_ɲ|> ]o캂B۲[)=M͝˼r[|wo ]<|&w㭬^^:oy17 wkQ䆸ߧlݫĖ.]bm'*MOՕVMH"?soʴ@8<@h`b.lPq~j2tsuX͛/5{ps\ f'}Ѓ>/H~7Vu5:B M@IDAT~o&, ؖjrIei#'y15 ٰܑ\B:W@ԳdʚWP %k~i~gcoAQ<!&mn4,]?k֬zEZTPGAL sW1`Oڴmc.|Af͵Arn4>||7\5loewy_Ǫ֡V3cU 4΀$\g&\̔C̄§TfuXIMØ9U~-vݧ#r3?7gdB0d8mpɭyѱH(9ra_<JYzd҂,; 㠜go >y,2pp4åymy#PM[>s.@%[d~* 72{[0A{P:<Iw@λ2_>!Jtkؗ{Aj}vu>1$H`;I{QLN,zGn(V\QM)maJӅʴitֶ2 X®z聲rZ>mܬY[Ҥ8^\P~ej1)QXbؿ5rD!: {@t.6{UV]VyW[_y$k\[&̐HMYXTCCRR` Bt76ӕڦɚlR:_k ]N'^0Y;M2bo/HXu6輗jΉ̈2[nZQl#u0\=|B#Y`qzc * 6ؾW^$q^Tߵ8swa Bepe 9v[va̬!ҥ0q:BY]w~@; k+H‘=mry/ƪUh b1#cP& EKt7e܅f#iIϞG̙st1s7nA.PIrIGW2m<]>l}y睏U_(ryBZMïeܸ&_jE&%%؜wWIcdr'I˔Wj$9ߺ3JK$;dK? ߪU_k9眢:}Hjwʕk/Fjl䩧nK.9]_%˭օ-UIX'S*p3.\tijSI=Inpʋr<>}@D νP{ڲy,I`QFYhB3Y_{㾉M93 n>k}#ys\8i,\0\GVO '\?[D|uմvVr;L5 {ZJ6t i:MjjSiJ{K*3a_[/{R3CFii۔<T{4mHGw .-oAxr٥ghY*'*JW_OeΜo7OIG^rݪ~NTj6T55zZ=ȤI3۔i?#>79}KN\:S|x@AAOxs=/ iP~{ 6Ύ\'dXU3UFb"j9=J+6֪y6F@ą;YJ{60jxW %Uc=Bu;D5Mp5X/|3*$ny:;yvJYs!Fp>m2ߧ#, \ptԕ/2mtԾ13(RԬɂ A'eF:pÌW9wֺb`p/ߥZx,%` m?t|)Mؕ+X#_Əq^|e.gi#%a4{^:!+VWEf8< ʱe%wɰaq DRSHyz7[+Yx|6䧟~ ښӑw\\!4%e.iPQ1n45i./t n&Hz5PWV\zjGX'SтR Xp35 I6;|j렛q:2x|-GLA a9H̾}7-9*H߾=զچYF4k5s媫|VG`/=r[VtlO{Dvdk/3'H5ku|X}?[ʕ+G #Y5Gۇ;<,wYnҠV㳴xPfӾQ&Ҥek9vjR;Rgnkt#n3 s\$qg`$ ^DN0 o#stJHQkg_4瞻YZhܧt6D|2:dK~Bw22mV@^|V^n&"iHQ5:H+ؓ^^Rk:%9X+G 5֓xNt4"(~r#hr"C\ktJ%uUMAI9ZsDž͚5_ZsV@t^1*SfSgٜؕ*Ue֬m`#'/Dj*ƚ:-Ou[JjJz6٠DfI /L@6۵ZK0xeVKu(:U$I4oٲ]1Ӡ(F9$Wtm?C}ՠ^&{gD?3fEY{p>q@"@;{zHk9_f9`殧wK%g޵ԅ"Ed;3OB$ki+m,[<9綰y9w]׆IףOTB/kN>- SOjJ#+j8E5״lu)U"$ϑƠg ah[W֭^h@C]FEyɯNz eGeikM4PBː?/ryx NJ: mGHA Hm/ ߢ_d!s!#Q!8s!\SUQEg2D4q^wqV:/nN.0!E U6oluc{RRzT{M<$׈)s]G \l=QAvh#d#v.OJ5 .K4\8><1;IɣI)y "d'CwHD]P GpA9/{.~BG3έW pʊmnxa$iS"So2':Q7= O*% KӱH /Z4Ո#O7:kNO\y_mRtT@&>kN6í֯tCIqm$;yV#͵#=yUT$/( Nw0Q89s40Džw~q3e. O.~V݌od/yeϸGȍ\|ˆ@/0/f0V̽ @{>5mo#N~7 @Pv$ؼ+DО\k㊰N`-WWXL5I9b͞n-B cIҙh0r!LyIp>gKLbOu,˓<8xar>wlOCgpZ<:[lԍ0шWe8SerƵ,k2 o; $Ʀ%TMgg 䤈=7DFrFLr{&}xs/{05NÙ5Q,hʊpt*vuՓxGN+v"+X}Ȯ3i>Q^tY ҁ~ ҇m]C<#`o33e_ s cA!vET]8fNn]|'OBBQSӺ,Apq3Q>19pʱsùcdw{G 85jbn,ichMʴdȵ3'X=>\&W`M=HG)lӆՅpo7Jsx#\m 51܉'ޢ.ӂoٳyXx%L #:w>BY|-]yo=+i\|O&oӅvЎx=+aF8Auw]e gjl QSz1&5ȑ2fJZ)jZ,5~zw]l137 iCM7>x.:^ ޖZ2}f2HP@o.;AWu\&=A]$f6Y>kDwsJoQ"\ZIFڧO~k9YƍGX_;vWT]4i?]8]p.BÈ.@IdwONR.~qr ɗ_^sL+J<۷RW9\xKJ`?~/+QלICF!صr@ߩ狤EUV`|cǫ堃ɑGVY[ny12}wPJY Lc]x^K1rZ"\%uD0ͷ%Z9hrW^].!hR9DQ%d43 ׌cwE&嗟eQMA2;u ?SlǏK_oszQ[mU 0ܦy<y#]i! jsT6Qvij#Z]O\A ӑG [I暇G2Dr#Bg,%Czҽ{]',v+%(uwՈ`2kR%oꪁu9ʟtwH5l{챏QAɠAg(;4ӧ$jo7J mFr1]apsL#^tYi׮junUӾR5Cte#Ռ(ќkrjF,3YVq 衺@ ip@%+Innuk׫)P'LFbq9JByOu)Z .کX#CX}B$oڴKWIU#e˚gu,C<}p+V>>}ٍ߷2=y{i40Gp Le;Bvr%ͥ4R LUda D&X좋T kci+<3LmVj+ZxKQWS~=BY?ȅV- nC6&ĞUK}d#̔MS V1rt6YljoV7׍t$Dg)/e.4thMxүqN;ӦW;1jqܡН<H%y ;RZY^L=[w?yo0Kܵ\?)/[Z%شAA _x:CݗN{<޸i- 8C I%QbJJ7A6բNosur9Hy6]FjYmymc۶ @3]E^t.JB&#R)c7o^yS[2bx-_.Sx"C J4UӓwWf)JV;Oct Of ҧX+BiԨ̙L!L#ҋu0~5_At[~lГQL}tS-}]8W6)l~ ֔];hz [kj2Iz~vt'{T}չd\Gk֬_J .]:I'զ7T ,Kh"z?6j'}v9mΐP\mW~$>s]\;G /X ]FeS0 B8/#PP!k~33}8.:cŝ:jV|C 0)Yf`#o Ję"Zj^p)6ŋF*I,6ć+|XIfmDK3G-&)=Ql=<\dnIgQg~%I:pjRK$($'T;L%I*4$N15(f3|z}uftZrM`Þm/^ܠT^j ]]mV_A6y nZ|%)2k`tY.K-goBy3J Z9$Wg?xr#ԑGКŽU>=A[$(Đ\c6 !=#)1$d̓ AR!U4sk), RiBw1P3 -9O}f wq`bu更zѢj4&>{:꟬۴PFBz)Q8yF+Aː! F|?+nˌѹb)K8.?|8<قi>$$IՒqA8$RG]*_dOe'@.#`+Gk@]ADkLCxr@ 5ё?qEЀ093i>g 92`8ψ gʂY&+FcSAvD{ha{0Ռ.eo/r?^ָ_F.,*y\2JkA]92>?q/{Fif>#23G wpOgvNS &gG` DZ`=K{h$̎rd;3Lv?%Wv>G`!4<{W0`R'KĐ\C,a_P%A3 "5w?#3xڜ=y3 y,wݽz<k32 J(\<!([ I#=G7ËĐ\S0K<_-XxL!ٙqy7B;)$kXO,$Z{W 1<}ꏙyvXz:_ _bD]!F^({<DB b"{cY^Vw,SG!crm[TGYQ|g7ċSH@IlqiH!)z+#xr@.k4{aokh_5V>G v0ý}G/A7oco~Jɑ`$#vǓ&%m&#85-[/"skk,?y<9F *{<gO#Gm=1O*}4oevYB i#YH>+ uX>1^3ؙZC2qom#11&׎}<@-()k:L< fRKr\['L #Yȿ ew8BM oZA9 un.Ę\;bmoA͗m!뜍:G#@dKk#lݙl ɱE^1xBܽX>Ԯئ&dWw1FnbdbLԂGrB)ճBQ`_H@@B3Ƣj:9'J#! ?tvvo\0e R[ ̗#0O 탳u [:yFAoG6<4 `IQ# OϓBYQ O:*ԕ}Es NMM׎C;iYX334gB|1ȋ:˖ˇ=ރȈwgiG \^\.޲eyV ḱ>L#M?k+.2irŧɒE $NdlP_]'q]Vu<Y]Y0wɓH ]:p\Dճ ͑KN!$+׹i!Lqy0/?_,XC]}+r sQm޴#UXy b]_q'ؠG \+n(m{uio\JU IW摦/5ʌ?5NbQX'4 _|Y o8>Ygc [$,N.;EH)ɋtM+cxQzf,gna->4.r(Lk*gOSSXV2gě?h5n<ĉYz7+6nXRl;wY J\5R@Q(t I,%Zoh1cъi(Vy-Mvsbɢ.Z0kt>q/6lQH%9q'Q#G\/8h9"e㈌CIgן7gّHdcrSK "WoBS[7u~؛:@:8% VtA#QW-x{]y]\aK,RL*jϸ>bCt_xi)_= Q&ljN/kW,ȋt/)EtCnJYdv霙F0cH5eAZz }jB_R|Eٲ%MfZed nZ)yӦUM939J׭Y%}M.92_c޹|H eެY<_+e.˖˴L 155͞2I~)2oY4o̟̞1CҴg>: N2 1`kneѣge|rI#vOhʗ/oW~=]"Gy$1$t5ˣ_!bꑦ5M?96-u̝LۗT]s496M#T@h)]nNZuTg-7TLKJ*+:i/jՐ~/3Fj6j#|52bm K-m}ܝ?`]lpYkv «6iGG|- +.>qRz5%)ˈo> &ǝGהW'}0Bjԩ)*o>t>I|'2RDIiڢv)gI5.?|E{>_V.]QYrXa3p}Vn 3&S\6iǡ\)[&ߐj')l>9$FiѢԨTyi&Kҭ<14@+7KOnQ*VdzÝ};G 'lCxq4شq9|(]$<|[N*S~\N5)[twHi%ueٓzv Qܜɿˇ+>Gzʓ$~iՄrΤ-'I#Δ x骮HH*}9DZp`)_] }>4S?N.zz,_U5P6Uj~IasJ-6?~"-[*={+LWj 2{&--ZɝW{Ndrѩ]T]\tz|+2g4*"U",Я8o=?ר<2G -eÆr9=e~.@upq99%]~U5*-Z5-?5 y0:ey˕,W_6_rBZ;C. U})ɚUǩe ȏjxZSϽTr)YJ;VS}vF0a C9/[ұrUgR{"Ag_ϛK׫g˙r晧k]뤟JXtu5x<ktFIyٲEl'IzjMlIEL+j湹lTMwɲij=\5q`A.}n2ʛ< TYhlXLHڟ|P߲Gbo٠%55ͯ7^%ß(nqVɆUKa'oߖZj2egqc3!']" f[ucTB_j-YrTY8cjH ҵ]Ҽ˱V#WLm+ɯ)*Tזͽ0! e%mQ9CzPH!?iR]!}. EZVr rߍڦfc]RV5UrG;2NJx;v&J"+wB{% ߀d(ŽEg`Ҹew䰮GKYoٸ^Gոuk6*1~E~#9s{̞{LQ}η{<)_{~J0&-6)W*oC:fCg-yB7j k/6iYkainkZo67i٣-d]CQ E?"P\9NRR%K[p]t 6Gp"l-txI)t8 )_f`Bu(nݴ^Ιb9|eL9yivcP}< VRRj]5ǘrMϹU k6z ˻3v~7;[7{ܩ2泧җݪKN ͪ;"IDAT\ ߲iCy/Z{\WqdH}S昹=i1. :~AmSD+e␎ݭ=:[Әb`&z4%IæFuUt*+QMRH;GxuΙ:iSM u -ѼIf'xU+uJ)+\O,]JV._Rz-%SٱqVSv|KHy;,o3?a4.U!cҵijc5߸]km80ذ1~FxZ^ڃ٨oE*JMA5+T(+Wѯ%e>RXCKPV/G ^G1kIS_ ,^Ξ_e.۲2XX)ٲA(6΍tpc9т9Mm"lZk/"JL&߆)ѶAv̬$ӲU똆ynl@ (Ng1 UPqdf@PA:#FGy4m6itۛ4{}׿=묽!܊͞73]KaیɅ~R<SmQėaVV9d=X=$eLs'lŷ o6ێz8ʓt! $ Aw_ȕCw =eπr6L -vFhX7؛^+l\7=7|۷o :QYU^t~n-`5#qMR")V͢ t`(EHqJ6å? Ho4ٽr]r}2 W/Yf>^$aX1"6jOKq7q/Oo榍1(;T½ĩs._J K7eP#1n}CM]p $4qbM"ͭ*횚@C!N Cz׷ **à @rOǚ&K8zgöo\ ۱(H,I.=[ s>0 ,8 XԯGfx.|϶AL3eGs<5[~sεV5֚7cOCOf=5̍8Xv1hNXlM Uc]yȧ,FX׳Bp%ڸA/1`g^d{-fKh c1HK쀃*LzKClC<֮杀5_{{{xWj/c:a7| ss[0qS'7(޼qw{GlqjGN9Wȃsx`lMk_ olKx,eXcp?WGQkXŏl U=PG^@e˰<_ԃm|_d)sN ަux3d0 no.l5`Wl{"Ӷm\X& ٓ?N_G}8ܞWo~Uy+˼{9eܳ̊n!ò?0*+3vMߴc@i ^2w}b 3;K`]హ﷿|>xw,68``R$ÂӗڬCGvo8CcRLt ~t !_?s>ڎ=yI }]Xv +*a]q)߂Ln ysɾ;*,i'`9s+Wa~'i 1 Ahmdt5Y,(sa8Ρx[&@9h揟$[3&ck^`޽^ tbs$>#vHvEU][}[ B+rB |sXc>mG!wӰD'oK. $#*}U۸ukyDX++Nr-w] G1Zwuob&[Ɋm:f7[з`U+Ϲ.L~lkg@Cl*$,.'0H?/14ωWjyDr ^x+Gۤ٘mYeO|+\nܱ Ȱu:[9"N,8yrH/j1Dz= ;wli!@T@R7m;vc 7\owQ4Pg2||9M\b9<<9ͫf}}l)~$ s"BzSg̲O v{m[vux ] 1?HSW`}]6Fb9FwOwvsWtz?j>U v\]g~/<ˤOT[ ~ˮ;XkOgM rnڸV7-- 9j/]*sb_>c#؆e)~kε8~dL <> $+e`ÏQ# P?ЕbDEG}Zem2nd?;sL# >㕰3ҫ:Nv.!a"w^͉u:& B7\aι:NIyLא[Y.@n88V7k{ccq5{պ)BJT 0{U7N= d nʁ{9|=nc뱏Hi|t]=m=:ÎYnHHL8u.}I&CzvwYԱY)^ig^q_uHg,!5`qx0n`}p΁w?u+HzǍo}7՗zݼ}\*L%}lo~X噧oH_of.@ ćcl7^Q,;9+ K_^𓦮{SzZdP{|0v&Ro~KTyHv_0n|,nPǜ m|1c"~=wښ21 QU6sF$C[Qx'Tmcr ykW+'+$;N,ZXӺ U,P+ijlIn~[B@H:&4F?=NX?YhN"m0f)sO{x:lGYbSϙCGby׉di{C;_m|3 -|f@@cŃtR|Yn\fGsrQx~g%bJ>+N`]=o(Lt` &p :y  ELq]tՍv1b "bzH7~ܭpê]l]poT|2[t9zxsL.ϯ3]>p & BA!V|[1.{4og'{E8f$S@Tn]$N'{XfPE"i: {ߟ碪zB` }c܅\{O7R3I:`D>yiclC_] ґ6Uqޟ1Lv@bj&O?-[X,7 h1)/8V.Nkc'LrOnwY5`en7B {MrM/N<]F'Mk÷Íd}ᧂwւ݉uwueaySO75e/L\ k0Q@|VL5n^yqu̹d}=V䚕Q 80~Zڊjȧ6TBB,]\BI_XRB `;!W*D1z\c0hzg)msMFX@vs /STp{+~ +;0_ @ 3g3ǴF3/ނȹ3aqth\Dm=4\;m #jn;^=\a'.2H5!!q&9lj45cw T E}C 'rE8Ii>د}Wފ'͌ۏ׾u\L)uH`7+°>k$NjqL=b!W5O'@pț]x֧L)6ohon'U.3$'Xc8վȄ39!  )"~hvV *rCX\;kG!  `DMX X# P%{G -Wn!cB K3sB@!H\G D@:*A[ !  E%b]mmC3k-\J' r! @&[Ȗ[ɖ_H& ڹ _"I A[ !  B -{9QH u(n! pH]Cd!:ס%BV/đWHi& mZH&ttNu)B@]d%}dڹ,u2WR <"JB@<ѴhB@i[I'# r=! @2@YoKR!,hcYӀѡ,R2! HWH25"sTc@d+.Ĵ^ND j!XduX)Bh>$ԩM!VTŔtB`@XB yТ[DP>)B {DJ)E[3D7ld"e[vNJ (" 2B@$dv.k\'q3IA !+"׽BB@!@ȧs|]n!\'%! r=`*c! @n! 0Y)i8b`{+!;"׹c;EI5?s?sVB wDsLw! j-q7?X6x«̅@ Lh 5-ڍbjn= r8$k&ˌ$`r&I{G:IM*YA\ !  _/Lt#.TAT^ ^S*\#c! ȈAڰTg'9'$ɑHAE@zPVaB@D. nJy/0Z5uxCKZ]ID`O"I $zO{RBɻ@Uu!*Bvas!H@7i,7/2/e*Ŗ:cs-N:WĔ^!PD0[aU2O" D%- rkLd#ӹB-%#wU肩`WR^3f:)r:&(BDEڬmg*s8|L0 q۶aJKK1z.$_\ mٴZ1[7a%B(kk5۴q}8_Y9"`]2J\gBE焀E픑ņqI0lZEdkk O°ĆgԎ4t;(LCz޻l v@w_.2ު}_yymlLwPLOz rНB@D!I0"bH+++lI ~a>q c% OE. _H/Zh'N V[n B3>e̘1cO_c˖-_?{VYUiˢ79fK/m۶~?j^tdy@;dt^!P8p"Rd;f|{O=C;[9耸Ba# @,^c v3+pqqaq}r1cM:ͮl3܋IN6d,4 Awb}_Gm'5*麑 \0zo٤V! HԵYkkܹZZZ`ֱmٲŶono=s?5q8HNN=t5k1FiUUUasxp+-) ϝv搿{=:ܡ}<^{ :-_<RK… mԩF'WVVce wwr2:/("H;oK]WWgcSMիḿ:id),0XK%.~%DmM5NhqNPI Z| T'ܧLxKØ:xҤIڌ3m͚5mV,ERyRQqhzuHJ00d z)ϧ7!E{CHׅE@h8pb߉5Ic '~!$73'xS]a\+ksPI}ylћ<"׽!B@"@;'Z6ɰáω-q˶Fa(8H'NCf=. w%Ec9)4'x;1λwl +Ťbw}ߗh"٠4B@"A;N(dP"׎Xq؉tB:s$N5)tpmmtLg#u6()BH"!N8XO?|=N6x_8 +;k:دdm:8}ߏX:F!P ;LyW=kL0<>g}M)A!,1  4  1"Ԃ ƈP B#B- a  1"Ԃ ƈP BcikAӧd9y4hPy?!n̙̜9;O?7YigֽfVZbk@RR={4﮶lGL^kĈ|WdddpB<<9y~\.̙… #))38?UUU/j*"##;v,^NءA#٣O,llU;S_~jՆ[oU*---d޸8: uŽ PrzxYYJHHPC{A=A͛7;v(ɤ]~z۷q:tP P;wVEEEJ)ݫ=c@v޽[uŐP)))j͚5z38CꬳR2z믿^O+@i:w4MS iz~_aÆ&BZ㊇zHH{PU_-ܢz!nݺ?XJ)&Lu窬,f5|R~ӕRJ9?Q]ve uŋ՚5kR>}(@1B-X@}jС P PJT骮Nol6ݖN:)@mܸQ)TFF:SM7ݤRJ=z6mR;v /VP p\oOǫHk*O###СC GU4MS}̤]b /t͛Ǽy8x ?Cp8p +WdѢEdffG R1N:ꫯꫯfݺu]H\.='Nd˖-L:_~z ŋ1w\}Myz{ȑ#u={6nͬYSOmlݺos'믳~Fep(((Я377e˖PG-Bk#??=zЭ[76o #͕W^y} GY5JPӦM3p gB"ƨg1Ϟ=[Ǝ[=V{*""p?3Ǩ̙v7ydVJ7xCp 2Tƨa֭ ޾}:s ]vUE5MSÇWgnս[ne|gڵ(INNF)ŗ_~9ʢo߾l6O>͛7ӱcG:%KM߾}IOO>#//l6z"!!uֱazEh!m+ q7|u)+++3.B;{*3gĉ=lH׷ Byʗ^z)+W$11{ɓ'_Vqe1fzŌ3 ;UWW3w\MO?Mbb"۶m㡇jU=wqSOeƌ+2}׷|rnw=ޑ OAۢu릇޽Kr饗pI'q-0o;wdذaz=W^^N~~>:ujQL&AhS .^˖- 22?_~cÆ rUNvL0A? xvXUTT_xz|jj6mayRJ}{ӕRJ_RНl۳AC^Φ h6//+Wp8>|aYb~Gn7cǎ%66kb283t[net҅ѣGÐ!Cؿ?eeeOу3<0nˉ3ti&bbb9rn͚5l޼}rꩧɟ'>C.y ="Ԃ mә6mkZ[l1zt]]:u"""}5d mOrr2z[rDO)))駟>DAژDcX=jvЁ)S0ebccX=6lꫯo>bug[AA!A0FZAjAAcDA!A0FZAjAAcDASR3EZ8_~=-/"Ԃ ) .$==6EhjAEQ[[ҥK DACJJJXf ,hckA8Yx1nv  q8ݻ۷5BSP g(Xh!LEZ8c|ai>(?)[hjAvBjjO>uFdZAjAAcDA!A0FZAjAAcDA!A0FZAjAAcDA!A0FZAjAAcDA!A0FZAK[ ljO U:Tm[BTC11NJ 3|(cJ* !`TtAq`IqΐؐvGn9^ks*0gS \_Y3oI$ځd[ I::bK  'v˽xpIÿ 8?!A—OZFP~g~qzCk>4gp/Mٮ!>Q.cR ߃})_|Amj:Rp$夸hV+ n Np:~vdW <ڜz<4B CcA"H/n!BoMFJ =6iohTNgmj|بhb{kGjDk1ص"4;v-fGCku~M65iЅ Tč&SAOC f:5]^㍔P54 q%*خ{@$d~IjgjUQfDh,%ͱ T1սC'txR͝H3g?J[4)獥qӢhDI CpH!}Oݕ*8o;p;)Ͼ,rʲqFo(ʱ ˺SM܅ADkGѲGD7E7]נBp+7{٘+Վ7\z  Ʊ roa^.` `M GٲÏnV7?\7v.}hahH^:α {Ak8qř136.m`Y6%-`.۽Ҽ˺Kȓ_XBZAU%rUE1v*qb]S6uC{.+W2d 111\q2{lf͚  X`6M{wYf?/,X7>S, :t`ڵ|WTTTO?n:>̞=cNJP;QS>rAvcǎwEz̙\}ռ̘1㐮aʕHϝ;q#[oCj*g}FBB999DEEh"ONNN;3|M|@~&܉G "l7t{XF&DYC\(a{f'kQ-GV o"::}8@AAa1N'ӧO7L&Mgl\jKJJt뮻HJJ"))/?s2vX.] 4iO=?h5|7 :/ϙ3իWR Cii)gĉdժUޭ[7uxGj[#CQ0,!EtEt[}n\~ÐhAeQ%/ȩw fĈ㏆|W^9: Z<fСgy&}iQ=zǻw֏} ¨Qtvc2t? 6ѣ:u*?<999曼ۗGyo%X`YYY?^z9R.z~;*y4Ol q~DtEt[1%-cY `մkS_gQfsr~ql>lЃcǎw0,SJeK>***HJJ2dHuSO=?̂ 7og{L8Ν;7 &pr=0`&L 83fK9x{Z=j. &+ǃ-& [U9,p؅zٲe0dv gՋJKKQJqWމ^\ytI̝;˗SZZJBBK.%//PvN㬬,g޽tÇw!##'ڵkyW:t(|w> :uDUU?^{.NܯpC4}3_;v0sL8stݻwX f ʁnva"͖z,jEݺG+AAnMv+k>:vٿ?ofR;衇x':u*֭p`yy)//Gz,Y$={MUU7nK,r|s:233q̜9SOO?č7ȏ?ٳҥ wq/ϟϤIXl˖-`ذa{>."Ν;Y|9ҝk[B?;wdΝGv+¡ǧ}gWtKvWL{08?qʭf˻{r{":؂,,*mwbzDZ"_Md7q嶳 m.4TL[}:.YOM8K]\eG]wNV0!{(Sp6C#MYXzͬ=* Dy{"''MEǎgɒ%233ҥ V:ht|ԩALRRR ]J)ސbGpl獢5&Z VA LsCEA? 9sF.Z=nJ=ڌd}alDŽ6;_a1~B ńQe1]{VsޯðZ{s٨Pm=\ŕdFwB<5M ?6ٴ;vԻCa2tL-iγ='(oӽW:Ej0 Ɛ+gy>I#gE>]L1V g1hԯm_SVjh&<݆ K[.DӃ΃Gy7 C_덫ҍfB~m!o4SS{ooh"B-~X8"cJ5]4(w}wDTxf"{YOT!"GƣY4rW׿n +ڍ%-=Dݵ&z`7Unƍ%:do~Nf UA2Gԇ6u W:nj8ݘhG86eH,ʭP.U;]17!͇85]G0aƌ)fQS/ `u8 p /W%TŖlӕIJ5-*l=@DÄ־[8.[munPߦ46eUê"|z,;xK@247/fm>)V\{+%+x#ee$1z\u۩]Up"LϢj_-C_]owÕ"oI)?-tfרRM_9kծwV(\=ʞ7?^ww&ϊX}6Fۏ zu%<]))gr'*Րu1+/wb}o8𿆊58K]Az>D DIɚJr$7,NW$aO(tm KON&լ #q:qWy;)Jw_՞Z*6W< ]u 9Dqe(T6ɖə~_NI޷d=LjHxuiϤs{vqƅ3/p+O+f}*=JL*3jU{+04Gŀ&F2;t[?1k_*>Fr烰6tyg'2jt5tu89̥C&x[}_gV]w~}G56qK~S hcW{7|.?aw&,H/m yA8]K/YG7. m ?M`{>N|uAWV/ȹglf; Ӗ &cOvadG[Rb1TeRwIvL}zBRCncyïhi9+ۭ)2=zwaŊ̙3R$;;v;?#wq3fj-!##k׶o·=偓^9dx;=o~ 9౮XzD3ҕud\ֿզ]HtVV|v[b xx& KH$ "~kGCyLyPNŎjD mx_h+(څ~V3("@(wd,J~$Dic{z='bl5Fur/P7wZg&=%Bim5:9BalƊPU}]B޽{6꺺:^x{9ۇng0i$.&p >o{̙DFG::hxo7h#c|7•t):jvU]lOcsw`OR+Ig4t{oeEe yȖlt}*9A&]BXOen?*jW-ux..ܵLvƏg #X?*b[5/I0PZMgQ*ʷ5j8bK5h2!ųUP}9u8ܠ PQ/ EnPLҹ^QDtQ߁ҍ9o#g_7Wk8^Uhqlp umm-ӦMno:䓙1c111ml[0W_ͬYx뭷oB Pt]宐YJ- tU ;rUy[!EߓoLcUV_x\YS^A[;.lqt5MBMzwuGWsYOUfsBp'D uQvWS,}ꫯe˖q7o׿իWsPVV饗x㯾뮻 oz'x=?S~i=\P_wu8Nk"##6mwsm1j(FMjj*lܸCt:u*wuW[qDٲe 'OŨQK+K{7%Nt[?Wߵ=&]5,heC ^9D( Wǻ)iLlDbK5$3$K٩Ϊ5Ϊ;x6P"v(pIč2 SQDyUjCnR&}q;kCnU}6`\G!#_ʨ%I*]| jBZasKy udw+;kYKܨ(C:CQazے_Qp|rwk<䓌;Zg}Vj|ƍw~`ʔ)\tE 8ҍۗ;S;:u*~߳gOHOO?&O>-~wڵkyHBAdއmîa4iLYQ5|yRx%Cy6pΈ5Ԙ:1-z!Auoo3Yra|~,:W>eDwG-E+*H0\w ,z? [Fuv]֕;]}Q5Xu4R&SK5{^> gC#gws;l)^rW-~] R i)?Eb-gϞ\wuPO4}|uڵkٽ{wk׮ /4 ҫǷwOF!J 5{9 dxzC/OK~D3k4'w"btD]:ARjsF? 0Ek.z'|PH֛;YzCrݴd`l}5/DLvy_1#[G @+&[x^N1wkZ{$"z{ǖX3݉AZv<w^ajޜpu;OJț[r+ߗBd_;BerTO8sp*q#QǮGrLK1C#}S߁KS ߵU(:ݘȁ9|U-oOch~9{EjH7waFSi 2c[ ԝ;wnQ}{^zܸqX,c9묳Xp!;w4gh+ZUTTLJh[E^sS4*|GyBl:m*Ucm)bDRͬ0:&k4awM5 bO0&i_g}ͬAkM43dVO_ȞvZH0mnKsd<}ɦ1䭞deĚxC)'oqٖϊtlM\ː)t'nJ~ٛE)_v|'%%5R8ZH1"L uxi %"wn)!jMpʷV &|R-N/*Q=uXb;H<#k 4͘_Ntmu $M\?2z (t30uӆč9֬oG'HCU&(GFfR_ͬqœTnUhfS9!onW&wJ# 5n(o}N13ÞTj{-uN)fbO$vDj68i^/TPKD]/{aj5\f~w.r+=\gtU~nVN'jVC ;#K~+^낧.| o#O[v;Uі_)cZSRn7%%%-<7ի)//'..:V^ Of&u{S.W^^@~ZdpdiHN),IA5nz&3FD J )Ԇ]S^n D/t>\֎R.ix=kc˙ 4 O H (m~QD B!f0F̰bElbRM #1#nA\S$CF2';#QC25QC  #K Ã_8;w)8cMR#[3{9sΤPPP@^^aLN'Æ o?(nFA\322x4hJP9>Axꩧ5j7t{xDHzMgI{Pi@E7tH㴶Oߢ{lԖt y#˞Za}kw˜!Ҳ**P^}W^B uZT'n i4s9O?~AaIDAT0swy߿_xɜ9s .?ŋK/p}%=>G߾}ٽ{7fbܸqPoذzgBјH-)L$P(ԅB2\uU||;~xӱ^{5 /<4hc_~̛7KRYYɘ1c޽aفng] FGee%'x"j_v--l63j(nJnn.G6+]EH0'Sː2~nǫ6_~p{W =H%fhdE7fx=H%vT+#+%kģ>j\r%xb\.4 wgh111\z饍[Vƌc,*k aon|ȲvZ&Mĭĉ;vlx͑f.ƌmӽld e6Wr Duޖ0]R mnߟ/VP 4f ͬя1՟Z}Z,ZEbc`@<8+낮wlx:j2uT>Kʶ6Y<onC˚5kڌf{ ?, KO|K"Kxzj=ݸkxjxݸk\:O+5L&݄)ލCybKrW]Wؾ5.A y뮻HOOgY ѣulܸ=z%,=zS0VBsamk~ݾnp*u/{< w WScOOiGS冪пmͪ+Dh^A1MAwxׇ9Jjʽk㽧mZVyAڄ'wMbKzNVqj|6W)dpZ22R' 6V M "M_E?pZYG.sx(,^+J.! 7y}si?pI?#w2"Ԃp Y|N!MrQJD E7.r)qhD4wKNldxppx-e_~{*k='u^'%20[zhP 1D[~2W :͉+qKDmDKT u 5s#lvt{Wvr|/,(v>易UkUo"5* GjA8XV_f|hx+@inN<he" _~A՗r]8N5{ &-) MZxQˮlܳ;8,dYٗ*""]JŽ* V˽n^u[K" X ;BJHȞ$310dI{^9|y󜃥Kk}lHZB=s9> Hڀz,h{ncSXU+YnnE|lwCV_5ʫI={TΗ^ՋrJP q2XyCdN@61X\.Uڹ'b *3G]}ذDRTVT8DMɺ~6mfu%A-MИ O]i+k8ͨZo 5o}6eG*18?*$ n0ƺZ55>M/ýaڭ#A-M\cɁD[6Ƃ_cBނI`(H^޺еka욻ĵ%.;]01a<û5^It~Lԙׯl`7<;f=k j!nJ=YVز f PN[oL%?vU%j  ebxzElJ́0726V02l\_Z[,>Js%p%0@a+*zW>tUBUvG;nkS_tUҞcڶ{˃ОSWٸz%jmu_^zq}D-IT5* ^*A--T̿6M8Ud7bmn`zhLl[dR+.Yt~XGDr{³e0[=5zK~F>KP qʯSﰥy}5MyڅtS~g ]߆TtT8_RԸ[~w0y$&Wk(*wȥ/ j!nqUl)-<4ugNO; Sٗ(W\7{2A><9qn+YL۽ʻ#'jZB6*5^u)++zON=?^gȂWZݮfB6Z!0GB62y$ztP,m)yn}Ǝ !m\ú3;'H *$EmUظh;G754oQ;|[|y\[; `ҼQ-jf( bטNٯJbƈ15RӫxfjWZ! LveZ7-٢vLfMYF[Yi^9;,*)>Wt[3׫֢Tlxۙޗq؞Ӯ3[r[!/cZΑoy0J[~Eo0zc%JQ,>*x7qBVp,,ZvV= 5>3iU{rD-m\|ȡl`-nGQ+HO~O{{??Zt>-9u!(^ZzaҼQ߆u${<^؝;UrR O/${ Ȁ{z۽edJ+z3+SOޙ-AQ3IF5O01|ܕUNl^ȹ#ybK hkTkfq"1`=hȆi}{ tGeBJ5Jj5][9>>3v ү!Lc?d_ɜH$?nz?M)l_W>m ~ObK.ʶѸI|?YhFVqdz$~zٯOf»/_ 0o*H!8FMIn9o~ɂ_L3\i/ l:zO BB}>!}{.s"1kSX*~(w}]֥cUjuC>r͠ pܪs-k]! sʼu^Tm_gѭj8u GV+pӹcd]Bf4_~r?Ii^9 &A3}1 &x!_ @ui X5s=Wy~\5'we> sb2?w8skJ뷶ھ@l|g; ?ƻ^=6(? OyI"5/eeo2C9*Y^ȘGqśï0Ր${??b >)]=spG[r*[rD-Terڛ;SS[]GѶ-UUºm+sӹ1x/#ߞf{A u5j~utIeQ5C_QSa,J ~}2A1Z|٤\[OXun ڇaٟ͡iܳ`,u'dZ F*wXpS#D. *á7$A-PVih ܴ3c MOqu9"`W :<pOou#< @̐ 8EQ(8YDQԿ׃W6WX{ ؓE4Kl [?CD\('ԋ>O~z!֧qT2un&Ooah7|؟FťJ*JʺZerZ!ZIա<#T2VǻQWN@/O,CuJXjH$e .]Ŗr{cD4`ed[WZGS76 4z[?[[#H`x7'h>T鼵=#u<ߙWl(dbogKfY\ﰆiNJ)O`1׳=-~Od8EyŴOrD-m0*|<_dͮdp9˃kSl/r)ٯ˲ǾBE~ap1C2AaxAKkPU<t¨{a>yK/Gi~EU|= 0!#SU©=Y?^΍S{q.9%?Ru@MJEM# sm %D=>K):[¢{rXYw Uj#8b2JȰqQ !DCln_$WڅZb \rե53>KYAa\"lu8W {ys ڇgow3՚)˷uZYG(:[B`qa]4cK1㥉>#FO?Ԫ>ɩo!h*,{ޮ|yvaLܭFsnҸF~7&0ㆴ^z5=z݅k O&]0OvJw 1 6Pٕi1?ڗ#j!h#baַ9^jWRx`DnFc1YXMBvG|tc魚 Exg&qaVeՔ "A-h1Цi-Nr=/'܇wCb[넫P+=]'A-==Nkܙ=z#{urD[Ykә5Z8$X,JZu]wc€qrt}W>ʶ䝔W;?]-A-hS׼{>&Ojvc7oFyE!lۓwrBr-ͷ:%A-Ly/Qk3cyub3 p<8ےwRTVh=JͯȯNZ!:T˻acWMsѸ0`čF49ɶ#;8_Rd=_w?].:?BщVgoQh8d=W #{۟]Qk:Ҫ2R384XGu5W{CZ!:YȪab.7-}۟(T6Vv8)Ntfb$ƚ"A-׉le,K eTjKE\L?czjMug %3L' v$:6U8uoq&;~DF5+n2S%%d_d)2r20Y~, ف=lB *Yq=V\Ab^V %286=n'b>r.!B34d x:-#A-7s]&Kq.ATH$r׷k\ȱs^q^ׇ12#^"Q֓BX9Mvm\oju[Z7w}O?/_yV۱XgҪ2J+(*J*JhKuG2=̌C/$A-7R6o2pBV󶆸ץ鍛n.b}P7MԙMuԙ3 bk0b3oi bj,fKBHP !M|u>[s6ta?I[ݩר5vꆫ+n.֟5ald69]uh5Z1(x8#2"l,s_B.Ԝ'p?Tc%)m:M~3 eP0E}󎈗B[b$0I8\tSV[z*5=}08gPj X{BۀBNe9ȩ2sTk`EJ}("~tGzk߆&A-jS5 r * f5&s55fҦkxййѹp Wh" G1Gw j!-Vg``&åUԘkpUڂR {sEw[oKIP !70J#B$BBqB!n`B! LZ!IP !70 j!&A-B=)IENDB`passenger-5.0.30/doc/images/typical_isolated_web_application.svg000644 000765 000024 00000021246 12233035540 025517 0ustar00honglistaff000000 000000 image/svg+xml Web application 1. HTTP request(input) 3. HTTP response(output) 2. Processing passenger-5.0.30/doc/images/icons/callouts/000755 000765 000024 00000000000 12233035540 021161 5ustar00honglistaff000000 000000 passenger-5.0.30/doc/images/icons/caution.png000644 000765 000024 00000004772 12233035540 021515 0ustar00honglistaff000000 000000 PNG  IHDR00`nsRGBgAMA a cHRMz&u0`:pQ< xIDATXGiPUGEI9AKQ30STY#E4[!A+qA0F0e" G-ƅXn%a2,AdxeEAp?݇qsu|˗}ު@o筢Q.xSz~#ݻw,]ԩSo{oKDW@@@z`ddi,>z`/G1"_PPPz t1ccc>,)rq9{"SHHH/z߯_?>\ۏ[)2Ver!L{)S>,|+Pz-jWLz] o߾| h*#r6ZZVl^6Qdڷo_'Ҹ8N6rE$2ڜtB?x^MKvҒ"΄ &S@ke 4FO<lqްܺ%ׇ״/W+)22;L]EGGN 2%WF)..51ىC[M+ ڰX g]2u!Ҭ\8Uaڔe*J[I)XOאR G:a3uՈ3yLSie\kk*V]< aƓ%y啤仵,DN 4kXMД(4?zOs i L4_Knhۚ/,21iֹMWh *vE @&Rz@YG.^~ *\ao7[b@Q9HKRߗc 5*UmB-2El&XȀO-`r\$m$3Q)ڐt;Hi363WH;6U?6jS7Sm云K7_HY6Rh>%iSƘ l3ZXXu0"00/b7(.oJh -j⍠!JXdkn$w)w1h$hiO2숢qȡ>zL" 6pZ!+.Ajl&W6҂ `-U%Z־VS9DՔ1:;:1nfH]p>E6m@Cב|Z[s5hVyNVI!S*4*EN4u_iNNTV=&@t!CtQ.9b.\dpMjWJ6!kpN:%I 1dqѺq; jQ;]4 |WpP\?^>a·>~ٳg`"1q 뭬tǚ&}I; bWTm~>2j e.88؏|ZZZvvvQQ10 f3 n޼XZvvv~k SRi,--9r | 8{ 222 N8+xy@ﮩA Edp:a *<3ssX\q@C 3a(LfDbDccuN#}1b@IIIPJJ hrssYʰHDFɷ#R`B9 Yg:!"zzzwQ333`@|F{)+ƈS" ²u[;/ܡ@***X|}}-c4h G 3 SEf#ng^&x 36?~̙3q9x j BI*xēᇐ.n\'0aA,}W 1um8 aZƣ4l#k⑨뻽hs(̤B\fv6z B PObm W,R/}8-^lw 8ÏATedd!2o5DF~)3N=K-2G6%t]S fpv1LIENDB`passenger-5.0.30/doc/images/icons/example.png000644 000765 000024 00000004462 12233035540 021502 0ustar00honglistaff000000 000000 PNG  IHDR00`nsRGBgAMA a cHRMz&u0`:pQ<IDATXGyLG^dA V,yYGk@1EGqfcdѴرIFS踋[wmwi<~|>wAA>Ԉr,Ȉ~zƌ~9>>ۃz5C>jk֬'g gϚ*-Q"`(zH/%o>S;wnxA7n(T/./UX8@A~~Dkk>իWe$w PXXP7:.W+S__o]v!hʕCJo߾K!C\ggeggfe ddsӣ Νxb+**9r˗/}AA!Ȝa]}mmVVV&`H RӧVXU͛7~۷s=z~puqe%p755nڴaɓ' ^+C?M`) ,0UPeeʠ7`WWd޽#up@1?=쌡evI =HJJLJLLHO.?TA͛3g(|Vw]zuɒ%˗/gNK|2s… z6J{79 ,qQ11QQ1|m bQ(zZڳgϖ.] _ǩӦݻwpɗ>17_~((A$<9bĤI>P +5:h#/_O:%##Sᆝ۔䲲/^(7550͛7۶m w&L AHHg5pUU,{RW9sڵkXVN.uV{$Kk׮yyy9Y!AP'Hmo7.Vh&-tYRٳglذA8҂Z0%{M۷l٢V==쬭|||0L?͸ ٨ueR Oչ0{>4TIܸqZ4g#88F5f?z__.:XZйU5]pk&RЉ=$XzeݝIllmm )'i\2sL vp r-0ax 55mmiD2u=V7W7tتT*'''ggeݻ7;''/O. ro/O'hRRVVV|^tqAVbsj$yO q \yA/ae b\+2G/Iޝ: !Lr VvR٫meDmaiii.aanxu'.E]N zJ삞ʈ YrIXX 0S^yYXhRx){'Ge꒾S%-,,-ħLЏ5$觩 .<?y|L^I]o>,8z M&đJ31gN$@)0iHA@5}'_EQ[c[-զiK&{sΌ۫ 72Co*01& 0!F/)ݬo~0J@w61"0+t|l>gA^fre@ O%\\2_լF8^wO~}?ɱNCy﵃[Glj.^g%D,\\clPٛT*.HϿ xa\g' < J[]6{FC[fXДwR[:;zxaJ(I76z1=y Ye ;|wI>mca["V+Ik6N^V{fnc !pl`AQk9JJ4KVzv2#dj}f1¶n/%)fl/hHX]斵Ȋ 0dэi22’R;<:,hiZd>4]F0:b=wέ).+A k)I Le*h&Fw?`.#eB<9QXnBFIγ8/M=Hv.U ^-m0!x˥: %&uzƟH>7M-oFKƦKw+l7/m6*u$nVY:kZY*MG];!\;Dmuy+t|"4*Yg05%fG 5@6ccI 5Tl^%z&' p,\c0Ti&D_0gWmpuCVEdmAϊE`=ⳏPT̎N#Z'#-95(iH)Cp{]qakkEqк.Z͔f=܌GsiE_QRej#\`|xIV~V}On+94^ӍJ4fv,2-%R1 Bk^o~џ{~tϳ@ /e%R <hI['jӾ&'w/?=vzzZN5-6EZ w}}'o˴%v\IENDB`passenger-5.0.30/doc/images/icons/important.png000644 000765 000024 00000005141 12233035540 022057 0ustar00honglistaff000000 000000 PNG  IHDR00`nsRGBgAMA a cHRMz&u0`:pQ< IDATXG͙iPUGǛ,&$ ~͢MH  # (DȢ2*F7!`Cr qɸ|Xze]o=sgÇO%Sx64z4FcQ˗oݺe4O"#DT99s}|0isم nܸa%2FG4Bi47onmmmoo?!۶Uvr]8ts ;TJYdG >|8P=)9s',%13gdI4fqAݻ XcsIzE)LqqgϞ~eߙa1€Fco_WYyqZ#?WTd?b QWPpyt}s3HF66VACwpXQ^~A`4LkRϷB5t2E9hxF ,fsuU&2:qȑ+Wܽ{פ Trv63"b׮],___ooTq}d|AtSș& yyy"Hܴi,F$''GEExxxt=22RODԒH&߼y8?_S`d6ʳ_ppp^!(b,?K $qc=$#:&Fė5x9f̘(hxtҬ>Jzcp!VQL{ .?zT)---DU0///5Ii]̄dLpsۻw/WN@&Jbc8`n"%$$O>j@x;8Hvq'NyH<f 5K ҪQ锴 :-IcT_Μ:dڴiS,4p@ނ,9rK/ )Hz@pqqtܲe 6VJ,X0b)&&F=jƍJ=)/&<)dy0@O6[=Yts, R6l@( ܃%S]Τ"I޽{f~GMo)Hs(JpCvKQ~3 rZ{Qkݺu˳>>2+b!eH}byV;!Ib==5 "˦+i\\***!tz Uȁx!BhZK%RHd(ݗ/_ FId !j33.oSe_rQZ?jX2#glOOvGMnHL|(=]=ZmOw"#파+WI chR54LS0f}߻vqο ~~_nҁεKN|t4@63- ZZO?w/Z?8+Ky80c;_۷wfjB~ڀ#3?Ĥu0 ivv3kqLq JNNCCHdEetI4\+V3gOȬ PIA6o86laIӎ;+..ΉNGS ,OS/A @Dڂc'3g.\{Ç\@ΎQLRB^L,r͚5+C|Рw w-5I_Ӹts>|?V쑛;cƌy[bΕ8KH打+ n;;w٪UֲeJKKΝRM2%;;;##شL,_PP%̙SRRdP^b CO A)`ANL@yhW__Ī* %>)@$pHccի,gɓ'q{{2/ Xk .QK!dYj!1-{ u -e =K_cfa< 67%CL l! p,yџ~O-d)퀹FE*EW"0y_f hIENDB`passenger-5.0.30/doc/images/icons/next.png000644 000765 000024 00000002426 12233035540 021023 0ustar00honglistaff000000 000000 PNG  IHDRĴl;bKGD pHYs ?@"tIME )(IDATxڕُU{kު{vbpƱ'1.T_ .o@#Yh2сapY{ꮪ{}IK:ʯ'2X@uu 4]T1z=k}끷@ H#8VEׁ}1 p5ۉx֤\amkri:ռbrbBM\W1K?ߜЇANnY>FC6scpy]z3/]bHz Xx^D2+})%c!#O1se/poL;~ubٴ1b)( b6tG82ҕ4}FfZS-Ɯ3ѰȦPAƉJ72X#f+@S4 In2:wN+1||t;=qє|Mm>P" Xbr|QrN/pڀd2%#$TK2U ,l̤h)-}qVį5RtcHH캢H%ЁVG̀"welpmY{R4afсaM l,يbcCϪO4;|f@!*΢+EILF}zV/ZaɱL&w͇/EPg) RJ4JF6eKF{Ao$V@X!wG _|>oNQ?  UT\U i[qcex1[0~hY3-jk$95,3ԔN.b5tb͎mIc5.lS`0Y\`|x-83-I 4TvMOγ׈MR5#f QSJCf* ]sl`Kח<߽Am& olduNJD(@ =7 sǁ0d@~ɢH1(lAMxv3eLN%{_9ExIENDB`passenger-5.0.30/doc/images/icons/note.png000644 000765 000024 00000005252 12233035540 021012 0ustar00honglistaff000000 000000 PNG  IHDR00`nsRGBgAMA a cHRMz&u0`:pQ< (IDATXG PSWǫ3V.VʨL.Km֎Uk (TA^,CQPy%H!! IHB{{oc_`Dgw;9ù?;ܓ}?T;'ޝv^}s%滊xw ey%}ŋt :(gk|@vD<-W;z@/\O\._Om7?PG|_Ŝ$U[SvJEiU# Ӛ/,V bt90դɁ~i9*0v`#Q \p'C0,He ).5\aBZꫯrh6O'MGyx fY%M/.ݺɣ`) đʳp҄hM:Nȉh׃ZdP%yigϞAP5kfp%UAjI8>CIUN5Yb,9N#׉rGGGZbaʮȴ[6-{jYO΄ɵ5>|n`oh,ѮL4G}9MNS۲e˽{Zz55⊼~/IϦuz;&{G'(/"Ou gDŽuX4U;M5pݺu@֭cq*_VXiV L43>7x@:kM2 C sb5N6o|]7ڰaKZ5ň슢39N + * R@I܀kA⚰~AB?ҝ 23h%c⳦ZgZ!֯_"Wird3a-!^H Q3DeG7t;ˢs5( VqF/_~,kK-beM98q%Ǐ DTd 8oeC5BE#<b0!53F-[EBu*DutmwpW;U';]+g EEg24TM^UWG ktk" <ª.)t\ۓQu5K եeW. RasC얊(fkq ΧheLk׺i{)m5v-|1.(tS/J $Ԝ#R܈ ;"iBbh,XJȦp >ת)ؗAIYEJFpzDJƁ6'F9INF82TG P[}bHЮq A_@֑@yq̩.\Na4]ˋhLkōA9ޫG8PH}%'>m @KBqIS*B0G l~GA'L(ơ+7 uU%LU0ԐH-I]24#阠.  P__^QJ$Z0U][d\j۴%v}]Wm :'QhDdRX+Kh;ͽ 8'CJY$Bo cӖY԰ZY4k" ., 2!0Bt>JHHHK ;-Y`|xCWiו>\fRPnVU=G2P[u;`at( Pyq(vZ:YBLc6#+J." rQ#56]@M NZXK/c5k8ۣ5w02Iaa&U + u`=liM:K7k~Lr(D(=]E* F0kjLJd8ħV! ]߹sg C㇦'N +Gi5CtA&RVm& (]_?/":'Ě{'Ea0˜NԀiҥ(q U_PwY̔cwp;}@P2e.Vp ,XԀە+W%91ΟH`c\x1[/ٹЧŞ(+Sܜs'C>?!%̻h"??*>/D"Qss3 6444**Μ9pܰk\>>>G 2Lܿ Aӯr4M`@B׏b!anSa,X,U$I=п;@лj% [PB ,*㱀1TApдF'F?O~̽s_Q,#IENDB`passenger-5.0.30/doc/images/icons/prev.png000644 000765 000024 00000002504 12233035540 021016 0ustar00honglistaff000000 000000 PNG  IHDRĴl;bKGD pHYs ,tIME 4ٸIDATxڍۋ\U{Oթ[Wv3t0J 3 C-,B<"/ DP$q=L NtWwUr.{IO4`}ǷYk)~JG $")@]6]wa'A(L;#M`^Ò{R? .y9NMx|szͲjFSFoT*;A:OdٷCx$opTN吓!__'M{ÓwΏ|z>sqt}JhDeNõLVj*˾o깲3 >6yw[1榓|tζ-Q$[ ٔQa fsp{rەK* |O=R$i݈\DU8{KԈ9=H!-=mU,@Y =WuA9)t"2!͞el?hvő= K}(@'|eɺwa%*~ieԝ 5;|rv_~1x_\+PI4r ll|.!lo8p}X\ 1 $i[ƶk<59R]xΆs߃7#cmQd6FILҍԭr^nvnUߏK 򗱴g,2\Zxl[XeK2`,B^Hڄծ8Yw-?$*']r!i?I&i/$mh&U!7o?٧>~9jU~Uz(;;*P$0 bk#v|MF' #$\Arym֙‰\BϔL.3ݏf)֬]PoGxsƁV-Wg&sNj`Rěj*ʩF]fεNjtt#]\ Db=Nl2_8qZT*/ȑ#G4>_GkUCۚr2^t44zA+3*qj%Hr^ns@T*{/{hRhIENDB`passenger-5.0.30/doc/images/icons/README000644 000765 000024 00000000342 12233035540 020212 0ustar00honglistaff000000 000000 Replaced the plain DocBook XSL admonition icons with Jimmac's DocBook icons (http://jimmac.musichall.cz/ikony.php3). I dropped transparency from the Jimmac icons to get round MS IE and FOP PNG incompatibilies. Stuart Rackham passenger-5.0.30/doc/images/icons/tip.png000644 000765 000024 00000005052 12233035540 020637 0ustar00honglistaff000000 000000 PNG  IHDR00`nsRGBgAMA a cHRMz&u0`:pQ< IDATXGXmPSWN3δ2(kU"7@;  !BB7AOŠNYNYն춊ֲu٥ ": ]}n\%@w{\/}}ŋ/z.z(4T d?v]?]xN2XrffٳgOb6l/wڕuԩOMMa<»dkY+zhhHծ_ V@+1.k3t֝088"79b%z!֭STccc .-q Q0`"4Qim.(ifGHKCEEiBvQiiJxZ=<<̀yRޟrJPP{[KAS [y,i($ @{EP5Ll DHm2{Jb:tww*̶44]]]GlqJ ӌ,^n)FR.E@%/V(g }}}FFF 1m_h4ҌbD3F^(V [UɨܗYUZ؈-3 FX_>Vqiмm ibbTJNffUǩk8Rx 3 d4$/Ga.-Q'?ao[Nwb%V]rho޼ =a~ <B)hf &Npgwtzf&CϩKȭ˪ JoFag1 2Vco҄MsnG_yDu$"Ew={bB#pL@_X^&xmו;OM30` Bn伃 ٵHxn݂g.& qËնfArawlv|t}$Lܜ:)MPj~c8_vZ{CI Bڴj%Q0A 4tn*ξ7N=?^wBqqCФswwAIwt[z)&[S^fƝqmhŖHٖph%e9sf34| cEwi/D5\fraXN?kݽ7$ĄFh|rJdj^&(|c؜? DIgEnsՕĂYpP# `RXۙh [M首{].{$LU ӀcӉo Oz D)FaƅFX&2d\tΝ;$X |&66]_O^rp}nPWꙞ˝TЄ0RJ__tCETJWݦѸ[plk/7nD(VQQ.)>",lOC===4 =0 W;{nSSANΗ _ܞ1#@#yh)481.44nJf\M6v槟@=aJ(QCVr,IS%ۼyw ADgφiQA +wg>}9LshdMGdXf͹s_ũI-C}b aT諯oр{&40)@#+nXq/zeT{ PZ[+eu`Jxr&a! MqUىDuG\;;;U1)Щ`?+HkBm|,1n1e't{Iz{{UK+˂Ѱ?Uڝ$)blNNG 7n:Yc 7+r_LFžQ7sTdEXݑOo+)^ #vnQ;!owG|R{6d5Y?;ΉVi!xzSV @72G>~ժUG=V\x`B4L˵u֞pCX,k֬l~mD@"ƍVsЀÇCCC `C"2M㒒D+|f:ti v0 $u PnVt 0h4:F?^ZTN@o)1###xuɓDѨY.\vm]]LaH4ð/$ENIdɒ @"͛e,ݻWIĪ(RS% 1@1͉F0iiD!pw׮P{! m۶4Ҷ/ˈBueF;O,Q&9pR2u~ٲe~Jrr7)OI)!buh^!z&YuG~J4- DgJǪcn0ZΊ+d~ Bo߾=4R9Ot^XY (97ל9Q25o"±f;۷ˀL&,vA~o)FH)*O<-mT{20o~0ɣ7Vw>TƄڍy؉%ꫯ|VkCB>t2isj(S5V*0?ԯn:1}nԾsȄ׋`0̞=[Rйٶ߀IVs_3nUHPvJpX8MK2|AeUeV_u՚ҟhm|"Ɩ c@]zh<" jאN{u%Y@7''2<+?=wn6~h}j8vD| tI|%9ZMS6(;^t< @ȧke9!i7j;$1DkIŤ~-M׉,Ρ>MK屔䶛]7-<93;m ''n'D% ' "i"Vܬd֩:{ZKNgu^U7UV(:8DIzTIku E "hY0i\oTRq^yb}EQqVfgEsT6ޤCb1eF4NI:A%߈et8^_x>q9&+cͤ[5o~DH6ZDاsg. ^ ~~{80 .l?6;ZXZEn6s7"jWunɮwY߃ߋ͛{-w]Xb#a%%!L~G\[q;a_@M6v*$.Ӿޛ6|.დco߮/^hR"c!kzL\׉@Lj$m53\vttpUw5{^sL@`d~"!n$kE l"!eWDӱjmDQAEl^~Ҷoؕ0* 8F#&EiUUWϧ͌7/C$bܑd34NxgҨj\.jmm4>7bc5 ]>l驅Ed DMa q2yY=XV}bjִF Rq XS^Fم+_#Lfkd x?p! X;Z7xD&6C#M(Nmah۴ ڑ:}.h9ѾѽŋCipJ0cƌtlA tҥ0̝ +Y}z~88D"'BET`T'H%NKKS(`I_Š ;je0ΙelנA/R, '&Lфđ gΝC}؁$Zyy̙3#f`Y~)SL<9333bĄ:^SPP½xXC!O"CdB-Pq@"½x31 ?VRIENDB`passenger-5.0.30/doc/images/icons/callouts/1.png000644 000765 000024 00000000511 12233035540 022024 0ustar00honglistaff000000 000000 PNG  IHDR s;bKGD#2cIDATxU 0.)Bft6#dH('XW 9cAM-!d>0(*?/c}֮5uƌ:x,TCtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignature58a072e070da22f6135cbd3e414546f9hj!tEXtPage12x12+0+0m}IENDB`passenger-5.0.30/doc/images/icons/callouts/10.png000644 000765 000024 00000000551 12233035540 022110 0ustar00honglistaff000000 000000 PNG  IHDR s;bKGD#2IDATx%!C#H,N^ [¶p\%${;/yI@l\\ySM}i㎋suȌaX̠ eڭvGj!=dR;?ݢCb kCtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignature386e83bb9bdfba3227f58bb897e2c8a5+ tEXtPage12x12+0+0m}IENDB`passenger-5.0.30/doc/images/icons/callouts/11.png000644 000765 000024 00000001065 12233035540 022112 0ustar00honglistaff000000 000000 PNG  IHDR ˰ pHYsttfxtIME-'kM8okǖejYVǗ˅F C3IENDB`passenger-5.0.30/doc/images/icons/callouts/14.png000644 000765 000024 00000000633 12233035540 022115 0ustar00honglistaff000000 000000 PNG  IHDR ˰bKGD pHYsss"tIME x8(IDATx}=@O2\ق۰X"Y;@)lT!H!}=CDZ;9V DDDqf3qӉ~qXkTQp8|.)mUUz~9EQh 0hQE|jǣ,b)ntnwx<.|q~IENDB`passenger-5.0.30/doc/images/icons/callouts/15.png000644 000765 000024 00000001200 12233035540 022105 0ustar00honglistaff000000 000000 PNG  IHDR ˰ pHYsttfxtIME0 JtEXtAuthorH tEXtDescription !# tEXtCopyright:tEXtCreation time5 tEXtSoftware]p: tEXtDisclaimertEXtWarningtEXtSourcetEXtComment̖tEXtTitle'IDATxu1˂`E`59-AZ[֜šhr /h1A-"6B||g":16BTDDD5dN\8纮.v,K墪eYO`8FQ\.kZNjb2H.1"l6{a0FQ\~^{<u6A|>OVefT eX,vrq?j x8mȔL?IDATZ9皦lN|9Nn+bbW*z)r]z=- !HIENDB`passenger-5.0.30/doc/images/icons/callouts/2.png000644 000765 000024 00000000541 12233035540 022030 0ustar00honglistaff000000 000000 PNG  IHDR s;bKGD#2{IDATx0 D?44,5, ]+fK UG{ukS@cBSChS{2y4Cms^% D+OJ)}:T5`/CtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignature80eae529c94a7f47deccfada28acb9dfo tEXtPage12x12+0+0m}IENDB`passenger-5.0.30/doc/images/icons/callouts/3.png000644 000765 000024 00000000536 12233035540 022035 0ustar00honglistaff000000 000000 PNG  IHDR s;bKGD#2xIDATx%N@ 4^0+ F``a+&U qXҠq K ]pq˟3&=ۿ-#S:bmR&jQ5cLCtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignature80bbda2726ddace8ab8a01f59de2ebdbutEXtPage12x12+0+0m}IENDB`passenger-5.0.30/doc/images/icons/callouts/4.png000644 000765 000024 00000000531 12233035540 022031 0ustar00honglistaff000000 000000 PNG  IHDR s;bKGD#2sIDATx!0C#XdeeP"\o+{%leʰ!b$ci1 q dCwCmJV$6huTj~<_²|㣴 KF6[CtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignature9f82fcac9e039cbdb72380a4591324f5vtEXtPage12x12+0+0m}IENDB`passenger-5.0.30/doc/images/icons/callouts/5.png000644 000765 000024 00000000534 12233035540 022035 0ustar00honglistaff000000 000000 PNG  IHDR s;bKGD#2vIDATx0  ~+_BhIlgvMZmmwb$|Sq$^%)%YP3]2Qj%|#[7/B_CtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignaturefe690463379eb25e562fcc8cc9b3c7e0߲9tEXtPage12x12+0+0m}IENDB`passenger-5.0.30/doc/images/icons/callouts/6.png000644 000765 000024 00000000543 12233035540 022036 0ustar00honglistaff000000 000000 PNG  IHDR s;bKGD#2}IDATx!0    FaPXXj' nn󩺵 oPHl\BuNح!i`d'נ,˖eԸgNLL< V?s8 YCtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignatured25d7176d67a038afc1c56558e3dfb1atEXtPage12x12+0+0m}IENDB`passenger-5.0.30/doc/images/icons/callouts/7.png000644 000765 000024 00000000530 12233035540 022033 0ustar00honglistaff000000 000000 PNG  IHDR s;bKGD#2rIDATx%0 OV"Y!LO Hd+H퇓e _pDlC0T+ʫ+ VAjݓ{O9lsLGIz>61GVSCtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignature298368142ac43cebd2586d8d1137c8df&9tEXtPage12x12+0+0m}IENDB`passenger-5.0.30/doc/images/icons/callouts/8.png000644 000765 000024 00000000545 12233035540 022042 0ustar00honglistaff000000 000000 PNG  IHDR s;bKGD#2IDATx0  v¬a` 544T ?ݻ/TܗW[Б!Dغ[`T3(fpgc31ؿ.0>_ +U99FbCtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignature57be19505c03f92f3847f535e9b114e94kCtEXtPage12x12+0+0m}IENDB`passenger-5.0.30/doc/images/icons/callouts/9.png000644 000765 000024 00000000545 12233035540 022043 0ustar00honglistaff000000 000000 PNG  IHDR s;bKGD#2IDATx!0 GFVbJ,WX ^YkTb++#{?/Yٗy/j!Rj+~ E#y@!s.gEOr /P8bCtEXtSoftware@(#)ImageMagick 4.2.8 99/08/01 cristy@mystic.es.dupont.com!*tEXtSignature34623e5e4d48310e409b280afe24760214$tEXtPage12x12+0+0m}IENDB`passenger-5.0.30/dev/ci/000755 000765 000024 00000000000 12233035540 015357 5ustar00honglistaff000000 000000 passenger-5.0.30/dev/copy_boost_headers000755 000765 000024 00000014441 12233035540 020571 0ustar00honglistaff000000 000000 #!/usr/bin/env ruby # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. ESSENTIALS = [ "boost/detail/{limits,endian}.hpp", "boost/config*", "boost/mpl", "boost/preprocessor/stringize.hpp", "boost/smart_ptr/detail/sp_counted_*", "boost/smart_ptr/detail/atomic_count*", "boost/smart_ptr/detail/spinlock*", "boost/atomic", "boost/pool", "boost/unordered*", "boost/thread", "boost/intrusive", "boost/container", "boost/predef", "boost/move", "boost/core", "boost/bind", "boost/function", "boost/optional.hpp", "boost/optional", "boost/utility", "libs/thread/src", "libs/system/src", "libs/regex/src", "boost/preprocessor", "boost/parameter", "boost/date_time/gregorian/formatters_limited.hpp", "boost/date_time/date_formatting_limited.hpp", "boost/type_traits/make_signed.hpp", "boost/type_traits/detail/*", "boost/typeof", "boost/date_time", "boost/type_traits", "boost/cstdint.hpp", "boost/*regex*", "boost/non_type.hpp", "boost/detail/fenv.hpp", "boost/foreach.hpp", "boost/none*", "boost/system/detail/error_code.ipp", "boost/detail/reference_content.hpp", "boost/algorithm/string" ] EXCLUDE = [ "libs/thread/src/win32/*", "libs/regex/src/w32_regex_traits.cpp", "libs/regex/src/fileiter.cpp", "libs/regex/src/icu.cpp", "libs/regex/src/usinstances.cpp", "boost/atomic/detail/windows.hpp", "boost/regex/icu.hpp" ] PROGRAM_SOURCE = %q{ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include } require 'fileutils' BOOST_DIR = ARGV[0] Dir.chdir(File.dirname(__FILE__) + "/../src/cxx_supportlib/vendor-modified") # Run the given command, and abort on error. def sh(*command) puts command.join(" ") if !system(*command) puts "*** ERROR" exit 1 end end def install(source_filename, target_filename) dir = File.dirname(target_filename) if !File.exist?(dir) sh "mkdir", "-p", dir end command = ["install", "-m", "u+rw,g+r,o+r", source_filename, target_filename] sh(*command) end def copy_boost_files(patterns, exclude = nil) patterns.each do |pattern| files = Dir["#{BOOST_DIR}/#{pattern}"] files -= exclude if exclude files.each do |source| if File.directory?(source) source.slice!(0 .. BOOST_DIR.size) copy_boost_files(["#{source}/*"], exclude) else target = source.slice(BOOST_DIR.size + 1 .. source.size - 1) if target =~ /^libs\// target = "boost/#{target}" end if !File.exist?(target) install(source, target) end end end end end def copy_essential_files exclude = [] EXCLUDE.each do |pattern| exclude.concat(Dir["#{BOOST_DIR}/#{pattern}"]) end copy_boost_files(ESSENTIALS, exclude) end def prepare File.open("test.cpp", "w") do |f| f.write(PROGRAM_SOURCE) end end def cleanup FileUtils.rm_rf("boost/thread/win32") FileUtils.rm_rf("boost/src/win32") FileUtils.rm_rf("boost/asio/win32") FileUtils.rm_rf("boost/atomic/detail/caps_windows.hpp") FileUtils.rm_rf("boost/atomic/detail/ops_windows.hpp") FileUtils.rm_rf("boost/smart_ptr/detail/spinlock_w32.hpp") FileUtils.rm_rf("boost/smart_ptr/detail/sp_counted_base_w32.hpp") FileUtils.rm_rf("boost/smart_ptr/detail/atomic_count_win32.hpp") FileUtils.rm_rf("boost/config/platform/win32.hpp") File.unlink("test.cpp") rescue nil end # Compile PROGRAM_SOURCE and copy whatever missing header files the compiler needs. def copy_dependencies(cflags) done = false while !done compiler_output = `c++ test.cpp -c -I. #{cflags} 2>&1` File.unlink('test.o') missing_headers = compiler_output. split("\n"). grep(/(error: .*: No such file|fatal error: '.*' file not found)/). map do |line| if line =~ /error: (.*): No such file/ file = $1 elsif line =~ /fatal error: '(.*)' file not found/ file = $1 else abort "Bug" end if file =~ /^boost\// file else line =~ /(.*?):/ source = $1 File.dirname(source) + "/" + file end end missing_headers.each do |header| install("#{BOOST_DIR}/#{header}", header) end done = missing_headers.empty? end end def start if BOOST_DIR.nil? || BOOST_DIR.empty? puts "Usage: copy_boost_headers.rb " exit 1 end begin prepare copy_essential_files copy_dependencies("-std=gnu++11") copy_dependencies("") ensure cleanup end end start passenger-5.0.30/dev/index_cxx_dependencies.rb000755 000765 000024 00000007133 12233035540 022017 0ustar00honglistaff000000 000000 #!/usr/bin/env ruby require 'pp' SEARCH_PATHS = [ "src", "src/agent", "src/cxx_supportlib", "src/cxx_supportlib/vendor-copy", "src/cxx_supportlib/vendor-modified", "test/cxx" ] SCAN_FILES = Dir[ "src/**/*.{c,cpp,h,hpp}", "test/oxt/**/*.{c,cpp,h,hpp}", "test/cxx/**/*.{c,cpp,h,hpp}" ] EXCLUDE_FILES = Dir[ "src/cxx_supportlib/vendor-copy/**/*", "src/cxx_supportlib/vendor-modified/**/*" ] EXCLUDE_FILES_INDEX = Hash[EXCLUDE_FILES.map { |v| [v, true] }] EXCLUDE_NAMES = %w{ string vector map list set exception utility stdexcept iterator iostream ostream sstream fstream algorithm iomanip ios memory new unordered_map typeinfo deque queue limits hash_map ext/hash_map cctype cassert cstdio cstdlib cstddef cstdarg cstring ctime csignal cmath cerrno climits stdio.h stdio_ext.h stdlib.h stdint.h stddef.h string.h limits.h ctype.h assert.h errno.h time.h alloca.h features.h cxxabi.h unistd.h signal.h pthread.h pwd.h port.h grp.h fcntl.h sched.h execinfo.h poll.h dirent.h utime.h libgen.h netdb.h Availability.h selinux/selinux.h BaseTsd.h libkern/OSAtomic.h httpd.h unixd.h nginx.h util_script.h ev++.h ev.h uv.h zlib.h version.h } EXCLUDE_NAMES_INDEX = Hash[EXCLUDE_NAMES.map { |v| [v, true] }] EXCLUDE_NAME_REGEXP = %r{ ^( sys/ |mach/ |netinet/ |inet/ |vm/ |arpa/ |linux/ |tr1/ |curl/ |ruby |apr_ |ap_ |http_ |ngx_ ).+ }x def extract_dependencies(source) result = [] File.open(source, "r") do |f| f.each do |line| next if line !~ /^[\s\t]*#include (<.+?>|".+?")/ raw_name = $1 name = raw_name.gsub(/["<>]/, "") next if name_excluded?(name) if raw_name =~ /"(.+)"/ dependency = search_include_file($1, File.dirname(source)) elsif raw_name =~ /<(.+)>/ dependency = search_include_file($1) else STDERR.puts "Warning: ignoring invalid include statement in #{source}: #{line.strip}" dependency = nil end if dependency if !EXCLUDE_FILES_INDEX.has_key?(dependency) result << dependency end else STDERR.puts "Warning: cannot find include file #{raw_name} (referenced from #{source})" end end end result end def search_include_file(name, first_search_path = nil) if first_search_path search_paths = [first_search_path] + SEARCH_PATHS else search_paths = SEARCH_PATHS end search_paths.each do |path| if File.exist?("#{path}/#{name}") return "#{path}/#{name}" end end nil end def name_excluded?(name) EXCLUDE_NAMES_INDEX.has_key?(name) || EXCLUDE_NAME_REGEXP =~ name end def generate_basic_map result = {} (SCAN_FILES - EXCLUDE_FILES).each do |source_file| result[source_file] = extract_dependencies(source_file) end result end def gather_all_dependencies_recursively(source_file, basic_map, result) deps = basic_map[source_file] if deps deps.each do |dep| result[dep] = true gather_all_dependencies_recursively(dep, basic_map, result) end end end def generate_full_map(basic_map) result = {} basic_map.keys.sort.each do |source_file| deps = basic_map[source_file] gather_results = {} gather_all_dependencies_recursively( source_file, basic_map, gather_results) result[source_file] = gather_results.keys.sort end result end result = generate_full_map(generate_basic_map) puts "# Autogenerated by dev/index_cxx_dependencies.rb" puts "CXX_DEPENDENCY_MAP =" PP.pp(result, STDOUT, 1) passenger-5.0.30/dev/install_scripts_bootstrap_code.rb000755 000765 000024 00000002473 12233035540 023626 0ustar00honglistaff000000 000000 #!/usr/bin/env ruby # encoding: utf-8 # This script changes the bootstrap code for all Phusion Passenger commands, # as well as the Nginx module config script, so that they work no # matter which Ruby interpreter is currently in $PATH, and no matter how # Phusion Passenger is packaged. # # The bootstrap code must not add ruby_libdir to $LOAD_PATH. The active Ruby # can be *any* Ruby interpreter, maybe not even MRI. ruby_libdir belongs to # a Ruby interpreter installed by the distribution, and the files in it may # may be incompatible with the active Ruby. type = ARGV.shift if type == "--ruby" ruby_libdir = ARGV.shift BOOTSTRAP_CODE = %Q{ ENV["PASSENGER_LOCATION_CONFIGURATION_FILE"] = "#{ruby_libdir}/phusion_passenger/locations.ini" begin require 'rubygems' rescue LoadError end require '#{ruby_libdir}/phusion_passenger' } elsif type == "--nginx-module-config" bindir = ARGV.shift BOOTSTRAP_CODE = %Q{ PASSENGER_CONFIG=#{bindir}/passenger-config } else abort "Invalid type" end BOOTSTRAP_CODE.gsub!(/^ ( )?/, '').strip ARGV.each do |filename| File.open(filename, "r+") do |f| text = f.read text.sub!( /^## Magic comment: begin bootstrap ##.*## Magic comment: end bootstrap \#\#$/m, BOOTSTRAP_CODE) f.rewind f.truncate(0) f.write(text) end end passenger-5.0.30/dev/list_tests000755 000765 000024 00000001725 12233035540 017114 0ustar00honglistaff000000 000000 #!/usr/bin/env ruby # # Usage: ./dev/list_tests.rb # # Lists the test names in the given .cpp test file. require_relative '../src/ruby_supportlib/phusion_passenger/utils/ansi_colors' include PhusionPassenger::Utils::AnsiColors def extract_category_name(occurrence) occurrence =~ / (.+) / return $1 end def extract_test_name(occurrence) occurrence = occurrence.sub(/.*?\((.+)\).*/m, '\1') occurrence.gsub!(/"\n[ \t]*"/m, '') occurrence.sub!(/\A"/, '') occurrence.sub!(/"\Z/, '') return occurrence end def start(filename) STDOUT.write(DEFAULT_TERMINAL_COLOR) begin occurrences = File.read(filename).scan(%r{/\*\*\*\*\* .+? \*\*\*\*\*/|set_test_name\(.+?\);}m) occurrences.each do |occurrence| if occurrence =~ %r{\A/} puts ansi_colorize("" + extract_category_name(occurrence) + "") else puts " " + extract_test_name(occurrence) end end ensure STDOUT.write(RESET) end end start(ARGV[0]) passenger-5.0.30/dev/parse_file_descriptor_log000755 000765 000024 00000005056 12233035540 022130 0ustar00honglistaff000000 000000 #!/usr/bin/env ruby # Parses a file descriptor log as produced by Passenger, # and display the file descriptors that are still open # according to the log. class ParserApp Agent = Struct.new(:name, :fds) Entry = Struct.new(:source, :purpose) def initialize(path) @io = File.open(path, "r") @agents = {} end def close @io.close end def analyze @lineno = 1 while !@io.eof? line = @io.readline.strip pid, source, message = parse_line(line) case message when /^Starting agent: (.+)$/ agent_name = $1 if (old_pid = find_agent(agent_name)) warn "#{agent_name} restarted" @agents.delete(old_pid) end @agents[pid] = Agent.new(agent_name, {}) when /^File descriptor opened: (.+)/ fd = $1.to_i if agent = @agents[pid] if agent.fds.has_key?(fd) warn "FD #{fd} already opened" end agent.fds[fd] = Entry.new(source) else warn "No agent information about #{pid}" end when /^File descriptor closed: (.+)/ fd = $1.to_i if agent = @agents[pid] if agent.fds.has_key?(fd) agent.fds.delete(fd) else warn "FD #{fd} not opened" end else warn "No agent information about #{pid}" end when /^File descriptor purpose: (.+?): (.+)/ fd = $1.to_i purpose = $2 if agent = @agents[pid] if entry = agent.fds[fd] entry.purpose = purpose else warn "FD #{fd} not opened" end else warn "No agent information about #{pid}" end end @lineno += 1 end rescue EOFError end def report @agents.each_pair do |pid, agent| puts puts "#{pid}: #{agent.name}" puts("-" * 80) agent.fds.keys.sort.each do |fd| entry = agent.fds[fd] printf "%-5d %-30s %s\n", fd, entry.source, entry.purpose end end end private def parse_line(line) if line =~ /^\[ (.+?) \]: (.+)/ info = $1 message = $2 fragments = info.split(" ") pid = fragments[2].sub(/\/.*/, '') source = fragments.last [pid, source, message] else nil end end def find_agent(name) @agents.each_pair do |pid, agent| if agent.name == name return pid end end nil end def warn(message) STDERR.puts "Warning:#{@lineno}: #{message}" end end app = ParserApp.new(ARGV[0]) app.analyze app.report app.close passenger-5.0.30/dev/rack.test/000755 000765 000024 00000000000 12233035540 016662 5ustar00honglistaff000000 000000 passenger-5.0.30/dev/ruby_server.rb000755 000765 000024 00000013750 12233035540 017671 0ustar00honglistaff000000 000000 #!/usr/bin/env ruby # A simple pure-Ruby HTTP server, meant as a helper tool in benchmarks. # It supports HTTP keep-alive and it supports forwarding the request to # another server. require 'thread' require 'socket' require 'optparse' class TestServer REQUEST = "GET / HTTP/1.1\r\n" << "Connection: Keep-Alive\r\n" << "Host: 127.0.0.1:3001\r\n" << "User-Agent: ApacheBench/2.3\r\n" << "Accept: */*\r\n\r\n" RESPONSE = "HTTP/1.1 200 OK\r\n" << "Status: 200 OK\r\n" << "Content-Type: text/plain\r\n" << "Content-Length: 3\r\n" << "Connection: keep-alive\r\n" << "\r\n" << "ok\n" def initialize(options = {}) @options = options @options[:transport] ||= :tcp @options[:protocol] ||= :http @options[:port] ||= 3000 @options[:file] ||= './socket' @options[:threads] ||= 2 @options[:processes] ||= 2 @forward = @options[:forward] @forward_transport = @options[:forward_transport] @forward_file = @options[:forward_file] @forward_port = @options[:forward_port] @forward_keep_alive = @options[:forward_keep_alive] end def run case @options[:transport] when :tcp @server = TCPServer.new('127.0.0.1', @options[:port]) @server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) puts "Listening on http://127.0.0.1:#{@options[:port]}/" when :unix File.unlink(@options[:file]) rescue nil @server = UNIXServer.new(@options[:file]) puts "Listening on Unix domain socket: #{@options[:file]}" else abort "Unknown transport #{@options[:transport]}" end @server.listen(100) case @options[:protocol] when :http puts "Using HTTP protocol" @protocol = :http when :session puts "Using session protocol" @protocol = :session else abort "Unknown protocol #{@options[:protocol]}" end if @forward case @forward_transport when :tcp puts "Forwarding to http://127.0.0.1:#{@forward_port}/" when :unix puts "Forwarding to Unix domain socket: #{@forward_file}" end end puts "Using #{@options[:processes]} processes" puts "Using #{@options[:threads]} threads per process" fork_children threads = [] @options[:threads].times { threads << start_thread } begin threads.each { |t| t.join } rescue Interrupt end end private def fork_children if @options[:processes] == 1 return end children = [] @options[:processes].times do pid = fork if pid # Parent puts "Spawned child process: #{pid}" children << pid else return end end if !children.empty? # Parent begin sleep 999999 rescue Interrupt exit ensure children.each do |pid| puts "Reaping child process: #{pid}" Process.kill('INT', pid) end children.each do |pid| Process.waitpid(pid) end end end end def start_thread Thread.new do Thread.current.abort_on_exception = true if @forward && @forward_keep_alive forward_connection = connect_to_forwarding_target end while true handle_next_client(forward_connection) end end end def handle_next_client(forward_connection) client = @server.accept begin buffer = "".force_encoding("binary") while true begin read_header(client, buffer) if @forward forward(forward_connection) end # Write response client.write(RESPONSE) rescue EOFError, Errno::ECONNRESET break end end ensure client.close end end def read_header(client, buffer) if @protocol == :http while client.readline != "\r\n" # Do nothing. end else temp = client.read(4, buffer) raise EOFError if temp.nil? size = temp.unpack('N')[0] temp = client.read(size, buffer) raise EOFError if temp.nil? end end def forward(target_connection) if target_connection io = target_connection else io = connect_to_forwarding_target end begin io.write(REQUEST) while io.readline != "ok\n" # Do nothing end ensure if !target_connection io.close end end end def connect_to_forwarding_target if @forward_transport == :unix UNIXSocket.new(@forward_file) else TCPSocket.new('127.0.0.1', @forward_port) end end end options = {} parser = OptionParser.new do |opts| opts.banner = "Usage: ./ruby.rb [options]" opts.separator "" opts.separator "Options:" opts.on("--port PORT", Integer, "Listen on the given TCP port. Default: 3000") do |val| options[:transport] = :tcp options[:port] = val end opts.on("--file PATH", String, "Listen on the given Unix domain socket file") do |val| options[:transport] = :unix options[:file] = val end opts.on("--session-protocol", "Accept session protocol instead of HTTP") do options[:protocol] = :session end opts.on("--threads N", Integer, "Number of threads to use. Default: 2") do |val| options[:threads] = val end opts.on("--processes N", Integer, "Number of processes to use. Default: 2") do |val| options[:processes] = val end opts.on("--forward-tcp PORT", Integer, "Forward request to another TCP server") do |val| options[:forward] = true options[:forward_transport] = :tcp options[:forward_port] = val end opts.on("--forward-file PATH", String, "Forward request to another Unix domain socket server") do |val| options[:forward] = true options[:forward_transport] = :unix options[:forward_file] = val end opts.on("--forward-keep-alive", "Use keep-alive when forwarding") do options[:forward_keep_alive] = true end end begin parser.parse! rescue OptionParser::ParseError => e puts e puts puts "Please see '--help' for valid options." exit 1 end TestServer.new(options).run passenger-5.0.30/dev/runner000755 000765 000024 00000001157 12233035540 016227 0ustar00honglistaff000000 000000 #!/usr/bin/env ruby # # Usage: ./dev/runner # # Evaluates Ruby code in an environment that has the Phusion Passenger # libraries loaded. require File.expand_path(File.dirname(__FILE__) + "/../src/ruby_supportlib/phusion_passenger") PhusionPassenger.locate_directories while true if ARGV[0] =~ /^-r(.*)/ if $1.empty? lib = ARGV[1] ARGV.shift ARGV.shift else lib = $1 ARGV.shift end begin PhusionPassenger.require_passenger_lib(lib) rescue LoadError require(lib) end else break end end module PhusionPassenger p eval(ARGV.join(" ")) end passenger-5.0.30/dev/vagrant/000755 000765 000024 00000000000 12233035540 016426 5ustar00honglistaff000000 000000 passenger-5.0.30/dev/vagrant/apache_default_site.conf000644 000765 000024 00000002715 12233035540 023253 0ustar00honglistaff000000 000000 # This file is overwritten by 'vagrant provision'. For the source, # see dev/vagrant/apache_default_site.conf in the Phusion Passenger source # tree. # The ServerName directive sets the request scheme, hostname and port that # the server uses to identify itself. This is used when creating # redirection URLs. In the context of virtual hosts, the ServerName # specifies what hostname must appear in the request's Host: header to # match this virtual host. For the default virtual host (this file) this # value is not decisive as it is used as a last resort host regardless. # However, you must set it for any further virtual host explicitly. #ServerName www.example.com ServerAdmin webmaster@localhost DocumentRoot /var/www/html # Available loglevels: trace8, ..., trace1, debug, info, notice, warn, # error, crit, alert, emerg. # It is also possible to configure the loglevel for particular # modules, e.g. #LogLevel info ssl:warn ErrorLog ${APACHE_LOG_DIR}/error.log CustomLog ${APACHE_LOG_DIR}/access.log combined # For most configuration files from conf-available/, which are # enabled or disabled at a global level, it is possible to # include a line for only one particular virtual host. For example the # following line enables the CGI configuration for this host only # after it has been globally disabled with "a2disconf". #Include conf-available/serve-cgi-bin.conf # vim: syntax=apache ts=4 sw=4 sts=4 sr noet passenger-5.0.30/dev/vagrant/apache_passenger.conf000644 000765 000024 00000000173 12233035540 022566 0ustar00honglistaff000000 000000 PassengerRoot /vagrant PassengerDefaultRuby /usr/bin/ruby PassengerLogLevel 1 passenger-5.0.30/dev/vagrant/apache_passenger.load000644 000765 000024 00000000107 12233035540 022555 0ustar00honglistaff000000 000000 LoadModule passenger_module /vagrant/buildout/apache2/mod_passenger.so passenger-5.0.30/dev/vagrant/apache_ports.conf000644 000765 000024 00000001020 12233035540 021736 0ustar00honglistaff000000 000000 # This file is overwritten by 'vagrant provision'. For the source, # see dev/vagrant/apache_ports.conf in the Phusion Passenger source # tree. # If you just change the port or add more ports here, you will likely also # have to change the VirtualHost statement in # /etc/apache2/sites-enabled/000-default.conf Listen 8000 Listen 8001 Listen 8002 Listen 8003 Listen 8004 Listen 8005 Listen 8010 Listen 8010 # vim: syntax=apache ts=4 sw=4 sts=4 sr noet passenger-5.0.30/dev/vagrant/apache_rack_test.conf000644 000765 000024 00000000334 12233035540 022555 0ustar00honglistaff000000 000000 ServerName rack.test DocumentRoot /vagrant/dev/rack.test/public Allow from all Options -MultiViews Require all granted passenger-5.0.30/dev/vagrant/bashrc000644 000765 000024 00000001226 12233035540 017614 0ustar00honglistaff000000 000000 # This file is overwritten by 'vagrant provision'. For the source, # see dev/vagrant/bashrc in the Phusion Passenger source # tree. # Display git branch in bash prompt. export PS1='\[\e]0;\u@\h: \w\a\]${debian_chroot:+($debian_chroot)}\u@\h:\w$(__git_ps1 " (%s)")]\$ ' # Add Phusion Passenger command line tools to PATH. export PATH=/vagrant/bin:$PATH # Tell Phusion Passenger's build system to use ccache. export USE_CCACHE=1 export CCACHE_COMPRESS=1 # Tell Phusion Passenger Standalone to run in debug mode. export PASSENGER_DEBUG=1 export PASSENGER_VAGRANT_ENVIRONMENT=1 alias ls='ls -Fh --color' alias dir='ls -l' alias free='free -m' alias df='df -h' passenger-5.0.30/dev/vagrant/nginx.conf000644 000765 000024 00000001315 12233035540 020420 0ustar00honglistaff000000 000000 daemon off; worker_processes 1; #user nobody; #error_log logs/error.log debug; #pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; #gzip on; passenger_root /vagrant; passenger_ruby /usr/bin/ruby; passenger_log_level 1; server { listen 8100; server_name localhost; location / { root html; index index.html index.htm; } } server { listen 8101; server_name rack.test; root /vagrant/dev/rack.test/public; passenger_enabled on; } } passenger-5.0.30/dev/vagrant/nginx_rakefile000644 000765 000024 00000002156 12233035540 021342 0ustar00honglistaff000000 000000 ENV['CC'] ||= 'ccache cc' ENV['CXX'] ||= 'ccache c++' desc "Bootstrap Nginx for the first time" task :bootstrap => :configure do sh "make -j2" sh "make install" sh "rm -f inst/sbin/nginx" puts puts "--------------------------" puts "You're all set! You can start Nginx by running:" puts puts " ./start" puts puts "Nginx can be reached from the host machine on http://127.0.0.1:8100/" puts puts "You never have to run `rake bootstrap` again. You also never have to " + "run `make install`. If you've made changes to the Passenger Nginx module, " + "simply run `make && ./start` in /home/vagrant/nginx. The `start` script " + "will start the newly compiled Nginx binary directly." end desc "Configure Nginx source tree" task :configure do sh "./configure --prefix=/home/vagrant/nginx/inst" + " --add-module=/vagrant/src/nginx_module" + " --with-http_ssl_module" + " --with-http_gzip_static_module" + " --with-http_stub_status_module" + " --with-http_v2_module" + " --with-ipv6" + " --with-debug" sh "sed", "-E", "-i", 's/ -O[0-9]? / -ggdb /g', "objs/Makefile" end passenger-5.0.30/dev/vagrant/nginx_start000755 000765 000024 00000001351 12233035540 020714 0ustar00honglistaff000000 000000 #!/usr/bin/ruby # This file is overwritten by 'vagrant provision'. For the source, # see dev/vagrant/nginx_start in the Phusion Passenger source # tree. ENV['PASSENGER_BEEP_ON_ABORT'] = '1' def run_in_bg(*command) return fork do Process.setsid exec(*command) end end File.open('inst/logs/error.log', 'a') do |f| f.write("\n\n\n\n-------------- NGINX START #{Time.now} --------------\n\n\n\n") end tail_pid = run_in_bg("tail -n 0 -f inst/logs/error.log") nginx_pid = run_in_bg("./objs/nginx", *ARGV) begin Process.waitpid(nginx_pid) nginx_pid = nil rescue Interrupt ensure Process.kill('INT', tail_pid) Process.waitpid(tail_pid) if nginx_pid Process.kill('INT', nginx_pid) Process.waitpid(nginx_pid) end end passenger-5.0.30/dev/vagrant/provision.sh000755 000765 000024 00000006631 12233035540 021023 0ustar00honglistaff000000 000000 #!/bin/bash set -ex set -o pipefail ### Update /etc/hosts if ! grep -q passenger.test /etc/hosts; then cat >>/etc/hosts <<-EOF 127.0.0.1 passenger.test 127.0.0.1 mycook.passenger.test 127.0.0.1 zsfa.passenger.test 127.0.0.1 norails.passenger.test 127.0.0.1 1.passenger.test 2.passenger.test 3.passenger.test 127.0.0.1 4.passenger.test 5.passenger.test 6.passenger.test 127.0.0.1 7.passenger.test 8.passenger.test 9.passenger.test 127.0.0.1 rack.test foobar.test EOF fi ### Update bashrc and bash profile if ! grep -q bashrc.mine /etc/bash.bashrc; then echo ". /etc/bash.bashrc.mine" >> /etc/bash.bashrc fi if ! grep -q bashrc.mine /home/vagrant/.bashrc; then echo ". /etc/bash.bashrc.mine" >> /home/vagrant/.bashrc fi if ! grep -q /vagrant /home/vagrant/.profile; then echo "if tty -s; then cd /vagrant; fi" >> /home/vagrant/.profile fi cp /vagrant/dev/vagrant/bashrc /etc/bash.bashrc.mine cp /vagrant/dev/vagrant/sudoers.conf /etc/sudoers.d/passenger chmod 440 /etc/sudoers.d/passenger ### Install native dependencies apt-get update apt-get install -y build-essential git bash-completion ccache wget \ libxml2-dev libxslt1-dev libsqlite3-dev libcurl4-openssl-dev libpcre3-dev \ ruby ruby-dev nodejs npm \ apache2-mpm-worker apache2-threaded-dev ### Install basic gems if [[ ! -e /usr/local/bin/rake ]]; then gem install rake --no-rdoc --no-ri fi if [[ ! -e /usr/local/bin/drake ]]; then gem install drake --no-rdoc --no-ri fi if [[ ! -e /usr/local/bin/bundler ]]; then gem install bundler --no-rdoc --no-ri fi ### Install Phusion Passenger development dependencies pushd /vagrant if [[ ! -e ~/.test_deps_installed ]]; then rake test:install_deps SUDO=1 DEPS_TARGET=~/bundle touch ~/.test_deps_installed else bundle install --path ~/bundle fi popd ### Install Nginx source code pushd /home/vagrant if [[ ! -e nginx ]]; then sudo -u vagrant -H git clone -b branches/stable-1.6 https://github.com/nginx/nginx.git fi sudo -u vagrant -H mkdir -p nginx/inst/conf sudo -u vagrant -H cp /vagrant/dev/vagrant/nginx_start nginx/start if [[ ! -e nginx/Rakefile ]]; then sudo -u vagrant -H cp /vagrant/dev/vagrant/nginx_rakefile nginx/Rakefile fi if [[ ! -e nginx/inst/conf/nginx.conf ]]; then sudo -u vagrant -H cp /vagrant/dev/vagrant/nginx.conf nginx/inst/conf/ fi if [[ ! -e nginx/nginx.conf && ! -h nginx/nginx.conf ]]; then sudo -u vagrant -H ln -s inst/conf/nginx.conf nginx/nginx.conf fi if [[ ! -e nginx/access.log && ! -h nginx/access.log ]]; then sudo -u vagrant -H ln -s inst/logs/access.log nginx/access.log fi if [[ ! -e nginx/error.log && ! -h nginx/error.log ]]; then sudo -u vagrant -H ln -s inst/logs/error.log nginx/error.log fi popd ### Set up Apache should_restart_apache=false cp /vagrant/dev/vagrant/apache_ports.conf /etc/apache2/ports.conf cp /vagrant/dev/vagrant/apache_default_site.conf /etc/apache2/sites-available/000-default.conf if [[ ! -e /etc/apache2/mods-available/passenger.conf ]]; then cp /vagrant/dev/vagrant/apache_passenger.conf /etc/apache2/mods-available/passenger.conf fi if [[ ! -e /etc/apache2/mods-available/passenger.load ]]; then cp /vagrant/dev/vagrant/apache_passenger.load /etc/apache2/mods-available/passenger.load fi if [[ ! -e /etc/apache2/sites-available/010-rack.test.conf ]]; then cp /vagrant/dev/vagrant/apache_rack_test.conf /etc/apache2/sites-available/010-rack.test.conf a2ensite 010-rack.test should_restart_apache=true fi if $should_restart_apache; then service apache2 restart fi passenger-5.0.30/dev/vagrant/sudoers.conf000644 000765 000024 00000000354 12233035540 020763 0ustar00honglistaff000000 000000 # This file is overwritten by 'vagrant provision'. For the source, # see dev/vagrant/sudoers.conf in the Phusion Passenger source # tree. Defaults secure_path="/vagrant/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" passenger-5.0.30/dev/rack.test/config.ru000644 000765 000024 00000000131 12233035540 020472 0ustar00honglistaff000000 000000 app = lambda do |env| [200, { "Content-Type" => "text/plain" }, ["ok\n"]] end run app passenger-5.0.30/dev/rack.test/public/000755 000765 000024 00000000000 12233035540 020140 5ustar00honglistaff000000 000000 passenger-5.0.30/dev/rack.test/public/asset.txt000644 000765 000024 00000000030 12233035540 022011 0ustar00honglistaff000000 000000 This is a static asset. passenger-5.0.30/dev/ci/inituidgid000755 000765 000024 00000001066 12233035540 017441 0ustar00honglistaff000000 000000 #!/bin/bash set -e if grep -q docker_env /etc/group; then delgroup docker_env >/dev/null fi chown -R "$APP_UID:$APP_GID" /home/appa groupmod -g "$APP_GID" appa usermod -u "$APP_UID" -g "$APP_GID" appa groupmod -g "$DOCKER_GID" docker usermod -a -G docker appa # There's something strange with either Docker or the kernel, so that # the 'appa' user cannot access its home directory even after a proper # chown/chmod. We work around it like this. mv /home/appa /home/appa2 cp -dpR /home/appa2 /home/appa rm -rf /home/appa2 if [[ $# -gt 0 ]]; then exec "$@" fi passenger-5.0.30/dev/ci/run_jenkins.sh000755 000765 000024 00000004203 12233035540 020242 0ustar00honglistaff000000 000000 #!/bin/bash # This script is run by Jenkins, to execute tests in the CI environment. set -e PASSENGER_ROOT=`dirname "$0"` PASSENGER_ROOT=`cd "$PASSENGER_ROOT/../.." && pwd` if [[ "$WORKSPACE" = "" ]]; then echo "Please set WORKSPACE." exit 1 fi JENKINS_CACHE_DIR="$WORKSPACE/.jenkins_cache" mkdir -p "$JENKINS_CACHE_DIR" # run_travis.sh defaults COMPILE_CONCURRENCY to 2, but # Jenkins jobs are run on a single machine so we'll want # a COMPILE_CONCURRENCY of 1. COMPILE_CONCURRENCY=${COMPILE_CONCURRENCY:-1} # Relax permissions. Necessary for unit tests which test permissions. echo "Relaxing permissions" umask u=rwx,g=rx,o=rx ( set -x chmod g+rx,o+rx . find * -type f -print0 | xargs -0 -n 512 chmod g+r,o+r find * -type d -print0 | xargs -0 -n 512 chmod g+rx,o+rx # Create this file now because otherwise it would be owned by root, # which Jenkins cannot remove. touch test/test.log ) function run_exec() { echo "$ $@" exec "$@" } # We do not use the my_init inside the image to work around # a bug in baseimage-docker 0.9.12: pressing Ctrl-C does # not properly result in a non-zero exit status. run_exec docker run --rm \ -v "$PASSENGER_ROOT:/passenger" \ -v /var/run/docker.sock:/docker.sock \ -v "$JENKINS_CACHE_DIR:/host_cache" \ -e "DOCKER_HOST=unix:///docker.sock" \ -e "DOCKER_GID=`getent group docker | cut -d: -f3`" \ -e "PASSENGER_ROOT_ON_DOCKER_HOST=$PASSENGER_ROOT" \ -e "CACHE_DIR_ON_DOCKER_HOST=$JENKINS_CACHE_DIR" \ -e "APP_UID=`id -u`" \ -e "APP_GID=`id -g`" \ -e "SUDO=$SUDO" \ -e "COMPILE_CONCURRENCY=$COMPILE_CONCURRENCY" \ -e "TEST_CXX=$TEST_CXX" \ -e "TEST_RUBY=$TEST_RUBY" \ -e "TEST_NODE=$TEST_NODE" \ -e "TEST_RUBY_VERSION=$TEST_RUBY_VERSION" \ -e "TEST_RUBYGEMS_VERSION=$TEST_RUBYGEMS_VERSION" \ -e "TEST_NGINX=$TEST_NGINX" \ -e "TEST_APACHE2=$TEST_APACHE2" \ -e "TEST_STANDALONE=$TEST_STANDALONE" \ -e "TEST_SOURCE_PACKAGING=$TEST_SOURCE_PACKAGING" \ phusion/apachai-hopachai-sandbox \ python /passenger/packaging/rpm/internal/scripts/my_init --skip-runit --skip-startup-files --quiet -- \ /passenger/dev/ci/inituidgid \ /sbin/setuser appa \ /bin/bash -lc "cd /passenger && exec ./dev/ci/run_travis.sh" passenger-5.0.30/dev/ci/run_travis.sh000755 000765 000024 00000015242 12233035540 020116 0ustar00honglistaff000000 000000 #!/bin/bash # This script is run by Travis, to execute tests in the CI environment. set -e PASSENGER_ROOT=`dirname "$0"` PASSENGER_ROOT=`cd "$PASSENGER_ROOT/../.." && pwd` PASSENGER_ROOT_ON_DOCKER_HOST=${PASSENGER_ROOT_ON_DOCKER_HOST:-$PASSENGER_ROOT} if [[ "$CACHE_DIR_ON_DOCKER_HOST" != "" ]]; then CACHE_DIR=/host_cache else CACHE_DIR="$PWD/cache" CACHE_DIR_ON_DOCKER_HOST="$PWD/cache" fi COMPILE_CONCURRENCY=${COMPILE_CONCURRENCY:-2} TEST_DYNAMIC_WITH_NGINX_VERSION=1.9.15 export VERBOSE=1 export TRACE=1 export DEVDEPS_DEFAULT=no export rvmsudo_secure_path=1 export LC_CTYPE=C.UTF-8 export DEPS_TARGET="$CACHE_DIR/bundle" export USE_CCACHE=true export CCACHE_DIR="$CACHE_DIR/ccache" export CCACHE_COMPRESS=1 export CCACHE_COMPRESS_LEVEL=3 unset BUNDLE_GEMFILE if [[ -e /etc/workaround-docker-2267 ]]; then HOSTS_FILE=/etc/workaround-docker-2267/hosts sudo ln -s /etc/hosts /etc/workaround-docker-2267/hosts sudo workaround-docker-2267 find /usr/local/rvm/rubies/*/lib/ruby -name resolv.rb | sudo xargs sed -i 's|/etc/hosts|/cte/hosts|g' else HOSTS_FILE=/etc/hosts fi mkdir -p "$DEPS_TARGET" mkdir -p "$CCACHE_DIR" sudo sh -c "cat >> $HOSTS_FILE" </dev/null <<<"force-unsafe-io" run cp test/config.json.travis test/config.json # Relax permissions on home directory so that the application root # permission checks pass. run chmod g+x,o+x $HOME if [[ "$TEST_RUBY_VERSION" != "" ]]; then echo "$ rvm use $TEST_RUBY_VERSION" if [[ -f ~/.rvm/scripts/rvm ]]; then source ~/.rvm/scripts/rvm else source /usr/local/rvm/scripts/rvm fi rvm use $TEST_RUBY_VERSION if [[ "$TEST_RUBYGEMS_VERSION" = "" ]]; then run gem --version fi fi if [[ "$TEST_RUBYGEMS_VERSION" != "" ]]; then retry_run 3 rvm install rubygems $TEST_RUBYGEMS_VERSION run gem --version fi ORIG_GEM_PATH="$GEM_PATH" if [[ "$INSTALL_ALL_DEPS" = 1 ]]; then run rake_test_install_deps DEVDEPS_DEFAULT=yes INSTALL_DEPS=0 fi if [[ "$TEST_CXX" = 1 ]]; then install_base_test_deps run bundle exec drake -j$COMPILE_CONCURRENCY test:cxx run bundle exec rake test:oxt fi if [[ "$TEST_RUBY" = 1 ]]; then retry_run 3 rake_test_install_deps BASE_DEPS=yes run bundle exec drake -j$COMPILE_CONCURRENCY test:ruby fi if [[ "$TEST_USH" = 1 ]]; then retry_run 3 rake_test_install_deps BASE_DEPS=yes USH_BUNDLES=yes export PASSENGER_CONFIG="$PWD/bin/passenger-config" run "$PASSENGER_CONFIG" install-standalone-runtime --auto pushd src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core bundle exec rake spec:travis TRAVIS_WITH_SUDO=1 popd pushd src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails bundle exec rake spec:travis GEM_BUNDLE_PATH="$DEPS_TARGET" popd fi if [[ "$TEST_NODE" = 1 ]]; then install_node_and_modules run bundle exec drake -j$COMPILE_CONCURRENCY test:node fi if [[ "$TEST_NGINX" = 1 ]]; then install_base_test_deps install_node_and_modules run ./bin/passenger-install-nginx-module --auto --prefix=/tmp/nginx --auto-download run bundle exec drake -j$COMPILE_CONCURRENCY test:integration:nginx run curl -sSLO http://www.nginx.org/download/nginx-$TEST_DYNAMIC_WITH_NGINX_VERSION.tar.gz run tar zxf nginx-$TEST_DYNAMIC_WITH_NGINX_VERSION.tar.gz run cd nginx-$TEST_DYNAMIC_WITH_NGINX_VERSION run ./configure --add-dynamic-module=$(../bin/passenger-config --nginx-addon-dir) run make run cd .. fi if [[ "$TEST_APACHE2" = 1 ]]; then apt_get_update run sudo apt-get install -y --no-install-recommends \ apache2-mpm-worker apache2-threaded-dev install_base_test_deps install_node_and_modules run ./bin/passenger-install-apache2-module --auto #--no-update-config run rvmsudo ./bin/passenger-install-apache2-module --auto --no-compile run bundle exec drake -j$COMPILE_CONCURRENCY test:integration:apache2 fi if [[ "$TEST_STANDALONE" = 1 ]]; then install_base_test_deps run bundle exec drake -j$COMPILE_CONCURRENCY test:integration:standalone fi if [[ "$TEST_SOURCE_PACKAGING" = 1 ]]; then apt_get_update run sudo apt-get install -y --no-install-recommends source-highlight install_test_deps_with_doctools run bundle exec rspec -f s -c test/integration_tests/source_packaging_test.rb fi passenger-5.0.30/build/agent.rb000644 000765 000024 00000006631 12233035540 016736 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. AGENT_TARGET = "#{AGENT_OUTPUT_DIR}#{AGENT_EXE}" AGENT_MAIN_OBJECT = "#{AGENT_OUTPUT_DIR}AgentMain.o" AGENT_OBJECTS = { AGENT_MAIN_OBJECT => "src/agent/AgentMain.cpp", "#{AGENT_OUTPUT_DIR}AgentBase.o" => "src/agent/Shared/Base.cpp", "#{AGENT_OUTPUT_DIR}WatchdogMain.o" => "src/agent/Watchdog/WatchdogMain.cpp", "#{AGENT_OUTPUT_DIR}CoreMain.o" => "src/agent/Core/CoreMain.cpp", "#{AGENT_OUTPUT_DIR}CoreApplicationPool.o" => "src/agent/Core/ApplicationPool/Implementation.cpp", "#{AGENT_OUTPUT_DIR}CoreController.o" => "src/agent/Core/Controller/Implementation.cpp", "#{AGENT_OUTPUT_DIR}UstRouterMain.o" => "src/agent/UstRouter/UstRouterMain.cpp", "#{AGENT_OUTPUT_DIR}SystemMetricsMain.o" => "src/agent/SystemMetrics/SystemMetricsMain.cpp", "#{AGENT_OUTPUT_DIR}TempDirToucherMain.o" => "src/agent/TempDirToucher/TempDirToucherMain.cpp", "#{AGENT_OUTPUT_DIR}SpawnPreparerMain.o" => "src/agent/SpawnPreparer/SpawnPreparerMain.cpp" } # Define compilation tasks for object files. AGENT_OBJECTS.each_pair do |object, source| define_cxx_object_compilation_task( object, source, :include_paths => [ "src/agent", *CXX_SUPPORTLIB_INCLUDE_PATHS ], :flags => [ AGENT_CFLAGS, LIBEV_CFLAGS, LIBUV_CFLAGS, PlatformInfo.curl_flags, PlatformInfo.zlib_flags ] ) end # Define compilation task for the agent executable. agent_libs = COMMON_LIBRARY. only(:base, :bas64, :json, :union_station_filter, :other). exclude('WatchdogLauncher.o') dependencies = AGENT_OBJECTS.keys + [ LIBBOOST_OXT, agent_libs.link_objects, LIBEV_TARGET, LIBUV_TARGET ].flatten.compact file(AGENT_TARGET => dependencies) do sh "mkdir -p #{AGENT_OUTPUT_DIR}" if !File.directory?(AGENT_OUTPUT_DIR) create_cxx_executable(AGENT_TARGET, [ agent_libs.link_objects_as_string, AGENT_OBJECTS.keys, LIBBOOST_OXT_LINKARG ], :flags => [ libev_libs, libuv_libs, PlatformInfo.curl_libs, PlatformInfo.zlib_libs, PlatformInfo.portability_cxx_ldflags, AGENT_LDFLAGS ] ) end task 'common:clean' do sh "rm -rf #{AGENT_OUTPUT_DIR}" end passenger-5.0.30/build/apache2.rb000644 000765 000024 00000011477 12233035540 017147 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. # apxs totally sucks. We couldn't get it working correctly # on MacOS X (it had various problems with building universal # binaries), so we decided to ditch it and build/install the # Apache module ourselves. # # Oh, and libtool sucks too. Do we even need it anymore in 2008? APACHE2_TARGET = "#{APACHE2_OUTPUT_DIR}mod_passenger.so" APACHE2_OBJECTS = { "#{APACHE2_OUTPUT_DIR}mod_passenger.o" => "src/apache2_module/mod_passenger.c", "#{APACHE2_OUTPUT_DIR}Configuration.o" => "src/apache2_module/Configuration.cpp", "#{APACHE2_OUTPUT_DIR}Bucket.o" => "src/apache2_module/Bucket.cpp", "#{APACHE2_OUTPUT_DIR}Hooks.o" => "src/apache2_module/Hooks.cpp" } APACHE2_AUTOGENERATED_SOURCES = %w( src/apache2_module/ConfigurationCommands.cpp src/apache2_module/ConfigurationFields.hpp src/apache2_module/CreateDirConfig.cpp src/apache2_module/MergeDirConfig.cpp src/apache2_module/ConfigurationSetters.cpp src/apache2_module/SetHeaders.cpp ) # Define compilation tasks for object files. APACHE2_OBJECTS.each_pair do |object, source| if source =~ /\.c$/ define_c_object_compilation_task( object, source, :include_paths => [ "src/agent", *CXX_SUPPORTLIB_INCLUDE_PATHS ], :flags => PlatformInfo.apache2_module_cflags ) else define_cxx_object_compilation_task( object, source, :include_paths => [ "src/agent", *CXX_SUPPORTLIB_INCLUDE_PATHS ], :flags => PlatformInfo.apache2_module_cxxflags ) end end # Define compilation task for the Apache 2 module. APACHE2_MODULE_BOOST_OXT_LIBRARY, APACHE2_MODULE_BOOST_OXT_LINKARG = define_libboost_oxt_task("apache2", APACHE2_OUTPUT_DIR + "module_libboost_oxt", PlatformInfo.apache2_module_cflags) APACHE2_MODULE_COMMON_LIBRARIES = COMMON_LIBRARY. only(:base, :bas64, 'AppTypes.o'). set_namespace("apache2"). set_output_dir(APACHE2_OUTPUT_DIR + "module_libpassenger_common"). define_tasks(PlatformInfo.apache2_module_cflags). link_objects dependencies = [ APACHE2_MODULE_COMMON_LIBRARIES, APACHE2_MODULE_BOOST_OXT_LIBRARY, APACHE2_OBJECTS.keys ].flatten file(APACHE2_TARGET => dependencies) do PlatformInfo.apxs2.nil? and raise "Could not find 'apxs' or 'apxs2'." PlatformInfo.apache2ctl.nil? and raise "Could not find 'apachectl' or 'apache2ctl'." PlatformInfo.httpd.nil? and raise "Could not find the Apache web server binary." sh "mkdir -p #{APACHE2_OUTPUT_DIR}" if !File.directory?(APACHE2_OUTPUT_DIR) create_shared_library(APACHE2_TARGET, APACHE2_OBJECTS.keys, :flags => [ APACHE2_MODULE_COMMON_LIBRARIES, APACHE2_MODULE_BOOST_OXT_LINKARG, PlatformInfo.apache2_module_cxx_ldflags, PlatformInfo.portability_cxx_ldflags ] ) end desc "Build Apache 2 module" task :apache2 => [ APACHE2_TARGET, AGENT_TARGET, NATIVE_SUPPORT_TARGET ].compact # Workaround for https://github.com/jimweirich/rake/issues/274 task :_apache2 => :apache2 task :clean => 'apache2:clean' desc "Clean all compiled Apache 2 files" task 'apache2:clean' => 'common:clean' do files = APACHE2_OBJECTS.keys.dup files << APACHE2_TARGET sh("rm", "-rf", *files) end def create_apache2_auto_generated_source_task(source) dependencies = [ "#{source}.cxxcodebuilder", 'src/ruby_supportlib/phusion_passenger/apache2/config_options.rb' ] file(source => dependencies) do template = CxxCodeTemplateRenderer.new("#{source}.cxxcodebuilder") template.render_to(source) end end APACHE2_AUTOGENERATED_SOURCES.each do |source| create_apache2_auto_generated_source_task(source) end passenger-5.0.30/build/basics.rb000644 000765 000024 00000016447 12233035540 017112 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. begin require 'rubygems' rescue LoadError end require 'fileutils' require 'phusion_passenger' PhusionPassenger.locate_directories PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'packaging' PhusionPassenger.require_passenger_lib 'utils/shellwords' PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/operating_system' PhusionPassenger.require_passenger_lib 'platform_info/binary_compatibility' PhusionPassenger.require_passenger_lib 'platform_info/ruby' PhusionPassenger.require_passenger_lib 'platform_info/apache' PhusionPassenger.require_passenger_lib 'platform_info/curl' PhusionPassenger.require_passenger_lib 'platform_info/zlib' PhusionPassenger.require_passenger_lib 'platform_info/compiler' PhusionPassenger.require_passenger_lib 'platform_info/cxx_portability' include PhusionPassenger include PhusionPassenger::PlatformInfo require 'build/support/cxx_dependency_map' require 'build/support/general' require 'build/support/cplusplus' if string_option('OUTPUT_DIR') OUTPUT_DIR = string_option('OUTPUT_DIR') + "/" else OUTPUT_DIR = "buildout/" end verbose true if !boolean_option('REALLY_QUIET') if boolean_option('STDERR_TO_STDOUT') # Just redirecting the file descriptor isn't enough because # data written to STDERR might arrive in an unexpected order # compared to STDOUT. STDERR.reopen(STDOUT) Object.send(:remove_const, :STDERR) STDERR = STDOUT $stderr = $stdout end if boolean_option('CACHING', true) && !boolean_option('RELEASE') PlatformInfo.cache_dir = OUTPUT_DIR + "cache" FileUtils.mkdir_p(PlatformInfo.cache_dir) end # https://github.com/phusion/passenger/issues/672 ENV.delete('CDPATH') ################################################# PACKAGE_NAME = PhusionPassenger::PACKAGE_NAME PACKAGE_VERSION = PhusionPassenger::VERSION_STRING MAINTAINER_NAME = "Phusion" MAINTAINER_EMAIL = "info@phusion.nl" CC = maybe_wrap_in_ccache(PhusionPassenger::PlatformInfo.cc) CXX = maybe_wrap_in_ccache(PhusionPassenger::PlatformInfo.cxx) LIBEXT = PlatformInfo.library_extension USE_DMALLOC = boolean_option('USE_DMALLOC') USE_EFENCE = boolean_option('USE_EFENCE') USE_ASAN = boolean_option('USE_ASAN') USE_SELINUX = boolean_option('USE_SELINUX') OPTIMIZE = boolean_option('OPTIMIZE') LTO = OPTIMIZE && boolean_option('LTO') CXX_SUPPORTLIB_INCLUDE_PATHS = [ "src/cxx_supportlib", "src/cxx_supportlib/vendor-copy", "src/cxx_supportlib/vendor-modified" ] # Agent-specific compiler flags. AGENT_CFLAGS = "" AGENT_CFLAGS << " -O" if OPTIMIZE AGENT_CFLAGS << " -DUSE_SELINUX" if USE_SELINUX AGENT_CFLAGS << " -flto" if LTO AGENT_CFLAGS << " #{PlatformInfo.adress_sanitizer_flag}" if USE_ASAN AGENT_CFLAGS.strip! # Agent-specific linker flags. AGENT_LDFLAGS = "" AGENT_LDFLAGS << " -O" if OPTIMIZE AGENT_LDFLAGS << " -flto" if LTO AGENT_LDFLAGS << " #{PlatformInfo.dmalloc_ldflags}" if USE_DMALLOC AGENT_LDFLAGS << " #{PlatformInfo.electric_fence_ldflags}" if USE_EFENCE AGENT_LDFLAGS << " #{PlatformInfo.adress_sanitizer_flag}" if USE_ASAN AGENT_LDFLAGS << " -lselinux" if USE_SELINUX # Extra linker flags for backtrace_symbols() to generate useful output (see agent/Base.cpp). AGENT_LDFLAGS << " #{PlatformInfo.export_dynamic_flags}" # Enable dead symbol elimination on OS X. AGENT_LDFLAGS << " -Wl,-dead_strip" if PlatformInfo.os_name_simple == "macosx" AGENT_LDFLAGS.strip! # Extra compiler flags that should always be passed to the C/C++ compiler. # These should be included first in the command string, before anything else. EXTRA_PRE_CFLAGS = compiler_flag_option('EXTRA_PRE_CFLAGS') EXTRA_PRE_CXXFLAGS = compiler_flag_option('EXTRA_PRE_CXXFLAGS') # These should be included last in the command string. EXTRA_CFLAGS = PlatformInfo.default_extra_cflags.dup EXTRA_CFLAGS << " " << compiler_flag_option('EXTRA_CFLAGS') if !compiler_flag_option('EXTRA_CFLAGS').empty? EXTRA_CXXFLAGS = PlatformInfo.default_extra_cxxflags.dup EXTRA_CXXFLAGS << " " << compiler_flag_option('EXTRA_CXXFLAGS') if !compiler_flag_option('EXTRA_CXXFLAGS').empty? [EXTRA_CFLAGS, EXTRA_CXXFLAGS].each do |flags| flags << " -fno-omit-frame-pointers" if USE_ASAN flags << " -DPASSENGER_DISABLE_THREAD_LOCAL_STORAGE" if !boolean_option('PASSENGER_THREAD_LOCAL_STORAGE', true) end # Extra linker flags that should always be passed to the linker. # These should be included first in the command string. EXTRA_PRE_C_LDFLAGS = compiler_flag_option('EXTRA_PRE_LDFLAGS') + " " + compiler_flag_option('EXTRA_PRE_C_LDFLAGS') EXTRA_PRE_CXX_LDFLAGS = compiler_flag_option('EXTRA_PRE_LDFLAGS') + " " + compiler_flag_option('EXTRA_PRE_CXX_LDFLAGS') # These should be included last in the command string, even after portability_*_ldflags. EXTRA_C_LDFLAGS = compiler_flag_option('EXTRA_LDFLAGS') + " " + compiler_flag_option('EXTRA_C_LDFLAGS') EXTRA_CXX_LDFLAGS = compiler_flag_option('EXTRA_LDFLAGS') + " " + compiler_flag_option('EXTRA_CXX_LDFLAGS') AGENT_OUTPUT_DIR = string_option('AGENT_OUTPUT_DIR', OUTPUT_DIR + "support-binaries") + "/" COMMON_OUTPUT_DIR = string_option('COMMON_OUTPUT_DIR', OUTPUT_DIR + "common") + "/" APACHE2_OUTPUT_DIR = string_option('APACHE2_OUTPUT_DIR', OUTPUT_DIR + "apache2") + "/" NGINX_DYNAMIC_OUTPUT_DIR = string_option('NGINX_DYNAMIC_OUTPUT_DIR', OUTPUT_DIR + "nginx_dynamic") + "/" LIBEV_OUTPUT_DIR = string_option('LIBEV_OUTPUT_DIR', OUTPUT_DIR + "libev") + "/" LIBUV_OUTPUT_DIR = string_option('LIBUV_OUTPUT_DIR', OUTPUT_DIR + "libuv") + "/" ruby_extension_archdir = PlatformInfo.ruby_extension_binary_compatibility_id RUBY_EXTENSION_OUTPUT_DIR = string_option('RUBY_EXTENSION_OUTPUT_DIR', OUTPUT_DIR + "ruby/" + ruby_extension_archdir) + "/" PKG_DIR = string_option('PKG_DIR', "pkg") TEST_OUTPUT_DIR = string_option('TEST_OUTPUT_DIR', OUTPUT_DIR + "test") + "/" # Whether to use the vendored libev or the system one. USE_VENDORED_LIBEV = boolean_option("USE_VENDORED_LIBEV", true) # Whether to use the vendored libuv or the system one. USE_VENDORED_LIBUV = boolean_option("USE_VENDORED_LIBUV", true) passenger-5.0.30/build/common_library.rb000644 000765 000024 00000022435 12233035540 020654 0ustar00honglistaff000000 000000 # encoding: utf-8 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. PhusionPassenger.require_passenger_lib 'platform_info/compiler' PhusionPassenger.require_passenger_lib 'platform_info/cxx_portability' ########## Phusion Passenger common library ########## PhusionPassenger.require_passenger_lib 'common_library' ########## libboost_oxt ########## # Defines tasks for compiling a static library containing Boost and OXT. def define_libboost_oxt_task(namespace, output_dir, extra_compiler_flags = nil) output_file = "#{output_dir}.a" flags = "-Isrc/cxx_supportlib -Isrc/cxx_supportlib/vendor-copy -Isrc/cxx_supportlib/vendor-modified " + "#{extra_compiler_flags} #{EXTRA_CXXFLAGS}" if OPTIMIZE optimize = "-O2" if LTO optimize << " -flto" end end # Define compilation targets for .cpp files in src/cxx_supportlib/vendor-modified/boost/src/pthread. boost_object_files = [] Dir['src/cxx_supportlib/vendor-modified/boost/libs/**/*.cpp'].each do |source_file| object_name = File.basename(source_file.sub(/\.cpp$/, '.o')) boost_output_dir = "#{output_dir}/boost" object_file = "#{boost_output_dir}/#{object_name}" boost_object_files << object_file define_cxx_object_compilation_task( object_file, source_file, :include_paths => CXX_SUPPORTLIB_INCLUDE_PATHS, :flags => [optimize, extra_compiler_flags] ) end # Define compilation targets for .cpp files in src/cxx_supportlib/oxt. oxt_object_files = [] Dir['src/cxx_supportlib/oxt/*.cpp'].each do |source_file| object_name = File.basename(source_file.sub(/\.cpp$/, '.o')) oxt_output_dir = "#{output_dir}/oxt" object_file = "#{oxt_output_dir}/#{object_name}" oxt_object_files << object_file define_cxx_object_compilation_task( object_file, source_file, :include_paths => CXX_SUPPORTLIB_INCLUDE_PATHS, :flags => [optimize, extra_compiler_flags] ) end object_files = boost_object_files + oxt_object_files file(output_file => object_files) do create_static_library(output_file, object_files) end task "#{namespace}:clean" do sh "rm -rf #{output_file} #{output_dir}" end if OPTIMIZE && LTO # Clang -flto does not support static libraries containing # .o files that are compiled with -flto themselves. [output_file, [output_file, boost_object_files, oxt_object_files].flatten.join(" ")] else [output_file, output_file] end end ########## libev ########## if USE_VENDORED_LIBEV LIBEV_SOURCE_DIR = File.expand_path("../src/cxx_supportlib/vendor-modified/libev", File.dirname(__FILE__)) + "/" LIBEV_CFLAGS = "-Isrc/cxx_supportlib/vendor-modified/libev" LIBEV_TARGET = LIBEV_OUTPUT_DIR + ".libs/libev.a" task :libev => LIBEV_TARGET dependencies = [ "src/cxx_supportlib/vendor-modified/libev/configure", "src/cxx_supportlib/vendor-modified/libev/config.h.in", "src/cxx_supportlib/vendor-modified/libev/Makefile.am" ] file LIBEV_OUTPUT_DIR + "Makefile" => dependencies do cc = CC cxx = CXX if OPTIMIZE && LTO cc = "#{cc} -flto" cxx = "#{cxx} -flto" end # Disable all warnings: http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#COMPILER_WARNINGS cflags = "#{EXTRA_CFLAGS} -w" sh "mkdir -p #{LIBEV_OUTPUT_DIR}" if !File.directory?(LIBEV_OUTPUT_DIR) sh "cd #{LIBEV_OUTPUT_DIR} && sh #{LIBEV_SOURCE_DIR}configure " + "--disable-shared --enable-static " + # libev's configure script may select a different default compiler than we # do, so we force our compiler choice. "CC='#{cc}' CXX='#{cxx}' CFLAGS='#{cflags}' orig_CFLAGS=1" end libev_sources = Dir["src/cxx_supportlib/vendor-modified/libev/{*.c,*.h}"] file LIBEV_OUTPUT_DIR + ".libs/libev.a" => [LIBEV_OUTPUT_DIR + "Makefile"] + libev_sources do sh "rm -f #{LIBEV_OUTPUT_DIR}libev.la" sh "cd #{LIBEV_OUTPUT_DIR} && make libev.la V=1" end task 'libev:clean' do patterns = %w(Makefile config.h config.log config.status libtool stamp-h1 *.o *.lo *.la .libs .deps) patterns.each do |pattern| sh "rm -rf #{LIBEV_OUTPUT_DIR}#{pattern}" end end task :clean => 'libev:clean' def libev_libs la_contents = File.open(LIBEV_OUTPUT_DIR + ".libs/libev.la", "r") do |f| f.read end la_contents =~ /dependency_libs='(.+)'/ "#{LIBEV_OUTPUT_DIR}.libs/libev.a #{$1}".strip end else LIBEV_CFLAGS = string_option('LIBEV_CFLAGS', '-I/usr/include/libev') LIBEV_TARGET = nil task :libev # do nothing def libev_libs string_option('LIBEV_LIBS', '-lev') end end # Apple Clang 4.2 complains about ambiguous member templates in ev++.h. LIBEV_CFLAGS << " -Wno-ambiguous-member-template" if PlatformInfo.compiler_supports_wno_ambiguous_member_template? ########## libuv ########## if USE_VENDORED_LIBUV LIBUV_SOURCE_DIR = File.expand_path("../src/cxx_supportlib/vendor-copy/libuv", File.dirname(__FILE__)) + "/" LIBUV_CFLAGS = "-Isrc/cxx_supportlib/vendor-copy/libuv/include" LIBUV_TARGET = LIBUV_OUTPUT_DIR + ".libs/libuv.a" task :libuv => LIBUV_TARGET dependencies = [ "src/cxx_supportlib/vendor-copy/libuv/configure", "src/cxx_supportlib/vendor-copy/libuv/Makefile.am" ] file LIBUV_OUTPUT_DIR + "Makefile" => dependencies do cc = CC cxx = CXX if OPTIMIZE && LTO cc = "#{cc} -flto" cxx = "#{cxx} -flto" end # Disable all warnings. The author has a clear standpoint on that: # http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#COMPILER_WARNINGS cflags = "#{EXTRA_CFLAGS} -w" sh "mkdir -p #{LIBUV_OUTPUT_DIR}" if !File.directory?(LIBUV_OUTPUT_DIR) # Prevent 'make' from regenerating autotools files sh "cd #{LIBUV_SOURCE_DIR} && (touch aclocal.m4 configure Makefile.in || true)" sh "cd #{LIBUV_OUTPUT_DIR} && sh #{LIBUV_SOURCE_DIR}configure " + "--disable-shared --enable-static " + # libuv's configure script may select a different default compiler than we # do, so we force our compiler choice. "CC='#{cc}' CXX='#{cxx}' CFLAGS='#{cflags}'" end libuv_sources = Dir["src/cxx_supportlib/vendor-copy/libuv/**/{*.c,*.h}"] file LIBUV_OUTPUT_DIR + ".libs/libuv.a" => [LIBUV_OUTPUT_DIR + "Makefile"] + libuv_sources do sh "rm -f #{LIBUV_OUTPUT_DIR}/libuv.la" sh "cd #{LIBUV_OUTPUT_DIR} && make -j2 libuv.la V=1" end task 'libuv:clean' do patterns = %w(Makefile config.h config.log config.status libtool stamp-h1 src test *.o *.lo *.la *.pc .libs .deps) patterns.each do |pattern| sh "rm -rf #{LIBUV_OUTPUT_DIR}#{pattern}" end end task :clean => 'libuv:clean' def libuv_libs la_contents = File.open(LIBUV_OUTPUT_DIR + ".libs/libuv.la", "r") do |f| f.read end la_contents =~ /dependency_libs='(.+)'/ "#{LIBUV_OUTPUT_DIR}.libs/libuv.a #{$1}".strip end else LIBUV_CFLAGS = string_option('LIBUV_CFLAGS', '-I/usr/include/libuv') LIBUV_TARGET = nil task :libuv # do nothing def libuv_libs string_option('LIBUV_LIBS', '-luv') end end ########## Shared definitions ########## # Shared definition files should be in source control so that they don't # have to be built by users. Users may not have write access to the source # root, for example as is the case with Passenger Standalone. # # If you add a new shared definition file, don't forget to update # src/ruby_supportlib/phusion_passenger/packaging.rb! dependencies = ['src/cxx_supportlib/Constants.h.cxxcodebuilder', 'src/ruby_supportlib/phusion_passenger.rb', 'src/ruby_supportlib/phusion_passenger/constants.rb'] file 'src/cxx_supportlib/Constants.h' => dependencies do PhusionPassenger.require_passenger_lib 'constants' template = CxxCodeTemplateRenderer.new('src/cxx_supportlib/Constants.h.cxxcodebuilder') template.render_to('src/cxx_supportlib/Constants.h') end ############################## libboost_oxt_cflags = "" libboost_oxt_cflags << " #{PlatformInfo.adress_sanitizer_flag}" if USE_ASAN libboost_oxt_cflags.strip! LIBBOOST_OXT, LIBBOOST_OXT_LINKARG = define_libboost_oxt_task("common", COMMON_OUTPUT_DIR + "libboost_oxt", libboost_oxt_cflags) COMMON_LIBRARY.enable_optimizations!(LTO) if OPTIMIZE COMMON_LIBRARY.define_tasks(libboost_oxt_cflags) passenger-5.0.30/build/cxx_tests.rb000644 000765 000024 00000020637 12233035540 017666 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. ### C++ components tests ### TEST_CXX_TARGET = "#{TEST_OUTPUT_DIR}cxx/main" TEST_CXX_OBJECTS = { "#{TEST_OUTPUT_DIR}cxx/CxxTestMain.o" => "test/cxx/CxxTestMain.cpp", "#{TEST_OUTPUT_DIR}cxx/TestSupport.o" => "test/cxx/TestSupport.cpp", "#{TEST_OUTPUT_DIR}cxx/Core/ApplicationPool/OptionsTest.o" => "test/cxx/Core/ApplicationPool/OptionsTest.cpp", "#{TEST_OUTPUT_DIR}cxx/Core/ApplicationPool/ProcessTest.o" => "test/cxx/Core/ApplicationPool/ProcessTest.cpp", "#{TEST_OUTPUT_DIR}cxx/Core/ApplicationPool/PoolTest.o" => "test/cxx/Core/ApplicationPool/PoolTest.cpp", "#{TEST_OUTPUT_DIR}cxx/Core/SpawningKit/DirectSpawnerTest.o" => "test/cxx/Core/SpawningKit/DirectSpawnerTest.cpp", "#{TEST_OUTPUT_DIR}cxx/Core/SpawningKit/SmartSpawnerTest.o" => "test/cxx/Core/SpawningKit/SmartSpawnerTest.cpp", "#{TEST_OUTPUT_DIR}cxx/Core/UnionStationTest.o" => "test/cxx/Core/UnionStationTest.cpp", "#{TEST_OUTPUT_DIR}cxx/Core/ResponseCacheTest.o" => "test/cxx/Core/ResponseCacheTest.cpp", "#{TEST_OUTPUT_DIR}cxx/Core/ControllerTest.o" => "test/cxx/Core/ControllerTest.cpp", "#{TEST_OUTPUT_DIR}cxx/UstRouter/TransactionTest.o" => "test/cxx/UstRouter/TransactionTest.cpp", "#{TEST_OUTPUT_DIR}cxx/ServerKit/ChannelTest.o" => "test/cxx/ServerKit/ChannelTest.cpp", "#{TEST_OUTPUT_DIR}cxx/ServerKit/FileBufferedChannelTest.o" => "test/cxx/ServerKit/FileBufferedChannelTest.cpp", "#{TEST_OUTPUT_DIR}cxx/ServerKit/HeaderTableTest.o" => "test/cxx/ServerKit/HeaderTableTest.cpp", "#{TEST_OUTPUT_DIR}cxx/ServerKit/ServerTest.o" => "test/cxx/ServerKit/ServerTest.cpp", "#{TEST_OUTPUT_DIR}cxx/ServerKit/HttpServerTest.o" => "test/cxx/ServerKit/HttpServerTest.cpp", "#{TEST_OUTPUT_DIR}cxx/ServerKit/CookieUtilsTest.o" => "test/cxx/ServerKit/CookieUtilsTest.cpp", "#{TEST_OUTPUT_DIR}cxx/MemoryKit/MbufTest.o" => "test/cxx/MemoryKit/MbufTest.cpp", "#{TEST_OUTPUT_DIR}cxx/MemoryKit/PallocTest.o" => "test/cxx/MemoryKit/PallocTest.cpp", "#{TEST_OUTPUT_DIR}cxx/DataStructures/LStringTest.o" => "test/cxx/DataStructures/LStringTest.cpp", "#{TEST_OUTPUT_DIR}cxx/DataStructures/StringKeyTableTest.o" => "test/cxx/DataStructures/StringKeyTableTest.cpp", "#{TEST_OUTPUT_DIR}cxx/MessageReadersWritersTest.o" => "test/cxx/MessageReadersWritersTest.cpp", "#{TEST_OUTPUT_DIR}cxx/StaticStringTest.o" => "test/cxx/StaticStringTest.cpp", "#{TEST_OUTPUT_DIR}cxx/FileChangeCheckerTest.o" => "test/cxx/FileChangeCheckerTest.cpp", "#{TEST_OUTPUT_DIR}cxx/FileDescriptorTest.o" => "test/cxx/FileDescriptorTest.cpp", "#{TEST_OUTPUT_DIR}cxx/SystemTimeTest.o" => "test/cxx/SystemTimeTest.cpp", "#{TEST_OUTPUT_DIR}cxx/FilterSupportTest.o" => "test/cxx/FilterSupportTest.cpp", "#{TEST_OUTPUT_DIR}cxx/CachedFileStatTest.o" => "test/cxx/CachedFileStatTest.cpp", "#{TEST_OUTPUT_DIR}cxx/BufferedIOTest.o" => "test/cxx/BufferedIOTest.cpp", "#{TEST_OUTPUT_DIR}cxx/MessageIOTest.o" => "test/cxx/MessageIOTest.cpp", "#{TEST_OUTPUT_DIR}cxx/MessagePassingTest.o" => "test/cxx/MessagePassingTest.cpp", "#{TEST_OUTPUT_DIR}cxx/VariantMapTest.o" => "test/cxx/VariantMapTest.cpp", "#{TEST_OUTPUT_DIR}cxx/StringMapTest.o" => "test/cxx/StringMapTest.cpp", "#{TEST_OUTPUT_DIR}cxx/ProcessMetricsCollectorTest.o" => "test/cxx/ProcessMetricsCollectorTest.cpp", "#{TEST_OUTPUT_DIR}cxx/DateParsingTest.o" => "test/cxx/DateParsingTest.cpp", "#{TEST_OUTPUT_DIR}cxx/UtilsTest.o" => "test/cxx/UtilsTest.cpp", "#{TEST_OUTPUT_DIR}cxx/Utils/StrIntUtilsTest.o" => "test/cxx/Utils/StrIntUtilsTest.cpp", "#{TEST_OUTPUT_DIR}cxx/IOUtilsTest.o" => "test/cxx/IOUtilsTest.cpp", "#{TEST_OUTPUT_DIR}cxx/TemplateTest.o" => "test/cxx/TemplateTest.cpp" } def basic_test_cxx_flags @basic_test_cxx_flags ||= begin flags = [ LIBEV_CFLAGS, LIBUV_CFLAGS, PlatformInfo.curl_flags, TEST_COMMON_CFLAGS ] if USE_ASAN flags << PlatformInfo.adress_sanitizer_flag end flags end end def test_cxx_include_paths @test_cxx_include_paths ||= [ "test/cxx", "test/support", "src/agent", *CXX_SUPPORTLIB_INCLUDE_PATHS ] end def test_cxx_flags @test_cxx_flags ||= ["-include test/cxx/TestSupport.h"] + basic_test_cxx_flags end def test_cxx_ldflags @test_cxx_ldflags ||= begin result = "#{EXTRA_PRE_CXX_LDFLAGS} " << "#{TEST_COMMON_LIBRARY.link_objects_as_string} " << "#{TEST_BOOST_OXT_LIBRARY} #{libev_libs} #{libuv_libs} " << "#{PlatformInfo.curl_libs} " << "#{PlatformInfo.zlib_libs} " << "#{PlatformInfo.portability_cxx_ldflags}" result << " #{PlatformInfo.dmalloc_ldflags}" if USE_DMALLOC result << " #{PlatformInfo.adress_sanitizer_flag}" if USE_ASAN result << " #{EXTRA_CXX_LDFLAGS}" result.strip! result end end # Define compilation tasks for object files. TEST_CXX_OBJECTS.each_pair do |object, source| define_cxx_object_compilation_task( object, source, :include_paths => test_cxx_include_paths, :flags => test_cxx_flags, :deps => 'test/cxx/TestSupport.h.gch' ) end # Define compilation task for the agent executable. dependencies = [ TEST_CXX_OBJECTS.keys, LIBEV_TARGET, LIBUV_TARGET, TEST_BOOST_OXT_LIBRARY, TEST_COMMON_LIBRARY.link_objects, AGENT_OBJECTS.keys - [AGENT_MAIN_OBJECT] ].flatten.compact file(TEST_CXX_TARGET => dependencies) do create_cxx_executable( TEST_CXX_TARGET, TEST_CXX_OBJECTS.keys + AGENT_OBJECTS.keys - [AGENT_MAIN_OBJECT], :flags => test_cxx_ldflags ) end dependencies = [ TEST_CXX_TARGET, "#{TEST_OUTPUT_DIR}allocate_memory", NATIVE_SUPPORT_TARGET, AGENT_TARGET ].compact desc "Run unit tests for the C++ components" task 'test:cxx' => dependencies do args = ENV['GROUPS'].to_s.split(";").map{ |name| "-g #{name}" } command = "#{File.expand_path(TEST_CXX_TARGET)} #{args.join(' ')}".strip if boolean_option('GDB') command = "gdb --args #{command}" elsif boolean_option('VALGRIND') valgrind_args = "--dsymutil=yes --vgdb=yes --vgdb-error=1 --child-silent-after-fork=yes" if boolean_option('LEAK_CHECK') valgrind_args << " --leak-check=yes" end if RUBY_PLATFORM =~ /darwin/ valgrind_args << " --suppressions=valgrind-osx.supp" end command = "valgrind #{valgrind_args} #{command}" end if boolean_option('SUDO') command = "#{PlatformInfo.ruby_sudo_command} #{command}" end if boolean_option('REPEAT') if boolean_option('GDB') abort "You cannot set both REPEAT=1 and GDB=1." end sh "cd test && while #{command}; do echo -------------------------------------------; done" elsif boolean_option('REPEAT_FOREVER') if boolean_option('GDB') abort "You cannot set both REPEAT_FOREVER=1 and GDB=1." end sh "cd test && while true; do #{command}; echo -------------------------------------------; done" else sh "cd test && exec #{command}" end end file('test/cxx/TestSupport.h.gch' => generate_compilation_task_dependencies('test/cxx/TestSupport.h')) do compile_cxx( 'test/cxx/TestSupport.h.gch', 'test/cxx/TestSupport.h', :include_paths => test_cxx_include_paths, :flags => [ "-x c++-header", basic_test_cxx_flags ].flatten ) end passenger-5.0.30/build/documentation.rb000644 000765 000024 00000006753 12233035540 020516 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010, 2011, 2012 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. desc "Generate all documentation" task :doc => Packaging::ASCII_DOCS Packaging::ASCII_DOCS.each do |target| source = target.sub(/\.html$/, '.txt') file target => [source] + Dir["doc/users_guide_snippets/**/*"] do if target =~ /apache/i type = "apache" juvia_site_key = "5jpmkyjqlml8rktsfldfpbwth8ig7w9" elsif target =~ /nginx/i type = "nginx" juvia_site_key = "q0ptarhn8o9xanwomq8zkgewbtwffyz" elsif target =~ /standalone/i type = "standalone" juvia_site_key = "amggdy0k65hb4hbjg3dh7pnb9zd8dwy" else type = nil juvia_site_key = nil end command = "mizuho '#{source}'" command << " -a #{type}" if type if juvia_site_key command << " -c juvia --juvia-url http://juvia.phusion.nl --juvia-site-key #{juvia_site_key}" end sh(command) end task :clean do if boolean_option('CLEAN_DOCS', true) sh "rm -f '#{target}'" end end end def create_markdown_compilation_task(target) source = target.sub(/\.html$/, '.txt.md') dependencies = [ source, 'doc/templates/markdown.html.erb', 'doc/templates/bootstrap.min.css' ] file(target => dependencies) do sh "bluecloth -f #{source} > #{target}.tmp" begin puts "Creating #{target}" require 'erb' template = ERB.new(File.read('doc/templates/markdown.html.erb')) title = File.basename(target, '.html').gsub(/([A-Z])/, ' \1').strip content = File.read("#{target}.tmp") css = File.read('doc/templates/bootstrap.min.css') data = template.result(binding) File.open(target, 'w') do |f| f.write(data) end ensure sh "rm -f #{target}.tmp" end end task :doc => target task :clean do if boolean_option('CLEAN_DOCS', true) sh "rm -f #{target}" end end end create_markdown_compilation_task('doc/Packaging.html') create_markdown_compilation_task('doc/CloudLicensingConfiguration.html') create_markdown_compilation_task('doc/ServerOptimizationGuide.html') desc "Upload documentation to the Phusion web server" task 'doc:rsync' => :doc do sh "cd doc && rsync -rv --progress --partial-dir=.rsync-partial --human-readable . " + "shell.phusion.nl:/home/phusion/websites/passenger_docs/" end passenger-5.0.30/build/integration_tests.rb000644 000765 000024 00000012333 12233035540 021401 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. ### Integration tests ### def integration_test_dependencies(runtime_target_name) if string_option('PASSENGER_LOCATION_CONFIGURATION_FILE') return [] else return [runtime_target_name, NATIVE_SUPPORT_TARGET].compact end end desc "Run all integration tests" task 'test:integration' => ['test:integration:apache2', 'test:integration:nginx'] do end dependencies = integration_test_dependencies(:_apache2) desc "Run Apache 2 integration tests" task 'test:integration:apache2' => dependencies do command = "bundle exec rspec -c -f s --tty integration_tests/apache2_tests.rb" if boolean_option('SUDO') command = "#{PlatformInfo.ruby_sudo_command} -E #{command}" end if grep = string_option('E') require 'shellwords' command << " -e #{Shellwords.escape(grep)}" end sh "cd test && exec #{command}" end dependencies = integration_test_dependencies(:_nginx) desc "Run Nginx integration tests" task 'test:integration:nginx' => dependencies do command = "bundle exec rspec -c -f s --tty integration_tests/nginx_tests.rb" if boolean_option('SUDO') command = "#{PlatformInfo.ruby_sudo_command} -E #{command}" end if grep = string_option('E') require 'shellwords' command << " -e #{Shellwords.escape(grep)}" end repeat = true while repeat sh "cd test && exec #{command}" repeat = boolean_option('REPEAT') end end dependencies = integration_test_dependencies(:_nginx) desc "Run Passenger Standalone integration tests" task 'test:integration:standalone' => dependencies do command = "bundle exec rspec -c -f s --tty integration_tests/standalone_tests.rb" if grep = string_option('E') require 'shellwords' command << " -e #{Shellwords.escape(grep)}" end sh "cd test && exec #{command}" end desc "Run native packaging tests" task 'test:integration:native_packaging' do command = "bundle exec rspec -c -f s --tty integration_tests/native_packaging_spec.rb" if boolean_option('SUDO') command = "#{PlatformInfo.ruby_sudo_command} -E #{command}" end if grep = string_option('E') require 'shellwords' command << " -e #{Shellwords.escape(grep)}" end case PlatformInfo.os_name_simple when "linux" if PlatformInfo.linux_distro_tags.include?(:debian) rubylibdir = RbConfig::CONFIG["vendordir"] command = "env NATIVE_PACKAGING_METHOD=deb " + "LOCATIONS_INI=#{rubylibdir}/phusion_passenger/locations.ini " + command elsif PlatformInfo.linux_distro_tags.include?(:redhat) vendorlibdir = RbConfig::CONFIG["vendorlibdir"] locations_ini = File.join(vendorlibdir, "phusion_passenger/locations.ini") command = "env NATIVE_PACKAGING_METHOD=rpm " + "LOCATIONS_INI=#{locations_ini} " + command else abort "Unsupported Linux distribution" end when "macosx" # The tests put /usr/bin and /usr/sbin first in PATH, causing /usr/bin/ruby to be used. # We should run the tests in /usr/bin/ruby too, so that native_support is compiled for # the same Ruby. prefix = "env NATIVE_PACKAGING_METHOD=homebrew " + "LOCATIONS_INI=/usr/local/Cellar/passenger/#{VERSION_STRING}/libexec/src/ruby_supportlib/phusion_passenger/locations.ini" if PlatformInfo.in_rvm? prefix << " rvm-exec system /usr/bin/ruby -S" end command = "#{prefix} #{command}" else abort "Unsupported operating system" end sh "cd test && exec #{command}" end dependencies = integration_test_dependencies(:_apache2) desc "Run the 'apache2' integration test infinitely, and abort if/when it fails" task 'test:restart' => dependencies do require 'shellwords' color_code_start = "\e[33m\e[44m\e[1m" color_code_end = "\e[0m" i = 1 while true do puts "#{color_code_start}Test run #{i} (press Ctrl-C multiple times to abort)#{color_code_end}" command = "bundle exec rspec -c -f s --tty integration_tests/apache2_tests.rb" if grep = string_option('E') command << " -e #{Shellwords.escape(grep)}" end sh "cd test && exec #{command}" i += 1 end end passenger-5.0.30/build/misc.rb000644 000765 000024 00000013205 12233035540 016566 0ustar00honglistaff000000 000000 # encoding: utf-8 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. desc "Run 'sloccount' to see how much code Passenger has" task :sloccount do ENV['LC_ALL'] = 'C' begin # sloccount doesn't recognize the scripts in # bin/ as Ruby, so we make symlinks with proper # extensions. tmpdir = ".sloccount" system "rm -rf #{tmpdir}" mkdir tmpdir Dir['bin/*'].each do |file| safe_ln file, "#{tmpdir}/#{File.basename(file)}.rb" end sh "sloccount", *Dir[ "#{tmpdir}/*", "src" ] ensure system "rm -rf #{tmpdir}" end end def extract_latest_news_contents_and_items # The text is in the following format: # # Release x.x.x # ------------- # # * Text. # * More text. # * A header. # With yet more text. # # Release y.y.y # ------------- # ..... contents = File.read("CHANGELOG") # We're only interested in the latest release, so extract the text for that. contents =~ /\A(Release.*?)^(Release|Older releases)/m contents = $1 contents.sub!(/\A.*?\n-+\n+/m, '') contents.sub!(/\n+\Z/, '') # Now split the text into individual items. items = contents.split(/^ \* /) items.shift while items.first == "" return [contents, items] end desc "Convert the Changelog items for the latest release to HTML" task :changelog_as_html do require 'cgi' contents, items = extract_latest_news_contents_and_items puts "

    " items.each do |item| def format_paragraph(text) # Get rid of newlines: convert them into spaces. text.gsub!("\n", ' ') while text.index(' ') text.gsub!(' ', ' ') end # Auto-link to issue tracker. text.gsub!(/(bug #|issue #|GH-)(\d+)/i) do url = "https://github.com/phusion/passenger/issues/#{$2}" %Q(<{a href="#{url}"}>#{$1}#{$2}<{/a}>) end text.strip! text = CGI.escapeHTML(text) text.gsub!(%r(<\{(.*?)\}>(.*?)<\{/(.*?)\}>)) do "<#{CGI.unescapeHTML $1}>#{$2}" end text end puts "
  • " + format_paragraph(item.strip) + "
  • " end puts "
" end desc "Convert the Changelog items for the latest release to Markdown" task :changelog_as_markdown do contents, items = extract_latest_news_contents_and_items # Auto-link to issue tracker. contents.gsub!(/(bug #|issue #|GH-)(\d+)/i) do url = "https://github.com/phusion/passenger/issues/#{$2}" %Q([#{$1}#{$2}](#{url})) end puts contents end desc "Update CONTRIBUTORS file" task :contributors do entries = `git log --format='%aN' | sort -u`.split("\n") entries.delete "Hongli Lai" entries.delete "Hongli Lai (Phusion" entries.delete "Ninh Bui" entries.push "Ninh Bui (Phusion)" entries.delete "Phusion Dev" entries.delete "Tinco Andringa" entries.push "Tinco Andringa (Phusion)" entries.delete "Goffert van Gool" entries.push "Goffert van Gool (Phusion)" entries.delete "Gokulnath" entries.push "Gokulnath Manakkattil" entries.push "Sean Wilkinson" entries.push "Yichun Zhang" entries.delete "OnixGH" entries.push "Ruslan Ermilov (NGINX Inc)" File.open("CONTRIBUTORS", "w") do |f| f.puts(entries.sort{ |a, b| a.downcase <=> b.downcase }.join("\n")) end puts "Updated CONTRIBUTORS" end desc "Update the C++ dependency map" task :dependency_map do sh "./dev/index_cxx_dependencies.rb > build/support/cxx_dependency_map.rb" end dependencies = [ COMMON_LIBRARY.link_objects, LIBBOOST_OXT, LIBEV_TARGET, LIBUV_TARGET ].flatten.compact task :compile_app => dependencies do source = ENV['SOURCE'] || ENV['FILE'] || ENV['F'] if !source STDERR.puts "Please specify the source filename with SOURCE=(...)" exit 1 end if source =~ /\.h/ File.open('_source.cpp', 'w') do |f| f.puts "#include \"#{source}\"" end source = '_source.cpp' end object = source.sub(/\.cpp$/, '.o') exe = source.sub(/\.cpp$/, '') begin compile_cxx(object, source, :include_paths => CXX_SUPPORTLIB_INCLUDE_PATHS, :flags => [ "-DSTANDALONE", LIBEV_CFLAGS, LIBUV_CFLAGS ] ) create_cxx_executable(exe, object, :flags => [ "-DSTANDALONE", LIBEV_CFLAGS, LIBUV_CFLAGS, COMMON_LIBRARY.link_objects_as_string, LIBBOOST_OXT_LINKARG, libev_libs, libuv_libs, PlatformInfo.portability_cxx_ldflags ] ) ensure File.unlink('_source.cpp') rescue nil end end passenger-5.0.30/build/nginx.rb000644 000765 000024 00000006175 12233035540 016766 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. auto_generated_sources = %w( src/nginx_module/ConfigurationCommands.c src/nginx_module/CreateLocationConfig.c src/nginx_module/MergeLocationConfig.c src/nginx_module/CacheLocationConfig.c src/nginx_module/LocationConfig.h ) desc "Build Nginx support files" task :nginx => [ :nginx_without_native_support, NATIVE_SUPPORT_TARGET ].compact desc "Build Nginx support files, including objects suitable for dynamic linking against Nginx" task 'nginx:as_dynamic_module' => [ :nginx_dynamic_without_native_support, NATIVE_SUPPORT_TARGET ].compact # Workaround for https://github.com/jimweirich/rake/issues/274 task :_nginx => :nginx task :nginx_without_native_support => [ auto_generated_sources, AGENT_TARGET, COMMON_LIBRARY.only(*NGINX_LIBS_SELECTOR).link_objects ].flatten # define_tasks creates an extra compilation to the specified output_dir, with extra compiler flags; # it also creates a namespace:clean task to clean up the output_dir task :nginx_dynamic_without_native_support => [ auto_generated_sources, AGENT_TARGET, define_libboost_oxt_task("nginx", NGINX_DYNAMIC_OUTPUT_DIR + "libboost_oxt", "-fPIC"), COMMON_LIBRARY.only(*NGINX_LIBS_SELECTOR). set_namespace("nginx").set_output_dir(NGINX_DYNAMIC_OUTPUT_DIR + "module_libpassenger_common").define_tasks("-fPIC"). link_objects ].flatten task :clean => 'nginx:clean' desc "Clean all compiled Nginx files" task 'nginx:clean' => 'common:clean' do # Nothing extra to clean at this time. end def create_nginx_auto_generated_source_task(source) dependencies = [ "#{source}.cxxcodebuilder", 'src/ruby_supportlib/phusion_passenger/nginx/config_options.rb' ] file(source => dependencies) do template = CxxCodeTemplateRenderer.new("#{source}.cxxcodebuilder") template.render_to(source) end end auto_generated_sources.each do |source| create_nginx_auto_generated_source_task(source) end passenger-5.0.30/build/node_tests.rb000644 000765 000024 00000003170 12233035540 020002 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2013 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. ### Node.js components tests ### desc "Run unit tests for the Node.js libraries" task 'test:node' do require 'shellwords' command = "cd test && env NODE_PATH=#{PhusionPassenger.node_libdir} NODE_ENV=test " + "../node_modules/.bin/mocha -R spec node/*_spec.js" if grep = string_option('G') command << " -g #{Shellwords.escape(grep)}" end sh(command) end passenger-5.0.30/build/oxt_tests.rb000644 000765 000024 00000005130 12233035540 017665 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. ### OXT library tests ### TEST_OXT_TARGET = "#{TEST_OUTPUT_DIR}oxt/main" TEST_OXT_OBJECTS = { "#{TEST_OUTPUT_DIR}oxt/oxt_test_main.o" => "test/oxt/oxt_test_main.cpp", "#{TEST_OUTPUT_DIR}oxt/backtrace_test.o" => "test/oxt/backtrace_test.cpp", "#{TEST_OUTPUT_DIR}oxt/spin_lock_test.o" => "test/oxt/spin_lock_test.cpp", "#{TEST_OUTPUT_DIR}oxt/dynamic_thread_group_test.o" => "test/oxt/dynamic_thread_group_test.cpp", "#{TEST_OUTPUT_DIR}oxt/syscall_interruption_test.o" => "test/oxt/syscall_interruption_test.cpp" } # Define compilation tasks for object files. TEST_OXT_OBJECTS.each_pair do |object, source| define_cxx_object_compilation_task( object, source, :include_paths => [ "test/support", *CXX_SUPPORTLIB_INCLUDE_PATHS ], :flags => TEST_COMMON_CFLAGS ) end # Define compilation task for the test executable. dependencies = TEST_OXT_OBJECTS.keys + [TEST_BOOST_OXT_LIBRARY] file(TEST_OXT_TARGET => dependencies) do flags = [ TEST_BOOST_OXT_LIBRARY, PlatformInfo.portability_cxx_ldflags ] if USE_ASAN flags << PlatformInfo.adress_sanitizer_flag end create_cxx_executable( TEST_OXT_TARGET, TEST_OXT_OBJECTS.keys, :flags => flags ) end desc "Run unit tests for the OXT library" task 'test:oxt' => TEST_OXT_TARGET do sh "cd test && #{File.expand_path(TEST_OXT_TARGET)}" end passenger-5.0.30/build/packaging.rb000644 000765 000024 00000064515 12233035540 017571 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. ORIG_TARBALL_FILES = lambda { PhusionPassenger::Packaging.files } def recursive_copy_files(files, destination_dir, preprocess = false, variables = {}) require 'fileutils' if !defined?(FileUtils) if !STDOUT.tty? puts "Copying files..." end files.each_with_index do |filename, i| dir = File.dirname(filename) if !File.exist?("#{destination_dir}/#{dir}") FileUtils.mkdir_p("#{destination_dir}/#{dir}") end if !File.directory?(filename) if preprocess && filename =~ /\.erb$/ real_filename = filename.sub(/\.erb$/, '') FileUtils.install(filename, "#{destination_dir}/#{real_filename}", :preserve => true) Preprocessor.new.start(filename, "#{destination_dir}/#{real_filename}", variables) else FileUtils.install(filename, "#{destination_dir}/#{filename}", :preserve => true) end end if STDOUT.tty? printf "\r[%5d/%5d] [%3.0f%%] Copying files...", i + 1, files.size, i * 100.0 / files.size STDOUT.flush end end if STDOUT.tty? printf "\r[%5d/%5d] [%3.0f%%] Copying files...\n", files.size, files.size, 100 end end def word_wrap(text, max = 72) while index = (lines = text.split("\n")).find_index{ |line| line.size > max } line = lines[index] pos = max while pos >= 0 && line[pos..pos] != " " pos -= 1 end if pos < 0 raise "Cannot wrap line: #{line}" else lines[index] = line[0 .. pos - 1] lines.insert(index + 1, line[pos + 1 .. -1]) text = lines.join("\n") end end return text end def is_open_source? return !is_enterprise? end def is_enterprise? return PACKAGE_NAME =~ /enterprise/ end def enterprise_git_url return "TODO" end def git_tag_prefix if is_open_source? return "release" else return "enterprise" end end def git_tag return "#{git_tag_prefix}-#{VERSION_STRING}" end def apt_repo_name if is_open_source? "passenger" else "passenger-enterprise" end end def homebrew_dir return "/tmp/homebrew" end task :clobber => 'package:clean' task 'package:set_official' do ENV['OFFICIAL_RELEASE'] = '1' # These environment variables interfere with 'brew install' # and maybe other stuff, so unset them. ENV.delete('CC') ENV.delete('CXX') ENV.delete('USE_CCACHE') end desc "Build, sign & upload gem & tarball" task 'package:release' => ['package:set_official', 'package:gem', 'package:tarball', 'package:sign'] do PhusionPassenger.require_passenger_lib 'platform_info' require 'yaml' require 'uri' require 'net/http' require 'net/https' basename = "#{PhusionPassenger::PACKAGE_NAME}-#{PhusionPassenger::VERSION_STRING}" version = PhusionPassenger::VERSION_STRING is_beta = !!version.split('.')[3] if !`git status --porcelain | grep -Ev '^\\?\\? '`.empty? STDERR.puts "-------------------" abort "*** ERROR: There are uncommitted files. See 'git status'" end begin website_config = YAML.load_file(File.expand_path("~/.passenger_website.yml")) rescue Errno::ENOENT STDERR.puts "-------------------" abort "*** ERROR: Please put the Phusion Passenger website admin " + "password in ~/.passenger_website.yml:\n" + "admin_password: ..." end if !PhusionPassenger::PlatformInfo.find_command("hub") STDERR.puts "-------------------" abort "*** ERROR: Please 'brew install hub' first" end if is_open_source? if boolean_option('HOMEBREW_UPDATE', true) && !is_beta puts "Updating Homebrew formula..." Rake::Task['package:update_homebrew'].invoke else puts "HOMEBREW_UPDATE set to false, not updating Homebrew formula." end end sh "git tag -s #{git_tag} -u 0A212A8C -m 'Release #{version}'" puts "Proceed with pushing tag to remote Git repo and uploading the gem and signatures? [y/n]" if STDIN.readline == "y\n" sh "git push origin #{git_tag}" if is_open_source? sh "s3cmd -P put #{PKG_DIR}/passenger-#{version}.{gem,tar.gz,gem.asc,tar.gz.asc} s3://phusion-passenger/releases/" sh "gem push #{PKG_DIR}/passenger-#{version}.gem" puts "Updating version number on website..." if is_beta uri = URI.parse("https://www.phusionpassenger.com/latest_beta_version") else uri = URI.parse("https://www.phusionpassenger.com/latest_stable_version") end http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_PEER request = Net::HTTP::Post.new(uri.request_uri) request.basic_auth("admin", website_config["admin_password"]) request.set_form_data("version" => version) response = http.request(request) if response.code != 200 && response.body != "ok" abort "*** ERROR: Cannot update version number on www.phusionpassenger.com:\n" + "Status: #{response.code}\n\n" + response.body end puts "Initiating building of binaries" Rake::Task['package:initiate_binaries_building'].invoke if !is_beta puts "Initiating building of Debian packages" Rake::Task['package:initiate_debian_building'].invoke puts "Initiating building of RPM packages" Rake::Task['package:initiate_rpm_building'].invoke end puts "Building OS X binaries..." Rake::Task['package:build_osx_binaries'].invoke if !is_beta && boolean_option('HOMEBREW_UPDATE', true) if boolean_option('HOMEBREW_DRY_RUN', false) puts "HOMEBREW_DRY_RUN set, not submitting pull request. Please find the repo in /tmp/homebrew." else puts "Submitting Homebrew pull request..." sh "cd #{homebrew_dir} && hub pull-request -m 'passenger #{version}' -b Homebrew:master" end end puts "--------------" puts "All done." else dir = "/u/apps/passenger_website/shared" subdir = string_option('NAME', version) sh "scp #{PKG_DIR}/#{basename}.{gem,tar.gz,gem.asc,tar.gz.asc} app@shell.phusion.nl:#{dir}/" sh "ssh app@shell.phusion.nl 'mkdir -p \"#{dir}/assets/#{subdir}\" && mv #{dir}/#{basename}.{gem,tar.gz,gem.asc,tar.gz.asc} \"#{dir}/assets/#{subdir}/\"'" command = "curl -F file=@#{PKG_DIR}/#{basename}.gem --user admin:'#{website_config['admin_password']}' " + "--output /dev/stderr --write-out '%{http_code}' --silent " + "https://www.phusionpassenger.com/enterprise_gems/upload" puts command result = `#{command}` if result != "200" abort "Gem upload failed. HTTP status code: #{result.inspect}" else # The response body does not contain a newline, # so fix terminal output. puts end puts "Initiating building of binaries" Rake::Task['package:initiate_binaries_building'].invoke if !is_beta puts "Initiating building of Debian packages" Rake::Task['package:initiate_debian_building'].invoke puts "Initiating building of RPM packages" Rake::Task['package:initiate_rpm_building'].invoke end puts "Building OS X binaries..." Rake::Task['package:build_osx_binaries'].invoke puts "--------------" puts "All done." end else puts "Did not upload anything." end end task 'package:gem' => Packaging::PREGENERATED_FILES do require 'phusion_passenger' if ENV['OFFICIAL_RELEASE'] release_file = "#{PhusionPassenger.resources_dir}/release.txt" File.unlink(release_file) rescue nil end begin if release_file File.open(release_file, "w").close end sh("gem build #{PhusionPassenger::PACKAGE_NAME}.gemspec") ensure if release_file File.unlink(release_file) rescue nil end end sh "mkdir -p #{PKG_DIR}" sh "mv #{PhusionPassenger::PACKAGE_NAME}-#{PhusionPassenger::VERSION_STRING}.gem #{PKG_DIR}/" end task 'package:tarball' => Packaging::PREGENERATED_FILES do require 'phusion_passenger' require 'fileutils' basename = "#{PhusionPassenger::PACKAGE_NAME}-#{PhusionPassenger::VERSION_STRING}" sh "rm -rf #{PKG_DIR}/#{basename}" sh "mkdir -p #{PKG_DIR}/#{basename}" recursive_copy_files(ORIG_TARBALL_FILES.call, "#{PKG_DIR}/#{basename}") if ENV['OFFICIAL_RELEASE'] File.open("#{PKG_DIR}/#{basename}/resources/release.txt", "w").close end if PlatformInfo.os_name_simple == "macosx" sh "cd #{PKG_DIR}/#{basename} && find . -print0 | xargs -0 touch -t '201310270000'" else sh "cd #{PKG_DIR}/#{basename} && find . -print0 | xargs -0 touch -d '2013-10-27 00:00:00 UTC'" end sh "cd #{PKG_DIR} && tar -c #{basename} | gzip --no-name --best > #{basename}.tar.gz" sh "rm -rf #{PKG_DIR}/#{basename}" end task 'package:sign' do if File.exist?(File.expand_path("~/.gnupg/gpg-agent.conf")) || ENV['GPG_AGENT_INFO'] puts "It looks like you're using gpg-agent, so skipping automatically password caching." else begin require 'highline' rescue LoadError abort "Please run `gem install highline` first." end h = HighLine.new password = h.ask("Password for software-signing@phusion.nl GPG key: ") { |q| q.echo = false } passphrase_opt = "--passphrase-file .gpg-password" end begin if password File.open(".gpg-password", "w", 0600) do |f| f.write(password) end end version = PhusionPassenger::VERSION_STRING ["passenger-#{version}.gem", "passenger-#{version}.tar.gz", "passenger-enterprise-server-#{version}.gem", "passenger-enterprise-server-#{version}.tar.gz"].each do |name| if File.exist?("pkg/#{name}") sh "gpg --sign --detach-sign #{passphrase_opt} --local-user software-signing@phusion.nl --armor pkg/#{name}" end end ensure File.unlink('.gpg-password') if File.exist?('.gpg-password') end end task 'package:update_homebrew' do require 'digest/sha2' version = VERSION_STRING sha256 = File.open("#{PKG_DIR}/passenger-#{version}.tar.gz", "rb") do |f| Digest::SHA256.hexdigest(f.read) end if !File.exist?(homebrew_dir) sh "git clone git@github.com:phusion/homebrew-core.git #{homebrew_dir}" sh "cd #{homebrew_dir} && git remote add Homebrew https://github.com/Homebrew/homebrew-core.git" end sh "cd #{homebrew_dir} && git fetch Homebrew" sh "cd #{homebrew_dir} && git reset --hard Homebrew/master" formula = File.read("/tmp/homebrew/Formula/passenger.rb") formula.gsub!(/passenger-.+?\.tar\.gz/, "passenger-#{version}.tar.gz") || abort("Unable to substitute Homebrew formula tarball filename") formula.gsub!(/^ sha256 .*/, " sha256 \"#{sha256}\"") || abort("Unable to substitute Homebrew formula SHA-256") necessary_dirs = ORIG_TARBALL_FILES.call.map{ |filename| filename.split("/").first }.uniq necessary_dirs -= Packaging::HOMEBREW_EXCLUDE necessary_dirs += ["buildout"] necessary_dirs_str = word_wrap(necessary_dirs.inspect).split("\n").join("\n ") formula.sub!(/necessary_files = .*?\]/m, "necessary_files = Dir#{necessary_dirs_str}") || abort("Unable to substitute file whitelist") File.open("/tmp/homebrew/Formula/passenger.rb", "w") do |f| f.write(formula) end sh "cd #{homebrew_dir} && git commit -a -m 'passenger #{version}'" sh "cd #{homebrew_dir} && git push -f" if boolean_option('HOMEBREW_TEST', true) sh "cp /tmp/homebrew/Formula/passenger.rb /usr/local/Library/Formula/passenger.rb" if `brew info passenger` !~ /^Not installed$/ sh "brew uninstall passenger" end sh "cp #{PKG_DIR}/passenger-#{version}.tar.gz `brew --cache`/" sh "brew install passenger" Rake::Task['test:integration:native_packaging'].invoke end end task 'package:initiate_binaries_building' do require 'yaml' require 'uri' require 'net/http' require 'net/https' version = VERSION_STRING begin website_config = YAML.load_file(File.expand_path("~/.passenger_website.yml")) rescue Errno::ENOENT STDERR.puts "-------------------" abort "*** ERROR: Please put the Phusion Passenger website admin " + "password in ~/.passenger_website.yml:\n" + "admin_password: ..." end if is_open_source? type = "open%20source" jenkins_token = website_config["jenkins_token"] if !jenkins_token abort "*** ERROR: Please put the Passenger open source Jenkins " + "authentication token in ~/.passenger_website.yml, under " + "the 'jenkins_token' key." end else type = "Enterprise" jenkins_token = website_config["jenkins_enterprise_token"] if !jenkins_token abort "*** ERROR: Please put the Passenger Enterprise Jenkins " + "authentication token in ~/.passenger_website.yml, under " + "the 'jenkins_enterprise_token' key." end end uri = URI.parse("https://oss-jenkins.phusion.nl/buildByToken/buildWithParameters?" + "job=Passenger%20#{type}%20binaries%20(release)&tag=#{git_tag}") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_PEER request = Net::HTTP::Post.new(uri.request_uri) request.set_form_data("token" => jenkins_token) response = http.request(request) if response.code != 200 && response.body != "Scheduled.\n" abort "*** ERROR: Cannot initiate building of binaries:\n" + "Status: #{response.code}\n\n" + response.body end puts "Initiated building of binaries." end task 'package:initiate_debian_building' do require 'yaml' require 'uri' require 'net/http' require 'net/https' version = VERSION_STRING begin website_config = YAML.load_file(File.expand_path("~/.passenger_website.yml")) rescue Errno::ENOENT STDERR.puts "-------------------" abort "*** ERROR: Please put the Phusion Passenger website admin " + "password in ~/.passenger_website.yml:\n" + "admin_password: ..." end if is_open_source? type = "open%20source" jenkins_token = website_config["jenkins_token"] if !jenkins_token abort "*** ERROR: Please put the Passenger open source Jenkins " + "authentication token in ~/.passenger_website.yml, under " + "the 'jenkins_token' key." end else type = "Enterprise" jenkins_token = website_config["jenkins_enterprise_token"] if !jenkins_token abort "*** ERROR: Please put the Passenger Enterprise Jenkins " + "authentication token in ~/.passenger_website.yml, under " + "the 'jenkins_enterprise_token' key." end end uri = URI.parse("https://oss-jenkins.phusion.nl/buildByToken/buildWithParameters?" + "job=Passenger%20#{type}%20Debian%20packages%20(release)&ref=#{git_tag}&repo=#{apt_repo_name}") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_PEER request = Net::HTTP::Post.new(uri.request_uri) request.set_form_data("token" => jenkins_token) response = http.request(request) if response.code != 200 && response.body != "Scheduled.\n" abort "*** ERROR: Cannot initiate building of Debian packages:\n" + "Status: #{response.code}\n\n" + response.body end puts "Initiated building of Debian packages." end task 'package:initiate_rpm_building' do require 'yaml' require 'uri' require 'net/http' require 'net/https' version = VERSION_STRING begin website_config = YAML.load_file(File.expand_path("~/.passenger_website.yml")) rescue Errno::ENOENT STDERR.puts "-------------------" abort "*** ERROR: Please put the Phusion Passenger website admin " + "password in ~/.passenger_website.yml:\n" + "admin_password: ..." end if is_open_source? type = "open%20source" jenkins_token = website_config["jenkins_token"] if !jenkins_token abort "*** ERROR: Please put the Passenger open source Jenkins " + "authentication token in ~/.passenger_website.yml, under " + "the 'jenkins_token' key." end else type = "Enterprise" jenkins_token = website_config["jenkins_enterprise_token"] if !jenkins_token abort "*** ERROR: Please put the Passenger Enterprise Jenkins " + "authentication token in ~/.passenger_website.yml, under " + "the 'jenkins_enterprise_token' key." end end uri = URI.parse("https://oss-jenkins.phusion.nl/buildByToken/buildWithParameters?" + "job=Passenger%20#{type}%20RPM%20packages%20(release)&ref=#{git_tag}&repo=#{apt_repo_name}") http = Net::HTTP.new(uri.host, uri.port) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_PEER request = Net::HTTP::Post.new(uri.request_uri) request.set_form_data("token" => jenkins_token) response = http.request(request) if response.code != 200 && response.body != "Scheduled.\n" abort "*** ERROR: Cannot initiate building of RPM packages:\n" + "Status: #{response.code}\n\n" + response.body end puts "Initiated building of RPM packages." end task 'package:build_osx_binaries' do if is_open_source? sh "cd ../passenger_autobuilder && " + "git pull && " + "./autobuild-osx https://github.com/phusion/passenger.git passenger " + "psg_autobuilder_chroot@juvia-helper.phusion.nl --tag=#{git_tag}" else sh "cd ../passenger_autobuilder && " + "git pull && " + "./autobuild-osx #{enterprise_git_url} passenger-enterprise " + "psg_autobuilder_chroot@juvia-helper.phusion.nl --tag=#{git_tag}" end end desc "Remove gem, tarball and signatures" task 'package:clean' do require 'phusion_passenger' basename = "#{PhusionPassenger::PACKAGE_NAME}-#{PhusionPassenger::VERSION_STRING}" sh "rm -f pkg/#{basename}.{gem,gem.asc,tar.gz,tar.gz.asc}" end def change_shebang(filename, value) contents = File.open(filename, "r") do |f| f.read end contents.gsub!(/\A#\!.+$/, "#!#{value}") File.open(filename, "w") do |f| f.write(contents) end end desc "Create a fakeroot, useful for building native packages" task :fakeroot => [:apache2, :nginx, 'nginx:as_dynamic_module', :doc] do require 'rbconfig' require 'fileutils' include RbConfig fs_prefix = ENV['FS_PREFIX'] || "/usr" fs_bindir = ENV['FS_BINDIR'] || "#{fs_prefix}/bin" fs_sbindir = ENV['FS_SBINDIR'] || "#{fs_prefix}/sbin" fs_datadir = ENV['FS_DATADIR'] || "#{fs_prefix}/share" fs_docdir = ENV['FS_DOCDIR'] || "#{fs_datadir}/doc" fs_libdir = ENV['FS_LIBDIR'] || "#{fs_prefix}/lib" # We don't use CONFIG['archdir'] and the like because we want # the files to be installed to /usr, and the Ruby interpreter # on the packaging machine might be in /usr/local. psg_rubylibdir = ENV['RUBYLIBDIR'] || "#{fs_libdir}/ruby/vendor_ruby" psg_nodelibdir = "#{fs_datadir}/#{GLOBAL_NAMESPACE_DIRNAME}/node" psg_libdir = "#{fs_libdir}/#{GLOBAL_NAMESPACE_DIRNAME}" psg_native_support_dir = ENV["RUBYARCHDIR"] || "#{fs_libdir}/ruby/#{CONFIG['ruby_version']}/#{CONFIG['arch']}" psg_support_binaries_dir = "#{fs_libdir}/#{GLOBAL_NAMESPACE_DIRNAME}/support-binaries" psg_helper_scripts_dir = "#{fs_datadir}/#{GLOBAL_NAMESPACE_DIRNAME}/helper-scripts" psg_resources_dir = "#{fs_datadir}/#{GLOBAL_NAMESPACE_DIRNAME}" psg_include_dir = "#{fs_datadir}/#{GLOBAL_NAMESPACE_DIRNAME}/include" psg_docdir = "#{fs_docdir}/#{GLOBAL_NAMESPACE_DIRNAME}" psg_bindir = "#{fs_bindir}" psg_sbindir = "#{fs_sbindir}" psg_apache2_module_path = ENV['APACHE2_MODULE_PATH'] || "#{fs_libdir}/apache2/modules/mod_passenger.so" psg_ruby_extension_source_dir = "#{fs_datadir}/#{GLOBAL_NAMESPACE_DIRNAME}/ruby_extension_source" psg_nginx_module_source_dir = "#{fs_datadir}/#{GLOBAL_NAMESPACE_DIRNAME}/ngx_http_passenger_module" psg_ruby = ENV['RUBY'] || "#{fs_bindir}/ruby" psg_free_ruby = ENV['FREE_RUBY'] || "/usr/bin/env ruby" fakeroot = "pkg/fakeroot" fake_rubylibdir = "#{fakeroot}#{psg_rubylibdir}" fake_nodelibdir = "#{fakeroot}#{psg_nodelibdir}" fake_libdir = "#{fakeroot}#{psg_libdir}" fake_native_support_dir = "#{fakeroot}#{psg_native_support_dir}" fake_support_binaries_dir = "#{fakeroot}#{psg_support_binaries_dir}" fake_helper_scripts_dir = "#{fakeroot}#{psg_helper_scripts_dir}" fake_resources_dir = "#{fakeroot}#{psg_resources_dir}" fake_include_dir = "#{fakeroot}#{psg_include_dir}" fake_docdir = "#{fakeroot}#{psg_docdir}" fake_bindir = "#{fakeroot}#{psg_bindir}" fake_sbindir = "#{fakeroot}#{psg_sbindir}" fake_apache2_module_path = "#{fakeroot}#{psg_apache2_module_path}" fake_ruby_extension_source_dir = "#{fakeroot}#{psg_ruby_extension_source_dir}" fake_nginx_module_source_dir = "#{fakeroot}#{psg_nginx_module_source_dir}" packaging_method = ENV['NATIVE_PACKAGING_METHOD'] || ENV['PACKAGING_METHOD'] || "deb" sh "rm -rf #{fakeroot}" sh "mkdir -p #{fakeroot}" # Ruby sources sh "mkdir -p #{fake_rubylibdir}" sh "cp #{PhusionPassenger.ruby_libdir}/phusion_passenger.rb #{fake_rubylibdir}/" sh "cp -R #{PhusionPassenger.ruby_libdir}/phusion_passenger #{fake_rubylibdir}/" # Node.js sources sh "mkdir -p #{fake_nodelibdir}" sh "cp -R #{PhusionPassenger.node_libdir}/* #{fake_nodelibdir}/" # C++ support libraries sh "mkdir -p #{fake_libdir}" sh "cp -R #{COMMON_OUTPUT_DIR} #{fake_libdir}/" sh "rm -rf #{fake_libdir}/common/libboost_oxt" sh "cp -R #{NGINX_DYNAMIC_OUTPUT_DIR} #{fake_libdir}/" sh "rm -rf #{fake_libdir}/nginx_dynamic/libboost_oxt" # Ruby extension binaries sh "mkdir -p #{fake_native_support_dir}" native_support_archdir = PlatformInfo.ruby_extension_binary_compatibility_id sh "mkdir -p #{fake_native_support_dir}" sh "cp -R buildout/ruby/#{native_support_archdir}/*.#{LIBEXT} #{fake_native_support_dir}/" # Support binaries sh "mkdir -p #{fake_support_binaries_dir}" sh "cp -R #{PhusionPassenger.support_binaries_dir}/* #{fake_support_binaries_dir}/" sh "rm -rf #{fake_support_binaries_dir}/*.dSYM" sh "rm -rf #{fake_support_binaries_dir}/*/*.dSYM" sh "rm -rf #{fake_support_binaries_dir}/*.o" # Helper scripts sh "mkdir -p #{fake_helper_scripts_dir}" sh "cp -R #{PhusionPassenger.helper_scripts_dir}/* #{fake_helper_scripts_dir}/" # Resources sh "mkdir -p #{fake_resources_dir}" sh "cp -R resources/* #{fake_resources_dir}/" # Headers necessary for building the Nginx module sh "mkdir -p #{fake_include_dir}" # Infer headers that the Nginx module needs headers = [] Dir["src/nginx_module/*.[ch]"].each do |filename| File.read(filename).split("\n").grep(%r{#include "cxx_supportlib/(.+)"}) do |match| headers << ["src/cxx_supportlib/#{$1}", "cxx_supportlib/#{$1}"] end end # Manually add headers that could not be inferred through # the above code headers.concat([ ["src/cxx_supportlib/Exceptions.h", "cxx_supportlib/Exceptions.h"], ["src/cxx_supportlib/vendor-modified/modp_b64.h", "cxx_supportlib/vendor-modified/modp_b64.h"], ["src/cxx_supportlib/vendor-modified/modp_b64_data.h", "cxx_supportlib/vendor-modified/modp_b64_data.h"] ]) headers.each do |header| target = "#{fake_include_dir}/#{header[1]}" dir = File.dirname(target) if !File.directory?(dir) sh "mkdir -p #{dir}" end sh "cp #{header[0]} #{target}" end # Nginx module sources sh "mkdir -p #{fake_nginx_module_source_dir}" sh "cp src/nginx_module/* #{fake_nginx_module_source_dir}/" # Documentation sh "mkdir -p #{fake_docdir}" sh "cp doc/*.html #{fake_docdir}/" sh "cp -R doc/images #{fake_docdir}/" # User binaries sh "mkdir -p #{fake_bindir}" Packaging::USER_EXECUTABLES.each do |exe| sh "cp bin/#{exe} #{fake_bindir}/" if Packaging::EXECUTABLES_WITH_FREE_RUBY.include?(exe) shebang = psg_free_ruby else shebang = psg_ruby end change_shebang("#{fake_bindir}/#{exe}", shebang) end # Superuser binaries sh "mkdir -p #{fake_sbindir}" Packaging::SUPER_USER_EXECUTABLES.each do |exe| sh "cp bin/#{exe} #{fake_sbindir}/" if Packaging::EXECUTABLES_WITH_FREE_RUBY.include?(exe) shebang = psg_free_ruby else shebang = psg_ruby end change_shebang("#{fake_sbindir}/#{exe}", shebang) end # Apache 2 module sh "mkdir -p #{File.dirname(fake_apache2_module_path)}" sh "cp #{APACHE2_TARGET} #{fake_apache2_module_path}" # Ruby extension sources sh "mkdir -p #{fake_ruby_extension_source_dir}" sh "cp -R #{PhusionPassenger.ruby_extension_source_dir}/* #{fake_ruby_extension_source_dir}" puts "Creating #{fake_rubylibdir}/phusion_passenger/locations.ini" File.open("#{fake_rubylibdir}/phusion_passenger/locations.ini", "w") do |f| f.puts "[locations]" f.puts "packaging_method=#{packaging_method}" f.puts "bin_dir=#{psg_bindir}" f.puts "support_binaries_dir=#{psg_support_binaries_dir}" f.puts "lib_dir=#{psg_libdir}" f.puts "helper_scripts_dir=#{psg_helper_scripts_dir}" f.puts "resources_dir=#{psg_resources_dir}" f.puts "include_dir=#{psg_include_dir}" f.puts "doc_dir=#{psg_docdir}" f.puts "ruby_libdir=#{psg_rubylibdir}" f.puts "node_libdir=#{psg_nodelibdir}" f.puts "apache2_module_path=#{psg_apache2_module_path}" f.puts "ruby_extension_source_dir=#{psg_ruby_extension_source_dir}" f.puts "nginx_module_source_dir=#{psg_nginx_module_source_dir}" end # Sanity check the locations.ini file options = PhusionPassenger.parse_ini_file("#{fake_rubylibdir}/phusion_passenger/locations.ini") PhusionPassenger::REQUIRED_LOCATIONS_INI_FIELDS.each do |field| if !options[field.to_s] raise "Bug in build/packaging.rb: the generated locations.ini is missing the '#{field}' field" end end sh "find #{fakeroot} -name .DS_Store -print0 | xargs -0 rm -f" end passenger-5.0.30/build/README.md000644 000765 000024 00000000373 12233035540 016567 0ustar00honglistaff000000 000000 This directory contains the Passenger build system. It is based on [Rake](http://docs.seattlerb.org/rake/), which is a Make-like tool but written in Ruby. You can access the build system with the `rake` command. For example, try running: rake -T passenger-5.0.30/build/ruby_extension.rb000644 000765 000024 00000004527 12233035540 020717 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. if !PlatformInfo.passenger_needs_ruby_dev_header? NATIVE_SUPPORT_TARGET = nil task :native_support do # Do nothing. end task 'native_support:clean' do # Do nothing. end else output_dir = RUBY_EXTENSION_OUTPUT_DIR output_name = "passenger_native_support.#{LIBEXT}" source_dir = "src/ruby_native_extension" NATIVE_SUPPORT_TARGET = File.join(output_dir, output_name) task :native_support => NATIVE_SUPPORT_TARGET task :clean => 'native_support:clean' dependencies = [ File.join(output_dir, "Makefile"), "#{source_dir}/passenger_native_support.c" ] file(NATIVE_SUPPORT_TARGET => dependencies) do sh "mkdir -p '#{output_dir}'" if !File.exist?(output_dir) sh "cd '#{output_dir}' && make" end file(File.join(output_dir, "Makefile") => "#{source_dir}/extconf.rb") do sh "mkdir -p '#{output_dir}'" if !File.exist?(output_dir) extconf_rb = File.expand_path("#{source_dir}/extconf.rb") sh "cd '#{output_dir}' && #{PlatformInfo.ruby_command} '#{extconf_rb}'" end task 'native_support:clean' do sh "rm -rf #{output_dir}" end end passenger-5.0.30/build/ruby_tests.rb000644 000765 000024 00000003317 12233035540 020041 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. ### Ruby components tests ### dependencies = [NATIVE_SUPPORT_TARGET, AGENT_TARGET].compact desc "Run unit tests for the Ruby libraries" task 'test:ruby' => dependencies do if maybe_grep = string_option('E') require 'shellwords' maybe_grep = "-e #{Shellwords.escape(maybe_grep)}" end command = "rspec -c -f s --tty -P 'dont-autoload-anything' #{maybe_grep} ruby/*_spec.rb ruby/*/*_spec.rb" sh "cd test && exec bundle exec #{command}" end passenger-5.0.30/build/support/000755 000765 000024 00000000000 12233035540 017021 5ustar00honglistaff000000 000000 passenger-5.0.30/build/test_basics.rb000644 000765 000024 00000007341 12233035540 020142 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. TEST_BOOST_OXT_LIBRARY = LIBBOOST_OXT TEST_COMMON_LIBRARY = COMMON_LIBRARY TEST_COMMON_CFLAGS = "-DTESTING_APPLICATION_POOL" desc "Run all unit tests and integration tests" task :test => ['test:oxt', 'test:cxx', 'test:ruby', 'test:node', 'test:integration'] desc "Clean all compiled test files" task 'test:clean' do sh("rm -rf #{TEST_OUTPUT_DIR}") sh("rm -f test/cxx/*.gch") end task :clean => 'test:clean' file "#{TEST_OUTPUT_DIR}allocate_memory" => 'test/support/allocate_memory.c' do compile_c("#{TEST_OUTPUT_DIR}allocate_memory.o", 'test/support/allocate_memory.c') create_c_executable("#{TEST_OUTPUT_DIR}allocate_memory", "#{TEST_OUTPUT_DIR}allocate_memory.o") end desc "Install developer dependencies" task 'test:install_deps' do gem_install = PlatformInfo.gem_command + " install --no-rdoc --no-ri" gem_install = "#{PlatformInfo.ruby_sudo_command} #{gem_install}" if boolean_option('SUDO') default = boolean_option('DEVDEPS_DEFAULT', true) install_base_deps = boolean_option('BASE_DEPS', default) install_doctools = boolean_option('DOCTOOLS', default) if deps_target = string_option('DEPS_TARGET') bundle_args = "--path #{deps_target} #{ENV['BUNDLE_ARGS']}".strip else bundle_args = ENV['BUNDLE_ARGS'].to_s end if !PlatformInfo.locate_ruby_tool('bundle') || bundler_too_old? # workaround for issue "bluecloth not found" when using 1.12.1 sh "#{gem_install} bundler --version 1.11.2" end if install_base_deps && install_doctools sh "bundle install #{bundle_args} --without=" else if install_base_deps sh "bundle install #{bundle_args} --without doc" end if install_doctools sh "bundle install #{bundle_args} --without base" end end if boolean_option('USH_BUNDLES', default) sh "cd src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_core" \ " && bundle install #{bundle_args} --with travis --without doc notravis" sh "cd src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails" \ " && bundle install #{bundle_args} --without doc notravis" sh "cd src/ruby_supportlib/phusion_passenger/vendor/union_station_hooks_rails" \ " && bundle exec rake install_test_app_bundles" \ " BUNDLE_ARGS='#{bundle_args}'" end if boolean_option('NODE_MODULES', default) sh "npm install" end end def bundler_too_old? `bundle --version` =~ /version (.+)/ version = $1.split('.').map { |x| x.to_i } version[0] < 1 || version[0] == 1 && version[1] < 10 end passenger-5.0.30/build/support/cplusplus.rb000644 000765 000024 00000015137 12233035540 021407 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2014 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. # Rake functions for compiling/linking C++ stuff. def run_compiler(*command) PhusionPassenger.require_passenger_lib 'utils/ansi_colors' if !defined?(PhusionPassenger::Utils::AnsiColors) show_command = command.join(' ') puts show_command if !system(*command) if $? && $?.exitstatus == 4 # This probably means the compiler ran out of memory. msg = "" + "-----------------------------------------------\n" + "Your compiler failed with the exit status 4. This " + "probably means that it ran out of memory. To solve " + "this problem, try increasing your swap space: " + "https://www.digitalocean.com/community/articles/how-to-add-swap-on-ubuntu-12-04" + "" fail(PhusionPassenger::Utils::AnsiColors.ansi_colorize(msg)) elsif $? && $?.termsig == 9 msg = "" + "-----------------------------------------------\n" + "Your compiler was killed by the operating system. This " + "probably means that it ran out of memory. To solve " + "this problem, try increasing your swap space: " + "https://www.digitalocean.com/community/articles/how-to-add-swap-on-ubuntu-12-04" + "" fail(PhusionPassenger::Utils::AnsiColors.ansi_colorize(msg)) else fail "Command failed with status (#{$? ? $?.exitstatus : 1}): [#{show_command}]" end end end def build_compiler_flags_from_options_or_flags(options_or_flags) if options_or_flags.is_a?(Hash) result = [] options = options_or_flags (options[:include_paths] || []).each do |path| result << "-I#{path}" end if flags = options[:flags] result.concat([flags].flatten) end result.flatten.reject{ |x| x.nil? || x.empty? }.join(" ") elsif options_or_flags.is_a?(String) options_or_flags elsif options_or_flags.respond_to?(:call) build_compiler_flags_from_options_or_flags(options_or_flags.call) elsif options_or_flags.nil? "" else raise ArgumentError, "Invalid argument type: #{options_or_flags.inspect}" end end def generate_compilation_task_dependencies(source, options = nil) result = [source] if dependencies = CXX_DEPENDENCY_MAP[source] result.concat(dependencies) end if options && options[:deps] result.concat([options[:deps]].flatten.compact) end result end def compile_c(object, source, options_or_flags = nil) flags = build_compiler_flags_from_options_or_flags(options_or_flags) ensure_target_directory_exists(object) run_compiler("#{CC} -o #{object} #{EXTRA_PRE_CFLAGS} #{flags} #{EXTRA_CFLAGS} -c #{source}") end def compile_cxx(object, source, options_or_flags = nil) flags = build_compiler_flags_from_options_or_flags(options_or_flags) ensure_target_directory_exists(object) run_compiler("#{CXX} -o #{object} #{EXTRA_PRE_CXXFLAGS} #{flags} #{EXTRA_CXXFLAGS} -c #{source}") end def create_c_executable(target, objects, options_or_flags = nil) objects = [objects].flatten.join(" ") flags = build_compiler_flags_from_options_or_flags(options_or_flags) ensure_target_directory_exists(target) run_compiler("#{CC} -o #{target} #{objects} #{EXTRA_PRE_C_LDFLAGS} #{flags} #{EXTRA_C_LDFLAGS}") end def create_cxx_executable(target, objects, options_or_flags = nil) objects = [objects].flatten.join(" ") flags = build_compiler_flags_from_options_or_flags(options_or_flags) ensure_target_directory_exists(target) run_compiler("#{CXX} -o #{target} #{objects} #{EXTRA_PRE_CXX_LDFLAGS} #{flags} #{EXTRA_CXX_LDFLAGS}") end def create_static_library(target, objects) # On OS X, 'ar cru' will sometimes fail with an obscure error: # # ar: foo.a is a fat file (use libtool(1) or lipo(1) and ar(1) on it) # ar: foo.a: Inappropriate file type or format # # So here we delete the ar file before creating it, which bypasses this problem. objects = [objects].flatten.join(" ") ensure_target_directory_exists(target) sh "rm -rf #{target}" sh "ar cru #{target} #{objects}" sh "ranlib #{target}" end def create_shared_library(target, objects, options_or_flags = nil) if PlatformInfo.os_name_simple == "macosx" shlib_flag = "-flat_namespace -bundle -undefined dynamic_lookup" else shlib_flag = "-shared" end if PhusionPassenger::PlatformInfo.cxx_is_sun_studio? fPIC = "-KPIC" else fPIC = "-fPIC" end objects = [objects].flatten.join(" ") flags = build_compiler_flags_from_options_or_flags(options_or_flags) ensure_target_directory_exists(target) run_compiler("#{CXX} #{shlib_flag} #{objects} #{fPIC} -o #{target} #{flags}") end def define_c_object_compilation_task(object, source, options_or_flags = nil) options = options_or_flags if options_or_flags.is_a?(Hash) file(object => generate_compilation_task_dependencies(source, options)) do compile_c(object, source, options_or_flags) end end def define_cxx_object_compilation_task(object, source, options_or_flags = nil) options = options_or_flags if options_or_flags.is_a?(Hash) file(object => generate_compilation_task_dependencies(source, options)) do compile_cxx(object, source, options_or_flags) end end def define_c_or_cxx_object_compilation_task(object, source, options_or_flags = nil) if source =~ /\.c$/ define_c_object_compilation_task(object, source, options_or_flags) else define_cxx_object_compilation_task(object, source, options_or_flags) end end passenger-5.0.30/build/support/cxx_dependency_map.rb000644 000765 000024 00001531151 12233035540 023212 0ustar00honglistaff000000 000000 # Autogenerated by dev/index_cxx_dependencies.rb CXX_DEPENDENCY_MAP = {"src/agent/AgentMain.cpp"=> ["src/cxx_supportlib/Constants.h"], "src/agent/Core/ApiServer.h"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/ErrorRenderer.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/Controller.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/Controller/Client.h", "src/agent/Core/Controller/Request.h", "src/agent/Core/Controller/TurboCaching.h", "src/agent/Core/ResponseCache.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApiServerUtils.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/AbstractSession.h"=> ["src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/BasicGroupInfo.h"=> ["src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/BasicProcessInfo.h"=> ["src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Common.h"=> ["src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Context.h"=> ["src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/ErrorRenderer.h"=> ["src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Group.h"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Group/InitializationAndShutdown.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Group/InternalUtils.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Group/LifetimeAndBasics.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Group/Miscellaneous.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Group/OutOfBandWork.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Group/ProcessListManagement.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Group/SessionManagement.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Group/SpawningAndRestarting.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Group/StateInspection.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Group/Verification.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Implementation.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/ErrorRenderer.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Group/InitializationAndShutdown.cpp", "src/agent/Core/ApplicationPool/Group/InternalUtils.cpp", "src/agent/Core/ApplicationPool/Group/LifetimeAndBasics.cpp", "src/agent/Core/ApplicationPool/Group/Miscellaneous.cpp", "src/agent/Core/ApplicationPool/Group/OutOfBandWork.cpp", "src/agent/Core/ApplicationPool/Group/ProcessListManagement.cpp", "src/agent/Core/ApplicationPool/Group/SessionManagement.cpp", "src/agent/Core/ApplicationPool/Group/SpawningAndRestarting.cpp", "src/agent/Core/ApplicationPool/Group/StateInspection.cpp", "src/agent/Core/ApplicationPool/Group/Verification.cpp", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Pool/AnalyticsCollection.cpp", "src/agent/Core/ApplicationPool/Pool/GarbageCollection.cpp", "src/agent/Core/ApplicationPool/Pool/GeneralUtils.cpp", "src/agent/Core/ApplicationPool/Pool/GroupUtils.cpp", "src/agent/Core/ApplicationPool/Pool/InitializationAndShutdown.cpp", "src/agent/Core/ApplicationPool/Pool/Miscellaneous.cpp", "src/agent/Core/ApplicationPool/Pool/ProcessUtils.cpp", "src/agent/Core/ApplicationPool/Pool/StateInspection.cpp", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Options.h"=> ["src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Pool.h"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Pool/AnalyticsCollection.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Pool/GarbageCollection.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Pool/GeneralUtils.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Pool/GroupUtils.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Pool/InitializationAndShutdown.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Pool/Miscellaneous.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Pool/ProcessUtils.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Pool/StateInspection.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Process.h"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Session.h"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/Socket.h"=> ["src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ApplicationPool/TestSession.h"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/Controller.h"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/ErrorRenderer.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/Controller/Client.h", "src/agent/Core/Controller/Request.h", "src/agent/Core/Controller/TurboCaching.h", "src/agent/Core/ResponseCache.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/Controller/AppResponse.h"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/Controller/BufferBody.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/ErrorRenderer.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/Controller.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/Controller/Client.h", "src/agent/Core/Controller/Request.h", "src/agent/Core/Controller/TurboCaching.h", "src/agent/Core/ResponseCache.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/Controller/CheckoutSession.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/ErrorRenderer.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/Controller.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/Controller/Client.h", "src/agent/Core/Controller/Request.h", "src/agent/Core/Controller/TurboCaching.h", "src/agent/Core/ResponseCache.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/Controller/Client.h"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/Controller/Request.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/Controller/ForwardResponse.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/ErrorRenderer.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/Controller.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/Controller/Client.h", "src/agent/Core/Controller/Request.h", "src/agent/Core/Controller/TurboCaching.h", "src/agent/Core/ResponseCache.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/Controller/Hooks.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/ErrorRenderer.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/Controller.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/Controller/Client.h", "src/agent/Core/Controller/Request.h", "src/agent/Core/Controller/TurboCaching.h", "src/agent/Core/ResponseCache.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/Controller/Implementation.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/ErrorRenderer.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/Controller.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/Controller/BufferBody.cpp", "src/agent/Core/Controller/CheckoutSession.cpp", "src/agent/Core/Controller/Client.h", "src/agent/Core/Controller/ForwardResponse.cpp", "src/agent/Core/Controller/Hooks.cpp", "src/agent/Core/Controller/InitRequest.cpp", "src/agent/Core/Controller/InitializationAndShutdown.cpp", "src/agent/Core/Controller/InternalUtils.cpp", "src/agent/Core/Controller/Miscellaneous.cpp", "src/agent/Core/Controller/Request.h", "src/agent/Core/Controller/SendRequest.cpp", "src/agent/Core/Controller/StateInspectionAndConfiguration.cpp", "src/agent/Core/Controller/TurboCaching.h", "src/agent/Core/ResponseCache.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/Controller/InitRequest.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/ErrorRenderer.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/Controller.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/Controller/Client.h", "src/agent/Core/Controller/Request.h", "src/agent/Core/Controller/TurboCaching.h", "src/agent/Core/ResponseCache.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/Controller/InitializationAndShutdown.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/ErrorRenderer.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/Controller.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/Controller/Client.h", "src/agent/Core/Controller/Request.h", "src/agent/Core/Controller/TurboCaching.h", "src/agent/Core/ResponseCache.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/Controller/InternalUtils.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/ErrorRenderer.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/Controller.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/Controller/Client.h", "src/agent/Core/Controller/Request.h", "src/agent/Core/Controller/TurboCaching.h", "src/agent/Core/ResponseCache.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/Controller/Miscellaneous.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/ErrorRenderer.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/Controller.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/Controller/Client.h", "src/agent/Core/Controller/Request.h", "src/agent/Core/Controller/TurboCaching.h", "src/agent/Core/ResponseCache.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/Controller/Request.h"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/Controller/SendRequest.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/ErrorRenderer.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/Controller.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/Controller/Client.h", "src/agent/Core/Controller/Request.h", "src/agent/Core/Controller/TurboCaching.h", "src/agent/Core/ResponseCache.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/Controller/StateInspectionAndConfiguration.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/ErrorRenderer.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/Controller.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/Controller/Client.h", "src/agent/Core/Controller/Request.h", "src/agent/Core/Controller/TurboCaching.h", "src/agent/Core/ResponseCache.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/Controller/TurboCaching.h"=> ["src/agent/Core/ResponseCache.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/CoreMain.cpp"=> ["src/agent/Core/ApiServer.h", "src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/ErrorRenderer.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/Controller.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/Controller/Client.h", "src/agent/Core/Controller/Request.h", "src/agent/Core/Controller/TurboCaching.h", "src/agent/Core/OptionParser.h", "src/agent/Core/ResponseCache.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApiServerUtils.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/agent/Shared/Base.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/BackgroundEventLoop.cpp", "src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/AcceptLoadBalancer.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/OptionParsing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/OptionParser.h"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/OptionParsing.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/ResponseCache.h"=> ["src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp"], "src/agent/Core/SpawningKit/BackgroundIOCapturer.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/SpawningKit/Config.h"=> ["src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/SpawningKit/DirectSpawner.h"=> ["src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/SpawningKit/DummySpawner.h"=> ["src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/SpawningKit/Factory.h"=> ["src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/SpawningKit/Options.h"=> ["src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/SpawningKit/PipeWatcher.h"=> ["src/agent/Core/SpawningKit/Config.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/SpawningKit/Result.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/SpawningKit/SmartSpawner.h"=> ["src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/SpawningKit/Spawner.h"=> ["src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/SpawningKit/UserSwitchingRules.h"=> ["src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/UnionStation/Connection.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/UnionStation/Context.h"=> ["src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Transaction.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/UnionStation/StopwatchLog.h"=> ["src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Transaction.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Core/UnionStation/Transaction.h"=> ["src/agent/Core/UnionStation/Connection.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Shared/ApiServerUtils.h"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Shared/ApplicationPoolApiKey.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Shared/Base.cpp"=> ["src/agent/Shared/Base.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/initialize.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Shared/Base.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/SpawnPreparer/SpawnPreparerMain.cpp"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/SystemMetrics/SystemMetricsMain.cpp"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/TempDirToucher/TempDirToucherMain.cpp"=> ["src/cxx_supportlib/Constants.h"], "src/agent/UstRouter/ApiServer.h"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApiServerUtils.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/agent/UstRouter/Client.h", "src/agent/UstRouter/Controller.h", "src/agent/UstRouter/FileSink.h", "src/agent/UstRouter/LogSink.h", "src/agent/UstRouter/RemoteSender.h", "src/agent/UstRouter/RemoteSink.h", "src/agent/UstRouter/Transaction.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/UnionStationFilterSupport.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BlockingQueue.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/Curl.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ReleaseableScopedPointer.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/UstRouter/Client.h"=> ["src/agent/UstRouter/Transaction.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/UstRouter/Controller.h"=> ["src/agent/UstRouter/Client.h", "src/agent/UstRouter/FileSink.h", "src/agent/UstRouter/LogSink.h", "src/agent/UstRouter/RemoteSender.h", "src/agent/UstRouter/RemoteSink.h", "src/agent/UstRouter/Transaction.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/UnionStationFilterSupport.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BlockingQueue.h", "src/cxx_supportlib/Utils/Curl.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ReleaseableScopedPointer.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/UstRouter/FileSink.h"=> ["src/agent/UstRouter/LogSink.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/UstRouter/LogSink.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/UstRouter/OptionParser.h"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/OptionParsing.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/UstRouter/RemoteSender.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BlockingQueue.h", "src/cxx_supportlib/Utils/Curl.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/UstRouter/RemoteSink.h"=> ["src/agent/UstRouter/LogSink.h", "src/agent/UstRouter/RemoteSender.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BlockingQueue.h", "src/cxx_supportlib/Utils/Curl.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/UstRouter/Transaction.h"=> ["src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/UstRouter/UstRouterMain.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApiServerUtils.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/agent/Shared/Base.h", "src/agent/UstRouter/ApiServer.h", "src/agent/UstRouter/Client.h", "src/agent/UstRouter/Controller.h", "src/agent/UstRouter/FileSink.h", "src/agent/UstRouter/LogSink.h", "src/agent/UstRouter/OptionParser.h", "src/agent/UstRouter/RemoteSender.h", "src/agent/UstRouter/RemoteSink.h", "src/agent/UstRouter/Transaction.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/UnionStationFilterSupport.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BlockingQueue.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/Curl.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/OptionParsing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ReleaseableScopedPointer.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Watchdog/AgentWatcher.cpp"=> [], "src/agent/Watchdog/ApiServer.h"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApiServerUtils.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/agent/Watchdog/CoreWatcher.cpp"=> [], "src/agent/Watchdog/InstanceDirToucher.cpp"=> [], "src/agent/Watchdog/UstRouterWatcher.cpp"=> [], "src/agent/Watchdog/WatchdogMain.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/OptionParser.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApiServerUtils.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/agent/Shared/Base.h", "src/agent/UstRouter/OptionParser.h", "src/agent/Watchdog/AgentWatcher.cpp", "src/agent/Watchdog/ApiServer.h", "src/agent/Watchdog/CoreWatcher.cpp", "src/agent/Watchdog/InstanceDirToucher.cpp", "src/agent/Watchdog/UstRouterWatcher.cpp", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/OptionParsing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/apache2_module/Bucket.cpp"=> ["src/apache2_module/Bucket.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/apache2_module/Bucket.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/apache2_module/Configuration.cpp"=> ["src/apache2_module/Configuration.h", "src/apache2_module/Configuration.hpp", "src/apache2_module/ConfigurationCommands.cpp", "src/apache2_module/ConfigurationFields.hpp", "src/apache2_module/ConfigurationSetters.cpp", "src/apache2_module/CreateDirConfig.cpp", "src/apache2_module/MergeDirConfig.cpp", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/UnionStationFilterSupport.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ReleaseableScopedPointer.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/apache2_module/Configuration.h"=> [], "src/apache2_module/Configuration.hpp"=> ["src/apache2_module/Configuration.h", "src/apache2_module/ConfigurationFields.hpp", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/apache2_module/ConfigurationCommands.cpp"=> [], "src/apache2_module/ConfigurationFields.hpp"=> [], "src/apache2_module/ConfigurationSetters.cpp"=> [], "src/apache2_module/CreateDirConfig.cpp"=> [], "src/apache2_module/DirectoryMapper.h"=> ["src/apache2_module/Configuration.h", "src/apache2_module/Configuration.hpp", "src/apache2_module/ConfigurationFields.hpp", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/apache2_module/Hooks.cpp"=> ["src/apache2_module/Bucket.h", "src/apache2_module/Configuration.h", "src/apache2_module/Configuration.hpp", "src/apache2_module/ConfigurationFields.hpp", "src/apache2_module/DirectoryMapper.h", "src/apache2_module/Hooks.h", "src/apache2_module/SetHeaders.cpp", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MessageClient.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ReleaseableScopedPointer.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/WatchdogLauncher.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/initialize.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/apache2_module/Hooks.h"=> [], "src/apache2_module/MergeDirConfig.cpp"=> [], "src/apache2_module/SetHeaders.cpp"=> [], "src/apache2_module/mod_passenger.c"=> ["src/apache2_module/Configuration.h", "src/apache2_module/Hooks.h"], "src/cxx_supportlib/Algorithms/MovingAverage.h"=> ["src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/AppTypes.cpp"=> ["src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/AppTypes.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/BackgroundEventLoop.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/BackgroundEventLoop.h"=> [], "src/cxx_supportlib/Constants.h"=> [], "src/cxx_supportlib/DataStructures/HashedStaticString.h"=> ["src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/DataStructures/LString.cpp"=> ["src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/DataStructures/LString.h"=> ["src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/DataStructures/StringKeyTable.h"=> ["src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/Exceptions.cpp"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Exceptions.h"=> ["src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/FileDescriptor.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Hooks.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/InstanceDirectory.h"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Integrations/LibevJsonUtils.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Logging.cpp"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Logging.h"=> ["src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp"], "src/cxx_supportlib/LveLoggingDecorator.h"=> ["src/cxx_supportlib/Logging.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp"], "src/cxx_supportlib/MemoryKit/mbuf.cpp"=> ["src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp"], "src/cxx_supportlib/MemoryKit/mbuf.h"=> ["src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/MemoryKit/palloc.cpp"=> ["src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/MemoryKit/palloc.h"=> ["src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/MessageClient.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/MessageReadersWriters.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/RandomGenerator.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/ResourceLocator.h"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/SafeLibev.h"=> ["src/cxx_supportlib/Logging.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp"], "src/cxx_supportlib/ServerKit/AcceptLoadBalancer.h"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/ServerKit/Channel.h"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/ServerKit/Client.h"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/ServerKit/ClientRef.h"=> [], "src/cxx_supportlib/ServerKit/Context.h"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/ServerKit/CookieUtils.h"=> ["src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/ServerKit/Errors.h"=> ["src/cxx_supportlib/ServerKit/http_parser.h"], "src/cxx_supportlib/ServerKit/FdSinkChannel.h"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/ServerKit/FdSourceChannel.h"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/ServerKit/FileBufferedChannel.h"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/ServerKit/HeaderTable.h"=> ["src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/ServerKit/Hooks.h"=> [], "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h"=> ["src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/http_parser.h"], "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h"=> [], "src/cxx_supportlib/ServerKit/HttpClient.h"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/ServerKit/HttpHeaderParser.h"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h"=> ["src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/ServerKit/HttpRequest.h"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/ServerKit/HttpRequestRef.h"=> [], "src/cxx_supportlib/ServerKit/HttpServer.h"=> ["src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/ServerKit/Implementation.cpp"=> ["src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/ServerKit/Server.h"=> ["src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/ServerKit/http_parser.cpp"=> ["src/cxx_supportlib/ServerKit/http_parser.h"], "src/cxx_supportlib/ServerKit/http_parser.h"=> [], "src/cxx_supportlib/StaticString.h"=> ["src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/UnionStationFilterSupport.cpp"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/UnionStationFilterSupport.h", "src/cxx_supportlib/Utils/ReleaseableScopedPointer.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/UnionStationFilterSupport.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/ReleaseableScopedPointer.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils.cpp"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/AnsiColorConstants.h"=> [], "src/cxx_supportlib/Utils/BlockingQueue.h"=> [], "src/cxx_supportlib/Utils/BufferedIO.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/CachedFileStat.cpp"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/CachedFileStat.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/CachedFileStat.h"=> [], "src/cxx_supportlib/Utils/CachedFileStat.hpp"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/ClassUtils.h"=> [], "src/cxx_supportlib/Utils/Curl.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/DateParsing.h"=> ["src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/Utils/FastStringStream.h"=> ["src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/Utils/FileChangeChecker.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/HashMap.h"=> [], "src/cxx_supportlib/Utils/Hasher.cpp"=> ["src/cxx_supportlib/Utils/Hasher.h"], "src/cxx_supportlib/Utils/Hasher.h"=> [], "src/cxx_supportlib/Utils/HttpConstants.h"=> [], "src/cxx_supportlib/Utils/IOUtils.cpp"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/IOUtils.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/IniFile.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/JsonUtils.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/LargeFiles.cpp"=> ["src/cxx_supportlib/Utils/LargeFiles.h"], "src/cxx_supportlib/Utils/LargeFiles.h"=> [], "src/cxx_supportlib/Utils/Lock.h"=> [], "src/cxx_supportlib/Utils/MemZeroGuard.h"=> [], "src/cxx_supportlib/Utils/MemoryBarrier.h"=> [], "src/cxx_supportlib/Utils/MessageIO.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/MessagePassing.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/OptionParsing.h"=> [], "src/cxx_supportlib/Utils/ProcessMetricsCollector.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/ReleaseableScopedPointer.h"=> [], "src/cxx_supportlib/Utils/ScopeGuard.h"=> ["src/cxx_supportlib/Logging.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp"], "src/cxx_supportlib/Utils/SpeedMeter.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/StrIntUtils.cpp"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/StrIntUtils.h"=> ["src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/Utils/StrIntUtilsNoStrictAliasing.cpp"=> ["src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/Utils/StringMap.h"=> ["src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/Utils/StringScanning.h"=> ["src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/Utils/SystemMetricsCollector.h"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/SystemTime.cpp"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/SystemTime.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/Template.h"=> ["src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/Utils/Timer.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/Utils/VariantMap.h"=> ["src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/WatchdogLauncher.cpp"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MessageClient.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/WatchdogLauncher.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/WatchdogLauncher.h"=> ["src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MessageClient.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/oxt/backtrace.hpp"=> ["src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp"], "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp"=> [], "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp"=> [], "src/cxx_supportlib/oxt/detail/context.hpp"=> ["src/cxx_supportlib/oxt/detail/../spin_lock.hpp"], "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp"=> [], "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp"=> [], "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp"=> [], "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp"=> ["src/cxx_supportlib/oxt/detail/../macros.hpp"], "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp"=> [], "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp"=> [], "src/cxx_supportlib/oxt/dynamic_thread_group.hpp"=> ["src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp"], "src/cxx_supportlib/oxt/implementation.cpp"=> ["src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/cxx_supportlib/oxt/initialize.hpp"=> [], "src/cxx_supportlib/oxt/macros.hpp"=> [], "src/cxx_supportlib/oxt/spin_lock.hpp"=> ["src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/oxt/system_calls.cpp"=> ["src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp"], "src/cxx_supportlib/oxt/system_calls.hpp"=> ["src/cxx_supportlib/oxt/macros.hpp"], "src/cxx_supportlib/oxt/thread.hpp"=> ["src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp"], "src/cxx_supportlib/oxt/tracable_exception.hpp"=> ["src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp"], "src/nginx_module/CacheLocationConfig.c"=> [], "src/nginx_module/Configuration.c"=> ["src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/UnionStationFilterSupport.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/ReleaseableScopedPointer.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "src/nginx_module/CacheLocationConfig.c", "src/nginx_module/Configuration.h", "src/nginx_module/ConfigurationCommands.c", "src/nginx_module/ContentHandler.h", "src/nginx_module/CreateLocationConfig.c", "src/nginx_module/LocationConfig.h", "src/nginx_module/MergeLocationConfig.c"], "src/nginx_module/Configuration.h"=> ["src/nginx_module/LocationConfig.h"], "src/nginx_module/ConfigurationCommands.c"=> [], "src/nginx_module/ContentHandler.c"=> ["src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "src/nginx_module/Configuration.h", "src/nginx_module/ContentHandler.h", "src/nginx_module/LocationConfig.h", "src/nginx_module/StaticContentHandler.h"], "src/nginx_module/ContentHandler.h"=> ["src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/nginx_module/CreateLocationConfig.c"=> [], "src/nginx_module/LocationConfig.h"=> [], "src/nginx_module/MergeLocationConfig.c"=> [], "src/nginx_module/StaticContentHandler.c"=> ["src/nginx_module/StaticContentHandler.h"], "src/nginx_module/StaticContentHandler.h"=> [], "src/nginx_module/ngx_http_passenger_module.c"=> ["src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "src/nginx_module/Configuration.h", "src/nginx_module/ContentHandler.h", "src/nginx_module/LocationConfig.h"], "src/nginx_module/ngx_http_passenger_module.h"=> ["src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MessageClient.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/CachedFileStat.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/WatchdogLauncher.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp"], "src/ruby_native_extension/passenger_native_support.c"=> [], "test/cxx/BufferedIOTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/CachedFileStatTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/Core/ApplicationPool/OptionsTest.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/Core/ApplicationPool/PoolTest.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/Core/ApplicationPool/ProcessTest.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/Core/ControllerTest.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/ErrorRenderer.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/ApplicationPool/TestSession.h", "src/agent/Core/Controller.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/Controller/Client.h", "src/agent/Core/Controller/Request.h", "src/agent/Core/Controller/TurboCaching.h", "src/agent/Core/ResponseCache.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/Core/RequestHandlerTest.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/ErrorRenderer.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/Controller.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/Controller/Client.h", "src/agent/Core/Controller/Request.h", "src/agent/Core/Controller/TurboCaching.h", "src/agent/Core/ResponseCache.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/Core/ResponseCacheTest.cpp"=> ["src/agent/Core/ApplicationPool/AbstractSession.h", "src/agent/Core/ApplicationPool/BasicGroupInfo.h", "src/agent/Core/ApplicationPool/BasicProcessInfo.h", "src/agent/Core/ApplicationPool/Common.h", "src/agent/Core/ApplicationPool/Context.h", "src/agent/Core/ApplicationPool/Group.h", "src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/ApplicationPool/Pool.h", "src/agent/Core/ApplicationPool/Process.h", "src/agent/Core/ApplicationPool/Session.h", "src/agent/Core/ApplicationPool/Socket.h", "src/agent/Core/Controller/AppResponse.h", "src/agent/Core/Controller/Request.h", "src/agent/Core/ResponseCache.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/DummySpawner.h", "src/agent/Core/SpawningKit/Factory.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/StopwatchLog.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/Shared/ApplicationPoolApiKey.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/Hooks.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSinkChannel.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/AnsiColorConstants.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/ClassUtils.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/Lock.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/SpeedMeter.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemMetricsCollector.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/Core/SpawningKit/DirectSpawnerTest.cpp"=> ["src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/DirectSpawner.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/Core/SpawningKit/SpawnerTestCases.cpp", "test/cxx/TestSupport.h"], "test/cxx/Core/SpawningKit/SmartSpawnerTest.cpp"=> ["src/agent/Core/ApplicationPool/Options.h", "src/agent/Core/SpawningKit/BackgroundIOCapturer.h", "src/agent/Core/SpawningKit/Config.h", "src/agent/Core/SpawningKit/Options.h", "src/agent/Core/SpawningKit/PipeWatcher.h", "src/agent/Core/SpawningKit/Result.h", "src/agent/Core/SpawningKit/SmartSpawner.h", "src/agent/Core/SpawningKit/Spawner.h", "src/agent/Core/SpawningKit/UserSwitchingRules.h", "src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/cxx_supportlib/AppTypes.h", "src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/LveLoggingDecorator.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/Core/SpawningKit/SpawnerTestCases.cpp", "test/cxx/TestSupport.h"], "test/cxx/Core/SpawningKit/SpawnerTestCases.cpp"=> [], "test/cxx/Core/UnionStationTest.cpp"=> ["src/agent/Core/UnionStation/Connection.h", "src/agent/Core/UnionStation/Context.h", "src/agent/Core/UnionStation/Transaction.h", "src/agent/UstRouter/Client.h", "src/agent/UstRouter/Controller.h", "src/agent/UstRouter/FileSink.h", "src/agent/UstRouter/LogSink.h", "src/agent/UstRouter/RemoteSender.h", "src/agent/UstRouter/RemoteSink.h", "src/agent/UstRouter/Transaction.h", "src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/MessageClient.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/UnionStationFilterSupport.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BlockingQueue.h", "src/cxx_supportlib/Utils/Curl.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ReleaseableScopedPointer.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/CxxTestMain.cpp"=> ["src/agent/Shared/Base.h", "src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/initialize.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/../tut/tut_reporter.h", "test/cxx/TestSupport.h"], "test/cxx/DataStructures/LStringTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/DataStructures/StringKeyTableTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/StringKeyTable.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/DateParsingTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/DateParsing.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/FileChangeCheckerTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/CachedFileStat.hpp", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/FileChangeChecker.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/FileDescriptorTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/FilterSupportTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/UnionStationFilterSupport.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/ReleaseableScopedPointer.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/IOUtilsTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/MemoryKit/MbufTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/MemoryKit/PallocTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/MessageIOTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/MessagePassingTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/MessagePassing.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Timer.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/MessageReadersWritersTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MessageReadersWriters.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/ProcessMetricsCollectorTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/ProcessMetricsCollector.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringScanning.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/ServerKit/ChannelTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/ServerKit/CookieUtilsTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/ServerKit/CookieUtils.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/ServerKit/FileBufferedChannelTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/ServerKit/HeaderTableTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/ServerKit/HttpServerTest.cpp"=> ["src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/HashedStaticString.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Integrations/LibevJsonUtils.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/HeaderTable.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParser.h", "src/cxx_supportlib/ServerKit/HttpChunkedBodyParserState.h", "src/cxx_supportlib/ServerKit/HttpClient.h", "src/cxx_supportlib/ServerKit/HttpHeaderParser.h", "src/cxx_supportlib/ServerKit/HttpHeaderParserState.h", "src/cxx_supportlib/ServerKit/HttpRequest.h", "src/cxx_supportlib/ServerKit/HttpRequestRef.h", "src/cxx_supportlib/ServerKit/HttpServer.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/BufferedIO.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/HttpConstants.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/ServerKit/ServerTest.cpp"=> ["src/cxx_supportlib/Algorithms/MovingAverage.h", "src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/SafeLibev.h", "src/cxx_supportlib/ServerKit/Channel.h", "src/cxx_supportlib/ServerKit/Client.h", "src/cxx_supportlib/ServerKit/ClientRef.h", "src/cxx_supportlib/ServerKit/Context.h", "src/cxx_supportlib/ServerKit/Errors.h", "src/cxx_supportlib/ServerKit/FdSourceChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedChannel.h", "src/cxx_supportlib/ServerKit/FileBufferedFdSinkChannel.h", "src/cxx_supportlib/ServerKit/Hooks.h", "src/cxx_supportlib/ServerKit/Server.h", "src/cxx_supportlib/ServerKit/http_parser.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/StaticStringTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/StringMapTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/SystemTimeTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/TemplateTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/HashMap.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/StringMap.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/Template.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/TestSupport.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../support/valgrind.h", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/TestSupport.h"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h"], "test/cxx/UstRouter/TransactionTest.cpp"=> ["src/agent/UstRouter/Transaction.h", "src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/DataStructures/LString.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/MemoryKit/mbuf.h", "src/cxx_supportlib/MemoryKit/palloc.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/Hasher.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/JsonUtils.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/Utils/StrIntUtilsTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/UtilsTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/cxx/VariantMapTest.cpp"=> ["src/cxx_supportlib/BackgroundEventLoop.h", "src/cxx_supportlib/Constants.h", "src/cxx_supportlib/Exceptions.h", "src/cxx_supportlib/FileDescriptor.h", "src/cxx_supportlib/InstanceDirectory.h", "src/cxx_supportlib/Logging.h", "src/cxx_supportlib/RandomGenerator.h", "src/cxx_supportlib/ResourceLocator.h", "src/cxx_supportlib/StaticString.h", "src/cxx_supportlib/Utils.h", "src/cxx_supportlib/Utils/FastStringStream.h", "src/cxx_supportlib/Utils/IOUtils.h", "src/cxx_supportlib/Utils/IniFile.h", "src/cxx_supportlib/Utils/LargeFiles.h", "src/cxx_supportlib/Utils/MemZeroGuard.h", "src/cxx_supportlib/Utils/MessageIO.h", "src/cxx_supportlib/Utils/ScopeGuard.h", "src/cxx_supportlib/Utils/StrIntUtils.h", "src/cxx_supportlib/Utils/SystemTime.h", "src/cxx_supportlib/Utils/VariantMap.h", "src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/cxx/../tut/tut.h", "test/cxx/TestSupport.h"], "test/oxt/backtrace_test.cpp"=> ["src/cxx_supportlib/oxt/backtrace.hpp", "src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/backtrace_disabled.hpp", "src/cxx_supportlib/oxt/detail/backtrace_enabled.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_disabled.hpp", "src/cxx_supportlib/oxt/detail/tracable_exception_enabled.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "src/cxx_supportlib/oxt/tracable_exception.hpp", "test/oxt/../tut/tut.h", "test/oxt/counter.hpp"], "test/oxt/counter.hpp"=> [], "test/oxt/dynamic_thread_group_test.cpp"=> ["src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/dynamic_thread_group.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "test/oxt/../tut/tut.h", "test/oxt/counter.hpp"], "test/oxt/oxt_test_main.cpp"=> ["src/cxx_supportlib/oxt/initialize.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "test/oxt/../tut/tut.h", "test/oxt/../tut/tut_reporter.h"], "test/oxt/spin_lock_test.cpp"=> ["src/cxx_supportlib/oxt/detail/../macros.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_darwin.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_gcc_x86.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_portable.hpp", "src/cxx_supportlib/oxt/detail/spin_lock_pthreads.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/spin_lock.hpp", "test/oxt/../tut/tut.h"], "test/oxt/syscall_interruption_test.cpp"=> ["src/cxx_supportlib/oxt/detail/../spin_lock.hpp", "src/cxx_supportlib/oxt/detail/context.hpp", "src/cxx_supportlib/oxt/macros.hpp", "src/cxx_supportlib/oxt/system_calls.hpp", "src/cxx_supportlib/oxt/thread.hpp", "test/oxt/../tut/tut.h"]} passenger-5.0.30/build/support/general.rb000644 000765 000024 00000007773 12233035540 021001 0ustar00honglistaff000000 000000 # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2016 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. class TemplateRenderer def initialize(filename) require 'erb' if !defined?(ERB) @erb = ERB.new(File.read(filename), nil, "-") @erb.filename = filename end def render return @erb.result(binding) end def render_to(filename) puts "Creating #{filename}" text = render # When packaging, some timestamps may be modified. The user may not # have write access to the source root (for example, when Passenger # Standalone is compiling its runtime), so we only write to the file # when necessary. if !File.exist?(filename) || File.writable?(filename) || File.read(filename) != text File.open(filename, 'w') do |f| f.write(text) end end end end class CxxCodeTemplateRenderer def initialize(filename) if !defined?(CxxCodeBuilder) require 'build/support/vendor/cxxcodebuilder/lib/cxxcodebuilder' end code = File.open(filename, 'rb') do |f| f.read end @builder = CxxCodeBuilder::Builder.new @builder.instance_eval(code, filename) end def render @builder.to_s end def render_to(filename) puts "Creating #{filename}" text = render # When packaging, some timestamps may be modified. The user may not # have write access to the source root (for example, when Passenger # Standalone is compiling its runtime), so we only write to the file # when necessary. if !File.exist?(filename) || File.writable?(filename) || File.read(filename) != text File.open(filename, 'w') do |f| f.write(text) end end end end class Pathname if !method_defined?(:/) def /(other) self + other.to_s end end end def string_option(name, default_value = nil) value = ENV[name] if value.nil? || value.empty? if default_value.respond_to?(:call) default_value.call else default_value end else value end end def pathname_option(name, default_value) Pathname.new(string_option(name, default_value)) end def compiler_flag_option(name, default_value = '') string_option(name, default_value).gsub("\n", " ") end def boolean_option(name, default_value = false) value = ENV[name] if value.nil? || value.empty? default_value else value == "yes" || value == "on" || value == "true" || value == "1" end end def maybe_wrap_in_ccache(command) if boolean_option('USE_CCACHE', false) && command !~ /^ccache / "ccache #{command}" else command end end def copyright_header_for(filename) contents = File.open(filename, 'rb') do |f| f.read end contents =~ /\A(#.+?)\n\n/m $1.gsub(/^# */, '') end def ensure_target_directory_exists(target) dir = File.dirname(target) if !File.exist?(dir) sh "mkdir -p #{dir}" end end def shesc(path) Shellwords.escape(path) end passenger-5.0.30/build/support/vendor/000755 000765 000024 00000000000 12233035540 020316 5ustar00honglistaff000000 000000 passenger-5.0.30/build/support/vendor/cxxcodebuilder/000755 000765 000024 00000000000 12233035540 023322 5ustar00honglistaff000000 000000 passenger-5.0.30/build/support/vendor/cxxcodebuilder/CxxCodeBuilder.sublime-project000644 000765 000024 00000000055 12233035540 031214 0ustar00honglistaff000000 000000 { "folders": [ { "path": "." } ] } passenger-5.0.30/build/support/vendor/cxxcodebuilder/Gemfile000644 000765 000024 00000000067 12233035540 024620 0ustar00honglistaff000000 000000 source 'https://rubygems.org/' gem 'rake' gem 'rspec' passenger-5.0.30/build/support/vendor/cxxcodebuilder/Gemfile.lock000644 000765 000024 00000001021 12233035540 025536 0ustar00honglistaff000000 000000 GEM remote: https://rubygems.org/ specs: diff-lcs (1.2.5) rake (11.1.2) rspec (3.4.0) rspec-core (~> 3.4.0) rspec-expectations (~> 3.4.0) rspec-mocks (~> 3.4.0) rspec-core (3.4.4) rspec-support (~> 3.4.0) rspec-expectations (3.4.0) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.4.0) rspec-mocks (3.4.1) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.4.0) rspec-support (3.4.1) PLATFORMS ruby DEPENDENCIES rake rspec BUNDLED WITH 1.11.2 passenger-5.0.30/build/support/vendor/cxxcodebuilder/lib/000755 000765 000024 00000000000 12233035540 024070 5ustar00honglistaff000000 000000 passenger-5.0.30/build/support/vendor/cxxcodebuilder/LICENSE.md000644 000765 000024 00000002050 12233035540 024723 0ustar00honglistaff000000 000000 Copyright (c) 2016 Phusion Holding B.V. 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, sublicense, 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 NONINFRINGEMENT. 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. passenger-5.0.30/build/support/vendor/cxxcodebuilder/Rakefile000644 000765 000024 00000000120 12233035540 024760 0ustar00honglistaff000000 000000 desc "Run test suite" task :test do sh "bundle exec rspec spec/*_spec.rb" end passenger-5.0.30/build/support/vendor/cxxcodebuilder/README.md000644 000765 000024 00000003717 12233035540 024611 0ustar00honglistaff000000 000000 # cxxcodebuilder: generate C/C++ code with a Builder-style DSL Cxxcodebuilder gives you a simple Ruby API for programmatically outputting C/C++ code with proper indenting and formatting. Code using this API is much more readable than code building raw strings. Cxxcodebuilder is similar to [Jbuilder](https://github.com/rails/jbuilder), which is for outputting JSON. ## Use cases This library is useful in build systems for automatically generating C/C++ code. It is much cleaner compared to using ERB or other generic text templating systems for the job. ## Example Suppose that you want to write the following piece of code: ~~~c++ #include static int limit = 0; static int magicNumbers[] = [1, 2, 3]; static Foo foos[] = [ { "hello", 1 }, { "world", 2 } ]; /* * This is an awesome model * for a futuristic car. */ struct Car { unsigned int seats; }; static int modifyLimit(int diff) { int oldLimit = limit; limit += diff; printf("The new limit is: %s\n", limit); return oldLimit; } ~~~ Use Cxxcodebuilder as follows: ~~~ruby require 'cxxcodebuilder' builder = CxxCodeBuilder::Builder.new do include '' separator field 'static int limit', 0 field 'static int magicNumbers[]' do array_initializer do element 1 element 2 element 3 end end field 'static Foo foo[]' do array_initializer do struct_element do string_element 'hello' element 1 end struct_element do string_element 'world' element 2 end end end separator comment %q{ This is an awesome model for a futuristic car. } struct 'Car' do field 'unsigned int seats' end separator function('static int modifyLimit(int diff)', %q{ int oldLimit = limit; limit += diff; printf("The new limit is: %s\n", limit); return oldLimit; }) end puts(builder) ~~~ ## API See the comments in lib/cxxcodebuilder/builder.rb for the full API. passenger-5.0.30/build/support/vendor/cxxcodebuilder/lib/cxxcodebuilder/000755 000765 000024 00000000000 12233035540 027074 5ustar00honglistaff000000 000000 passenger-5.0.30/build/support/vendor/cxxcodebuilder/lib/cxxcodebuilder.rb000644 000765 000024 00000002365 12233035540 027427 0ustar00honglistaff000000 000000 # Copyright (c) 2016 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require File.expand_path(File.dirname(__FILE__) + '/cxxcodebuilder/builder') passenger-5.0.30/build/support/vendor/cxxcodebuilder/lib/cxxcodebuilder/builder.rb000644 000765 000024 00000036105 12233035540 031054 0ustar00honglistaff000000 000000 # encoding: utf-8 # Copyright (c) 2016 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. require File.expand_path(File.dirname(__FILE__) + '/initializer_builder') module CxxCodeBuilder # Builds C/C++ code. Use it as follows: # # 1. Create a CxxCodeBuilder::Builder object. # 2. Call API methods in the Builder object to generate C/C++ code. # 3. When done, call `#to_s` to obtain the generated code. # # There are two ways to use CxxCodeBuilder::Builder: # # 1. By passing a block to the constructor. The block is evaluated in # the context of the builder object, so inside the block you have # access to all the API methods of the Builder class. See README.md # for an example of this usage. # 2. By calling the API methods of the Builder class, without passing # a block to the constructor. # # ## Internal buffer # # Builder has an internal buffer containing the code generated so far. # Most API methods, such as `#function` and `#field`, generate new code # and append to this internal buffer. `#to_s` returns the contents of # the buffer. # # ## Indentation # # Builder keeps track of the current indentation level and generates # code accordingly. You can temporarily increase it with the `indent` # method. Builder generates tabs for indentation. class Builder def initialize(&block) @indent_string = "\t" @indent_level = 0 @code = "" if block instance_eval(&block) end end # Customizes the indentation string. The default is a tab, # but you can use this to set it to e.g. 4 spaces. You should # call this method as early as possible because it won't # affect the indentation of already generated code. def set_indent_string(str) @indent_string = str end # Adds some code to the internal buffer. Before adding to the internal # buffer, extraneous indentation and leading and trailing empty lines in # `code` are removed, and new indentation is added based on the current # indentation level. The code is suffixed with a newline. # # # Example 1: # # add_code 'foo();' # # Output 1: # # foo(); # # # Example 2: # # # The argument contains extraneous leading and trailing empty lines, # # as well as extraneous indenting. # add_code %q{ # foo(); # if (true) { # bar(); # } # } # # Output 2 (extraneous leading/trailing newlines and extraneous indenting removed): # # foo(); # if (true) { # bar(); # } def add_code(code) add_code_without_newline(code) newline end # Like `#add_code`, but does not suffix the generated code with a newline. def add_code_without_newline(code) @code << reindent(unindent(code.to_s), @indent_string * @indent_level, true) end # Adds some raw code to the internal buffer. Unlike `#add_code`, no # preprocessing is performed to remove extraneous newlines or indenting. def add_raw_code(code) @code << code end # Temporarily increase the indentation level by 1. This new indentation # level is only active inside the given block. # # Example: # # add_code 'foo();' # # indent do # add_code 'bar();' # end # # Output: # # foo(); # bar(); def indent @indent_level += 1 begin yield ensure @indent_level -= 1 end end # Adds a newline to the internal buffer. def separator @code << "\n" end alias newline separator # Adds an `#include` statement to the internal buffer. # `header_name` is verbatim added to the statement, so you # need to pass either `""` or `'"header_name.h"'` # as argument. # # Example: # # include '' # include '"Config.h"' # # Output: # # #include # #include "Config.h" def include(header_name) add_code("#include #{header_name}") end # Adds a `#define` statement to the internal buffer. # `macro` is verbatim added to the statement. If you want # to #define a string constant then you should use the # `#define_string` method. # # Example: # # define 'HAVE_STDINT_H' # define 'FOO bar' # define 'TWO 1 + 2' # # Output: # # #define HAVE_STDINT_H # #define FOO bar # #define TWO 1 + 2 def define(macro) add_code("#define #{macro}") end # Adds a `#define` statement to the internal buffer for defining # a string macro. # # Example: # # define 'NAME', 'Joe Dalton' # # Output: # # #define NAME "Joe Dalton" def define_string(name, value) define("#{name} #{str_val(value)}") end # Adds header guard macros to the internal buffer. Expects # a block which generates the code to insert inside the guard. # # Example: # # guard_macros 'MY_HEADER_H' do # field 'int foo' # end # # Output: # # #ifndef MY_HEADER_H # #define MY_HEADER_H # # int foo; # # #endif /* MY_HEADER_H */ def guard_macros(name) add_code("#ifndef #{name}") define(name) separator yield separator add_code("#endif /* #{name} */") end # Adds a comment to the internal buffer. Before adding to the internal # buffer, extraneous indentation and leading and trailing empty lines in # `text` are removed, and new indentation is added based on the current # indentation level. The text is also prefixed with the '*' character. # # Example: # # comment "hello\nworld" # comment %q{ # foo # bar # } # # Output: # # /* # * hello # * world # */ # /* # * foo # * bar # */ def comment(text) add_code '/*' prefix = @indent_string * @indent_level prefix << ' * ' @code << reindent(unindent(text.to_s), prefix, false) @code << "\n" add_code_without_newline '-' @code.gsub!(/-\Z/, ' ') add_raw_code "*/\n" end # Adds a struct definition to the internal buffer. Expects a block # in which you must define the struct's contents. Inside the block # you can use any Builder API methods, but you are most likely # interested in `#member`, `#comment`, `#separator` and `#function`. # # Example: # # struct 'Car' do # comment "The car's name" # member 'string name' # # separator # member 'unsigned int seats' # end # # Outputs: # # struct Car { # /* # * The car's name. # */ # string name; # # unsigned int seats; # }; def struct(name) add_code "struct #{name} {" indent do yield end add_code '};' end # Adds a struct typedef definition to the internal buffer. This works # like the `#struct` method, but outputs a typedef struct instead. # # Example: # # typedef_struct 'Car' do # comment "The car's name" # member 'string name' # # separator # member 'unsigned int seats' # end # # Outputs: # # typedef struct { # /* # * The car's name. # */ # string name; # # unsigned int seats; # } Car; def typedef_struct(name) add_code 'typedef struct {' indent do yield end add_code "} #{name};" end # Adds a function definition to the internal buffer. There are two ways to # supply the function body. The first is by passing a string. The second is # by passing a block, which is expected to use Builder API methods to # generate code for the body. # # If a string is passed, then extraneous indentation and leading and trailing # empty lines inside it are removed. # # No matter how the body is body is supplied, the generated body is indented. # # Example: # # function 'static void foo(int x)', %q{ # printf("x = %d\n", x); # } # # function 'static void bar(int x)' do # add_code 'printf("x = %d\n", x);' # end # # Output: # # static void # foo(int x) { # printf("x = %d\n", x); # } # # static void # bar(int x) { # printf("x = %d\n", x); # } def function(declaration, body = nil) declaration =~ /(.*?)([a-z0-9_:]+)\s*\((.*)\s*(const)?/mi return_type_and_attributes = $1 name_and_params = "#{$2}(#{$3} #{$4}".strip add_code return_type_and_attributes.strip add_code "#{name_and_params.strip} {" indent do if block_given? yield else add_code body end end add_code '}' separator end # Adds a field/member/variable definition to the internal buffer. # You can optionally supply a value, either by passing it directly as # an argument, or by passing a block which will generate the code for # the value. # # When supplying an argument, the argument is added verbatim to the # internal buffer, so you can even supply an expression. If you want to # set the value to a string, then you should use the `#str_val` helper # method. See the example below. # # The block form is especially useful for generating # array/struct initializer code (see also `#array_initializer` and # `#struct_initializer` in that case). # # Example: # # field('int a') # field('int b', 123); # field('int c', '1 + 2') # field('const char *str', str_val("hello world")); # # separator # # field('int magicNumbers[]') do # array_initializer do # element 1 # element 2 # end # end # # separator # # field('const char *magicStrings[]') do # array_initializer do # string_element "foo" # # Equivalent: # element str_val("foo") # end # end # # Output: # # int a; # int b = 123; # int c = 1 + 2; # const char *str = "hello world"; # # int magicNumbers[] = [ # 1, # 2 # ]; # # const char *magicStrings[] = [ # "foo", # "foo" # ]; # def field(declaration, value = nil) if block_given? add_code_without_newline "#{declaration} =" add_raw_code ' ' yield @code.gsub!(/\n*\Z/m, '') add_raw_code ';' newline elsif value add_code "#{declaration} = #{value};" else add_code "#{declaration};" end end alias variable field alias member field # Adds an array initializer (in the form of `[x, y, z]`) to the internal # buffer. Expects a block which defines the elements inside the array. # The block does not expose the Builder API methods, but exposes the # InitializerBuilder API methods instead. See the comments in # initializer_builder.rb for an example and to learn what API methods # are available. # # Does not add a trailing newline. # # Example: # # array_initializer do # element 1 # element 2 # end # # separator # # array_initializer do # string_element "foo" # # Equivalent: # element str_val("foo") # end # # Output: # # [ # 1, # 2 # ] # # [ # "foo", # "foo" # ] def array_initializer(&block) subbuilder = InitializerBuilder.new(self, '[', ']') subbuilder.instance_eval(&block) subbuilder.write_code_without_newline end # Adds a struct initializer (in the form of `{x, y, z}`) to the internal # buffer. Expects a block which defines the elements inside the array. # The block does not expose the Builder API methods, but exposes the # InitializerBuilder API methods instead. See the comments in # initializer_builder.rb for an example and to learn what API methods # are available. # # Does not add a trailing newline. # # Example: # # struct_initializer do # element 1 # element 2 # end # # separator # # struct_initializer do # string_element "foo" # # Equivalent: # element str_val("foo") # end # # Output: # # { # 1, # 2 # } # # { # "foo", # "foo" # } def struct_initializer(&block) subbuilder = InitializerBuilder.new(self, '{', '}') subbuilder.instance_eval(&block) subbuilder.write_code_without_newline end # Returns (and does not modify the internal buffer!) a C string representation # of `str`. This is especially useful for supplying a string value to `#field`. def str_val(str) str.to_s.inspect end def to_s @code end private def unindent(str) str = str.dup str.gsub!(/\A([\s\t]*\n)+/, '') str.gsub!(/[\s\t\n]+\Z/, '') indent = str.split("\n").select{ |line| !line.strip.empty? }.map{ |line| line.index(/[^\s]/) }.compact.min || 0 str.gsub!(/^[[:blank:]]{#{indent}}/, '') str end def reindent(str, prefix, convert_ruby_indentation) str = unindent(str) # Convert Ruby two-space indentation to our own indentation format if convert_ruby_indentation str.gsub!(/^( )+/) do |match| @indent_string * (match.size / 2) end end # Prepend supplied prefix to each line str.gsub!(/^/, prefix) # Remove trailing whitespaces str.gsub!(/[ \t]+$/, '') str end end end passenger-5.0.30/build/support/vendor/cxxcodebuilder/lib/cxxcodebuilder/initializer_builder.rb000644 000765 000024 00000011120 12233035540 033445 0ustar00honglistaff000000 000000 # encoding: utf-8 # Copyright (c) 2016 Phusion Holding B.V. # # "Union Station" and "Passenger" are trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. module CxxCodeBuilder # Builds array and struct initialization code. class InitializerBuilder def initialize(builder, start_character, end_character) @builder = builder @start_character = start_character @end_character = end_character @elements = [] end # Adds an array/struct initializer element to the internal buffer. # The code is added verbatim, so you can supply expressions. If you # want to add a string element, use `#string_element` instead. # # Example: # # array_initializer do # element '1 + 2' # end # # Output: # # [ # 1 + 2 # ] def element(code) @elements << code end # Adds an array/struct initializer string element to the internal buffer. # # Example: # # array_initializer do # string_element 'hello world' # end # # Output: # # [ # "hello world" # ] def string_element(str) element(str.inspect) end # Adds an array initializer (in the form of `[x, y, z]`) to the internal # buffer. This works exactly the same as `Builder#array_initializer`, and # allows you to nest an array initializer inside a parent array/struct # initializer. # # Example: # # struct_initializer do # array_initializer_element do # element 1 # element 2 # end # array_initializer_element do # element 3 # element 4 # end # end # # Output: # # { # [ # 1, # 2 # ], # [ # 3, # 4 # ] # } def array_initializer_element(&block) subbuilder = InitializerBuilder.new(@builder, '[', ']') subbuilder.instance_eval(&block) @elements << subbuilder end # Adds a struct initializer (in the form of `{x, y, z}`) to the internal # buffer. This works exactly the same as `Builder#struct_initializer`, and # allows you to nest a struct initializer inside a parent array/struct # initializer. # # Example: # # array_initializer do # struct_initializer_element do # string_element 'Joe' # element 1 # end # struct_initializer_element do # string_element 'Jane' # element 2 # end # end # # Output: # # [ # { # "Joe", # 1 # }, # { # "Jane", # 2 # } # ] def struct_initializer_element(&block) subbuilder = InitializerBuilder.new(@builder, '{', '}') subbuilder.instance_eval(&block) @elements << subbuilder end alias array_element array_initializer_element alias struct_element struct_initializer_element # @private def write_code_without_newline @builder.add_code(@start_character) @builder.indent do @elements.each_with_index do |elem, i| if elem.respond_to?(:write_code_without_newline) elem.write_code_without_newline else @builder.add_code_without_newline(elem) end if i != @elements.size - 1 @builder.add_raw_code(',') end @builder.newline end end @builder.add_code_without_newline(@end_character) end end end passenger-5.0.30/bin/passenger000755 000765 000024 00000003467 12233035540 016705 0ustar00honglistaff000000 000000 #!/usr/bin/env ruby # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. ## Magic comment: begin bootstrap ## source_root = File.expand_path("..", File.dirname(__FILE__)) $LOAD_PATH.unshift("#{source_root}/src/ruby_supportlib") begin require 'rubygems' rescue LoadError end require 'phusion_passenger' ## Magic comment: end bootstrap ## PhusionPassenger.locate_directories PhusionPassenger.require_passenger_lib 'standalone/main' STDOUT.sync = STDERR.sync = true # Fixes https://github.com/phusion/passenger-ruby-heroku-demo/issues/11 STDOUT.binmode STDERR.binmode PhusionPassenger::Standalone.run!(ARGV) passenger-5.0.30/bin/passenger-config000755 000765 000024 00000003245 12233035540 020142 0ustar00honglistaff000000 000000 #!/usr/bin/env ruby # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. ## Magic comment: begin bootstrap ## source_root = File.expand_path("..", File.dirname(__FILE__)) $LOAD_PATH.unshift("#{source_root}/src/ruby_supportlib") begin require 'rubygems' rescue LoadError end require 'phusion_passenger' ## Magic comment: end bootstrap ## PhusionPassenger.locate_directories PhusionPassenger.require_passenger_lib 'config/main' PhusionPassenger::Config.run!(ARGV) passenger-5.0.30/bin/passenger-install-apache2-module000755 000765 000024 00000103634 12233035540 023132 0ustar00honglistaff000000 000000 #!/usr/bin/env ruby # encoding: binary # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. ## Magic comment: begin bootstrap ## source_root = File.expand_path("..", File.dirname(__FILE__)) $LOAD_PATH.unshift("#{source_root}/src/ruby_supportlib") begin require 'rubygems' rescue LoadError end require 'phusion_passenger' ## Magic comment: end bootstrap ## PhusionPassenger.locate_directories # The Apache executable may be located in an 'sbin' folder. We add # the 'sbin' folders to $PATH just in case. On some systems # 'sbin' isn't in $PATH unless the user is logged in as root from # the start (i.e. not via 'su' or 'sudo'). EXTRA_INSTALLER_PATHS = ":/usr/sbin:/sbin:/usr/local/sbin" ENV["PATH"] += EXTRA_INSTALLER_PATHS require 'optparse' require 'stringio' PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'platform_info/ruby' PhusionPassenger.require_passenger_lib 'platform_info/operating_system' PhusionPassenger.require_passenger_lib 'platform_info/linux' PhusionPassenger.require_passenger_lib 'platform_info/apache' PhusionPassenger.require_passenger_lib 'platform_info/apache_detector' PhusionPassenger.require_passenger_lib 'abstract_installer' PhusionPassenger.require_passenger_lib 'config/validate_install_command' PhusionPassenger.require_passenger_lib 'utils/terminal_choice_menu' class Installer < PhusionPassenger::AbstractInstaller include PhusionPassenger TerminalChoiceMenu = PhusionPassenger::Utils::TerminalChoiceMenu AUTOINSTALL_BEGIN_LOAD_BLOCK = "### Begin automatically installed #{PROGRAM_NAME} load snippet ###" AUTOINSTALL_END_LOAD_BLOCK = "### End automatically installed #{PROGRAM_NAME} load snippet ###" AUTOINSTALL_BEGIN_CONF_BLOCK = "### Begin automatically installed #{PROGRAM_NAME} config snippet ###" AUTOINSTALL_END_CONF_BLOCK = "### End automatically installed #{PROGRAM_NAME} config snippet ###" def dependencies specs = [ 'depcheck_specs/compiler_toolchain', 'depcheck_specs/ruby', 'depcheck_specs/gems', 'depcheck_specs/libs', 'depcheck_specs/apache2' ] ids = [ 'cc', 'c++', 'libcurl-dev', 'zlib-dev', 'apache2', 'apache2-dev', 'rake', 'ruby-openssl', 'rubygems' ] if @languages.include?("ruby") if PlatformInfo.passenger_needs_ruby_dev_header? ids << 'ruby-dev' end ids << 'rack' end # Some broken servers don't have apr-config or apu-config installed. # Nevertheless, it is possible to compile Apache modules if Apache # was configured with --included-apr. So here we check whether # apr-config and apu-config are available. If they're not available, # then we only register them as required dependency if no Apache # module can be compiled without their presence. if (PlatformInfo.apr_config && PlatformInfo.apu_config) || PlatformInfo.apr_config_needed_for_building_apache_modules? ids << 'apr-dev' ids << 'apu-dev' end return [specs, ids] end def install_doc_url "https://www.phusionpassenger.com/library/install/apache/" end def troubleshooting_doc_url "https://www.phusionpassenger.com/library/admin/apache/troubleshooting/" end def run_steps if PhusionPassenger.build_system_dir.nil? # Invariant: PhusionPassenger.custom_packaged? if apache_module_available? notify_apache_module_installed show_deployment_example else install_apache_module_from_native_package || exit(1) end exit end Dir.chdir(PhusionPassenger.build_system_dir) show_welcome_screen check_selinux_and_suggest_rpm query_interested_languages check_gem_install_permission_problems || exit(1) check_directory_accessible_by_web_server check_dependencies || exit(1) check_whether_there_are_multiple_apache_installs || exit check_whether_apache_uses_compatible_mpm check_whether_os_is_broken check_whether_system_has_enough_ram check_write_permission_to_passenger_root || exit(1) check_write_permission_to_web_server_config_files || exit(1) if compile_apache2_module install_apache2_config_snippets || exit(1) validate_install show_deployment_example else show_possible_solutions_for_compilation_and_installation_problems exit(1) end end private def show_welcome_screen render_template 'apache2/welcome', :version => VERSION_STRING wait end def check_selinux_and_suggest_rpm return if !PhusionPassenger.originally_packaged? || PlatformInfo.os_name_simple != "linux" || !(PlatformInfo.linux_distro_tags.include?(:rhel) || PlatformInfo.linux_distro_tags.include?(:centos)) !PlatformInfo.find_command('sestatus') begin sestatus = `sestatus` getenforce = `getenforce` rescue Errno::ENOENT return end status = sestatus.scan(/^SELinux status:.*/).first if status =~ /enabled/ && getenforce =~ /Enforcing/ new_screen render_template 'apache2/rpm_installation_recommended' wait end end def query_interested_languages menu = TerminalChoiceMenu.new(["Ruby", "Python", "Node.js", "Meteor"]) menu["Ruby"].checked = interesting_language?('ruby') menu["Python"].checked = interesting_language?('python') menu["Node.js"].checked = interesting_language?('nodejs', 'node') menu["Meteor"].checked = interesting_language?('meteor') new_screen puts "Which languages are you interested in?" puts if interactive? puts "Use to select." puts "If the menu doesn't display correctly, press '!'" else puts "Override selection with --languages." end puts if interactive? begin menu.query rescue Interrupt raise Abort end else menu.display_choices puts end @languages = menu.selected_choices.map{ |x| x.downcase.gsub(/\./, '') } end def interesting_language?(name, command = nil) if @languages return @languages.include?(name) else return !!PlatformInfo.find_command(command || name) end end def check_whether_there_are_multiple_apache_installs new_screen puts 'Checking whether there are multiple Apache installations...' output = StringIO.new detector = PlatformInfo::ApacheDetector.new(output) begin detector.detect_all detector.report @apache2 = detector.result_for(PlatformInfo.apxs2) if @apache2.nil? # Print an extra newline because the autodetection routines # may have run some commands which printed stuff to stderr. puts if Process.uid == 0 render_template 'apache2/apache_install_broken', :apxs2 => PlatformInfo.apxs2, :extra_paths => EXTRA_INSTALLER_PATHS, :sudo_s_e => PhusionPassenger::PlatformInfo.ruby_sudo_shell_command("-E"), :ruby => PhusionPassenger::PlatformInfo.ruby_command, :passenger_config => "#{PhusionPassenger.bin_dir}/passenger-config" else render_template 'apache2/run_installer_as_root_for_apache_analysis', :sudo => PhusionPassenger::PlatformInfo.ruby_sudo_command, :sudo_s_e => PhusionPassenger::PlatformInfo.ruby_sudo_shell_command("-E"), :ruby => PhusionPassenger::PlatformInfo.ruby_command, :installer => "#{PhusionPassenger.bin_dir}/passenger-install-apache2-module #{ORIG_ARGV.join(' ')}" end return false elsif detector.results.size > 1 other_installs = detector.results - [@apache2] render_template 'apache2/multiple_apache_installations_detected', :current => @apache2, :other_installs => other_installs puts if interactive? result = prompt_confirmation "Are you sure you want to install " + "against Apache #{@apache2.version} (#{@apache2.apxs2})?" if !result puts line render_template 'apache2/installing_against_a_different_apache', :other_installs => other_installs end return result else puts 'Continuing installation because --auto is given.' return true end else puts 'Only a single installation detected. This is good.' return true end ensure detector.finish end end def check_whether_apache_uses_compatible_mpm # 'apache2ctl -V' output is in the form of: # # Server MPM: Prefork # <--- this line is not always available! # ... # Server compiled with.... # -D APACHE_MPM_DIR="server/mpm/prefork" output = PlatformInfo.apache2ctl_V if output output =~ /^Server MPM: +(.*)$/ if $1 mpm = $1.downcase else output =~ /APACHE_MPM_DIR="server\/mpm\/(.*)"/ if $1 mpm = $1.downcase else mpm = nil end end if mpm != "prefork" && mpm != "worker" && mpm != "event" new_screen render_template 'apache2/apache_must_be_compiled_with_compatible_mpm', :current_mpm => mpm wait end elsif !@apache2.config_file_broken? # 'output' may be nil because the config file is broken (see # PlatformInfo.apache2ctl_V for more information), but we already # warn the user about in #validate_install. # So here we only warn about not being able to detect an MPM type # if 'output' is nil but not as a result of the config file being broken. new_screen render_template 'apache2/mpm_unknown', :control_command => @apache2.ctl wait end end def check_write_permission_to_passenger_root File.new("__test__.txt", "w").close return true rescue SystemCallError puts line if Process.uid == 0 render_template 'installer_common/cannot_access_files_as_root', :type => "directory", :files => [PhusionPassenger.build_system_dir] else render_template 'installer_common/run_installer_as_root', :dir => PhusionPassenger.build_system_dir, :sudo => PhusionPassenger::PlatformInfo.ruby_sudo_command, :sudo_s_e => PhusionPassenger::PlatformInfo.ruby_sudo_shell_command("-E"), :ruby => PhusionPassenger::PlatformInfo.ruby_command, :installer => "#{PhusionPassenger.bin_dir}/passenger-install-apache2-module #{ORIG_ARGV.join(' ')}" end return false ensure File.unlink("__test__.txt") rescue nil end def check_write_permission_to_web_server_config_files return true if !@update_config config_file = PlatformInfo.httpd_default_config_file return if !config_file || !File.exist?(config_file) all_config_files = PlatformInfo.httpd_included_config_files(config_file) if all_config_files[:unreadable_files].any? puts line if Process.uid == 0 render_template 'installer_common/cannot_access_files_as_root', :access => "read from", :files => all_config_files[:unreadable_files] else render_template 'installer_common/run_installer_as_root', :access => "read from", :desc => "an Apache configuration file", :sudo => PhusionPassenger::PlatformInfo.ruby_sudo_command, :sudo_s_e => PhusionPassenger::PlatformInfo.ruby_sudo_shell_command("-E"), :ruby => PhusionPassenger::PlatformInfo.ruby_command, :installer => "#{PhusionPassenger.bin_dir}/passenger-install-apache2-module #{ORIG_ARGV.join(' ')}" end puts render_template 'apache2/present_choice_for_no_update_config' return false end unwriteable_files = [] all_config_files[:files].each do |filename| if !File.writable_real?(filename) unwriteable_files << filename end end if unwriteable_files.empty? return true else puts line if Process.uid == 0 render_template 'installer_common/cannot_access_files_as_root', :files => unwriteable_files else render_template 'installer_common/run_installer_as_root', :desc => "an Apache configuration file", :sudo => PhusionPassenger::PlatformInfo.ruby_sudo_command, :sudo_s_e => PhusionPassenger::PlatformInfo.ruby_sudo_shell_command("-E"), :ruby => PhusionPassenger::PlatformInfo.ruby_command, :installer => "#{PhusionPassenger.bin_dir}/passenger-install-apache2-module #{ORIG_ARGV.join(' ')}" end puts render_template 'apache2/present_choice_for_no_update_config' return false end end def compile_apache2_module puts line puts 'Compiling and installing Apache 2 module...' if @compile puts "cd #{PhusionPassenger.build_system_dir}" if ENV['TRACE'] rake = "#{PlatformInfo.rake_command} --trace RELEASE=yes" else rake = "#{PlatformInfo.rake_command} RELEASE=yes" end command = "env NOEXEC_DISABLE=1 #{rake} apache2:clean apache2" if PhusionPassenger.packaging_method == "homebrew" # Running apache2:clean deletes some object files needed # by passenger-install-nginx-module, so we ensure those # object files are compiled. command << " nginx" end return sh(command) else puts "Skipping compilation" return true end end def load_snippet return "LoadModule passenger_module #{PhusionPassenger.apache2_module_path}" end def config_snippet result = "\n" + " PassengerRoot #{PhusionPassenger.install_spec}\n" + " PassengerDefaultRuby #{PlatformInfo.ruby_command}\n" if PhusionPassenger.packaging_method == "rpm" result << " PassengerInstanceRegistryDir /var/run/passenger-instreg\n" end result << "" result end def apache2_config_snippets return "#{load_snippet}\n#{config_snippet}\n" end def backup_config_files(config_file, all_config_files) now = Time.now.strftime("%Y-%m-%d-%H:%M:%S") archive = "#{config_file}.#{GLOBAL_NAMESPACE_DIRNAME}-backup-#{now}.tar.gz" backup_files = all_config_files.dup # Some people create a regular file in /etc/apache2/mods-enabled, for convenience # reasons. This file is not actually managed by a2enmod. We will remove such files # because we're going to use mods-eanbled ourselves, so we need to back them up. if (dir = PlatformInfo.httpd_mods_enabled_directory) && PlatformInfo.a2enmod if File.file?("#{dir}/#{APACHE2_MODULE_CONF_NAME}.load") backup_files << "#{dir}/#{APACHE2_MODULE_CONF_NAME}.load" end if File.file?("#{dir}/#{APACHE2_MODULE_CONF_NAME}.conf") backup_files << "#{dir}/#{APACHE2_MODULE_CONF_NAME}.conf" end end puts "Backing up existing configuration files to #{archive}..." backup_files.uniq! backup_files.map! { |x| x.sub(/^\//, '') } Dir.chdir("/") do sh! "tar", "-czf", archive, *backup_files end end def uninstall_or_comment_out_existing_config_snippets(all_config_files) files_containing_autoinstall_load_blocks = [] files_containing_autoinstall_conf_blocks = [] # Some people create a regular file in /etc/apache2/mods-enabled, for convenience # reasons. This file is not actually managed by a2enmod. We remove such files # because we're going to use mods-enabled ourselves. They've already been backed up. if (dir = PlatformInfo.httpd_mods_enabled_directory) && PlatformInfo.a2enmod filename = "#{dir}/#{APACHE2_MODULE_CONF_NAME}.load" if File.file?(filename) && !File.symlink?(filename) puts "Removing #{filename}" File.unlink(filename) end filename = "#{dir}/#{APACHE2_MODULE_CONF_NAME}.conf" if File.file?(filename) && !File.symlink?(filename) puts "Removing #{filename}" File.unlink(filename) end end # Uncomment Phusion Passenger config snippets. all_config_files.each do |filename| next if !File.exist?(filename) contents = File.open(filename, "rb") do |f| f.read end if contents =~ /#{Regexp.escape AUTOINSTALL_BEGIN_LOAD_BLOCK}.*?#{Regexp.escape AUTOINSTALL_END_LOAD_BLOCK}/m files_containing_autoinstall_load_blocks << filename end if contents =~ /#{Regexp.escape AUTOINSTALL_BEGIN_CONF_BLOCK}.*?#{Regexp.escape AUTOINSTALL_END_CONF_BLOCK}/m files_containing_autoinstall_conf_blocks << filename end subst1 = contents.gsub!(/^([ \t]*LoadModule[ \t]+passenger_module[ \t]+.*)$/i, '# \1') subst2 = contents.gsub!(/^([ \t]*PassengerRoot[ \t]+.*)$/i, '# \1') subst3 = contents.gsub!(/^([ \t]*PassengerDefaultRuby[ \t]+.*)$/i, '# \1') if subst1 || subst2 || subst3 puts "Uninstalling previous #{PROGRAM_NAME} from #{filename}..." File.open(filename, "wb") do |f| f.write(contents) end end end # If there are multiple auto-install comment blocks, remove the duplicates. # First, we remove all comment blocks from all files besides the first one. if files_containing_autoinstall_load_blocks.size > 1 files_containing_autoinstall_load_blocks[1..-1].each do |filename| puts "Removing duplicate load snippets from #{filename}..." remove_comment_blocks(filename, AUTOINSTALL_BEGIN_LOAD_BLOCK, AUTOINSTALL_END_LOAD_BLOCK) end end if files_containing_autoinstall_conf_blocks.size > 1 files_containing_autoinstall_conf_blocks[1..-1].each do |filename| puts "Removing duplicate conf snippets from #{filename}..." remove_comment_blocks(filename, AUTOINSTALL_BEGIN_CONF_BLOCK, AUTOINSTALL_END_CONF_BLOCK) end end # Then we are left with exactly 0 or exactly 1 file with comment blocks # of each type. There may be duplicates inside that single file, so # remove duplicates there too. if files_containing_autoinstall_load_blocks.size > 0 filename = files_containing_autoinstall_load_blocks[0] puts "Removing duplicate load snippets inside #{filename}..." remove_duplicate_comment_blocks(filename, AUTOINSTALL_BEGIN_LOAD_BLOCK, AUTOINSTALL_END_LOAD_BLOCK) end if files_containing_autoinstall_conf_blocks.size > 0 filename = files_containing_autoinstall_conf_blocks[0] puts "Removing duplicate configuration snippets inside #{filename}..." remove_duplicate_comment_blocks(filename, AUTOINSTALL_BEGIN_CONF_BLOCK, AUTOINSTALL_END_CONF_BLOCK) end # One or more files may have been removed, so filter out the ones # that are left. all_config_files.reject! do |filename| !File.exist?(filename) end end def remove_comment_blocks(filename, begin_marker, end_marker) contents = File.open(filename, "rb") do |f| f.read end regexp = /#{Regexp.escape begin_marker}.*?#{Regexp.escape end_marker}\n?/m contents.gsub!(regexp, '') File.open(filename, "wb") do |f| f.write(contents) end end def remove_duplicate_comment_blocks(filename, begin_marker, end_marker) contents = File.open(filename, "rb") do |f| f.read end regexp = /#{Regexp.escape begin_marker}.*?#{Regexp.escape end_marker}\n?/m if m = regexp.match(contents) offset = m.end(0) rest = contents.slice!(m.end(0) .. -1) rest.gsub!(regexp, '') contents << rest File.open(filename, "wb") do |f| f.write(contents) end end end def add_new_config_snippets(all_config_files) # Look for the file containing the auto-install load and conf comment blocks. # The uninstall_or_comment_out_existing_config_snippets method has already # guaranteed that there is at most 1 file per comment block type, and that # inside each file there are no duplicate comment blocks. load_block_file = find_config_file_containing_comment_block(all_config_files, AUTOINSTALL_BEGIN_LOAD_BLOCK, AUTOINSTALL_END_LOAD_BLOCK) conf_block_file = find_config_file_containing_comment_block(all_config_files, AUTOINSTALL_BEGIN_CONF_BLOCK, AUTOINSTALL_END_CONF_BLOCK) if load_block_file && conf_block_file puts "Updating #{PROGRAM_NAME} module load snippet inside #{load_block_file}..." update_comment_block(load_block_file, AUTOINSTALL_BEGIN_LOAD_BLOCK, AUTOINSTALL_END_LOAD_BLOCK, load_snippet) puts "Updating #{PROGRAM_NAME} configuration snippet inside #{conf_block_file}..." update_comment_block(conf_block_file, AUTOINSTALL_BEGIN_CONF_BLOCK, AUTOINSTALL_END_CONF_BLOCK, config_snippet) elsif !load_block_file && !conf_block_file create_load_snippet_file(:maybe_in_mods_available) create_conf_snippet_file(:maybe_in_mods_available) if PlatformInfo.httpd_mods_available_directory && PlatformInfo.a2enmod sh! "#{PlatformInfo.a2enmod} #{APACHE2_MODULE_CONF_NAME}" end # It looks like either the load or conf file isn't available. # If the file that is available is inside mods-available, then it # means that the mods-available files are broken. elsif is_file_inside_mods_available?(load_block_file, "#{APACHE2_MODULE_CONF_NAME}.load") || is_file_inside_mods_available?(conf_block_file, "#{APACHE2_MODULE_CONF_NAME}.conf") # We fix it if a2enmod is available. Otherwise, we remove the block. if PlatformInfo.a2enmod if !load_block_file create_load_snippet_file(:must_be_in_mods_available) else create_conf_snippet_file(:must_be_in_mods_available) end else if load_block_file puts "Removing load snippets from #{filename}..." remove_comment_blocks(load_block_file, AUTOINSTALL_BEGIN_LOAD_BLOCK, AUTOINSTALL_END_LOAD_BLOCK) else puts "Removing configuration snippets from #{filename}..." remove_comment_blocks(conf_block_file, AUTOINSTALL_BEGIN_CONF_BLOCK, AUTOINSTALL_END_CONF_BLOCK) end create_load_snippet_file(:must_be_in_mods_available) create_conf_snippet_file(:must_be_in_mods_available) end sh! "#{PlatformInfo.a2enmod} passenger" else if !load_block_file create_load_snippet_file(:not_in_mods_available) puts "Updating #{PROGRAM_NAME} configuration snippet inside #{conf_block_file}..." update_comment_block(conf_block_file, AUTOINSTALL_BEGIN_CONF_BLOCK, AUTOINSTALL_END_CONF_BLOCK, config_snippet) else create_conf_snippet_file(:not_in_mods_available) puts "Updating #{PROGRAM_NAME} module load snippet inside #{load_block_file}..." update_comment_block(load_block_file, AUTOINSTALL_BEGIN_LOAD_BLOCK, AUTOINSTALL_END_LOAD_BLOCK, load_snippet) end end end def find_config_file_containing_comment_block(all_config_files, begin_marker, end_marker) regexp = /#{Regexp.escape begin_marker}.*?#{Regexp.escape end_marker}/m all_config_files.each do |filename| contents = File.open(filename, "rb") do |f| f.read end if contents =~ regexp return filename end end return nil end def update_comment_block(filename, begin_marker, end_marker, block_contents) regexp = /#{Regexp.escape begin_marker}.*?#{Regexp.escape end_marker}\n?/m contents = File.open(filename, "rb") do |f| f.read end if contents.sub!(regexp, "#{begin_marker}\n#{block_contents}\n#{end_marker}\n") File.open(filename, "wb") do |f| f.write(contents) end return true else return false end end def remove_comment_blocks(filename, begin_marker, end_marker) regexp = /#{Regexp.escape begin_marker}.*?#{Regexp.escape end_marker}\n?/m contents = File.open(filename, "rb") do |f| f.read end contents.gsub!(regexp, "") File.open(filename, "wb") do |f| f.write(contents) end end def is_file_inside_mods_available?(filename, basename) if dir = PlatformInfo.httpd_mods_available_directory return filename == "#{dir}/#{basename}" else return false end end def create_load_snippet_file(where) case where when :maybe_in_mods_available if PlatformInfo.httpd_mods_available_directory && PlatformInfo.a2enmod filename = "#{PlatformInfo.httpd_mods_available_directory}/#{APACHE2_MODULE_CONF_NAME}.load" else filename = PlatformInfo.httpd_default_config_file end when :must_be_in_mods_available if PlatformInfo.httpd_mods_available_directory && PlatformInfo.a2enmod filename = "#{PlatformInfo.httpd_mods_available_directory}/#{APACHE2_MODULE_CONF_NAME}.load" else raise "Apache does not support the mods-available directory" end when :not_in_mods_available filename = PlatformInfo.httpd_default_config_file else raise ArgumentError end if File.exist?(filename) # If this is a file inside mods-available, and the file didn't have a symlink # in mods-enabled, then the uninstall phase did not remove duplicates from this # file. So here we remove duplicates again. puts "Removing duplicate load snippets inside #{filename}..." remove_duplicate_comment_blocks(filename, AUTOINSTALL_BEGIN_LOAD_BLOCK, AUTOINSTALL_END_LOAD_BLOCK) puts "Installing #{PROGRAM_NAME} module load snippet to #{filename}..." should_add = !update_comment_block(filename, AUTOINSTALL_BEGIN_LOAD_BLOCK, AUTOINSTALL_END_LOAD_BLOCK, load_snippet) else puts "Installing #{PROGRAM_NAME} module load snippet to #{filename}..." should_add = true end if should_add File.open(filename, "ab") do |f| f.write("\n#{AUTOINSTALL_BEGIN_LOAD_BLOCK}\n" + "#{load_snippet}\n" + "#{AUTOINSTALL_END_LOAD_BLOCK}\n") end end end def create_conf_snippet_file(where) case where when :maybe_in_mods_available if PlatformInfo.httpd_mods_available_directory && PlatformInfo.a2enmod filename = "#{PlatformInfo.httpd_mods_available_directory}/#{APACHE2_MODULE_CONF_NAME}.conf" else filename = PlatformInfo.httpd_default_config_file end when :must_be_in_mods_available if PlatformInfo.httpd_mods_available_directory && PlatformInfo.a2enmod filename = "#{PlatformInfo.httpd_mods_available_directory}/#{APACHE2_MODULE_CONF_NAME}.conf" else raise "Apache does not support the mods-available directory" end when :not_in_mods_available filename = PlatformInfo.httpd_default_config_file else raise ArgumentError end if File.exist?(filename) # If this is a file inside mods-available, and the file didn't have a symlink # in mods-enabled, then the uninstall phase did not remove duplicates from this # file. So here we remove duplicates again. puts "Removing duplicate configuration snippets inside #{filename}..." remove_duplicate_comment_blocks(filename, AUTOINSTALL_BEGIN_CONF_BLOCK, AUTOINSTALL_END_CONF_BLOCK) puts "Installing #{PROGRAM_NAME} module configuration snippet to #{filename}..." should_add = !update_comment_block(filename, AUTOINSTALL_BEGIN_CONF_BLOCK, AUTOINSTALL_END_CONF_BLOCK, config_snippet) else puts "Installing #{PROGRAM_NAME} module configuration snippet to #{filename}..." should_add = true end if should_add File.open(filename, "ab") do |f| f.write("\n#{AUTOINSTALL_BEGIN_CONF_BLOCK}\n" + "#{config_snippet}\n" + "#{AUTOINSTALL_END_CONF_BLOCK}\n") end end end def install_apache2_config_snippets if !@update_config show_apache2_config_snippets return true end config_file = PlatformInfo.httpd_default_config_file if config_file && File.exist?(config_file) puts line puts "Updating Apache configuration files..." config_file = PlatformInfo.httpd_default_config_file all_config_files = PlatformInfo.httpd_included_config_files(config_file)[:files] backup_config_files(config_file, all_config_files) if @backup_config uninstall_or_comment_out_existing_config_snippets(all_config_files) add_new_config_snippets(all_config_files) return true else show_apache2_config_snippets return true end end def show_apache2_config_snippets puts line render_template 'apache2/config_snippets', :snippet => apache2_config_snippets wait end def validate_install # By compiling Passenger, we may have changed Apache's state. For example, # if 'apache2ctl -V' failed because it referenced an Apache module that # didn't exist, then it may exist now, causing 'apache2ctl -V' to succeed. # We want ValidateInstallCommand to reflect the current Apache state, # not the one before compilation started, so we clear PlatformInfo # memoizations here. PlatformInfo.clear_memoizations new_screen puts "Validating installation..." puts args = ["--validate-apache2", "--invoked-from-installer"] args << "--auto" if @auto # The validator will get the path to apxs2 from the APXS2 environment # variable that this installer sets. validator = PhusionPassenger::Config::ValidateInstallCommand.new(args) exit_code = validator.run_and_get_exit_code STDOUT.write(@colors.default_terminal_color) case exit_code when PhusionPassenger::Config::ValidateInstallCommand::FAIL_EXIT_CODE puts "Please solve the above issues, then press ENTER to continue." wait when PhusionPassenger::Config::ValidateInstallCommand::WARN_EXIT_CODE puts "Press ENTER to continue." wait else exit(exit_code) end end def show_deployment_example new_screen render_template 'apache2/deployment_example', :deployment_guide_url => "https://www.phusionpassenger.com/library/deploy/apache/deploy/", :phusion_website => PHUSION_WEBSITE, :passenger_website => PASSENGER_WEBSITE end def show_possible_solutions_for_compilation_and_installation_problems new_screen render_template 'apache2/possible_solutions_for_compilation_and_installation_problems', :support_url => SUPPORT_URL end def apache_module_available? return File.exist?(PhusionPassenger.apache2_module_path) end def install_apache_module_from_native_package case PhusionPassenger.packaging_method when 'deb' sh! "sudo apt-get update" sh! "sudo apt-get install #{DEB_APACHE_MODULE_PACKAGE}" return true when 'rpm' sh! "sudo yum install #{RPM_APACHE_MODULE_PACKAGE}-#{VERSION_STRING}" return true else puts "The #{PROGRAM_NAME} Apache module package is not installed." puts "Please ask your packager or operating system vendor how to install it." return false end end def notify_apache_module_installed render_template 'apache2/notify_apache_module_installed' wait end end ORIG_ARGV = ARGV.dup options = { :compile => true, :update_config => false, :backup_config => true } parser = OptionParser.new do |opts| opts.banner = "Usage: passenger-install-apache2-module [options]" opts.separator "" indent = ' ' * 37 opts.separator "Options:" opts.on("-a", "--auto", String, "Automatically build the Apache module,\n" << "#{indent}without interactively asking for user\n" << "#{indent}input.") do options[:auto] = true end opts.on("--apxs2-path PATH", String, "Path to 'apxs2' command.") do |value| ENV['APXS2'] = value end opts.on("--apr-config-path PATH", String, "Path to 'apr-config' command.") do |value| ENV['APR_CONFIG'] = value end opts.on("--languages NAMES", "Comma-separated list of interested\n" << "#{indent}languages (e.g.\n" << "#{indent}'ruby,python,nodejs,meteor')") do |value| options[:languages] = value.split(",") end opts.on("--no-compile", "Skip compilation.") do options[:compile] = false end #opts.on("--no-update-config", "Do not automatically update Apache config\n" << # "#{indent}files.") do # options[:update_config] = false #end #opts.on("--no-backup-config", "When updating Apache config files, do not\n" << # "#{indent}create backups of the existing files.") do # options[:backup_config] = false #end opts.on("--force-colors", "Display colors even if stdout is not a TTY") do options[:colorize] = true end opts.on("--snippet", "Show just the Apache config snippet.") do options[:snippet] = true end end begin parser.parse! rescue OptionParser::ParseError => e puts e puts puts "Please see '--help' for valid options." exit 1 end installer = Installer.new(options) if options[:snippet] puts installer.send(:apache2_config_snippets) else installer.run end passenger-5.0.30/bin/passenger-install-nginx-module000755 000765 000024 00000050174 12233035540 022752 0ustar00honglistaff000000 000000 #!/usr/bin/env ruby # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. ## Magic comment: begin bootstrap ## source_root = File.expand_path("..", File.dirname(__FILE__)) $LOAD_PATH.unshift("#{source_root}/src/ruby_supportlib") begin require 'rubygems' rescue LoadError end require 'phusion_passenger' ## Magic comment: end bootstrap ## PhusionPassenger.locate_directories require 'digest/sha2' require 'optparse' require 'fileutils' PhusionPassenger.require_passenger_lib 'platform_info/ruby' PhusionPassenger.require_passenger_lib 'platform_info/openssl' PhusionPassenger.require_passenger_lib 'abstract_installer' PhusionPassenger.require_passenger_lib 'utils/terminal_choice_menu' PhusionPassenger.require_passenger_lib 'utils/tmpio' PhusionPassenger.require_passenger_lib 'utils/shellwords' DOWNLOAD_OPTION = { :connect_timeout => 30, :idle_timeout => 30 } class Installer < PhusionPassenger::AbstractInstaller include PhusionPassenger TerminalChoiceMenu = PhusionPassenger::Utils::TerminalChoiceMenu def dependencies specs = [ 'depcheck_specs/compiler_toolchain', 'depcheck_specs/ruby', 'depcheck_specs/gems', 'depcheck_specs/libs', 'depcheck_specs/utilities' ] ids = [ 'cc', 'c++', 'download-tool', 'libcurl-dev', 'openssl-dev', 'zlib-dev', 'rake', 'ruby-openssl', 'rubygems' ] if @languages.include?("ruby") if PlatformInfo.passenger_needs_ruby_dev_header? ids << 'ruby-dev' end ids << 'rack' end return [specs, ids] end def install_doc_url "https://www.phusionpassenger.com/library/install/nginx/" end def troubleshooting_doc_url "https://www.phusionpassenger.com/library/admin/nginx/troubleshooting/" end def run_steps # Make sure the configure script finds the correct # passenger-config command. ENV['PATH'] = PhusionPassenger.bin_dir + ":" + ENV['PATH'] show_welcome_screen query_interested_languages check_gem_install_permission_problems || exit(1) check_directory_accessible_by_web_server check_nginx_module_sources_available || exit(1) check_dependencies || exit(1) if needs_compiling_support_files? check_whether_we_can_write_to(PhusionPassenger.build_system_dir) || exit(1) end check_whether_os_is_broken check_whether_system_has_enough_ram download_and_install = should_we_download_and_install_nginx_automatically? if pcre_is_installed? @pcre_source_dir = nil else @pcre_source_dir = download_and_extract_pcre end if download_and_install nginx_source_dir = download_and_extract_nginx if nginx_source_dir.nil? show_possible_solutions_for_download_and_extraction_problems exit(1) end nginx_prefix = ask_for_nginx_install_prefix check_whether_other_nginx_installations_exist(nginx_prefix) if @extra_configure_flags == "none" extra_nginx_configure_flags = nil else extra_nginx_configure_flags = @extra_configure_flags end else nginx_source_dir = ask_for_nginx_source_dir nginx_prefix = ask_for_nginx_install_prefix check_whether_other_nginx_installations_exist(nginx_prefix) extra_nginx_configure_flags = ask_for_extra_nginx_configure_flags(nginx_prefix) end check_whether_we_can_write_to(nginx_prefix) || exit(1) nginx_config_already_exists_before_installing = nginx_config_exists?(nginx_prefix) if needs_compiling_support_files? if !compile_passenger_support_files show_possible_solutions_for_compilation_and_installation_problems exit(1) end end if install_nginx(nginx_source_dir, nginx_prefix, extra_nginx_configure_flags) if nginx_config_already_exists_before_installing || !locate_nginx_config_file(nginx_prefix) show_passenger_config_snippets(nginx_prefix) else insert_passenger_config_snippets(nginx_prefix) end show_deployment_example else show_possible_solutions_for_compilation_and_installation_problems exit(1) end end def before_install super myself = `whoami`.strip @working_dir = PhusionPassenger::Utils.mktmpdir("passenger.", PlatformInfo.tmpexedir) end def after_install super FileUtils.remove_entry_secure(@working_dir) if @working_dir end private def show_welcome_screen render_template 'nginx/welcome', :version => VERSION_STRING wait end def query_interested_languages menu = TerminalChoiceMenu.new(["Ruby", "Python", "Node.js", "Meteor"]) menu["Ruby"].checked = interesting_language?('ruby') menu["Python"].checked = interesting_language?('python') menu["Node.js"].checked = interesting_language?('nodejs', 'node') menu["Meteor"].checked = interesting_language?('meteor') new_screen puts "Which languages are you interested in?" puts if interactive? puts "Use to select." puts "If the menu doesn't display correctly, press '!'" else puts "Override selection with --languages." end puts if interactive? begin menu.query rescue Interrupt raise Abort end else menu.display_choices puts end @languages = menu.selected_choices.map{ |x| x.downcase.gsub(/\./, '') } end def interesting_language?(name, command = nil) if @languages return @languages.include?(name) else return !!PlatformInfo.find_command(command || name) end end def check_nginx_module_sources_available if PhusionPassenger.custom_packaged? && (!PhusionPassenger.nginx_module_source_dir || !File.exist?(PhusionPassenger.nginx_module_source_dir)) new_screen render_template 'nginx/nginx_module_sources_not_available' return false else return true end end def needs_compiling_support_files? return !PhusionPassenger.build_system_dir.nil? end def compile_passenger_support_files new_screen puts "Compiling Passenger support files..." Dir.chdir(PhusionPassenger.build_system_dir) do return sh("env NOEXEC_DISABLE=1 #{PlatformInfo.rake_command} nginx:clean nginx RELEASE=yes") end end def should_we_download_and_install_nginx_automatically? new_screen render_template 'nginx/query_download_and_install', :nginx_version => PREFERRED_NGINX_VERSION puts if @auto_download puts "=> Proceeding with choice 1." return true elsif @nginx_source_dir puts "=> Proceeding with choice 2." return false elsif @auto puts "=> Proceeding with choice 1." return true else choice = prompt("Enter your choice (1 or 2) or press Ctrl-C to abort") do |input| if input == "1" || input == "2" true elsif input.empty? puts "No choice has been given." false else puts "'#{input}' is not a valid choice." false end end return choice == "1" end end def download_and_extract_pcre new_screen puts "PCRE (required by Nginx) not installed, downloading it..." url = "http://downloads.sourceforge.net/project/pcre/pcre/#{PREFERRED_PCRE_VERSION}/pcre-#{PREFERRED_PCRE_VERSION}.tar.gz" dirname = "pcre-#{PREFERRED_PCRE_VERSION}" tarball = "#{@working_dir}/pcre.tar.gz" if download(url, tarball, DOWNLOAD_OPTION) Dir.chdir(@working_dir) do puts "Verifying PCRE checksum..." if sha256_file(tarball) != PCRE_SHA256_CHECKSUM new_screen render_template "nginx/pcre_checksum_could_not_be_verified" wait return nil end puts "Extracting PCRE source tarball..." if sh("tar", "xzvf", tarball) return "#{@working_dir}/#{dirname}" else new_screen render_template "nginx/pcre_could_not_be_extracted" wait return nil end end else new_screen render_template "nginx/pcre_could_not_be_downloaded" wait return nil end rescue Interrupt exit 2 end def download_and_extract_nginx new_screen puts "Downloading Nginx..." url = "http://www.nginx.org/download/nginx-#{PREFERRED_NGINX_VERSION}.tar.gz" dirname = "nginx-#{PREFERRED_NGINX_VERSION}" tarball = "#{@working_dir}/nginx.tar.gz" if download(url, tarball, DOWNLOAD_OPTION) Dir.chdir(@working_dir) do puts "Verifying Nginx checksum..." if sha256_file(tarball) != NGINX_SHA256_CHECKSUM return nil end puts "Extracting Nginx source tarball..." if sh("tar", "xzvf", tarball) return "#{@working_dir}/#{dirname}" else return nil end end else return nil end rescue Interrupt exit 2 end def show_possible_solutions_for_download_and_extraction_problems new_screen render_template "nginx/possible_solutions_for_download_and_extraction_problems" puts end def ask_for_nginx_install_prefix new_screen puts "Where do you want to install Nginx to?" puts if @prefix puts "=> #{@prefix}" return @prefix elsif @auto puts "=> /opt/nginx" return "/opt/nginx" else prefix = prompt("Please specify a prefix directory [/opt/nginx]") do |input| if input.empty? || input =~ %r(/) true else puts "Please specify an absolute path." false end end if prefix.empty? prefix = "/opt/nginx" end return prefix end end def check_whether_other_nginx_installations_exist(nginx_prefix) check_for = [ "/usr/bin/nginx", "/usr/sbin/nginx" ] existing_binary = nil check_for.each do |filename| if File.exist?(filename) existing_binary = filename break end end if existing_binary new_screen render_template 'nginx/other_nginx_installations_exist', :existing_binary => existing_binary, :prefix => nginx_prefix wait end end def ask_for_nginx_source_dir new_screen puts "Where is your Nginx source code located?" puts if @nginx_source_dir puts "=> #{@nginx_source_dir}" return @nginx_source_dir else return prompt("Please specify the directory") do |input| if input =~ %r(/) if File.exist?("#{input}/src/core/nginx.c") true else puts "'#{input}' does not look like an Nginx source directory." false end else puts "Please specify an absolute path." false end end end end def ask_for_extra_nginx_configure_flags(prefix) done = false while !done new_screen render_template 'nginx/ask_for_extra_configure_flags', :command => build_nginx_configure_command(prefix) puts if @extra_configure_flags if @extra_configure_flags == "none" extra_args = "" puts "=> No extra configure flags." else extra_args = @extra_configure_flags puts "=> #{extra_args}" end return extra_args elsif @auto puts "=> No extra configure flags." return "" else extra_args = prompt "Extra arguments to pass to configure script" new_screen render_template 'nginx/confirm_extra_configure_flags', :command => build_nginx_configure_command(prefix, extra_args) puts answer = prompt("Is this what you want? (yes/no) [default=yes]") do |input| if input.empty? || input == "yes" || input == "no" true else puts "Please enter 'yes' or 'no'." false end end done = answer.empty? || answer == "yes" end end return extra_args end def check_whether_we_can_write_to(dir) FileUtils.mkdir_p(dir) File.new("#{dir}/__test__.txt", "w").close return true rescue new_screen if Process.uid == 0 render_template 'nginx/cannot_write_to_dir', :dir => dir else render_template 'installer_common/run_installer_as_root', :dir => dir, :sudo => PhusionPassenger::PlatformInfo.ruby_sudo_command, :sudo_s_e => PhusionPassenger::PlatformInfo.ruby_sudo_shell_command("-E"), :ruby => PhusionPassenger::PlatformInfo.ruby_command, :installer => "#{PhusionPassenger.bin_dir}/passenger-install-nginx-module #{ORIG_ARGV.join(' ')}" end return false ensure File.unlink("#{dir}/__test__.txt") rescue nil end def nginx_config_exists?(prefix) return !!locate_nginx_config_file(prefix) end def install_nginx(source_dir, prefix, extra_configure_flags) Dir.chdir(source_dir) do new_screen puts "Compiling and installing Nginx..." if !sh(build_nginx_configure_command(prefix, extra_configure_flags)) || !sh("make") || !sh("make install") return false end end return true rescue Interrupt raise Aborted end def show_passenger_config_snippets(prefix) new_screen render_template 'nginx/config_snippets', :config_file => locate_nginx_config_file(prefix), :passenger_root => PhusionPassenger.install_spec, :ruby => PlatformInfo.ruby_command wait end def show_deployment_example line puts render_template 'nginx/deployment_example', :deployment_guide_url => "https://www.phusionpassenger.com/library/deploy/nginx/deploy/", :phusion_website => PHUSION_WEBSITE, :passenger_website => PASSENGER_WEBSITE end def show_possible_solutions_for_compilation_and_installation_problems line puts render_template 'nginx/possible_solutions_for_compilation_and_installation_problems', :support_url => SUPPORT_URL end def locate_nginx_config_file(prefix) ["#{prefix}/conf/nginx.conf", "#{prefix}/etc/nginx.conf"].each do |filename| if File.exist?(filename) return filename end end return nil end def insert_passenger_config_snippets(prefix) config_file = locate_nginx_config_file(prefix) contents = File.read(config_file) contents.sub!(/^http \{/, "http {\n" << " passenger_root #{PhusionPassenger.install_spec};\n" << " passenger_ruby #{PlatformInfo.ruby_command};\n") File.open(config_file, 'w') do |f| f.write(contents) end new_screen render_template 'nginx/config_snippets_inserted', :config_file => config_file, :passenger_root => PhusionPassenger.install_spec, :ruby => PlatformInfo.ruby_command wait end def build_nginx_configure_command(prefix, extra_configure_flags = nil) extra_cflags = "-Wno-error #{PlatformInfo.openssl_extra_cflags}".strip extra_ldflags = PlatformInfo.openssl_extra_ldflags command = "sh ./configure --prefix='#{prefix}' " command << "--with-http_ssl_module " command << "--with-http_gzip_static_module " command << "--with-http_stub_status_module " command << "--with-cc-opt=#{Shellwords.escape extra_cflags} " command << "--with-ld-opt=#{Shellwords.escape extra_ldflags} " if @pcre_source_dir command << "--with-pcre='#{@pcre_source_dir}' " elsif !pcre_is_installed? command << "--without-http_rewrite_module " end command << "--add-module='#{PhusionPassenger.nginx_module_source_dir}' #{extra_configure_flags}" command.strip! return command end def pcre_is_installed? if @pcre_is_installed.nil? @pcre_is_installed = begin File.open('/tmp/passenger-check.c', 'w') do |f| f.puts("#include ") end Dir.chdir('/tmp') do # Nginx checks for PCRE in multiple places... system("(gcc -I/usr/local/include -I/usr/include/pcre " << "-I/usr/pkg/include -I/opt/local/include " << "-c passenger-check.c) >/dev/null 2>/dev/null") end ensure File.unlink('/tmp/passenger-check.c') rescue nil File.unlink('/tmp/passenger-check.o') rescue nil end end return @pcre_is_installed end def sha256_file(path) # We do this instead of using #file, for Ruby 1.8.5 support. digest = Digest::SHA256.new File.open(path, "rb") do |f| buf = '' buf.force_encoding('binary') if buf.respond_to?(:force_encoding) while !f.eof? f.read(1024 * 16, buf) digest.update(buf) end end return digest.hexdigest end end ORIG_ARGV = ARGV.dup options = {} parser = OptionParser.new do |opts| opts.banner = "Usage: passenger-install-nginx-module [options]" opts.separator "" indent = ' ' * 37 opts.separator "Options:" opts.on("--auto", "Automatically confirm 'Press ENTER to\n" << "#{indent}continue' prompts.") do options[:auto] = true end opts.on("--prefix=DIR", String, "Use the given Nginx install prefix instead\n" << "#{indent}of asking for it interactively.") do |dir| options[:prefix] = dir end opts.on("--auto-download", "Download and install Nginx automatically,\n" << "#{indent}instead of asking interactively whether to\n" << "#{indent}download+install or to use an existing\n" << "#{indent}Nginx source directory.") do options[:auto_download] = true end opts.on("--nginx-source-dir=DIR", String, "Compile and install Nginx using the given\n" << "#{indent}Nginx source directory, instead of\n" << "#{indent}interactively asking to download+install\n" << "#{indent}or to use an existing Nginx source\n" << "#{indent}directory. Conflicts with --auto-download.") do |dir| options[:nginx_source_dir] = dir end opts.on("--extra-configure-flags=STRING", String, "Pass these extra flags to Nginx's\n" << "#{indent}'configure' script, instead of asking for\n" << "#{indent}it interactively. Specify 'none' if you\n" << "#{indent}do not want to pass additional flags but do\n" << "#{indent}not want this installer to ask\n" << "#{indent}interactively either.") do |flags| options[:extra_configure_flags] = flags end opts.on("--languages NAMES", "Comma-separated list of interested\n" << "#{indent}languages (e.g.\n" << "#{indent}'ruby,python,nodejs,meteor')") do |value| options[:languages] = value.split(",") end opts.on("--force-colors", "Display colors even if stdout is not a TTY") do options[:colorize] = true end end begin parser.parse! rescue OptionParser::ParseError => e puts e puts puts "Please see '--help' for valid options." exit 1 end if options[:auto_download] && options[:nginx_source_dir] STDERR.puts "You cannot specify both --auto-download and --nginx-source-dir." exit 1 end Installer.new(options).run passenger-5.0.30/bin/passenger-memory-stats000755 000765 000024 00000013500 12233035540 021334 0ustar00honglistaff000000 000000 #!/usr/bin/env ruby # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. ## Magic comment: begin bootstrap ## source_root = File.expand_path("..", File.dirname(__FILE__)) $LOAD_PATH.unshift("#{source_root}/src/ruby_supportlib") begin require 'rubygems' rescue LoadError end require 'phusion_passenger' ## Magic comment: end bootstrap ## PhusionPassenger.locate_directories PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'platform_info/ruby' PhusionPassenger.require_passenger_lib 'admin_tools/memory_stats' PhusionPassenger.require_passenger_lib 'utils/ansi_colors' include PhusionPassenger # Container for tabular data. class Table def initialize(column_names, colors) @column_names = column_names @rows = [] @colors = colors end def add_row(values) @rows << values.to_a end def add_rows(list_of_rows) list_of_rows.each do |row| add_row(row) end end def remove_column(name) i = @column_names.index(name) @column_names.delete_at(i) @rows.each do |row| row.delete_at(i) end end def to_s(title = nil) max_column_widths = [1] * @column_names.size (@rows + [@column_names]).each do |row| row.each_with_index do |value, i| max_column_widths[i] = [value.to_s.size, max_column_widths[i]].max end end format_string = max_column_widths.map{ |i| "%#{-i}s" }.join(" ") header = sprintf(format_string, *@column_names).rstrip << "\n" if title free_space = header.size - title.size - 2 if free_space <= 0 left_bar_size = 3 right_bar_size = 3 else left_bar_size = free_space / 2 right_bar_size = free_space - left_bar_size end result = "#{@colors.blue_bg}#{@colors.bold}#{@colors.yellow}\n" result << "#{"-" * left_bar_size} #{title} #{"-" * right_bar_size}\n" if !@rows.empty? result << @colors.white result << header end else result = header.dup end if @rows.empty? result << @colors.reset else result << ("-" * header.size) << "#{@colors.reset}\n" @rows.each do |row| result << sprintf(format_string, *row).rstrip << "\n" end end result end end class App def initialize @stats = AdminTools::MemoryStats.new @colors = Utils::AnsiColors.new end def start puts "Version: #{PhusionPassenger::VERSION_STRING}" puts "Date : #{Time.now}" if @stats.apache_processes print_process_list("Apache processes", @stats.apache_processes) else puts "#{@colors.blue_bg}#{@colors.bold}#{@colors.yellow}------------- Apache processes -------------#{@colors.reset}\n" STDERR.puts "*** WARNING: The Apache executable cannot be found." STDERR.puts "Please set the APXS2 environment variable to your 'apxs2' " << "executable's filename, or set the HTTPD environment variable " << "to your 'httpd' or 'apache2' executable's filename." end puts print_process_list("Nginx processes", @stats.nginx_processes) puts print_process_list("Passenger processes", @stats.passenger_processes, :show_ppid => false) if @stats.platform_provides_private_dirty_rss_information? && Process.euid != 0 && @stats.root_privileges_required_for_private_dirty_rss? puts "*** WARNING: Please run this tool with #{@colors.bold}#{PlatformInfo.ruby_sudo_command}#{@colors.reset}. Otherwise the " << "private dirty RSS (a reliable metric for real memory usage) of processes cannot be determined." end end private def print_process_list(title, processes, options = {}) table = Table.new(%w{PID PPID VMSize Private Resident Name}, @colors) table.add_rows(processes) if options.has_key?(:show_ppid) && !options[:show_ppid] table.remove_column('PPID') end if @stats.platform_provides_private_dirty_rss_information? table.remove_column('Resident') else table.remove_column('Private') end puts table.to_s(title) if @stats.platform_provides_private_dirty_rss_information? total_private_dirty_rss = 0 some_private_dirty_rss_cannot_be_determined = false processes.each do |p| if p.private_dirty_rss.is_a?(Numeric) total_private_dirty_rss += p.private_dirty_rss else some_private_dirty_rss_cannot_be_determined = true end end puts "### Processes: #{processes.size}" printf "### Total private dirty RSS: %.2f MB", total_private_dirty_rss / 1024.0 if some_private_dirty_rss_cannot_be_determined puts " (?)" else puts end end end end App.new.start passenger-5.0.30/bin/passenger-status000755 000765 000024 00000025547 12233035540 020231 0ustar00honglistaff000000 000000 #!/usr/bin/env ruby # Phusion Passenger - https://www.phusionpassenger.com/ # Copyright (c) 2010-2015 Phusion Holding B.V. # # "Passenger", "Phusion Passenger" and "Union Station" are registered # trademarks of Phusion Holding B.V. # # 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, sublicense, 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 NONINFRINGEMENT. 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. ## Magic comment: begin bootstrap ## source_root = File.expand_path("..", File.dirname(__FILE__)) $LOAD_PATH.unshift("#{source_root}/src/ruby_supportlib") begin require 'rubygems' rescue LoadError end require 'phusion_passenger' ## Magic comment: end bootstrap ## PhusionPassenger.locate_directories PhusionPassenger.require_passenger_lib 'constants' PhusionPassenger.require_passenger_lib 'platform_info' PhusionPassenger.require_passenger_lib 'admin_tools/instance_registry' PhusionPassenger.require_passenger_lib 'config/utils' PhusionPassenger.require_passenger_lib 'utils/ansi_colors' require 'optparse' require 'socket' require 'net/http' include PhusionPassenger::SharedConstants include PhusionPassenger::AdminTools include PhusionPassenger::Utils::AnsiColors DEFAULT_OPTIONS = { :show => 'pool', :color => STDOUT.tty? }.freeze ##### Show status command ##### def command_show_status(argv, options) if argv.empty? instance = find_sole_instance else instance = find_instance_by_name_prefix(argv[0]) end show_status(instance, options) end def find_sole_instance instances = InstanceRegistry.new.list if instances.empty? abort "ERROR: #{PROGRAM_NAME} doesn't seem to be running. If you " + "are sure that it is running, then the causes of this problem could be:\n\n" + "1. You customized the instance registry directory using Apache's " + "PassengerInstanceRegistryDir option, Nginx's passenger_instance_registry_dir " + "option, or #{PROGRAM_NAME} Standalone's --instance-registry-dir command line " + "argument. If so, please set the environment variable PASSENGER_INSTANCE_REGISTRY_DIR " + "to that directory and run passenger-status again.\n" + "2. The instance directory has been removed by an operating system background service. " + "Please set a different instance registry directory using Apache's " + "PassengerInstanceRegistryDir option, Nginx's passenger_instance_registry_dir " + "option, or #{PROGRAM_NAME} Standalone's --instance-registry-dir command line " + "argument." elsif instances.size == 1 return instances.first else puts "It appears that multiple #{PROGRAM_NAME} instances are running. Please" puts "select a specific one by running:" puts puts " passenger-status " puts PhusionPassenger::Config::Utils.list_all_passenger_instances(instances) exit 1 end end def find_instance_by_name_prefix(name) instance = InstanceRegistry.new.find_by_name_prefix(name) if instance == :ambigious abort "ERROR: there are multiple instances whose name start with '#{name}'. Please specify the full name." elsif instance return instance else abort "ERROR: there doesn't seem to be a #{PROGRAM_NAME} instance running with the name '#{name}'." end end def show_status(instance, options) # if the noshow override is not specified, the default is to show the header, unless show=xml if options[:noheader] != true && options[:show] != 'xml' print_header(STDOUT, instance) end case options[:show] when 'pool' request = Net::HTTP::Get.new("/pool.txt?colorize=#{options[:color]}&verbose=#{options[:verbose]}") try_performing_ro_admin_basic_auth(request, instance) response = instance.http_request("agents.s/core_api", request) if response.code.to_i / 100 == 2 puts response.body elsif response.code.to_i == 401 if response["pool-empty"] == "true" puts "#{PROGRAM_NAME} is currently not serving any applications." else print_permission_error_message exit 2 end else STDERR.puts "*** An error occured." STDERR.puts "#{response.code}: #{response.body}" exit 2 end when 'requests', 'server' request = Net::HTTP::Get.new("/server.json") try_performing_ro_admin_basic_auth(request, instance) response = instance.http_request("agents.s/core_api", request) if response.code.to_i / 100 == 2 puts response.body elsif response.code.to_i == 401 print_permission_error_message exit 2 else STDERR.puts "*** An error occured." STDERR.puts "#{response.code}: #{response.body}" exit 2 end when 'backtraces' request = Net::HTTP::Get.new("/backtraces.txt") try_performing_ro_admin_basic_auth(request, instance) response = instance.http_request("agents.s/core_api", request) if response.code.to_i / 100 == 2 text = response.body # Colorize output color = PhusionPassenger::Utils::AnsiColors.new text.gsub!(/^(Thread .*:)$/, color.black_bg + color.yellow + '\1' + color.reset) text.gsub!(/^( +in '.*? )(.*?)\(/, '\1' + color.bold + '\2' + color.reset + '(') puts text elsif response.code.to_i == 401 print_permission_error_message exit 2 else STDERR.puts "*** An error occured." STDERR.puts "#{response.code}: #{response.body}" exit 2 end when 'xml' request = Net::HTTP::Get.new("/pool.xml?secrets=#{options[:verbose]}") try_performing_ro_admin_basic_auth(request, instance) response = instance.http_request("agents.s/core_api", request) if response.code.to_i / 100 == 2 indented = format_with_xmllint(response.body) if indented puts indented else puts response.body STDERR.puts "*** Tip: if you install the 'xmllint' command then the XML output will be indented." end elsif response.code.to_i == 401 if response["pool-empty"] == "true" puts "#{PROGRAM_NAME} is currently not serving any applications." else print_permission_error_message exit 2 end else STDERR.puts "*** An error occured." STDERR.puts "#{response.code}: #{response.body}" exit 2 end when 'union_station' request = Net::HTTP::Get.new("/server.json") try_performing_ro_admin_basic_auth(request, instance) response = instance.http_request("agents.s/ust_router_api", request) if response.code.to_i / 100 == 2 puts response.body elsif response.code.to_i == 401 print_permission_error_message exit 2 else STDERR.puts "*** An error occured." STDERR.puts "#{response.code}: #{response.body}" exit 2 end end end def print_header(io, instance) io.puts "Version : #{PhusionPassenger::VERSION_STRING}" io.puts "Date : #{Time.now}" io.puts "Instance: #{instance.name} (#{instance.server_software})" io.puts end def try_performing_ro_admin_basic_auth(request, instance) begin password = instance.read_only_admin_password rescue Errno::EACCES return end request.basic_auth("ro_admin", password) end def print_permission_error_message PhusionPassenger.require_passenger_lib 'platform_info/ruby' STDERR.puts "*** ERROR: You are not authorized to query the status for this " << "#{PROGRAM_NAME} instance. Please try again with '#{PhusionPassenger::PlatformInfo.ruby_sudo_command}'." end def format_with_xmllint(xml) return nil if !PhusionPassenger::PlatformInfo.find_command('xmllint') require 'open3' require 'thread' ENV['XMLLINT_INDENT'] = ' ' Open3.popen3("xmllint", "--format", "-") do |stdin, stdout, stderr| stdout_text = nil stderr_text = nil thread1 = Thread.new do stdin.write(xml) stdin.close end thread2 = Thread.new do stdout_text = stdout.read stdout.close end thread3 = Thread.new do stderr_text = stderr.read stderr.close end thread1.join thread2.join thread3.join if stdout_text.nil? || stdout_text.empty? if stderr_text !~ /No such file or directory/ && stderr_text !~ /command not found/ STDERR.puts stderr_text end return nil else return stdout_text end end end ##### Main command dispatcher ##### def create_option_parser(options) return OptionParser.new do |opts| nl = "\n" << " " * 37 opts.banner = "Usage: passenger-status [options] [instance name]" opts.separator "" opts.separator "Tool for inspecting Phusion Passenger's internal status." opts.separator "" opts.separator "Options:" opts.on("--show=pool|server|backtraces|xml|union_station", String, "Whether to show the pool's contents,#{nl}" << "the currently running requests,#{nl}" << "the backtraces of all threads or an XML#{nl}" << "description of the pool.") do |what| if what !~ /\A(pool|server|requests|backtraces|xml|union_station)\Z/ STDERR.puts "Invalid argument for --show." exit 1 else options[:show] = what end end opts.on("--no-header", "Do not display an informative header#{nl}" << "containing the timestamp, version number,#{nl}" << "etc.") do options[:noheader] = true end opts.on("--force-colors", "Display colors even if stdout is not a TTY") do options[:color] = true end opts.on("--verbose", "-v", "Show verbose information.") do options[:verbose] = true end end end def parse_argv options = DEFAULT_OPTIONS.dup parser = create_option_parser(options) begin parser.parse! rescue OptionParser::ParseError => e puts e puts puts "Please see '--help' for valid options." exit 1 end return options end def infer_command if !ARGV[0] || ARGV[0] !~ /^-/ return [:show_status, ARGV.dup] else command_name, *argv = ARGV if respond_to?("command_#{command_name}") return [command_name, argv] else abort "ERROR: unrecognized command '#{command_name}'" end end end def start options = parse_argv command, argv = infer_command send("command_#{command}", argv, options) end start