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