/* * Phusion Passenger - https://www.phusionpassenger.com/ * Copyright (c) 2010-2015 Phusion * * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui. * * 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_HOOKS_H_ #define _PASSENGER_HOOKS_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Passenger { using namespace std; using namespace oxt; struct HookScriptOptions { // Required. string name; string spec; // Optional. const VariantMap *agentsOptions; vector< pair > environment; HookScriptOptions() : agentsOptions(NULL) { } }; namespace { inline vector< pair > agentsOptionsToEnvVars(const VariantMap &agentsOptions) { vector< pair > result; VariantMap::ConstIterator it, end = agentsOptions.end(); result.reserve(agentsOptions.size()); for (it = agentsOptions.begin(); it != end; it++) { string key = "PASSENGER_"; const char *data = it->first.data(); const char *end = it->first.data() + it->first.size(); while (data < end) { key.append(1, toupper(*data)); data++; } result.push_back(make_pair(key, it->second)); } return result; } inline void setEnvVarsFromVector(const vector< pair > &envvars) { vector< pair >::const_iterator it; for (it = envvars.begin(); it != envvars.end(); it++) { setenv(it->first.c_str(), it->second.c_str(), 1); } } inline void createHookScriptEnvironment(const HookScriptOptions &options, vector< pair > &envvars) { vector< pair >::const_iterator it, end = options.environment.end(); if (options.agentsOptions) { envvars = agentsOptionsToEnvVars(*options.agentsOptions); } for (it = options.environment.begin(); it != end; it++) { envvars.push_back(*it); } envvars.push_back(make_pair("PASSENGER_HOOK_NAME", options.name)); } inline void parseHookScriptSpec(const HookScriptOptions &options, vector &commands) { split(options.spec, ';', commands); vector::iterator it, end = commands.end(); for (it = commands.begin(); it != end; it++) { *it = strip(*it); } } } inline bool runSingleHookScript(HookScriptOptions &options, const string &command, const vector< pair > &envvars) { TRACE_POINT_WITH_DATA(command.c_str()); pid_t pid; int e, status; P_INFO("Running " << options.name << " hook script: " << command); pid = fork(); if (pid == 0) { resetSignalHandlersAndMask(); disableMallocDebugging(); closeAllFileDescriptors(2); setEnvVarsFromVector(envvars); execlp(command.c_str(), command.c_str(), (const char * const) 0); e = errno; fprintf(stderr, "*** ERROR: Cannot execute %s hook script %s: %s (errno=%d)\n", options.name.c_str(), command.c_str(), strerror(e), e); fflush(stderr); _exit(1); return true; // Never reached. } else if (pid == -1) { e = errno; P_ERROR("Cannot fork a process for hook script " << command << ": " << strerror(e) << " (errno=" << e << ")"); return false; } else if (waitpid(pid, &status, 0) == -1) { e = errno; P_ERROR("Unable to wait for hook script " << command << " (PID " << pid << "): " << strerror(e) << " (errno=" << e << ")"); return false; } else { P_INFO("Hook script " << command << " (PID " << pid << ") exited with status " << WEXITSTATUS(status)); return WEXITSTATUS(status) == 0; } } inline bool runHookScripts(HookScriptOptions &options) { TRACE_POINT(); if (options.spec.empty()) { return true; } vector commands; vector< pair > envvars; parseHookScriptSpec(options, commands); if (commands.empty()) { return true; } createHookScriptEnvironment(options, envvars); foreach (const string command, commands) { if (!runSingleHookScript(options, command, envvars)) { return false; } } return true; } } // namespace Passenger #endif /* _PASSENGER_HOOKS_H_ */