passenger 5.0.9 → 5.0.10

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of passenger might be problematic. Click here for more details.

Files changed (106) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.asc +7 -7
  3. data.tar.gz.asc +7 -7
  4. data/CHANGELOG +15 -0
  5. data/CONTRIBUTORS +6 -0
  6. data/README.md +1 -1
  7. data/bin/passenger-install-apache2-module +24 -11
  8. data/bin/passenger-status +29 -14
  9. data/build/agents.rb +12 -10
  10. data/build/cxx_tests.rb +30 -30
  11. data/doc/Design and Architecture.html +1 -10
  12. data/doc/Design and Architecture.txt +1 -6
  13. data/doc/Users guide Apache.html +1 -19
  14. data/doc/Users guide Apache.txt +1 -1
  15. data/doc/Users guide Nginx.html +2 -20
  16. data/doc/Users guide Nginx.txt +2 -2
  17. data/doc/users_guide_snippets/tips.txt +0 -9
  18. data/ext/common/ApplicationPool2/ApiKey.h +158 -0
  19. data/ext/common/ApplicationPool2/BasicGroupInfo.h +81 -0
  20. data/ext/common/ApplicationPool2/BasicProcessInfo.h +106 -0
  21. data/ext/common/ApplicationPool2/Common.h +5 -44
  22. data/ext/common/ApplicationPool2/Context.h +94 -0
  23. data/ext/common/ApplicationPool2/Group.h +130 -1205
  24. data/ext/common/ApplicationPool2/Group/InitializationAndShutdown.cpp +190 -0
  25. data/ext/common/ApplicationPool2/Group/InternalUtils.cpp +329 -0
  26. data/ext/common/ApplicationPool2/Group/LifetimeAndBasics.cpp +103 -0
  27. data/ext/common/ApplicationPool2/{Pool/Debug.h → Group/Miscellaneous.cpp} +40 -38
  28. data/ext/common/ApplicationPool2/Group/OutOfBandWork.cpp +323 -0
  29. data/ext/common/ApplicationPool2/Group/ProcessListManagement.cpp +606 -0
  30. data/ext/common/ApplicationPool2/Group/SessionManagement.cpp +337 -0
  31. data/ext/common/ApplicationPool2/Group/SpawningAndRestarting.cpp +478 -0
  32. data/ext/common/ApplicationPool2/Group/StateInspection.cpp +197 -0
  33. data/ext/common/ApplicationPool2/Group/Verification.cpp +159 -0
  34. data/ext/common/ApplicationPool2/Implementation.cpp +19 -1401
  35. data/ext/common/ApplicationPool2/Options.h +5 -5
  36. data/ext/common/ApplicationPool2/Pool.h +260 -815
  37. data/ext/common/ApplicationPool2/Pool/{AnalyticsCollection.h → AnalyticsCollection.cpp} +55 -56
  38. data/ext/common/ApplicationPool2/Pool/{GarbageCollection.h → GarbageCollection.cpp} +49 -49
  39. data/ext/common/ApplicationPool2/Pool/GeneralUtils.cpp +241 -0
  40. data/ext/common/ApplicationPool2/Pool/GroupUtils.cpp +276 -0
  41. data/ext/common/ApplicationPool2/Pool/InitializationAndShutdown.cpp +145 -0
  42. data/ext/common/ApplicationPool2/Pool/Miscellaneous.cpp +244 -0
  43. data/ext/common/ApplicationPool2/Pool/ProcessUtils.cpp +330 -0
  44. data/ext/common/ApplicationPool2/Pool/StateInspection.cpp +299 -0
  45. data/ext/common/ApplicationPool2/Process.h +399 -205
  46. data/ext/common/ApplicationPool2/Session.h +70 -28
  47. data/ext/common/ApplicationPool2/Socket.h +1 -0
  48. data/ext/common/Constants.h +11 -3
  49. data/ext/common/Exceptions.h +1 -1
  50. data/ext/common/Logging.cpp +9 -4
  51. data/ext/common/Logging.h +6 -0
  52. data/ext/common/ServerKit/HttpServer.h +225 -215
  53. data/ext/common/ServerKit/Server.h +57 -57
  54. data/ext/common/SpawningKit/BackgroundIOCapturer.h +160 -0
  55. data/ext/common/SpawningKit/Config.h +107 -0
  56. data/ext/common/{ApplicationPool2 → SpawningKit}/DirectSpawner.h +17 -16
  57. data/ext/common/{ApplicationPool2 → SpawningKit}/DummySpawner.h +33 -33
  58. data/ext/common/{ApplicationPool2/SpawnerFactory.h → SpawningKit/Factory.h} +17 -17
  59. data/ext/common/{ApplicationPool2/ComponentInfo.h → SpawningKit/Options.h} +8 -21
  60. data/ext/common/SpawningKit/PipeWatcher.h +148 -0
  61. data/ext/common/{ApplicationPool2/PipeWatcher.h → SpawningKit/Result.h} +15 -33
  62. data/ext/common/{ApplicationPool2 → SpawningKit}/SmartSpawner.h +52 -57
  63. data/ext/common/{ApplicationPool2 → SpawningKit}/Spawner.h +83 -371
  64. data/ext/common/SpawningKit/UserSwitchingRules.h +265 -0
  65. data/ext/common/Utils/BufferedIO.h +24 -0
  66. data/ext/common/{ApplicationPool2/SpawnObject.h → Utils/ClassUtils.h} +24 -51
  67. data/ext/common/Utils/IOUtils.cpp +70 -0
  68. data/ext/common/Utils/IOUtils.h +19 -0
  69. data/ext/common/Utils/JsonUtils.h +113 -0
  70. data/ext/common/Utils/StrIntUtils.h +29 -0
  71. data/ext/common/Utils/json.h +1 -1
  72. data/ext/common/agents/ApiServerUtils.h +941 -0
  73. data/ext/common/agents/HelperAgent/{AdminServer.h → ApiServer.h} +163 -365
  74. data/ext/common/agents/HelperAgent/Main.cpp +86 -88
  75. data/ext/common/agents/HelperAgent/OptionParser.h +9 -10
  76. data/ext/common/agents/HelperAgent/RequestHandler/BufferBody.cpp +3 -0
  77. data/ext/common/agents/HelperAgent/RequestHandler/ForwardResponse.cpp +2 -0
  78. data/ext/common/agents/HelperAgent/RequestHandler/Hooks.cpp +1 -1
  79. data/ext/common/agents/HelperAgent/RequestHandler/SendRequest.cpp +2 -2
  80. data/ext/common/agents/LoggingAgent/ApiServer.h +279 -0
  81. data/ext/common/agents/LoggingAgent/Main.cpp +41 -51
  82. data/ext/common/agents/LoggingAgent/OptionParser.h +11 -11
  83. data/ext/common/agents/Watchdog/ApiServer.h +311 -0
  84. data/ext/common/agents/Watchdog/Main.cpp +91 -65
  85. data/helper-scripts/prespawn +2 -0
  86. data/lib/phusion_passenger.rb +1 -1
  87. data/lib/phusion_passenger/admin_tools/instance.rb +1 -1
  88. data/lib/phusion_passenger/common_library.rb +27 -14
  89. data/lib/phusion_passenger/config/{admin_command_command.rb → api_call_command.rb} +19 -16
  90. data/lib/phusion_passenger/config/detach_process_command.rb +6 -3
  91. data/lib/phusion_passenger/config/main.rb +3 -5
  92. data/lib/phusion_passenger/config/reopen_logs_command.rb +29 -7
  93. data/lib/phusion_passenger/config/restart_app_command.rb +13 -4
  94. data/lib/phusion_passenger/config/utils.rb +15 -8
  95. data/lib/phusion_passenger/constants.rb +6 -2
  96. data/lib/phusion_passenger/platform_info/apache.rb +4 -0
  97. data/lib/phusion_passenger/platform_info/apache_detector.rb +18 -3
  98. data/resources/templates/apache2/mpm_unknown.txt.erb +20 -0
  99. metadata +42 -21
  100. metadata.gz.asc +7 -7
  101. data/ext/common/ApplicationPool2/Pool/GeneralUtils.h +0 -127
  102. data/ext/common/ApplicationPool2/Pool/Inspection.h +0 -219
  103. data/ext/common/ApplicationPool2/Pool/ProcessUtils.h +0 -85
  104. data/ext/common/ApplicationPool2/SuperGroup.h +0 -706
  105. data/ext/common/agents/LoggingAgent/AdminServer.h +0 -435
  106. data/ext/common/agents/Watchdog/AdminServer.h +0 -432
@@ -94,6 +94,14 @@ void parseTcpSocketAddress(const StaticString & restrict_ref address,
94
94
  */
95
95
  bool isLocalSocketAddress(const StaticString &address);
96
96
 
97
+ /**
98
+ * Sets a socket in blocking mode.
99
+ *
100
+ * @throws SystemException Something went wrong.
101
+ * @ingroup Support
102
+ */
103
+ void setBlocking(int fd);
104
+
97
105
  /**
98
106
  * Sets a socket in non-blocking mode.
99
107
  *
@@ -591,6 +599,17 @@ int readFileDescriptor(int fd, unsigned long long *timeout = NULL);
591
599
  */
592
600
  void writeFileDescriptor(int fd, int fdToSend, unsigned long long *timeout = NULL);
593
601
 
602
+ /**
603
+ * Return the effective UID and GID of the peer connected to a Unix domain socket.
604
+ *
605
+ * @throws SystemException Something went wrong. If reading credentials over a Unix
606
+ * domain socket is not supported for the current platform,
607
+ * then the error code is ENOSYS. If the socket does not
608
+ * support credentials passing, then the error code is
609
+ * EPROTONOSUPPORT.
610
+ */
611
+ void readPeerCredentials(int sock, uid_t *uid, gid_t *gid);
612
+
594
613
  /**
595
614
  * Closes the given file descriptor and throws an exception if anything goes wrong.
596
615
  * This function also works around certain close() bugs and quirks on certain
@@ -28,15 +28,128 @@
28
28
  #include <string>
29
29
  #include <cstdio>
30
30
  #include <cstddef>
31
+ #include <boost/cstdint.hpp>
31
32
  #include <StaticString.h>
32
33
  #include <Utils/json.h>
33
34
  #include <Utils/StrIntUtils.h>
35
+ #include <Utils/VariantMap.h>
34
36
 
35
37
  namespace Passenger {
36
38
 
37
39
  using namespace std;
38
40
 
39
41
 
42
+ /**************************************************************
43
+ *
44
+ * Methods for querying fields from a JSON document.
45
+ * If the field is missing, thhese methods can either return
46
+ * a default value, or throw an exception.
47
+ *
48
+ **************************************************************/
49
+
50
+ inline const Json::Value &
51
+ getJsonField(const Json::Value &json, const char *key) {
52
+ Json::StaticString theKey(key);
53
+ if (json.isMember(theKey)) {
54
+ return json[theKey];
55
+ } else {
56
+ throw VariantMap::MissingKeyException(key);
57
+ }
58
+ }
59
+
60
+ inline Json::Value &
61
+ getJsonField(Json::Value &json, const char *key) {
62
+ Json::StaticString theKey(key);
63
+ if (json.isMember(theKey)) {
64
+ return json[theKey];
65
+ } else {
66
+ throw VariantMap::MissingKeyException(key);
67
+ }
68
+ }
69
+
70
+ inline int
71
+ getJsonIntField(const Json::Value &json, const char *key) {
72
+ Json::StaticString theKey(key);
73
+ if (json.isMember(theKey)) {
74
+ return json[theKey].asInt();
75
+ } else {
76
+ throw VariantMap::MissingKeyException(key);
77
+ }
78
+ }
79
+
80
+ inline int
81
+ getJsonIntField(const Json::Value &json, const char *key, int defaultValue) {
82
+ Json::StaticString theKey(key);
83
+ if (json.isMember(theKey)) {
84
+ return json[theKey].asInt();
85
+ } else {
86
+ return defaultValue;
87
+ }
88
+ }
89
+
90
+ inline unsigned int
91
+ getJsonUintField(const Json::Value &json, const char *key) {
92
+ Json::StaticString theKey(key);
93
+ if (json.isMember(theKey)) {
94
+ return json[theKey].asUInt();
95
+ } else {
96
+ throw VariantMap::MissingKeyException(key);
97
+ }
98
+ }
99
+
100
+ inline unsigned int
101
+ getJsonUintField(const Json::Value &json, const char *key, unsigned int defaultValue) {
102
+ Json::StaticString theKey(key);
103
+ if (json.isMember(theKey)) {
104
+ return json[theKey].asUInt();
105
+ } else {
106
+ return defaultValue;
107
+ }
108
+ }
109
+
110
+ inline boost::uint64_t
111
+ getJsonUint64Field(const Json::Value &json, const char *key) {
112
+ Json::StaticString theKey(key);
113
+ if (json.isMember(theKey)) {
114
+ return json[theKey].asUInt64();
115
+ } else {
116
+ throw VariantMap::MissingKeyException(key);
117
+ }
118
+ }
119
+
120
+ inline boost::uint64_t
121
+ getJsonUint64Field(const Json::Value &json, const char *key, unsigned int defaultValue) {
122
+ Json::StaticString theKey(key);
123
+ if (json.isMember(theKey)) {
124
+ return json[theKey].asUInt64();
125
+ } else {
126
+ return defaultValue;
127
+ }
128
+ }
129
+
130
+ inline StaticString
131
+ getJsonStaticStringField(const Json::Value &json, const char *key) {
132
+ Json::StaticString theKey(key);
133
+ if (json.isMember(theKey)) {
134
+ return json[theKey].asCString();
135
+ } else {
136
+ throw VariantMap::MissingKeyException(key);
137
+ }
138
+ }
139
+
140
+ inline StaticString
141
+ getJsonStaticStringField(const Json::Value &json, const char *key,
142
+ const StaticString &defaultValue)
143
+ {
144
+ Json::StaticString theKey(key);
145
+ if (json.isMember(theKey)) {
146
+ return json[theKey].asCString();
147
+ } else {
148
+ return defaultValue;
149
+ }
150
+ }
151
+
152
+
40
153
  /**************************************************************
41
154
  *
42
155
  * Methods for generating JSON.
@@ -34,6 +34,7 @@
34
34
  #include <cstdlib>
35
35
  #include <cstddef>
36
36
  #include <ctime>
37
+ #include <boost/move/utility.hpp>
37
38
  #include <oxt/macros.hpp>
38
39
  #include <StaticString.h>
39
40
 
@@ -47,11 +48,20 @@ using namespace std;
47
48
  * Upon destruction of a DynamicBuffer, the memory buffer is freed.
48
49
  */
49
50
  struct DynamicBuffer {
51
+ private:
52
+ BOOST_MOVABLE_BUT_NOT_COPYABLE(DynamicBuffer)
53
+
54
+ public:
50
55
  typedef string::size_type size_type;
51
56
 
52
57
  char *data;
53
58
  size_type size;
54
59
 
60
+ DynamicBuffer()
61
+ : data(NULL),
62
+ size(0)
63
+ { }
64
+
55
65
  /**
56
66
  * @throws std::bad_alloc The buffer cannot be allocated.
57
67
  */
@@ -64,9 +74,28 @@ struct DynamicBuffer {
64
74
  }
65
75
  }
66
76
 
77
+ DynamicBuffer(BOOST_RV_REF(DynamicBuffer) other)
78
+ : data(other.data),
79
+ size(other.size)
80
+ {
81
+ other.data = NULL;
82
+ other.size = 0;
83
+ }
84
+
67
85
  ~DynamicBuffer() throw() {
68
86
  free(data);
69
87
  }
88
+
89
+ DynamicBuffer &operator=(BOOST_RV_REF(DynamicBuffer) other) {
90
+ if (this != &other) {
91
+ free(data);
92
+ data = other.data;
93
+ size = other.size;
94
+ other.data = NULL;
95
+ other.size = 0;
96
+ }
97
+ return *this;
98
+ }
70
99
  };
71
100
 
72
101
 
@@ -553,7 +553,7 @@ namespace Json {
553
553
  # endif
554
554
  Value( bool value );
555
555
  Value( const Value &other );
556
- ~Value();
556
+ virtual ~Value();
557
557
 
558
558
  Value &operator=( const Value &other );
559
559
  /// Swap values.
@@ -0,0 +1,941 @@
1
+ /*
2
+ * Phusion Passenger - https://www.phusionpassenger.com/
3
+ * Copyright (c) 2015 Phusion
4
+ *
5
+ * "Phusion Passenger" is a trademark of Hongli Lai & Ninh Bui.
6
+ *
7
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ * of this software and associated documentation files (the "Software"), to deal
9
+ * in the Software without restriction, including without limitation the rights
10
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ * copies of the Software, and to permit persons to whom the Software is
12
+ * furnished to do so, subject to the following conditions:
13
+ *
14
+ * The above copyright notice and this permission notice shall be included in
15
+ * all copies or substantial portions of the Software.
16
+ *
17
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23
+ * THE SOFTWARE.
24
+ */
25
+ #ifndef _PASSENGER_API_SERVER_UTILS_H_
26
+ #define _PASSENGER_API_SERVER_UTILS_H_
27
+
28
+ /**
29
+ * Utility code shared by HelperAgent/ApiServer.h, LoggingAgent/ApiServer.h
30
+ * and Watchdog/ApiServer.h. This code handles authentication and authorization
31
+ * of connected ApiServer clients.
32
+ *
33
+ * This file consists of the following items.
34
+ *
35
+ * ## API accounts
36
+ *
37
+ * API servers can be password protected. They support multiple accounts,
38
+ * each with its own privilege level. These accounts are represented by
39
+ * ApiAccount, stored in ApiAccountDatabase objects.
40
+ *
41
+ * ## Authorization
42
+ *
43
+ * The authorizeXXX() family of functions implement authorization checking on a
44
+ * connected client. Given a client and a request, they perform various
45
+ * checks and return information on what the client is authorized to do.
46
+ *
47
+ * ## Utility
48
+ *
49
+ * Various utility functions
50
+ *
51
+ * ## Common endpoints
52
+ *
53
+ * The apiServerProcessXXX() family of functions implement common endpoints
54
+ * in the various API servers.
55
+ */
56
+
57
+ #include <boost/foreach.hpp>
58
+ #include <boost/bind.hpp>
59
+ #include <boost/regex.hpp>
60
+ #include <oxt/macros.hpp>
61
+ #include <oxt/backtrace.hpp>
62
+ #include <oxt/thread.hpp>
63
+ #include <sys/types.h>
64
+ #include <string>
65
+ #include <vector>
66
+ #include <cstddef>
67
+ #include <cstring>
68
+ #include <ApplicationPool2/Pool.h>
69
+ #include <ApplicationPool2/ApiKey.h>
70
+ #include <StaticString.h>
71
+ #include <Exceptions.h>
72
+ #include <DataStructures/LString.h>
73
+ #include <DataStructures/StringKeyTable.h>
74
+ #include <ServerKit/Server.h>
75
+ #include <ServerKit/HeaderTable.h>
76
+ #include <Utils.h>
77
+ #include <Utils/IOUtils.h>
78
+ #include <Utils/BufferedIO.h>
79
+ #include <Utils/StrIntUtils.h>
80
+ #include <Utils/modp_b64.h>
81
+ #include <Utils/json.h>
82
+ #include <Utils/VariantMap.h>
83
+
84
+ namespace Passenger {
85
+
86
+ using namespace std;
87
+
88
+
89
+ // Forward declarations
90
+ inline string truncateApiKey(const StaticString &apiKey);
91
+
92
+
93
+ /*******************************
94
+ *
95
+ * API accounts
96
+ *
97
+ *******************************/
98
+
99
+ struct ApiAccount {
100
+ string username;
101
+ string password;
102
+ bool readonly;
103
+ };
104
+
105
+ class ApiAccountDatabase {
106
+ private:
107
+ vector<ApiAccount> database;
108
+
109
+ bool levelDescriptionIsReadOnly(const StaticString &level) const {
110
+ if (level == "readonly") {
111
+ return true;
112
+ } else if (level == "full") {
113
+ return false;
114
+ } else {
115
+ throw ArgumentException("Invalid privilege level " + level);
116
+ }
117
+ }
118
+
119
+ public:
120
+ /**
121
+ * Add an account to the database with the given parameters.
122
+ *
123
+ * @throws ArgumentException One if the input arguments contain a disallowed value.
124
+ */
125
+ void add(const string &username, const string &password, bool readonly) {
126
+ if (OXT_UNLIKELY(username == "api")) {
127
+ throw ArgumentException("It is not allowed to register an API account with username 'api'");
128
+ }
129
+
130
+ ApiAccount account;
131
+ account.username = username;
132
+ account.password = password;
133
+ account.readonly = readonly;
134
+ database.push_back(account);
135
+ }
136
+
137
+ /**
138
+ * Add an account to the database. The account parameters are determined
139
+ * by a description string in the form of [LEVEL]:USERNAME:PASSWORDFILE.
140
+ * LEVEL is one of:
141
+ *
142
+ * readonly Read-only access
143
+ * full Full access (default)
144
+ *
145
+ * @throws ArgumentException One if the input arguments contain a disallowed value.
146
+ */
147
+ void add(const StaticString &description) {
148
+ ApiAccount account;
149
+ vector<string> args;
150
+
151
+ split(description, ':', args);
152
+
153
+ if (args.size() == 2) {
154
+ account.username = args[0];
155
+ account.password = strip(readAll(args[1]));
156
+ account.readonly = false;
157
+ } else if (args.size() == 3) {
158
+ account.username = args[1];
159
+ account.password = strip(readAll(args[2]));
160
+ account.readonly = levelDescriptionIsReadOnly(args[0]);
161
+ } else {
162
+ throw ArgumentException("Invalid authorization description '" + description + "'");
163
+ }
164
+
165
+ if (OXT_UNLIKELY(account.username == "api")) {
166
+ throw ArgumentException("It is not allowed to register an API account with username 'api'");
167
+ }
168
+ database.push_back(account);
169
+ }
170
+
171
+ bool empty() const {
172
+ return database.empty();
173
+ }
174
+
175
+ const ApiAccount *lookup(const StaticString &username) const {
176
+ vector<ApiAccount>::const_iterator it, end = database.end();
177
+
178
+ for (it = database.begin(); it != end; it++) {
179
+ if (it->username == username) {
180
+ return &(*it);
181
+ }
182
+ }
183
+
184
+ return NULL;
185
+ }
186
+ };
187
+
188
+
189
+ /*******************************
190
+ *
191
+ * Authorization functions
192
+ *
193
+ *******************************/
194
+
195
+
196
+ struct Authorization {
197
+ uid_t uid;
198
+ ApplicationPool2::ApiKey apiKey;
199
+ bool canReadPool;
200
+ bool canModifyPool;
201
+ bool canInspectState;
202
+ bool canAdminister;
203
+
204
+ Authorization()
205
+ : uid(-1),
206
+ canReadPool(false),
207
+ canModifyPool(false),
208
+ canInspectState(false),
209
+ canAdminister(false)
210
+ { }
211
+ };
212
+
213
+
214
+ template<typename Request>
215
+ inline bool
216
+ parseBasicAuthHeader(Request *req, string &username, string &password) {
217
+ const LString *auth = req->headers.lookup("authorization");
218
+
219
+ if (auth == NULL || auth->size <= 6 || !psg_lstr_cmp(auth, "Basic ", 6)) {
220
+ return false;
221
+ }
222
+
223
+ auth = psg_lstr_make_contiguous(auth, req->pool);
224
+ string authData = modp::b64_decode(
225
+ auth->start->data + sizeof("Basic ") - 1,
226
+ auth->size - (sizeof("Basic ") - 1));
227
+ string::size_type pos = authData.find(':');
228
+ if (pos == string::npos) {
229
+ return false;
230
+ }
231
+
232
+ username = authData.substr(0, pos);
233
+ password = authData.substr(pos + 1);
234
+ return true;
235
+ }
236
+
237
+ /*
238
+ * @throws oxt::tracable_exception
239
+ */
240
+ template<typename ApiServer, typename Client, typename Request>
241
+ inline Authorization
242
+ authorize(ApiServer *server, Client *client, Request *req) {
243
+ TRACE_POINT();
244
+ Authorization auth;
245
+ uid_t uid = -1;
246
+ gid_t gid = -1;
247
+ string username, password;
248
+
249
+ try {
250
+ readPeerCredentials(client->getFd(), &uid, &gid);
251
+ if (server->authorizeByUid(uid)) {
252
+ SKC_INFO_FROM_STATIC(server, client, "Authenticated with UID: " << uid);
253
+ auth.uid = uid;
254
+ auth.canReadPool = true;
255
+ auth.canModifyPool = true;
256
+ auth.canInspectState = auth.canInspectState || uid == 0 || uid == geteuid();
257
+ auth.canAdminister = auth.canAdminister || uid == 0 || uid == geteuid();
258
+ } else {
259
+ SKC_INFO_FROM_STATIC(server, client, "Authentication failed for UID: " << uid);
260
+ }
261
+ } catch (const SystemException &e) {
262
+ if (e.code() != ENOSYS && e.code() != EPROTONOSUPPORT) {
263
+ throw;
264
+ }
265
+ }
266
+
267
+ if (server->apiAccountDatabase->empty()) {
268
+ SKC_INFO_FROM_STATIC(server, client,
269
+ "Authenticated as administrator because API account database is empty");
270
+ auth.apiKey = ApplicationPool2::ApiKey::makeSuper();
271
+ auth.canReadPool = true;
272
+ auth.canModifyPool = true;
273
+ auth.canInspectState = true;
274
+ auth.canAdminister = true;
275
+ } else if (parseBasicAuthHeader(req, username, password)) {
276
+ SKC_DEBUG_FROM_STATIC(server, client,
277
+ "HTTP basic authentication supplied: " << username);
278
+ if (username == "api") {
279
+ auth.apiKey = ApplicationPool2::ApiKey(password);
280
+ if (server->authorizeByApiKey(auth.apiKey)) {
281
+ SKC_INFO_FROM_STATIC(server, client,
282
+ "Authenticated with API key: " << truncateApiKey(password));
283
+ assert(!auth.apiKey.isSuper());
284
+ auth.canReadPool = true;
285
+ auth.canModifyPool = true;
286
+ }
287
+ } else {
288
+ const ApiAccount *account = server->apiAccountDatabase->lookup(username);
289
+ if (account != NULL && constantTimeCompare(password, account->password)) {
290
+ SKC_INFO_FROM_STATIC(server, client,
291
+ "Authenticated with administrator account: " << username);
292
+ auth.apiKey = ApplicationPool2::ApiKey::makeSuper();
293
+ auth.canReadPool = true;
294
+ auth.canModifyPool = auth.canModifyPool || !account->readonly;
295
+ auth.canInspectState = true;
296
+ auth.canAdminister = auth.canAdminister || !account->readonly;
297
+ }
298
+ }
299
+ }
300
+
301
+ return auth;
302
+ }
303
+
304
+ template<typename ApiServer, typename Client, typename Request>
305
+ inline bool
306
+ authorizeStateInspectionOperation(ApiServer *server, Client *client, Request *req) {
307
+ return authorize(server, client, req).canInspectState;
308
+ }
309
+
310
+ template<typename ApiServer, typename Client, typename Request>
311
+ inline bool
312
+ authorizeAdminOperation(ApiServer *server, Client *client, Request *req) {
313
+ return authorize(server, client, req).canAdminister;
314
+ }
315
+
316
+
317
+ /*******************************
318
+ *
319
+ * Utility functions
320
+ *
321
+ *******************************/
322
+
323
+ inline VariantMap
324
+ parseQueryString(const StaticString &query) {
325
+ VariantMap params;
326
+ const char *pos = query.data();
327
+ const char *end = query.data() + query.size();
328
+
329
+ while (pos < end) {
330
+ const char *assignmentPos = (const char *) memchr(pos, '=', end - pos);
331
+ if (assignmentPos != NULL) {
332
+ string name = urldecode(StaticString(pos, assignmentPos - pos));
333
+ const char *sepPos = (const char *) memchr(assignmentPos + 1, '&',
334
+ end - assignmentPos - 1);
335
+ if (sepPos != NULL) {
336
+ string value = urldecode(StaticString(assignmentPos + 1,
337
+ sepPos - assignmentPos - 1));
338
+ params.set(name, value);
339
+ pos = sepPos + 1;
340
+ } else {
341
+ StaticString value(assignmentPos + 1, end - assignmentPos - 1);
342
+ params.set(name, value);
343
+ pos = end;
344
+ }
345
+ } else {
346
+ throw SyntaxError("Invalid query string format");
347
+ }
348
+ }
349
+
350
+ return params;
351
+ }
352
+
353
+ inline string
354
+ truncateApiKey(const StaticString &apiKey) {
355
+ assert(apiKey.size() == ApplicationPool2::ApiKey::SIZE);
356
+ char prefix[3];
357
+ memcpy(prefix, apiKey.data(), 3);
358
+ return string(prefix, 3) + "*****";
359
+ }
360
+
361
+ template<typename Server, typename Client, typename Request>
362
+ struct ApiServerInternalHttpResponse {
363
+ static const int ERROR_INVALID_HEADER = -1;
364
+ static const int ERROR_INVALID_BODY = -2;
365
+ static const int ERROR_INTERNAL = -3;
366
+
367
+ Server *server;
368
+ Client *client;
369
+ Request *req;
370
+ int status;
371
+ StringKeyTable<string> headers;
372
+ string body;
373
+
374
+ vector<string> debugLogs;
375
+ string errorLogs;
376
+ BufferedIO io;
377
+ };
378
+
379
+ template<typename Server, typename Client, typename Request>
380
+ struct ApiServerInternalHttpRequest {
381
+ Server *server;
382
+ Client *client;
383
+ Request *req;
384
+
385
+ string address;
386
+ http_method method;
387
+ string uri;
388
+ StringKeyTable<string> headers;
389
+ boost::function<void (ApiServerInternalHttpResponse<Server, Client, Request>)> callback;
390
+
391
+ unsigned long long timeout;
392
+ boost::function<
393
+ void (ApiServerInternalHttpRequest<Server, Client, Request> &req,
394
+ ApiServerInternalHttpResponse<Server, Client, Request> &resp,
395
+ BufferedIO &io
396
+ )> bodyProcessor;
397
+
398
+ ApiServerInternalHttpRequest()
399
+ : server(NULL),
400
+ client(NULL),
401
+ req(NULL),
402
+ method(HTTP_GET),
403
+ timeout(60 * 1000000)
404
+ { }
405
+ };
406
+
407
+ template<typename Server, typename Client, typename Request>
408
+ inline void
409
+ apiServerMakeInternalHttpRequestCallbackWrapper(
410
+ boost::function<void (ApiServerInternalHttpResponse<Server, Client, Request>)> callback,
411
+ ApiServerInternalHttpResponse<Server, Client, Request> resp)
412
+ {
413
+ if (!resp.debugLogs.empty()) {
414
+ foreach (string log, resp.debugLogs) {
415
+ SKC_DEBUG_FROM_STATIC(resp.server, resp.client, log);
416
+ }
417
+ }
418
+ if (!resp.errorLogs.empty()) {
419
+ SKC_ERROR_FROM_STATIC(resp.server, resp.client, resp.errorLogs);
420
+ }
421
+ callback(resp);
422
+ resp.server->unrefRequest(resp.req, __FILE__, __LINE__);
423
+ }
424
+
425
+ template<typename Server, typename Client, typename Request>
426
+ inline void
427
+ apiServerMakeInternalHttpRequestThreadMain(ApiServerInternalHttpRequest<Server, Client, Request> req) {
428
+ typedef ApiServerInternalHttpRequest<Server, Client, Request> InternalRequest;
429
+ typedef ApiServerInternalHttpResponse<Server, Client, Request> InternalResponse;
430
+
431
+ struct Guard {
432
+ InternalRequest &req;
433
+ InternalResponse &resp;
434
+ SafeLibevPtr libev;
435
+ bool cleared;
436
+
437
+ Guard(InternalRequest &_req, InternalResponse &_resp, const SafeLibevPtr &_libev)
438
+ : req(_req),
439
+ resp(_resp),
440
+ libev(_libev),
441
+ cleared(false)
442
+ { }
443
+
444
+ ~Guard() {
445
+ if (!cleared) {
446
+ resp.status = InternalResponse::ERROR_INTERNAL;
447
+ resp.headers.clear();
448
+ resp.body.clear();
449
+ libev->runLater(boost::bind(
450
+ apiServerMakeInternalHttpRequestCallbackWrapper<Server, Client, Request>,
451
+ req.callback, resp));
452
+ }
453
+ }
454
+
455
+ void clear() {
456
+ cleared = true;
457
+ }
458
+ };
459
+
460
+ InternalResponse resp;
461
+ SafeLibevPtr libev = req.server->getContext()->libev;
462
+
463
+ resp.server = req.server;
464
+ resp.client = req.client;
465
+ resp.req = req.req;
466
+ resp.status = InternalResponse::ERROR_INTERNAL;
467
+
468
+ Guard guard(req, resp, libev);
469
+
470
+
471
+ try {
472
+ FileDescriptor conn(connectToServer(req.address, NULL, 0), __FILE__, __LINE__);
473
+ BufferedIO io(conn);
474
+
475
+ string header;
476
+ header.append(http_method_str(req.method));
477
+ header.append(" ", 1);
478
+ header.append(req.uri);
479
+ header.append(" HTTP/1.1\r\n");
480
+
481
+ StringKeyTable<string>::ConstIterator it(req.headers);
482
+ while (*it != NULL) {
483
+ header.append(it.getKey());
484
+ header.append(": ", 2);
485
+ header.append(it.getValue());
486
+ header.append("\r\n", 2);
487
+ it.next();
488
+ }
489
+ header.append("Connection: close\r\n\r\n");
490
+
491
+ writeExact(conn, header, &req.timeout);
492
+
493
+
494
+ string statusLine = io.readLine();
495
+ resp.debugLogs.push_back("Internal request response data: \"" + cEscapeString(statusLine) + "\"");
496
+ boost::regex statusLineRegex("^HTTP/.*? ([0-9]+) (.*)$");
497
+ boost::smatch results;
498
+ if (!boost::regex_match(statusLine, results, statusLineRegex)) {
499
+ guard.clear();
500
+ resp.status = InternalResponse::ERROR_INVALID_HEADER;
501
+ libev->runLater(boost::bind(
502
+ apiServerMakeInternalHttpRequestCallbackWrapper<Server, Client, Request>,
503
+ req.callback, resp));
504
+ return;
505
+ }
506
+ resp.status = (int) stringToUint(results.str(1));
507
+ if (resp.status <= 0 || resp.status >= 1000) {
508
+ guard.clear();
509
+ resp.status = InternalResponse::ERROR_INVALID_HEADER;
510
+ libev->runLater(boost::bind(
511
+ apiServerMakeInternalHttpRequestCallbackWrapper<Server, Client, Request>,
512
+ req.callback, resp));
513
+ return;
514
+ }
515
+
516
+ string response;
517
+ while (true) {
518
+ response = io.readLine();
519
+ resp.debugLogs.push_back("Internal request response data: \"" + cEscapeString(response) + "\"");
520
+ if (response.empty()) {
521
+ guard.clear();
522
+ resp.status = InternalResponse::ERROR_INVALID_HEADER;
523
+ libev->runLater(boost::bind(
524
+ apiServerMakeInternalHttpRequestCallbackWrapper<Server, Client, Request>,
525
+ req.callback, resp));
526
+ return;
527
+ } else if (response == "\r\n") {
528
+ break;
529
+ } else {
530
+ const char *pos = (const char *) memchr(response.data(), ':', response.size());
531
+ if (pos == NULL) {
532
+ guard.clear();
533
+ resp.status = InternalResponse::ERROR_INVALID_HEADER;
534
+ libev->runLater(boost::bind(
535
+ apiServerMakeInternalHttpRequestCallbackWrapper<Server, Client, Request>,
536
+ req.callback, resp));
537
+ return;
538
+ }
539
+
540
+ string key(strip(response.substr(0, pos - response.data())));
541
+ string value(response.substr(pos - response.data()));
542
+ value.erase(0, 1);
543
+ value = strip(value);
544
+ if (!value.empty() && value[value.size() - 1] == '\r') {
545
+ value.erase(value.size() - 1, 1);
546
+ }
547
+
548
+ if (key.empty() || value.empty()) {
549
+ guard.clear();
550
+ resp.status = InternalResponse::ERROR_INVALID_HEADER;
551
+ libev->runLater(boost::bind(
552
+ apiServerMakeInternalHttpRequestCallbackWrapper<Server, Client, Request>,
553
+ req.callback, resp));
554
+ return;
555
+ }
556
+
557
+ resp.headers.insert(key, value);
558
+ }
559
+ }
560
+
561
+ if (req.bodyProcessor != NULL) {
562
+ req.bodyProcessor(req, resp, io);
563
+ } else {
564
+ resp.body = io.readAll(&req.timeout);
565
+ }
566
+ guard.clear();
567
+ libev->runLater(boost::bind(
568
+ apiServerMakeInternalHttpRequestCallbackWrapper<Server, Client, Request>,
569
+ req.callback, resp));
570
+ } catch (const oxt::tracable_exception &e) {
571
+ resp.errorLogs.append("Exception: ");
572
+ resp.errorLogs.append(e.what());
573
+ resp.errorLogs.append("\n");
574
+ resp.errorLogs.append(e.backtrace());
575
+ }
576
+ }
577
+
578
+ /**
579
+ * Utility function for API servers for making an internal HTTP request,
580
+ * usually to another agent. The request is made in a background thread.
581
+ * When done, the callback is called on the event loop. While the request
582
+ * is being made, a reference to the ServerKit request object is held.
583
+ *
584
+ * This is not a fully featured HTTP client and doesn't fully correctly
585
+ * parse HTTP, so it can't be used with arbitrary servers. It doesn't
586
+ * support keep-alive and chunked transfer-encodings.
587
+ */
588
+ template<typename Server, typename Client, typename Request>
589
+ inline void
590
+ apiServerMakeInternalHttpRequest(const ApiServerInternalHttpRequest<Server, Client, Request> &params) {
591
+ params.server->refRequest(params.req, __FILE__, __LINE__);
592
+ oxt::thread(boost::bind(
593
+ apiServerMakeInternalHttpRequestThreadMain<Server, Client, Request>,
594
+ params), "Internal HTTP request", 1024 * 128);
595
+ }
596
+
597
+
598
+ /*******************************
599
+ *
600
+ * Common endpoints
601
+ *
602
+ *******************************/
603
+
604
+ template<typename Server, typename Client, typename Request>
605
+ inline void
606
+ apiServerRespondWith401(Server *server, Client *client, Request *req) {
607
+ ServerKit::HeaderTable headers;
608
+ headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
609
+ headers.insert(req->pool, "WWW-Authenticate", "Basic realm=\"api\"");
610
+ server->writeSimpleResponse(client, 401, &headers, "Unauthorized");
611
+ if (!req->ended()) {
612
+ server->endRequest(&client, &req);
613
+ }
614
+ }
615
+
616
+ template<typename Server, typename Client, typename Request>
617
+ inline void
618
+ apiServerRespondWith404(Server *server, Client *client, Request *req) {
619
+ ServerKit::HeaderTable headers;
620
+ headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
621
+ server->writeSimpleResponse(client, 404, &headers, "Not found");
622
+ if (!req->ended()) {
623
+ server->endRequest(&client, &req);
624
+ }
625
+ }
626
+
627
+ template<typename Server, typename Client, typename Request>
628
+ inline void
629
+ apiServerRespondWith405(Server *server, Client *client, Request *req) {
630
+ ServerKit::HeaderTable headers;
631
+ headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
632
+ server->writeSimpleResponse(client, 405, &headers, "Method not allowed");
633
+ if (!req->ended()) {
634
+ server->endRequest(&client, &req);
635
+ }
636
+ }
637
+
638
+ template<typename Server, typename Client, typename Request>
639
+ inline void
640
+ apiServerRespondWith413(Server *server, Client *client, Request *req) {
641
+ ServerKit::HeaderTable headers;
642
+ headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
643
+ server->writeSimpleResponse(client, 413, &headers, "Request body too large");
644
+ if (!req->ended()) {
645
+ server->endRequest(&client, &req);
646
+ }
647
+ }
648
+
649
+ template<typename Server, typename Client, typename Request>
650
+ inline void
651
+ apiServerRespondWith422(Server *server, Client *client, Request *req, const StaticString &body) {
652
+ ServerKit::HeaderTable headers;
653
+ headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
654
+ headers.insert(req->pool, "Content-Type", "text/plain; charset=utf-8");
655
+ server->writeSimpleResponse(client, 422, &headers, body);
656
+ if (!req->ended()) {
657
+ server->endRequest(&client, &req);
658
+ }
659
+ }
660
+
661
+ template<typename Server, typename Client, typename Request>
662
+ inline void
663
+ apiServerRespondWith500(Server *server, Client *client, Request *req, const StaticString &body) {
664
+ ServerKit::HeaderTable headers;
665
+ headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
666
+ headers.insert(req->pool, "Content-Type", "text/plain; charset=utf-8");
667
+ server->writeSimpleResponse(client, 500, &headers, body);
668
+ if (!req->ended()) {
669
+ server->endRequest(&client, &req);
670
+ }
671
+ }
672
+
673
+ template<typename Server, typename Client, typename Request>
674
+ inline void
675
+ apiServerProcessPing(Server *server, Client *client, Request *req) {
676
+ Authorization auth(authorize(server, client, req));
677
+ if (auth.canReadPool || auth.canInspectState) {
678
+ ServerKit::HeaderTable headers;
679
+ headers.insert(req->pool, "Content-Type", "application/json");
680
+ server->writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }");
681
+ if (!req->ended()) {
682
+ server->endRequest(&client, &req);
683
+ }
684
+ } else {
685
+ apiServerRespondWith401(server, client, req);
686
+ }
687
+ }
688
+
689
+ template<typename Server, typename Client, typename Request>
690
+ inline void
691
+ apiServerProcessVersion(Server *server, Client *client, Request *req) {
692
+ Authorization auth(authorize(server, client, req));
693
+ if (auth.canReadPool || auth.canInspectState) {
694
+ ServerKit::HeaderTable headers;
695
+ headers.insert(req->pool, "Content-Type", "application/json");
696
+
697
+ Json::Value response;
698
+ response["program_name"] = PROGRAM_NAME;
699
+ response["program_version"] = PASSENGER_VERSION;
700
+ response["api_version"] = PASSENGER_API_VERSION;
701
+ response["api_version_major"] = PASSENGER_API_VERSION_MAJOR;
702
+ response["api_version_minor"] = PASSENGER_API_VERSION_MINOR;
703
+ #ifdef PASSENGER_IS_ENTERPRISE
704
+ response["passenger_enterprise"] = true;
705
+ #endif
706
+
707
+ server->writeSimpleResponse(client, 200, &headers,
708
+ response.toStyledString());
709
+ if (!req->ended()) {
710
+ server->endRequest(&client, &req);
711
+ }
712
+ } else {
713
+ apiServerRespondWith401(server, client, req);
714
+ }
715
+ }
716
+
717
+ template<typename Server, typename Client, typename Request>
718
+ inline void
719
+ apiServerProcessBacktraces(Server *server, Client *client, Request *req) {
720
+ if (authorizeStateInspectionOperation(server, client, req)) {
721
+ ServerKit::HeaderTable headers;
722
+ headers.insert(req->pool, "Content-Type", "text/plain");
723
+ server->writeSimpleResponse(client, 200, &headers,
724
+ psg_pstrdup(req->pool, oxt::thread::all_backtraces()));
725
+ if (!req->ended()) {
726
+ server->endRequest(&client, &req);
727
+ }
728
+ } else {
729
+ apiServerRespondWith401(server, client, req);
730
+ }
731
+ }
732
+
733
+ template<typename Server, typename Client, typename Request>
734
+ inline void
735
+ apiServerProcessShutdown(Server *server, Client *client, Request *req) {
736
+ if (req->method != HTTP_POST) {
737
+ apiServerRespondWith405(server, client, req);
738
+ } else if (authorizeAdminOperation(server, client, req)) {
739
+ ServerKit::HeaderTable headers;
740
+ headers.insert(req->pool, "Content-Type", "application/json");
741
+ server->exitEvent->notify();
742
+ server->writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }");
743
+ if (!req->ended()) {
744
+ server->endRequest(&client, &req);
745
+ }
746
+ } else {
747
+ apiServerRespondWith401(server, client, req);
748
+ }
749
+ }
750
+
751
+ template<typename Server, typename Client, typename Request>
752
+ inline void
753
+ apiServerProcessReopenLogs(Server *server, Client *client, Request *req) {
754
+ if (req->method != HTTP_POST) {
755
+ apiServerRespondWith405(server, client, req);
756
+ } else if (authorizeAdminOperation(server, client, req)) {
757
+ int e;
758
+ ServerKit::HeaderTable headers;
759
+ headers.insert(req->pool, "Content-Type", "application/json");
760
+
761
+ string logFile = getLogFile();
762
+ if (logFile.empty()) {
763
+ server->writeSimpleResponse(client, 500, &headers, "{ \"status\": \"error\", "
764
+ "\"code\": \"NO_LOG_FILE\", "
765
+ "\"message\": \"" PROGRAM_NAME " was not configured with a log file.\" }\n");
766
+ if (!req->ended()) {
767
+ server->endRequest(&client, &req);
768
+ }
769
+ return;
770
+ }
771
+
772
+ if (!setLogFile(logFile, &e)) {
773
+ unsigned int bufsize = 1024;
774
+ char *message = (char *) psg_pnalloc(req->pool, bufsize);
775
+ snprintf(message, bufsize, "{ \"status\": \"error\", "
776
+ "\"code\": \"LOG_FILE_OPEN_ERROR\", "
777
+ "\"message\": \"Cannot reopen log file %s: %s (errno=%d)\" }",
778
+ logFile.c_str(), strerror(e), e);
779
+ server->writeSimpleResponse(client, 500, &headers, message);
780
+ if (!req->ended()) {
781
+ server->endRequest(&client, &req);
782
+ }
783
+ return;
784
+ }
785
+ P_NOTICE("Log file reopened.");
786
+
787
+ if (hasFileDescriptorLogFile()) {
788
+ if (!setFileDescriptorLogFile(getFileDescriptorLogFile(), &e)) {
789
+ unsigned int bufsize = 1024;
790
+ char *message = (char *) psg_pnalloc(req->pool, bufsize);
791
+ snprintf(message, bufsize, "{ \"status\": \"error\", "
792
+ "\"code\": \"FD_LOG_FILE_OPEN_ERROR\", "
793
+ "\"message\": \"Cannot reopen file descriptor log file %s: %s (errno=%d)\" }",
794
+ getFileDescriptorLogFile().c_str(), strerror(e), e);
795
+ server->writeSimpleResponse(client, 500, &headers, message);
796
+ if (!req->ended()) {
797
+ server->endRequest(&client, &req);
798
+ }
799
+ return;
800
+ }
801
+ P_NOTICE("File descriptor log file reopened.");
802
+ }
803
+
804
+ server->writeSimpleResponse(client, 200, &headers, "{ \"status\": \"ok\" }\n");
805
+
806
+ if (!req->ended()) {
807
+ server->endRequest(&client, &req);
808
+ }
809
+ } else {
810
+ apiServerRespondWith401(server, client, req);
811
+ }
812
+ }
813
+
814
+ template<typename Server, typename Client, typename Request>
815
+ inline void
816
+ _apiServerProcessReinheritLogsResponseBody(
817
+ ApiServerInternalHttpRequest<Server, Client, Request> &req,
818
+ ApiServerInternalHttpResponse<Server, Client, Request> &resp,
819
+ BufferedIO &io)
820
+ {
821
+ typedef ApiServerInternalHttpResponse<Server, Client, Request> InternalResponse;
822
+
823
+ string logFilePath = resp.headers.lookupCopy("Filename");
824
+ if (logFilePath.empty()) {
825
+ resp.status = InternalResponse::ERROR_INVALID_BODY;
826
+ resp.errorLogs.append("Error communicating with Watchdog process: "
827
+ "no log filename received in response");
828
+ return;
829
+ }
830
+
831
+ int fd = readFileDescriptorWithNegotiation(io.getFd(), &req.timeout);
832
+ setLogFileWithFd(logFilePath, fd);
833
+ safelyClose(fd);
834
+ }
835
+
836
+ template<typename Server, typename Client, typename Request>
837
+ inline void
838
+ _apiServerProcessReinheritLogsDone(ApiServerInternalHttpResponse<Server, Client, Request> resp) {
839
+ typedef ApiServerInternalHttpResponse<Server, Client, Request> InternalResponse;
840
+
841
+ Server *server = resp.server;
842
+ Client *client = resp.client;
843
+ Request *req = resp.req;
844
+ int status;
845
+ StaticString body;
846
+
847
+ if (req->ended()) {
848
+ return;
849
+ }
850
+
851
+ if (resp.status < 0) {
852
+ status = 500;
853
+ switch (resp.status) {
854
+ case InternalResponse::ERROR_INVALID_HEADER:
855
+ body = "{ \"status\": \"error\", "
856
+ "\"code\": \"INHERIT_ERROR\", "
857
+ "\"message\": \"Error communicating with Watchdog process: "
858
+ "invalid response headers from Watchdog\" }\n";
859
+ break;
860
+ case InternalResponse::ERROR_INVALID_BODY:
861
+ body = "{ \"status\": \"error\", "
862
+ "\"code\": \"INHERIT_ERROR\", "
863
+ "\"message\": \"Error communicating with Watchdog process: "
864
+ "invalid response body from Watchdog\" }\n";
865
+ break;
866
+ case InternalResponse::ERROR_INTERNAL:
867
+ body = "{ \"status\": \"error\", "
868
+ "\"code\": \"INHERIT_ERROR\", "
869
+ "\"message\": \"Error communicating with Watchdog process: "
870
+ "an internal error occurred\" }\n";
871
+ break;
872
+ default:
873
+ body = "{ \"status\": \"error\", "
874
+ "\"code\": \"INHERIT_ERROR\", "
875
+ "\"message\": \"Error communicating with Watchdog process: "
876
+ "unknown error\" }\n";
877
+ break;
878
+ }
879
+ } else if (resp.status == 200) {
880
+ status = 200;
881
+ body = "{ \"status\": \"ok\" }\n";
882
+ } else {
883
+ status = 500;
884
+ body = "{ \"status\": \"error\", "
885
+ "\"code\": \"INHERIT_ERROR\", "
886
+ "\"message\": \"Error communicating with Watchdog process: non-200 response\" }\n";
887
+ }
888
+
889
+ ServerKit::HeaderTable headers;
890
+ headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
891
+ headers.insert(req->pool, "Content-Type", "application/json");
892
+ req->wantKeepAlive = false;
893
+ server->writeSimpleResponse(client, status, &headers, body);
894
+ if (!req->ended()) {
895
+ server->endRequest(&client, &req);
896
+ }
897
+ }
898
+
899
+ template<typename Server, typename Client, typename Request>
900
+ inline void
901
+ apiServerProcessReinheritLogs(Server *server, Client *client, Request *req,
902
+ const StaticString &instanceDir, const StaticString &fdPassingPassword)
903
+ {
904
+ if (req->method != HTTP_POST) {
905
+ apiServerRespondWith405(server, client, req);
906
+ } else if (authorizeAdminOperation(server, client, req)) {
907
+ ServerKit::HeaderTable headers;
908
+ headers.insert(req->pool, "Cache-Control", "no-cache, no-store, must-revalidate");
909
+ headers.insert(req->pool, "Content-Type", "application/json");
910
+
911
+ if (instanceDir.empty() || fdPassingPassword.empty()) {
912
+ server->writeSimpleResponse(client, 501, &headers,
913
+ "{ \"status\": \"error\", "
914
+ "\"code\": \"NO_WATCHDOG\", "
915
+ "\"message\": \"No Watchdog process\" }\n");
916
+ if (!req->ended()) {
917
+ server->endRequest(&client, &req);
918
+ }
919
+ return;
920
+ }
921
+
922
+ ApiServerInternalHttpRequest<Server, Client, Request> params;
923
+ params.server = server;
924
+ params.client = client;
925
+ params.req = req;
926
+ params.address = "unix:" + instanceDir + "/agents.s/watchdog_api";
927
+ params.method = HTTP_GET;
928
+ params.uri = "/config/log_file.fd";
929
+ params.headers.insert("Fd-Passing-Password", fdPassingPassword);
930
+ params.callback = _apiServerProcessReinheritLogsDone<Server, Client, Request>;
931
+ params.bodyProcessor = _apiServerProcessReinheritLogsResponseBody<Server, Client, Request>;
932
+ apiServerMakeInternalHttpRequest(params);
933
+ } else {
934
+ apiServerRespondWith401(server, client, req);
935
+ }
936
+ }
937
+
938
+
939
+ } // namespace Passenger
940
+
941
+ #endif /* _PASSENGER_API_SERVER_UTILS_H_ */