관리-도구
편집 파일: ManifestGeneration.h
/* * Phusion Passenger - https://www.phusionpassenger.com/ * Copyright (c) 2017 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. */ #ifndef _PASSENGER_APACHE2_MODULE_CONFIG_GENERAL_MANIFEST_GENERATION_H_ #define _PASSENGER_APACHE2_MODULE_CONFIG_GENERAL_MANIFEST_GENERATION_H_ #include <string> #include <cstddef> #include <string> #include <boost/function.hpp> #include <jsoncpp/json.h> #include "../Config.h" #include "../Utils.h" #include <StaticString.h> #include <FileTools/PathManip.h> // The APR headers must come after the Passenger headers. // See Hooks.cpp to learn why. #include <httpd.h> #include <http_config.h> // In Apache < 2.4, this macro was necessary for core_dir_config and other structs #define CORE_PRIVATE #include <http_core.h> extern "C" module AP_MODULE_DECLARE_DATA passenger_module; #ifndef ap_get_core_module_config #define ap_get_core_module_config(s) ap_get_module_config(s, &core_module) #endif namespace Passenger { namespace Apache2Module { using namespace std; class ConfigManifestGenerator { private: Json::Value manifest; server_rec *serverRec; apr_pool_t *pool; void autoGenerated_generateConfigManifestForServerConfig(); void autoGenerated_generateConfigManifestForDirConfig(server_rec *serverRec, core_server_config *csconf,core_dir_config *cdconf, DirConfig *pdconf, DirConfigContext context); void autoGenerated_setGlobalConfigDefaults(); void autoGenerated_setAppConfigDefaults(); void autoGenerated_setLocationConfigDefaults(); void processDirConfig(server_rec *serverRec, core_server_config *csconf, core_dir_config *cdconf, DirConfig *pdconf, DirConfigContext context) { if (pdconf->getEnabled()) { autoGenerated_generateConfigManifestForDirConfig( serverRec, csconf, cdconf, pdconf, context); } } void findOrCreateAppAndLocOptionsContainers(server_rec *serverRec, core_server_config *csconf, core_dir_config *cdconf, DirConfig *pdconf, DirConfigContext context, Json::Value **appOptionsContainer, Json::Value **locOptionsContainer) { if (*appOptionsContainer != NULL && *locOptionsContainer != NULL) { return; } if (context == DCC_GLOBAL) { *appOptionsContainer = &manifest["default_application_configuration"]; *locOptionsContainer = &manifest["default_location_configuration"]; } else if (context == DCC_VHOST) { string appGroupName = inferLocConfAppGroupName(csconf, pdconf); Json::Value &appConfigContainer = findOrCreateAppConfigContainer(appGroupName); *appOptionsContainer = &appConfigContainer["options"]; *locOptionsContainer = &appConfigContainer["default_location_configuration"]; // Create a default value for PassengerAppGroupName and // PassengerAppRoot if we just created this config container if ((*appOptionsContainer)->empty()) { addOptionsContainerInferredDefaultStr(**appOptionsContainer, "PassengerAppGroupName", appGroupName); addOptionsContainerInferredDefaultStr(**appOptionsContainer, "PassengerAppRoot", inferDefaultAppRoot(csconf)); } } else { // We are inside a <Directory> or <Location> string appGroupName = inferLocConfAppGroupName(csconf, pdconf); Json::Value &appConfigContainer = findOrCreateAppConfigContainer(appGroupName); Json::Value &locConfigContainer = findOrCreateLocConfigContainer(appConfigContainer, serverRec, cdconf, pdconf); *appOptionsContainer = &appConfigContainer["options"]; *locOptionsContainer = &locConfigContainer["options"]; } } string inferLocConfAppGroupName(core_server_config *csconf, DirConfig *pdconf) { if (pdconf->getAppGroupName().empty()) { string appRoot; StaticString appEnv; if (pdconf->getAppRoot().empty()) { // ap_document_root is already relativized against // the ServerRoot (see Apache server/core.c set_document_root()) appRoot = csconf->ap_document_root + P_STATIC_STRING("/.."); } else { appRoot = ap_server_root_relative(pool, pdconf->getAppRoot().c_str()); } appRoot = absolutizePath(appRoot); if (pdconf->getAppEnv().empty()) { appEnv = P_STATIC_STRING(DEFAULT_APP_ENV); } else { appEnv = pdconf->getAppEnv(); } return appRoot + " (" + appEnv + ")"; } else { return pdconf->getAppGroupName(); } } string inferDefaultAppRoot(core_server_config *csconf) { return absolutizePath(csconf->ap_document_root + P_STATIC_STRING("/..")); } Json::Value &findOrCreateAppConfigContainer(const string &appGroupName) { Json::Value &result = manifest["application_configurations"][appGroupName]; if (result.isNull()) { result["options"] = Json::objectValue; result["default_location_configuration"] = Json::objectValue; result["location_configurations"] = Json::arrayValue; } return result; } Json::Value &findOrCreateLocConfigContainer(Json::Value &appConfigContainer, server_rec *serverRec, core_dir_config *cdconf, DirConfig *pdconf) { Json::Value &locConfigsContainer = appConfigContainer["location_configurations"]; Json::Value *locConfigContainer = findLocConfigContainer( locConfigsContainer, serverRec, cdconf, pdconf); if (locConfigContainer == NULL) { locConfigContainer = &createLocConfigContainer(locConfigsContainer, serverRec, cdconf, pdconf); } return *locConfigContainer; } Json::Value *findLocConfigContainer(Json::Value &locConfigsContainer, server_rec *serverRec, core_dir_config *cdconf, DirConfig *pdconf) { Json::Value::iterator it, end = locConfigsContainer.end(); for (it = locConfigsContainer.begin(); it != end; it++) { Json::Value &locConfigContainer = *it; Json::Value &locationMatcherDoc = locConfigContainer["location_matcher"]; string jsonLocationMatcherType = locationMatcherDoc["type"].asString(); if (cdconf->r != NULL) { if (jsonLocationMatcherType != "regex") { continue; } } else { if (jsonLocationMatcherType != "prefix") { continue; } } string jsonLocationMatcherValue = locationMatcherDoc["value"].asString(); if (jsonLocationMatcherValue != cdconf->d) { continue; } Json::Value &serverNamesDoc = locConfigContainer["web_server_virtual_host"]["server_names"]; if (!matchesAnyServerNames(serverRec, serverNamesDoc)) { continue; } return &locConfigContainer; } return NULL; } Json::Value &createLocConfigContainer(Json::Value &locConfigsContainer, server_rec *serverRec, core_dir_config *cdconf, DirConfig *pdconf) { Json::Value vhostDoc; if (serverRec->defn_name) { vhostDoc["server_names"].append(serverRec->defn_name); } else { vhostDoc["server_names"].append("NOT_RECEIVED"); } Json::Value locationMatcherDoc; locationMatcherDoc["value"] = cdconf->d; if (cdconf->r != NULL) { locationMatcherDoc["type"] = "regex"; } else { locationMatcherDoc["type"] = "prefix"; } Json::Value locConfigContainer; locConfigContainer["web_server_virtual_host"] = vhostDoc; locConfigContainer["location_matcher"] = locationMatcherDoc; locConfigContainer["options"] = Json::objectValue; return locConfigsContainer.append(locConfigContainer); } bool matchesAnyServerNames(server_rec *serverRec, const Json::Value &serverNamesDoc) { Json::Value::const_iterator it, end = serverNamesDoc.end(); for (it = serverNamesDoc.begin(); it != end; it++) { // TODO: lowercase match if (it->asString() == serverRec->defn_name) { return true; } } return false; } Json::Value &findOrCreateOptionContainer(Json::Value &optionsContainer, const char *optionName, size_t optionNameLen) { Json::Value &result = optionsContainer[string(optionName, optionNameLen)]; if (result.isNull()) { initOptionContainer(result); } return result; } void initOptionContainer(Json::Value &doc) { doc["value_hierarchy"] = Json::arrayValue; } Json::Value &addOptionContainerHierarchyMember(Json::Value &optionContainer, const StaticString &sourceFile, unsigned int sourceLine) { Json::Value hierarchyMember; hierarchyMember["source"]["type"] = "web-server-config"; hierarchyMember["source"]["path"] = Json::Value(sourceFile.data(), sourceFile.data() + sourceFile.size()); hierarchyMember["source"]["line"] = sourceLine; return optionContainer["value_hierarchy"].append(hierarchyMember); } void reverseValueHierarchies() { Json::Value &appConfigsContainer = manifest["application_configurations"]; Json::Value::iterator it, end = appConfigsContainer.end(); reverseValueHierarchiesInOptionsContainer(manifest["global_configuration"]); reverseValueHierarchiesInOptionsContainer(manifest["default_application_configuration"]); reverseValueHierarchiesInOptionsContainer(manifest["default_location_configuration"]); for (it = appConfigsContainer.begin(); it != end; it++) { Json::Value &appConfigContainer = *it; reverseValueHierarchiesInOptionsContainer( appConfigContainer["options"]); reverseValueHierarchiesInOptionsContainer( appConfigContainer["default_location_configuration"]); if (appConfigContainer.isMember("location_configurations")) { Json::Value &locationConfigsContainer = appConfigContainer["location_configurations"]; Json::Value::iterator it2, end2 = locationConfigsContainer.end(); for (it2 = locationConfigsContainer.begin(); it2 != end2; it2++) { Json::Value &locationConfigContainer = *it2; reverseValueHierarchiesInOptionsContainer( locationConfigContainer["options"]); } } } } void reverseValueHierarchiesInOptionsContainer(Json::Value &optionsContainer) { Json::Value::iterator it, end = optionsContainer.end(); for (it = optionsContainer.begin(); it != end; it++) { Json::Value &optionContainer = *it; Json::Value &valueHierarchyDoc = optionContainer["value_hierarchy"]; unsigned int len = valueHierarchyDoc.size(); for (unsigned int i = 0; i < len / 2; i++) { valueHierarchyDoc[i].swap(valueHierarchyDoc[len - i - 1]); } } } void inheritApplicationValueHierarchies() { Json::Value &appConfigsContainer = manifest["application_configurations"]; Json::Value &defaultAppConfigContainer = manifest["default_application_configuration"]; Json::Value::iterator it, end = appConfigsContainer.end(); /* Iterate through all 'application_configurations' objects */ for (it = appConfigsContainer.begin(); it != end; it++) { Json::Value &appConfigContainer = *it; Json::Value::iterator it2, end2; /* Iterate through all its 'options' objects */ Json::Value &optionsContainer = appConfigContainer["options"]; end2 = optionsContainer.end(); for (it2 = optionsContainer.begin(); it2 != end2; it2++) { /* For each option, inherit the value hierarchies * from the 'default_application_configuration' object. * * Since the value hierarchy array is already in * most-to-least-specific order, simply appending * the 'default_application_configuration' hierarchy is * enough. */ const char *optionNameEnd; const char *optionName = it2.memberName(&optionNameEnd); if (defaultAppConfigContainer.isMember(optionName, optionNameEnd)) { Json::Value &optionContainer = *it2; Json::Value &defaultAppConfig = defaultAppConfigContainer[optionName]; Json::Value &valueHierarchyDoc = optionContainer["value_hierarchy"]; Json::Value &valueHierarchyFromDefault = defaultAppConfig["value_hierarchy"]; jsonAppendValues(valueHierarchyDoc, valueHierarchyFromDefault); maybeInheritStringArrayHierarchyValues(valueHierarchyDoc); maybeInheritStringKeyvalHierarchyValues(valueHierarchyDoc); } } /* Iterate through all 'default_application_configuration' options */ end2 = defaultAppConfigContainer.end(); for (it2 = defaultAppConfigContainer.begin(); it2 != end2; it2++) { /* For each default app config object, if there is no object in * the current context's 'options' with the same name, then add * it there. */ const char *optionNameEnd; const char *optionName = it2.memberName(&optionNameEnd); if (!optionsContainer.isMember(optionName, optionNameEnd)) { Json::Value &optionContainer = *it2; optionsContainer[optionName] = optionContainer; } } } } void maybeInheritStringArrayHierarchyValues(Json::Value &valueHierarchyDoc) { if (valueHierarchyDoc.empty()) { return; } if (!valueHierarchyDoc[0]["value"].isArray()) { return; } unsigned int len = valueHierarchyDoc.size(); for (unsigned int i = len - 1; i >= 1; i--) { Json::Value ¤t = valueHierarchyDoc[i]; Json::Value &next = valueHierarchyDoc[i - 1]; Json::Value ¤tValue = current["value"]; Json::Value &nextValue = next["value"]; Json::Value::iterator it, end = currentValue.end(); for (it = currentValue.begin(); it != end; it++) { if (!jsonArrayContains(nextValue, *it)) { nextValue.append(*it); } } } } void maybeInheritStringKeyvalHierarchyValues(Json::Value &valueHierarchyDoc) { if (valueHierarchyDoc.empty()) { return; } if (!valueHierarchyDoc[0]["value"].isObject()) { return; } unsigned int len = valueHierarchyDoc.size(); for (unsigned int i = len - 1; i >= 1; i--) { Json::Value ¤t = valueHierarchyDoc[i]; Json::Value &next = valueHierarchyDoc[i - 1]; Json::Value ¤tValue = current["value"]; Json::Value &nextValue = next["value"]; Json::Value::iterator it, end = currentValue.end(); for (it = currentValue.begin(); it != end; it++) { const char *nameEnd; const char *name = it.memberName(&nameEnd); if (!nextValue.isMember(name, nameEnd)) { nextValue[name] = *it; } } } } void inheritLocationValueHierarchies() { Json::Value &appConfigsContainer = manifest["application_configurations"]; Json::Value &defaultLocConfigContainer = manifest["default_location_configuration"]; Json::Value::iterator it, end = appConfigsContainer.end(); /* Iterate through all 'application_configurations' objects */ for (it = appConfigsContainer.begin(); it != end; it++) { Json::Value &appConfigContainer = *it; Json::Value::iterator it2, end2; /* Iterate through all its 'default_location_configuration' options */ Json::Value &appDefaultLocationConfigs = appConfigContainer[ "default_location_configuration"]; end2 = appDefaultLocationConfigs.end(); for (it2 = appDefaultLocationConfigs.begin(); it2 != end2; it2++) { /* For each option, inherit the value hierarchies * from the top-level 'default_application_configuration' object. * * Since the value hierarchy array is already in * most-to-least-specific order, simply appending * the 'default_application_configuration' hierarchy is * enough. */ const char *optionNameEnd; const char *optionName = it2.memberName(&optionNameEnd); if (defaultLocConfigContainer.isMember(optionName, optionNameEnd)) { Json::Value &optionContainer = *it2; Json::Value &defaultLocationConfig = defaultLocConfigContainer[optionName]; Json::Value &valueHierarchyDoc = optionContainer["value_hierarchy"]; Json::Value &valueHierarchyFromDefault = defaultLocationConfig["value_hierarchy"]; jsonAppendValues(valueHierarchyDoc, valueHierarchyFromDefault); maybeInheritStringArrayHierarchyValues(valueHierarchyDoc); maybeInheritStringKeyvalHierarchyValues(valueHierarchyDoc); } } /* Iterate through all top-level 'default_location_configuration' options */ end2 = defaultLocConfigContainer.end(); for (it2 = defaultLocConfigContainer.begin(); it2 != end2; it2++) { /* For each default top-level 'default_location_configuration' option, * if there is no object in the current context's 'default_application_configuration' * with the same name, then add it there. */ const char *optionNameEnd; const char *optionName = it2.memberName(&optionNameEnd); if (!appDefaultLocationConfigs.isMember(optionName, optionNameEnd)) { appDefaultLocationConfigs[optionName] = *it2; } } /* Iterate through all its 'location_configurations' options */ if (appConfigContainer.isMember("location_configurations")) { Json::Value &locationConfigsContainer = appConfigContainer["location_configurations"]; end2 = locationConfigsContainer.end(); for (it2 = locationConfigsContainer.begin(); it2 != end2; it2++) { Json::Value &locationContainer = *it2; Json::Value &optionsContainer = locationContainer["options"]; Json::Value::iterator it3, end3 = optionsContainer.end(); for (it3 = optionsContainer.begin(); it3 != end3; it3++) { /* For each option, inherit the value hierarchies * from the 'default_location_configuration' belonging * to the current app (which also contains the global * location config defaults). * * Since the value hierarchy array is already in * most-to-least-specific order, simply appending * the 'default_location_configuration' hierarchy is * enough. */ const char *optionNameEnd; const char *optionName = it3.memberName(&optionNameEnd); if (appDefaultLocationConfigs.isMember(optionName, optionNameEnd)) { Json::Value &optionContainer = *it3; Json::Value &defaultLocationConfig = appDefaultLocationConfigs[optionName]; Json::Value &valueHierarchyDoc = optionContainer["value_hierarchy"]; Json::Value &valueHierarchyFromDefault = defaultLocationConfig["value_hierarchy"]; jsonAppendValues(valueHierarchyDoc, valueHierarchyFromDefault); maybeInheritStringArrayHierarchyValues(valueHierarchyDoc); maybeInheritStringKeyvalHierarchyValues(valueHierarchyDoc); } } } } } } void addOptionsContainerDynamicDefault(Json::Value &optionsContainer, const char *optionName, const StaticString &desc) { Json::Value &optionContainer = optionsContainer[optionName]; if (optionContainer.isNull()) { initOptionContainer(optionContainer); } Json::Value hierarchyMember; hierarchyMember["source"]["type"] = "dynamic-default-description"; hierarchyMember["value"] = Json::Value(desc.data(), desc.data() + desc.size()); optionContainer["value_hierarchy"].append(hierarchyMember); } Json::Value &addOptionsContainerDefault(Json::Value &optionsContainer, const char *defaultType, const char *optionName) { Json::Value &optionContainer = optionsContainer[optionName]; if (optionContainer.isNull()) { initOptionContainer(optionContainer); } Json::Value hierarchyMember; hierarchyMember["source"]["type"] = defaultType; return optionContainer["value_hierarchy"].append(hierarchyMember); } void addOptionsContainerStaticDefaultStr(Json::Value &optionsContainer, const char *optionName, const StaticString &value) { Json::Value &hierarchyMember = addOptionsContainerDefault( optionsContainer, "default", optionName); hierarchyMember["value"] = Json::Value(value.data(), value.data() + value.size()); } void addOptionsContainerStaticDefaultInt(Json::Value &optionsContainer, const char *optionName, int value) { Json::Value &hierarchyMember = addOptionsContainerDefault( optionsContainer, "default", optionName); hierarchyMember["value"] = value; } void addOptionsContainerStaticDefaultBool(Json::Value &optionsContainer, const char *optionName, bool value) { Json::Value &hierarchyMember = addOptionsContainerDefault( optionsContainer, "default", optionName); hierarchyMember["value"] = value; } void addOptionsContainerInferredDefaultStr(Json::Value &optionsContainer, const char *optionName, const StaticString &value) { Json::Value &hierarchyMember = addOptionsContainerDefault( optionsContainer, "inferred-default", optionName); hierarchyMember["value"] = Json::Value(value.data(), value.data() + value.size()); } void jsonAppendValues(Json::Value &doc, const Json::Value &doc2) { Json::Value::const_iterator it, end = doc2.end(); for (it = doc2.begin(); it != end; it++) { doc.append(*it); } } bool jsonArrayContains(const Json::Value &doc, const Json::Value &elem) { Json::Value::const_iterator it, end = doc.end(); for (it = doc.begin(); it != end; it++) { if (*it == elem) { return true; } } return false; } public: ConfigManifestGenerator(server_rec *_serverRec, apr_pool_t *_pool) : serverRec(_serverRec), pool(_pool) { manifest["global_configuration"] = Json::objectValue; manifest["default_application_configuration"] = Json::objectValue; manifest["default_location_configuration"] = Json::objectValue; manifest["application_configurations"] = Json::objectValue; } const Json::Value &execute() { autoGenerated_generateConfigManifestForServerConfig(); traverseAllDirConfigs(serverRec, pool, boost::bind<void>(&ConfigManifestGenerator::processDirConfig, this, boost::placeholders::_1, boost::placeholders::_2, boost::placeholders::_3, boost::placeholders::_4, boost::placeholders::_5)); reverseValueHierarchies(); autoGenerated_setGlobalConfigDefaults(); autoGenerated_setAppConfigDefaults(); autoGenerated_setLocationConfigDefaults(); inheritApplicationValueHierarchies(); inheritLocationValueHierarchies(); return manifest; } }; } // namespace Apache2Module } // namespace Passenger #include "../ServerConfig/AutoGeneratedManifestGeneration.cpp" #include "../DirConfig/AutoGeneratedManifestGeneration.cpp" #include "AutoGeneratedManifestDefaultsInitialization.cpp" #endif /* _PASSENGER_APACHE2_MODULE_CONFIG_GENERAL_MANIFEST_GENERATION_H_ */