ext/common/agents/HelperAgent/AdminServer.h in passenger-5.0.4 vs ext/common/agents/HelperAgent/AdminServer.h in passenger-5.0.5
- old
+ new
@@ -1,8 +1,8 @@
/*
* Phusion Passenger - https://www.phusionpassenger.com/
- * Copyright (c) 2013-2014 Phusion
+ * Copyright (c) 2013-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
@@ -23,10 +23,11 @@
* THE SOFTWARE.
*/
#ifndef _PASSENGER_SERVER_AGENT_ADMIN_SERVER_H_
#define _PASSENGER_SERVER_AGENT_ADMIN_SERVER_H_
+#include <boost/regex.hpp>
#include <oxt/thread.hpp>
#include <sstream>
#include <string>
#include <cstring>
@@ -73,10 +74,16 @@
private:
typedef ServerKit::HttpServer<AdminServer, ServerKit::HttpClient<Request> > ParentClass;
typedef ServerKit::HttpClient<Request> Client;
typedef ServerKit::HeaderTable HeaderTable;
+ boost::regex serverConnectionPath;
+
+ bool regex_match(const StaticString &str, const boost::regex &e) const {
+ return boost::regex_match(str.data(), str.data() + str.size(), e);
+ }
+
bool parseAuthorizationHeader(Request *req, string &username,
string &password) const
{
const LString *auth = req->headers.lookup("authorization");
@@ -124,10 +131,23 @@
return auth != NULL
&& auth->level >= level
&& constantTimeCompare(password, auth->password);
}
+ int extractThreadNumberFromClientName(const string &clientName) const {
+ boost::smatch results;
+ boost::regex re("^([0-9]+)-.*");
+
+ if (!boost::regex_match(clientName, results, re)) {
+ return -1;
+ }
+ if (results.size() != 2) {
+ return -1;
+ }
+ return stringToUint(results.str(1));
+ }
+
static VariantMap parseQueryString(const StaticString &query) {
VariantMap params;
const char *pos = query.data();
const char *end = query.data() + query.size();
@@ -153,10 +173,56 @@
}
return params;
}
+ static void disconnectClient(RequestHandler *rh, string clientName) {
+ rh->disconnect(clientName);
+ }
+
+ void processServerConnectionOperation(Client *client, Request *req) {
+ if (!authorize(client, req, FULL)) {
+ respondWith401(client, req);
+ } else if (req->method == HTTP_DELETE) {
+ StaticString path = req->getPathWithoutQueryString();
+ boost::smatch results;
+
+ boost::regex_match(path.toString(), results, serverConnectionPath);
+ if (results.size() != 2) {
+ endAsBadRequest(&client, &req, "Invalid URI");
+ return;
+ }
+
+ int threadNumber = extractThreadNumberFromClientName(results.str(1));
+ P_WARN(results.str(1));
+ P_WARN(threadNumber);
+ if (threadNumber < 1 || (unsigned int) threadNumber > requestHandlers.size()) {
+ HeaderTable headers;
+ headers.insert(req->pool, "content-type", "application/json");
+ writeSimpleResponse(client, 400, &headers,
+ "{ \"status\": \"error\", \"reason\": \"Invalid thread number\" }");
+ if (!req->ended()) {
+ endRequest(&client, &req);
+ }
+ return;
+ }
+
+ requestHandlers[threadNumber - 1]->getContext()->libev->runLater(boost::bind(
+ disconnectClient, requestHandlers[threadNumber - 1], results.str(1)));
+
+ HeaderTable headers;
+ headers.insert(req->pool, "content-type", "application/json");
+ writeSimpleResponse(client, 200, &headers,
+ "{ \"status\": \"ok\" }");
+ if (!req->ended()) {
+ endRequest(&client, &req);
+ }
+ } else {
+ respondWith405(client, req);
+ }
+ }
+
static void inspectRequestHandlerState(RequestHandler *rh, Json::Value *json) {
*json = rh->inspectStateAsJson();
}
void processServerStatus(Client *client, Request *req) {
@@ -394,19 +460,23 @@
respondWith401(client, req);
}
HeaderTable headers;
string logFile = getLogFile();
+ string fileDescriptorLogFile = getFileDescriptorLogFile();
headers.insert(req->pool, "content-type", "application/json");
Json::Value doc;
requestHandlers[0]->getContext()->libev->runSync(boost::bind(
getRequestHandlerConfig, requestHandlers[0], &doc));
doc["log_level"] = getLogLevel();
if (!logFile.empty()) {
doc["log_file"] = logFile;
}
+ if (!fileDescriptorLogFile.empty()) {
+ doc["file_descriptor_log_file"] = fileDescriptorLogFile;
+ }
writeSimpleResponse(client, 200, &headers,
psg_pstrdup(req->pool, doc.toStyledString()));
if (!req->ended()) {
endRequest(&client, &req);
@@ -436,15 +506,31 @@
if (json.isMember("log_level")) {
setLogLevel(json["log_level"].asInt());
}
if (json.isMember("log_file")) {
- if (!setLogFile(json["log_file"].asCString())) {
- int e = errno;
+ string logFile = json["log_file"].asString();
+ try {
+ logFile = absolutizePath(logFile);
+ } catch (const SystemException &e) {
unsigned int bufsize = 1024;
char *message = (char *) psg_pnalloc(req->pool, bufsize);
snprintf(message, bufsize, "{ \"status\": \"error\", "
+ "\"message\": \"Cannot absolutize log file filename: %s\" }",
+ e.what());
+ writeSimpleResponse(client, 500, &headers, message);
+ if (!req->ended()) {
+ endRequest(&client, &req);
+ }
+ return;
+ }
+
+ int e;
+ if (!setLogFile(logFile, &e)) {
+ unsigned int bufsize = 1024;
+ char *message = (char *) psg_pnalloc(req->pool, bufsize);
+ snprintf(message, bufsize, "{ \"status\": \"error\", "
"\"message\": \"Cannot open log file: %s (errno=%d)\" }",
strerror(e), e);
writeSimpleResponse(client, 500, &headers, message);
if (!req->ended()) {
endRequest(&client, &req);
@@ -466,37 +552,59 @@
void processReopenLogs(Client *client, Request *req) {
if (req->method != HTTP_POST) {
respondWith405(client, req);
} else if (authorize(client, req, FULL)) {
+ int e;
HeaderTable headers;
headers.insert(req->pool, "content-type", "application/json");
string logFile = getLogFile();
if (logFile.empty()) {
writeSimpleResponse(client, 500, &headers, "{ \"status\": \"error\", "
"\"code\": \"NO_LOG_FILE\", "
"\"message\": \"" PROGRAM_NAME " was not configured with a log file.\" }\n");
- } else {
- if (!setLogFile(logFile.c_str())) {
- int e = errno;
+ if (!req->ended()) {
+ endRequest(&client, &req);
+ }
+ return;
+ }
+
+ if (!setLogFile(logFile, &e)) {
+ unsigned int bufsize = 1024;
+ char *message = (char *) psg_pnalloc(req->pool, bufsize);
+ snprintf(message, bufsize, "{ \"status\": \"error\", "
+ "\"code\": \"LOG_FILE_OPEN_ERROR\", "
+ "\"message\": \"Cannot reopen log file %s: %s (errno=%d)\" }",
+ logFile.c_str(), strerror(e), e);
+ writeSimpleResponse(client, 500, &headers, message);
+ if (!req->ended()) {
+ endRequest(&client, &req);
+ }
+ return;
+ }
+ P_NOTICE("Log file reopened.");
+
+ if (hasFileDescriptorLogFile()) {
+ if (!setFileDescriptorLogFile(getFileDescriptorLogFile(), &e)) {
unsigned int bufsize = 1024;
char *message = (char *) psg_pnalloc(req->pool, bufsize);
snprintf(message, bufsize, "{ \"status\": \"error\", "
- "\"code\": \"LOG_FILE_OPEN_ERROR\", "
- "\"message\": \"Cannot reopen log file: %s (errno=%d)\" }",
- strerror(e), e);
+ "\"code\": \"FD_LOG_FILE_OPEN_ERROR\", "
+ "\"message\": \"Cannot reopen file descriptor log file %s: %s (errno=%d)\" }",
+ getFileDescriptorLogFile().c_str(), strerror(e), e);
writeSimpleResponse(client, 500, &headers, message);
if (!req->ended()) {
endRequest(&client, &req);
}
return;
}
- P_NOTICE("Log file reopened.");
- writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }\n");
+ P_NOTICE("File descriptor log file reopened.");
}
+ writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }\n");
+
if (!req->ended()) {
endRequest(&client, &req);
}
} else {
respondWith401(client, req);
@@ -566,10 +674,12 @@
" " << StaticString(req->path.start->data, req->path.size));
try {
if (path == P_STATIC_STRING("/server.json")) {
processServerStatus(client, req);
+ } else if (regex_match(path, serverConnectionPath)) {
+ processServerConnectionOperation(client, req);
} else if (path == P_STATIC_STRING("/pool.xml")) {
processPoolStatusXml(client, req);
} else if (path == P_STATIC_STRING("/pool.txt")) {
processPoolStatusTxt(client, req);
} else if (path == P_STATIC_STRING("/pool/restart_app_group.json")) {
@@ -663,9 +773,10 @@
EventFd *exitEvent;
vector<Authorization> authorizations;
AdminServer(ServerKit::Context *context)
: ParentClass(context),
+ serverConnectionPath("^/server/(.+)\\.json$"),
exitEvent(NULL)
{ }
virtual StaticString getServerName() const {
return P_STATIC_STRING("AdminServer");