1 // This may look like C code, but it's really -*- C++ -*-
2 /*
3 * Copyright (C) 2011 Emweb bv, Herent, Belgium.
4 *
5 * See the LICENSE file for terms of use.
6 */
7 #include "Wt/WIOService.h"
8 #include "Wt/WServer.h"
9
10 #include <iostream>
11 #include <string>
12 #include <signal.h>
13
14 #include "Configuration.h"
15 #include "FCGIStream.h"
16 #include "Server.h"
17 #include "WebController.h"
18 #include "WebMain.h"
19
20 namespace {
21 Wt::WebMain *webMainInstance = nullptr;
22 }
23
24 namespace Wt {
25
26 LOGGER("WServer/wtfcgi");
27
28 struct WServer::Impl
29 {
ImplImpl30 Impl(WServer& server)
31 : server_(server),
32 running_(false),
33 webMain_(nullptr)
34 { }
35
runImpl36 void run()
37 {
38 running_ = true;
39
40 if (sessionId_.empty())
41 startSharedProcess();
42 else
43 runSession();
44 }
45
runSessionImpl46 void runSession()
47 {
48 Configuration& conf = server_.configuration();
49
50 if (!Server::bindUDStoStdin(conf.runDirectory() + "/" + sessionId_,
51 server_))
52 exit(1);
53
54 try {
55 FCGIStream fcgiStream;
56 webMainInstance = webMain_
57 = new WebMain(&server_, &fcgiStream, sessionId_);
58 webMain_->run();
59
60 sleep(1);
61
62 delete webMain_;
63 webMainInstance = webMain_ = nullptr;
64
65 } catch (std::exception& e) {
66 LOG_ERROR_S(&server_,
67 "fatal: dedicated session process for " << sessionId_ <<
68 ": caught unhandled exception: " << e.what());
69
70 throw;
71 } catch (...) {
72 LOG_ERROR_S(&server_,
73 "fatal: dedicated session process for " << sessionId_ <<
74 ": caught unknown, unhandled exception.");
75
76 throw;
77 }
78 }
79
startSharedProcessImpl80 void startSharedProcess()
81 {
82 Configuration& conf = server_.configuration();
83
84 if (!Server::bindUDStoStdin(conf.runDirectory() + "/server-"
85 + std::to_string(getpid()),
86 server_))
87 exit(1);
88
89 try {
90 FCGIStream fcgiStream;
91 webMainInstance = webMain_ = new WebMain(&server_, &fcgiStream);
92 webMain_->run();
93
94 delete webMain_;
95 webMainInstance = webMain_ = nullptr;
96 } catch (std::exception& e) {
97 LOG_ERROR_S(&server_,
98 "fatal: shared session process: caught unhandled exception: "
99 << e.what());
100
101 throw;
102 } catch (...) {
103 LOG_ERROR_S(&server_,
104 "fatal: shared session process: caught unknown, unhandled "
105 "exception.");
106
107 throw;
108 }
109 }
110
111 WServer& server_;
112 bool running_;
113 std::string sessionId_;
114 WebMain *webMain_;
115 };
116
117 namespace {
118
doShutdown(const char * signal)119 void doShutdown(const char *signal)
120 {
121 if (webMainInstance) {
122 LOG_INFO_S(webMainInstance->controller().server(), "Caught " << signal);
123 webMainInstance->shutdown();
124 }
125 }
126
handleSigTerm(int)127 void handleSigTerm(int)
128 {
129 doShutdown("SIGTERM");
130 }
131
handleSigUsr1(int)132 void handleSigUsr1(int)
133 {
134 doShutdown("SIGUSR1");
135 }
136
handleSigHup(int)137 void handleSigHup(int)
138 {
139 doShutdown("SIGHUP");
140 }
141
142 }
143
WServer(const std::string & applicationPath,const std::string & wtConfigurationFile)144 WServer::WServer(const std::string& applicationPath,
145 const std::string& wtConfigurationFile)
146 : impl_(new Impl(*this))
147 {
148 init(applicationPath, wtConfigurationFile);
149 }
150
WServer(int argc,char * argv[],const std::string & wtConfigurationFile)151 WServer::WServer(int argc, char *argv[], const std::string& wtConfigurationFile)
152 : impl_(new Impl(*this))
153 {
154 init(argv[0], "");
155
156 setServerConfiguration(argc, argv, wtConfigurationFile);
157 }
158
WServer(const std::string & applicationPath,const std::vector<std::string> & args,const std::string & wtConfigurationFile)159 WServer::WServer(const std::string &applicationPath,
160 const std::vector<std::string> &args,
161 const std::string &wtConfigurationFile)
162 : impl_(new Impl(*this))
163 {
164 init(applicationPath, "");
165
166 setServerConfiguration(applicationPath, args, wtConfigurationFile);
167 }
168
~WServer()169 WServer::~WServer()
170 {
171 delete impl_;
172 destroy();
173 }
174
sessions()175 std::vector<WServer::SessionInfo> WServer::sessions() const
176 {
177 return std::vector<WServer::SessionInfo>();
178 }
179
setServerConfiguration(int argc,char * argv[],const std::string &)180 void WServer::setServerConfiguration(int argc, char *argv[],
181 const std::string&)
182 {
183 std::string applicationPath = argv[0];
184 std::vector<std::string> args(argv + 1, argv + argc);
185
186 setServerConfiguration(applicationPath, args);
187 }
188
setServerConfiguration(const std::string & applicationPath,const std::vector<std::string> & args,const std::string & wtConfigurationFile)189 void WServer::setServerConfiguration(const std::string &applicationPath,
190 const std::vector<std::string> &args,
191 const std::string &wtConfigurationFile)
192 {
193 bool isRelayServer = args.size() < 1 || args[0] != "client";
194
195 if (isRelayServer) {
196 LOG_INFO_S(this, "initializing relay server");
197 Server relayServer(*this, applicationPath, args);
198 exit(relayServer.run());
199 } else if (args.size() >= 2) {
200 impl_->sessionId_ = args[1];
201 dedicatedProcessEnabled_ = true;
202 }
203 }
204
start()205 bool WServer::start()
206 {
207 if (isRunning()) {
208 LOG_ERROR_S(this, "start(): server already started!");
209 return false;
210 }
211
212 LOG_INFO_S(this, "initializing " <<
213 (impl_->sessionId_.empty() ? "shared" : "dedicated") <<
214 " wtfcgi session process");
215
216 if (configuration().webSockets()) {
217 LOG_ERROR_S(this, "FastCGI does not support web-sockets, disabling");
218 configuration().setWebSockets(false);
219 }
220
221 configuration().setNeedReadBodyBeforeResponse(true);
222
223 if (signal(SIGTERM, Wt::handleSigTerm) == SIG_ERR)
224 LOG_ERROR_S(this, "cannot catch SIGTERM: signal(): "
225 << (const char *)strerror(errno));
226 if (signal(SIGUSR1, Wt::handleSigUsr1) == SIG_ERR)
227 LOG_ERROR_S(this, "cannot catch SIGUSR1: signal(): "
228 << (const char *)strerror(errno));
229 if (signal(SIGHUP, Wt::handleSigHup) == SIG_ERR)
230 LOG_ERROR_S(this, "cannot catch SIGHUP: signal(): "
231 << (const char *)strerror(errno));
232
233 webController_ = new Wt::WebController(*this, impl_->sessionId_, false);
234
235 impl_->run();
236
237 return false;
238 }
239
isRunning()240 bool WServer::isRunning() const
241 {
242 return impl_->running_;
243 }
244
httpPort()245 int WServer::httpPort() const
246 {
247 // FIXME, we could get this from the CGI environment.
248 return -1;
249 }
250
docRoot()251 std::string WServer::docRoot() const
252 {
253 return "";
254 }
255
stop()256 void WServer::stop()
257 {
258 if (!isRunning()) {
259 LOG_ERROR_S(this, "stop(): server not yet started!");
260 return;
261 }
262 }
263
run()264 void WServer::run()
265 {
266 if (start()) {
267 waitForShutdown();
268 stop();
269 }
270 }
271
resume()272 void WServer::resume()
273 {
274 if (!isRunning()) {
275 LOG_ERROR_S(this, "resume(): server not yet started!");
276 return;
277 }
278 }
279
setSslPasswordCallback(const std::function<std::string (std::size_t max_length,int purpose)> & cb)280 void WServer::setSslPasswordCallback(const std::function<std::string
281 (std::size_t max_length, int purpose)>& cb)
282 {
283 LOG_INFO_S(this,
284 "setSslPasswordCallback(): has no effect in fcgi connector");
285 }
286
WRun(int argc,char * argv[],ApplicationCreator createApplication)287 int WRun(int argc, char *argv[], ApplicationCreator createApplication)
288 {
289 std::string applicationPath = argv[0];
290 std::vector<std::string> args(argv + 1, argv + argc);
291
292 return WRun(applicationPath, args, createApplication);
293 }
294
WRun(const std::string & applicationName,const std::vector<std::string> & args,ApplicationCreator createApplication)295 int WRun(const std::string &applicationName,
296 const std::vector<std::string> &args,
297 ApplicationCreator createApplication)
298 {
299 try {
300 WServer server(applicationName, "");
301
302 try {
303 server.setServerConfiguration(applicationName, args);
304 server.addEntryPoint(EntryPointType::Application, createApplication);
305 server.start();
306
307 return 0;
308 } catch (std::exception& e) {
309 LOG_ERROR_S(&server, "fatal: " << e.what());
310 return 1;
311 }
312 } catch (Wt::WServer::Exception& e) {
313 LOG_ERROR("fatal: " << e.what());
314 return 1;
315 } catch (std::exception& e) {
316 LOG_ERROR("fatal exception: " << e.what());
317 return 1;
318 }
319 }
320
321 }
322