Line data Source code
1 : /**
2 : Copyright (c) 2024 Stappler LLC <admin@stappler.dev>
3 :
4 : Permission is hereby granted, free of charge, to any person obtaining a copy
5 : of this software and associated documentation files (the "Software"), to deal
6 : in the Software without restriction, including without limitation the rights
7 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 : copies of the Software, and to permit persons to whom the Software is
9 : furnished to do so, subject to the following conditions:
10 :
11 : The above copyright notice and this permission notice shall be included in
12 : all copies or substantial portions of the Software.
13 :
14 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 : THE SOFTWARE.
21 : **/
22 :
23 : #include "SPWebRoot.h"
24 : #include "SPWebInputFilter.h"
25 : #include "SPWebRequest.h"
26 : #include "SPWebRequestHandler.h"
27 : #include "SPWebRequestController.h"
28 : #include "SPWebWebsocketConnection.h"
29 : #include "SPWebHostController.h"
30 :
31 : #include "SPPlatformUnistd.h"
32 : #include "SPLog.h"
33 : #include "SPDbUser.h"
34 :
35 : #if LINUX
36 : #include <signal.h>
37 : #endif
38 :
39 : namespace STAPPLER_VERSIONIZED stappler::web {
40 :
41 125 : static Root *getRootFromContext(pool_t *p, uint32_t tag, const void *ptr) {
42 125 : switch (tag) {
43 0 : case uint32_t(config::TAG_HOST): return ((HostController *)ptr)->getRoot(); break;
44 25 : case uint32_t(config::TAG_REQUEST): return ((RequestController *)ptr)->getHost()->getRoot(); break;
45 100 : case uint32_t(config::TAG_WEBSOCKET): return ((WebsocketConnection *)ptr)->getHost().getRoot(); break;
46 : }
47 0 : return nullptr;
48 : }
49 :
50 125 : Root *Root::getCurrent() {
51 125 : Root *ret = nullptr;
52 125 : pool::foreach_info(&ret, [] (void *ud, pool_t *p, uint32_t tag, const void *data) -> bool {
53 125 : auto ptr = getRootFromContext(p, tag, data);
54 125 : if (ptr) {
55 125 : *((Root **)ud) = ptr;
56 125 : return false;
57 : }
58 0 : return true;
59 : });
60 :
61 125 : return ret;
62 : }
63 :
64 0 : void Root::parseParameterList(Map<StringView, StringView> &target, StringView str) {
65 0 : StringView r(str);
66 0 : r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
67 0 : while (!r.empty()) {
68 0 : StringView params, n, v;
69 0 : if (r.is('"')) {
70 0 : ++ r;
71 0 : params = r.readUntil<StringView::Chars<'"'>>();
72 0 : if (r.is('"')) {
73 0 : ++ r;
74 : }
75 : } else {
76 0 : params = r.readUntil<StringView::CharGroup<CharGroupId::WhiteSpace>>();
77 : }
78 :
79 0 : if (!params.empty()) {
80 0 : n = params.readUntil<StringView::Chars<'='>>();
81 0 : ++ params;
82 0 : v = params;
83 :
84 0 : if (!n.empty() && ! v.empty()) {
85 0 : target.emplace(n.pdup(target.get_allocator()), v.pdup(target.get_allocator()));
86 : }
87 : }
88 :
89 0 : r.skipChars<StringView::CharGroup<CharGroupId::WhiteSpace>>();
90 : }
91 0 : }
92 :
93 249 : void Root::setErrorNotification(pool_t *p, Function<void(Value &&)> errorCb, Function<void(Value &&)> debugCb) {
94 249 : perform([&] {
95 249 : ErrorNotificator *err = nullptr;
96 249 : pool::userdata_get((void **)&err, ErrorNotificatorKey, p);
97 249 : if (err) {
98 0 : err->error = move(errorCb);
99 0 : err->debug = move(debugCb);
100 : } else {
101 249 : err = new (p) ErrorNotificator;
102 249 : err->error = move(errorCb);
103 249 : err->debug = move(debugCb);
104 249 : pool::userdata_set(err, ErrorNotificatorKey, nullptr, p);
105 : }
106 249 : }, p);
107 249 : }
108 :
109 50 : void Root::dumpCurrentState(StringView filepath) {
110 50 : String tmpPath;
111 50 : if (filepath.empty()) {
112 50 : if (auto host = Host::getCurrent()) {
113 50 : auto &hostInfo = host.getHostInfo();
114 50 : auto root = hostInfo.documentRoot;
115 50 : tmpPath = toString(root, "/.reports/crash.", Time::now().toMicros(), ".txt");
116 50 : filepath = tmpPath;
117 : }
118 : }
119 :
120 50 : if (filepath.empty()) {
121 0 : return;
122 : }
123 :
124 50 : if (auto f = ::fopen(filepath.data(), "w+")) {
125 50 : if (auto host = Host::getCurrent()) {
126 50 : auto &hostInfo = host.getHostInfo();
127 50 : auto root = hostInfo.documentRoot;
128 50 : ::fputs("Server:\n\tDocumentRoot: ", f);
129 50 : ::fputs(root.data(), f);
130 50 : ::fputs("\n\tName: ", f);
131 50 : ::fputs(hostInfo.hostname.data(), f);
132 50 : ::fputs("\n\tDate: ", f);
133 50 : ::fputs(Time::now().toHttp<Interface>().data(), f);
134 :
135 50 : if (auto req = Request::getCurrent()) {
136 50 : auto &reqInfo = req.getInfo();
137 50 : ::fputs("\nRequest:\n", f);
138 50 : ::fprintf(f, "\tUrl: %s%s\n", reqInfo.url.host.data(), reqInfo.unparserUri.data());
139 50 : ::fprintf(f, "\tRequest: %s\n", reqInfo.requestLine.data());
140 50 : ::fprintf(f, "\tIp: %s\n", reqInfo.useragentIp.data());
141 :
142 50 : ::fputs("\tHeaders:\n", f);
143 50 : req.foreachRequestHeaders([&] (StringView key, StringView value) {
144 300 : ::fprintf(f, "\t\t%s: %s\n", key.data(), value.data());
145 300 : });
146 50 : }
147 :
148 50 : ::fputs("\nBacktrace:\n", f);
149 :
150 50 : getBacktrace(2, [&] (StringView str) {
151 1400 : ::fprintf(f, "\t%s\n", str.data());
152 1400 : });
153 :
154 50 : ::fclose(f);
155 : }
156 : }
157 50 : }
158 :
159 25 : Root::~Root() {
160 25 : pool::destroy(_workerPool);
161 25 : }
162 :
163 25 : Root::Root(pool_t *p) : _rootPool(p) {
164 25 : _workerPool = pool::create(p);
165 :
166 25 : _serverNameLine = StringView(
167 50 : toString("Stappler/", getStapplerVersionString(), " ", "Webserver/", config::getWebserverVersionString())).pdup(_rootPool);
168 25 : }
169 :
170 25 : Root::Stat Root::getStat() const {
171 : return Stat{
172 25 : _requestsReceived.load(),
173 50 : _heartbeatCounter.load(),
174 50 : _dbQueriesReleased.load(),
175 50 : _dbQueriesPerformed.load()
176 100 : };
177 : }
178 :
179 50 : void Root::setDebugEnabled(bool val) {
180 50 : _debug = val;
181 50 : }
182 :
183 0 : bool Root::isSecureConnection(const Request &) const {
184 0 : return false;
185 : }
186 :
187 25 : db::sql::Driver * Root::getDbDriver(StringView driver) {
188 25 : auto it = _dbDrivers.find(driver);
189 25 : if (it != _dbDrivers.end()) {
190 25 : return it->second;
191 : } else {
192 0 : auto d = createDbDriver(driver);
193 0 : _dbDrivers.emplace(driver, d);
194 0 : return d;
195 : }
196 : }
197 :
198 0 : db::sql::Driver::Handle Root::dbdOpen(pool_t *, const Host &serv) {
199 0 : log::error("web::Root", "Root::dbdOpen is not implemented");
200 0 : return db::sql::Driver::Handle(nullptr);
201 : }
202 :
203 0 : void Root::dbdClose(const Host &serv, db::sql::Driver::Handle) {
204 0 : log::error("web::Root", "Root::dbdClose is not implemented");
205 0 : }
206 :
207 0 : db::sql::Driver::Handle Root::dbdAcquire(const Request &req) {
208 0 : log::error("web::Root", "Root::dbdAcquire is not implemented");
209 0 : return db::sql::Driver::Handle(nullptr);
210 : }
211 :
212 3100 : Status Root::runPostReadRequest(Request &r) {
213 6200 : return perform([&, this] {
214 3100 : _requestsReceived += 1;
215 : //OutputFilter::insert(r);
216 :
217 3100 : Request request(r);
218 3100 : Host host = request.host();
219 :
220 3100 : auto ret = host.handleRequest(request);
221 3100 : if (ret > 0 || ret == DONE) {
222 0 : return ret;
223 : }
224 :
225 3100 : RequestHandler *rhdl = request.getRequestHandler();
226 3100 : if (rhdl) {
227 3050 : return rhdl->onPostReadRequest(request);
228 : }
229 :
230 50 : return DECLINED;
231 12400 : }, r.pool(), config::TAG_REQUEST, r.config());
232 : }
233 :
234 3025 : Status Root::runTranslateName(Request &r) {
235 6050 : return perform([&] () {
236 3025 : Request request(r);
237 :
238 3025 : RequestHandler *rhdl = request.getRequestHandler();
239 3025 : if (rhdl) {
240 3025 : if (!rhdl->isRequestPermitted(request)) {
241 25 : auto status = request.getInfo().status;
242 25 : if (status == 0 || status == 200) {
243 0 : return HTTP_FORBIDDEN;
244 : }
245 25 : return status;
246 : }
247 3000 : auto res = rhdl->onTranslateName(request);
248 3000 : if (res == DECLINED
249 525 : && request.getInfo().method != RequestMethod::Post
250 150 : && request.getInfo().method != RequestMethod::Put
251 0 : && request.getInfo().method != RequestMethod::Patch
252 3525 : && request.getInfo().method != RequestMethod::Options) {
253 0 : request.setRequestHandler(nullptr);
254 3000 : } else if (res == DECLINED) {
255 525 : res = OK;
256 : }
257 3000 : return res;
258 : }
259 :
260 0 : return DECLINED;
261 12100 : }, r.pool(), config::TAG_REQUEST, r.config());
262 : }
263 :
264 725 : Status Root::runCheckAccess(Request &r) {
265 1450 : return perform([&] {
266 725 : Request request(r);
267 725 : RequestHandler *rhdl = request.getRequestHandler();
268 725 : if (rhdl) {
269 700 : return OK; // already checked by serenity
270 : }
271 25 : if (!request.getInfo().filename.empty()) {
272 25 : if (StringView(request.getInfo().filename).starts_with(StringView(request.getInfo().documentRoot))) {
273 25 : return OK;
274 : }
275 : }
276 0 : return DECLINED;
277 2900 : }, r.pool(), config::TAG_REQUEST, r.config());
278 : }
279 :
280 675 : Status Root::runQuickHandler(Request &r, int v) {
281 1350 : return perform([&] () -> Status {
282 675 : Request request(r);
283 675 : RequestHandler *rhdl = request.getRequestHandler();
284 675 : if (rhdl) {
285 675 : return rhdl->onQuickHandler(request, v);
286 : }
287 0 : return DECLINED;
288 2700 : }, r.pool(), config::TAG_REQUEST, r.config());
289 : }
290 :
291 675 : void Root::runInsertFilter(Request &r) {
292 675 : perform([&] {
293 675 : Request request(r);
294 :
295 675 : RequestHandler *rhdl = request.getRequestHandler();
296 675 : if (rhdl) {
297 675 : rhdl->onInsertFilter(request);
298 : }
299 1350 : }, r.pool(), config::TAG_REQUEST, r.config());
300 675 : }
301 :
302 675 : Status Root::runHandler(Request &r) {
303 1350 : return perform([&] () -> Status {
304 675 : Request request(r);
305 :
306 675 : RequestHandler *rhdl = request.getRequestHandler();
307 675 : if (rhdl) {
308 675 : return rhdl->onHandler(request);
309 : }
310 :
311 0 : return DECLINED;
312 2700 : }, r.pool(), config::TAG_REQUEST, r.config());
313 : }
314 :
315 650 : void Root::handleFilterInit(InputFilter *f) {
316 650 : RequestHandler *rhdl = f->getRequest().getRequestHandler();
317 650 : if (rhdl) {
318 650 : rhdl->onFilterInit(f);
319 : }
320 650 : }
321 825 : void Root::handleFilterUpdate(InputFilter *f) {
322 825 : RequestHandler *rhdl = f->getRequest().getRequestHandler();
323 825 : if (rhdl) {
324 825 : rhdl->onFilterUpdate(f);
325 : }
326 825 : }
327 650 : void Root::handleFilterComplete(InputFilter *f) {
328 650 : RequestHandler *rhdl = f->getRequest().getRequestHandler();
329 650 : if (rhdl) {
330 650 : rhdl->onFilterComplete(f);
331 : }
332 650 : }
333 :
334 0 : void Root::addDb(StringView str) {
335 0 : perform([&, this] {
336 0 : emplace_ordered(_dbs, str.pdup(_rootPool));
337 0 : }, _rootPool);
338 0 : }
339 :
340 0 : void Root::setDbParams(StringView str) {
341 0 : perform([&, this] {
342 0 : parseParameterList(_dbParams, str);
343 0 : }, _rootPool);
344 0 : }
345 :
346 0 : void Root::setThreadsCount(StringView init, StringView max) {
347 0 : _initThreads = std::max(size_t(init.readInteger(10).get(1)), size_t(1));
348 0 : _maxThreads = std::max(size_t(max.readInteger(10).get(1)), _initThreads);
349 0 : }
350 :
351 0 : void Root::handleHeartbeat(pool_t *pool) {
352 0 : foreachHost([&] (Host &serv) {
353 0 : perform([&] {
354 0 : serv.handleHeartBeat(pool);
355 0 : }, pool, config::TAG_HOST, serv.getController());
356 0 : });
357 0 : }
358 :
359 0 : void Root::handleBroadcast(const Value &res) {
360 0 : if (res.getBool("system")) {
361 0 : auto &option = res.getString("option");
362 0 : if (!option.empty()) {
363 0 : if (option == "debug") {
364 0 : _debug = res.getBool("debug");
365 : }
366 : }
367 0 : return;
368 0 : } else if (res.getBool("local")) {
369 0 : foreachHost([&] (Host &serv) {
370 0 : perform([&] {
371 0 : serv.handleBroadcast(res);
372 0 : }, serv.getThreadPool(), config::TAG_HOST, serv.getController());
373 0 : });
374 : }
375 : }
376 :
377 0 : void Root::handleChildInit(pool_t *p) {
378 0 : perform([&, this] {
379 0 : initDatabases();
380 :
381 0 : initSignals();
382 :
383 0 : _pending = new Vector<PendingTask>();
384 :
385 0 : foreachHost([&, this] (Host &host) {
386 0 : host.handleChildInit(_configPool);
387 0 : });
388 :
389 : // start threads only after all initialization is done
390 0 : initThreads();
391 0 : }, p);
392 0 : }
393 :
394 25 : Status Root::runTypeChecker(Request &r) {
395 25 : auto &info = r.getInfo();
396 25 : if (info.stat.isDir) {
397 0 : r.setContentType(config::DIR_MIME_TYPE);
398 0 : return OK;
399 : }
400 :
401 25 : if (info.filename.empty()) {
402 0 : return DECLINED;
403 : }
404 :
405 25 : StringView contentType;
406 25 : StringView charset;
407 25 : Vector<StringView> contentEncoding;
408 :
409 75 : auto onTypeCheckerExtension = [&, this] (StringView fileName, StringView ext) {
410 25 : auto ct = filesystem::detectMimeType(fileName);
411 25 : if (!ct.empty()) {
412 25 : contentType = ct;
413 : } else {
414 0 : ct = findTypeCheckerContentType(r, ext);
415 0 : if (!ct.empty()) {
416 0 : contentType = ct;
417 : }
418 : }
419 :
420 25 : auto cs = findTypeCheckerCharset(r, ext);
421 25 : if (!cs.empty()) {
422 0 : charset = cs;
423 : }
424 :
425 25 : auto ce = findTypeCheckerContentEncoding(r, ext);
426 25 : if (!ce.empty()) {
427 0 : contentEncoding.emplace_back(ce);
428 : }
429 25 : };
430 :
431 25 : StringView fileName = filepath::lastComponent(info.filename);
432 25 : fileName.skipChars<StringView::Chars<'.'>>();
433 :
434 25 : auto tmp = fileName;
435 25 : tmp.skipUntil<StringView::Chars<'.'>>();
436 25 : if (tmp.size() < 2 || !tmp.is('.') ) {
437 0 : return DECLINED;
438 : }
439 :
440 50 : while (!tmp.empty()) {
441 25 : if (tmp.is('.')) {
442 25 : tmp.skipChars<StringView::Chars<'.'>>();
443 25 : auto ext = tmp.readUntil<StringView::Chars<'.'>>();
444 25 : if (!ext.empty()) {
445 25 : auto tmpStr = ext.str<Interface>();
446 25 : string::apply_tolower_c(tmpStr);
447 25 : onTypeCheckerExtension(fileName, tmpStr);
448 25 : }
449 : } else {
450 0 : tmp.skipUntil<StringView::Chars<'.'>>();
451 : }
452 : }
453 :
454 25 : if (!contentEncoding.empty()) {
455 0 : if (info.contentEncoding.empty() && contentEncoding.size() == 1) {
456 0 : r.setContentEncoding(contentEncoding.front());
457 : } else {
458 0 : StringStream stream;
459 :
460 0 : bool start = true;
461 0 : if (!info.contentEncoding.empty()) {
462 0 : stream << info.contentEncoding;
463 0 : start = false;
464 : }
465 :
466 0 : for (auto &it : contentEncoding) {
467 0 : if (start) {
468 0 : start = false;
469 : } else {
470 0 : stream << ", ";
471 : }
472 0 : stream << it;
473 : }
474 :
475 0 : r.setContentEncoding(stream.weak());
476 0 : }
477 : }
478 :
479 25 : if (!contentType.empty()) {
480 25 : auto v = extractCharset(contentType);
481 25 : if (!charset.empty()) {
482 0 : if (v.empty()) {
483 0 : StringStream stream;
484 0 : stream << contentType << "; charset=" << charset;
485 0 : r.setContentType(stream.weak());
486 0 : } else {
487 0 : StringView start = StringView(contentType, v.data() - contentType.data());
488 0 : StringView end = StringView(v.data() + v.size(), contentType.size() - (v.data() - contentType.data() + v.size()));
489 :
490 0 : StringStream stream;
491 0 : stream << start << charset << end;
492 0 : r.setContentType(stream.weak());
493 0 : }
494 : } else {
495 25 : r.setContentType(contentType);
496 : }
497 : }
498 :
499 25 : if (info.contentType.empty()) {
500 0 : return DECLINED;
501 : }
502 :
503 25 : return OK;
504 25 : }
505 :
506 0 : StringView Root::findTypeCheckerContentType(Request &r, StringView ext) const {
507 0 : return StringView();
508 : }
509 :
510 25 : StringView Root::findTypeCheckerCharset(Request &r, StringView ext) const {
511 25 : return StringView();
512 : }
513 :
514 0 : StringView Root::findTypeCheckerContentLanguage(Request &r, StringView ext) const {
515 0 : return StringView();
516 : }
517 :
518 25 : StringView Root::findTypeCheckerContentEncoding(Request &r, StringView ext) const {
519 25 : return StringView();
520 : }
521 :
522 25 : void Root::initDatabases() {
523 25 : perform([&, this] {
524 25 : Map<db::sql::Driver *, Vector<StringView>> databases;
525 :
526 25 : if (!_dbParams.empty()) {
527 0 : StringView driver;
528 0 : StringView dbname;
529 :
530 0 : for (auto &it : _dbParams) {
531 0 : if (it.first == "driver") {
532 0 : driver = it.second;
533 0 : } else if (it.first == "dbname") {
534 0 : dbname = it.second;
535 : }
536 : }
537 :
538 0 : if (driver.empty()) {
539 0 : driver = StringView("pgsql");
540 : }
541 :
542 0 : if (auto d = createDbDriver(driver)) {
543 0 : _dbDrivers.emplace(driver, d);
544 0 : auto dit = databases.emplace(d, Vector<StringView>()).first;
545 0 : if (!dbname.empty()) {
546 0 : emplace_ordered(dit->second, dbname);
547 : }
548 0 : if (!_dbs.empty()) {
549 0 : for (auto &it : _dbs) {
550 0 : emplace_ordered(dit->second, it);
551 : }
552 : }
553 0 : _primaryDriver = d;
554 : }
555 : }
556 :
557 25 : foreachHost([&, this] (Host &host) {
558 25 : auto config = host.getController();
559 25 : if (!config->getDbParams().empty()) {
560 25 : StringView driver;
561 25 : StringView dbname;
562 175 : for (auto &it : config->getDbParams()) {
563 150 : if (it.first == "driver") {
564 25 : driver = it.second;
565 125 : } else if (it.first == "dbname") {
566 25 : dbname = it.second;
567 : }
568 : }
569 :
570 25 : if (!driver.empty()) {
571 25 : auto iit = _dbDrivers.find(driver);
572 25 : if (iit == _dbDrivers.end()) {
573 25 : if (auto d = createDbDriver(driver)) {
574 25 : iit = _dbDrivers.emplace(driver, d).first;
575 : }
576 : }
577 :
578 25 : if (!dbname.empty()) {
579 25 : auto dit = databases.find(iit->second);
580 25 : if (dit != databases.end()) {
581 0 : emplace_ordered(dit->second, dbname);
582 : }
583 : }
584 : }
585 : }
586 25 : });
587 :
588 25 : if (_primaryDriver && !_dbParams.empty()) {
589 0 : bool init = false;
590 0 : auto dIt = databases.find(_primaryDriver);
591 0 : if (dIt != databases.end()) {
592 0 : if (!dIt->second.empty()) {
593 0 : auto handle = _primaryDriver->connect(_dbParams);
594 0 : if (handle.get()) {
595 0 : _primaryDriver->init(handle, dIt->second);
596 0 : _primaryDriver->finish(handle);
597 0 : init = true;
598 : }
599 : }
600 : }
601 0 : if (!init) {
602 0 : auto handle = _primaryDriver->connect(_dbParams);
603 0 : if (handle.get()) {
604 0 : _primaryDriver->init(handle, Vector<StringView>());
605 0 : _primaryDriver->finish(handle);
606 0 : init = true;
607 : }
608 : }
609 : }
610 25 : }, _workerPool);
611 25 : pool::clear(_workerPool);
612 25 : }
613 :
614 : #if LINUX
615 : static struct sigaction s_sharedSigAction;
616 : static struct sigaction s_sharedSigOldAction;
617 :
618 0 : static void s_sigAction(int sig, siginfo_t *info, void *ucontext) {
619 0 : if (auto host = Host::getCurrent()) {
620 0 : auto &hostInfo = host.getHostInfo();
621 0 : auto root = hostInfo.documentRoot;
622 0 : Root::dumpCurrentState(toString(root, "/.reports/crash.", Time::now().toMicros(), ".txt"));
623 : }
624 :
625 0 : if ((s_sharedSigOldAction.sa_flags & SA_SIGINFO) != 0) {
626 0 : if (s_sharedSigOldAction.sa_sigaction) {
627 0 : s_sharedSigOldAction.sa_sigaction(sig, info, ucontext);
628 : }
629 : } else {
630 0 : if (s_sharedSigOldAction.sa_handler == SIG_DFL) {
631 0 : if (SIGURG == sig || SIGWINCH == sig || SIGCONT == sig) return;
632 :
633 : static struct sigaction tmpSig;
634 0 : tmpSig.sa_handler = SIG_DFL;
635 0 : ::sigemptyset(&tmpSig.sa_mask);
636 0 : ::sigaction(sig, &tmpSig, nullptr);
637 0 : ::kill(getpid(), sig);
638 0 : ::sigaction(sig, &s_sharedSigAction, nullptr);
639 0 : } else if (s_sharedSigOldAction.sa_handler == SIG_IGN) {
640 0 : return;
641 0 : } else if (s_sharedSigOldAction.sa_handler) {
642 0 : s_sharedSigOldAction.sa_handler(sig);
643 : }
644 : }
645 : }
646 : #endif
647 :
648 0 : void Root::initSignals() {
649 : #if LINUX
650 0 : ::setenv("GNUTLS_NO_IMPLICIT_INIT", "1", 0);
651 :
652 0 : memset(&s_sharedSigAction, 0, sizeof(s_sharedSigAction));
653 0 : s_sharedSigAction.sa_sigaction = &s_sigAction;
654 0 : s_sharedSigAction.sa_flags = SA_SIGINFO;
655 0 : sigemptyset(&s_sharedSigAction.sa_mask);
656 : //sigaddset(&s_sharedSigAction.sa_mask, SIGSEGV);
657 :
658 0 : ::sigaction(SIGSEGV, &s_sharedSigAction, &s_sharedSigOldAction);
659 : #endif
660 0 : }
661 :
662 0 : void Root::initThreads() {
663 :
664 0 : }
665 :
666 25 : db::sql::Driver *Root::createDbDriver(StringView driverName) {
667 25 : if (auto d = db::sql::Driver::open(_rootPool, this, driverName)) {
668 25 : d->setDbCtrl([this] (bool complete) {
669 0 : if (complete) {
670 0 : _dbQueriesReleased += 1;
671 : } else {
672 0 : _dbQueriesPerformed += 1;
673 : }
674 0 : });
675 25 : return d;
676 : }
677 :
678 0 : log::error("web::Root", "Fail to initialize driver: ", driverName);
679 0 : return nullptr;
680 : }
681 :
682 50 : void Root::pushErrorMessage(Value &&val) const {
683 50 : if (auto serv = Host::getCurrent()) {
684 50 : serv.reportError(val);
685 : }
686 :
687 50 : if (isDebugEnabled()) {
688 : Value bcast{
689 0 : std::make_pair("message", Value(true)),
690 0 : std::make_pair("level", Value("error")),
691 0 : std::make_pair("data", Value(val)),
692 0 : };
693 0 : if (auto a = db::Adapter::FromContext(this)) {
694 0 : a.broadcast(bcast);
695 : }
696 0 : }
697 :
698 : #if DEBUG
699 50 : log::error("web::Root", data::EncodeFormat::Pretty, val);
700 : #endif
701 :
702 50 : if (auto req = Request::getCurrent()) {
703 50 : req.getController()->pushErrorMessage(move(val));
704 50 : return;
705 50 : }
706 :
707 0 : auto pool = pool::acquire();
708 0 : ErrorNotificator *err = nullptr;
709 0 : pool::userdata_get((void **)&err, ErrorNotificatorKey, pool);
710 0 : if (err && err->error) {
711 0 : err->error(std::move(val));
712 : }
713 :
714 0 : log::error("web::Root", "Unhandled error: ", data::EncodeFormat::Pretty, val);
715 : }
716 :
717 25 : void Root::pushDebugMessage(Value &&val) const {
718 25 : if (auto serv = Host::getCurrent()) {
719 25 : serv.reportError(val);
720 : }
721 :
722 25 : if (isDebugEnabled()) {
723 : Value bcast{
724 0 : std::make_pair("message", Value(true)),
725 0 : std::make_pair("level", Value("debug")),
726 0 : std::make_pair("data", Value(val)),
727 0 : };
728 0 : if (auto a = db::Adapter::FromContext(this)) {
729 0 : a.broadcast(bcast);
730 : }
731 0 : }
732 :
733 : #if DEBUG
734 25 : log::error("web::Root", data::EncodeFormat::Pretty, val);
735 : #endif
736 :
737 25 : if (auto req = Request::getCurrent()) {
738 25 : req.getController()->pushDebugMessage(move(val));
739 25 : return;
740 25 : }
741 :
742 0 : auto pool = pool::acquire();
743 0 : ErrorNotificator *err = nullptr;
744 0 : pool::userdata_get((void **)&err, ErrorNotificatorKey, pool);
745 0 : if (err && err->debug) {
746 0 : err->debug(std::move(val));
747 0 : return;
748 : }
749 :
750 0 : log::debug("web::Root", "Unhandled debug message: ", data::EncodeFormat::Pretty, val);
751 : }
752 :
753 0 : db::Adapter Root::getAdapterFromContext() const {
754 0 : if (auto p = pool::acquire()) {
755 0 : db::BackendInterface *h = nullptr;
756 0 : stappler::memory::pool::userdata_get((void **)&h, db::config::STORAGE_INTERFACE_KEY.data(), p);
757 0 : if (h) {
758 0 : return db::Adapter(h, this);
759 : }
760 : }
761 :
762 0 : if (auto req = Request::getCurrent()) {
763 0 : return req.getController()->acquireDatabase();
764 0 : }
765 0 : return db::Adapter(nullptr, nullptr);
766 : }
767 :
768 25 : void Root::scheduleAyncDbTask(const Callback<Function<void(const db::Transaction &)>(pool_t *)> &setupCb) const {
769 25 : if (auto serv = Host::getCurrent()) {
770 25 : AsyncTask::perform(serv, [&] (AsyncTask &task) {
771 25 : auto cb = setupCb(task.pool());
772 25 : task.addExecuteFn([cb = std::move(cb)] (const AsyncTask &task) -> bool {
773 25 : task.performWithStorage([&] (const db::Transaction &t) {
774 25 : t.performAsSystem([&] () -> bool {
775 50 : cb(t);
776 25 : return true;
777 : });
778 25 : });
779 25 : return true;
780 : });
781 25 : });
782 : }
783 25 : }
784 :
785 300 : StringView Root::getDocumentRoot() const {
786 300 : if (auto req = Request::getCurrent()) {
787 275 : return req.getInfo().documentRoot;
788 300 : }
789 :
790 25 : if (auto serv = Host::getCurrent()) {
791 25 : return serv.getHostInfo().documentRoot;
792 : }
793 :
794 0 : return StringView();
795 : }
796 :
797 775 : const db::Scheme *Root::getFileScheme() const {
798 775 : if (auto serv = Host::getCurrent()) {
799 775 : return serv.getFileScheme();
800 : }
801 0 : return nullptr;
802 : }
803 :
804 1000 : const db::Scheme *Root::getUserScheme() const {
805 1000 : if (auto serv = Host::getCurrent()) {
806 1000 : return serv.getUserScheme();
807 : }
808 0 : return nullptr;
809 : }
810 :
811 550 : db::RequestData Root::getRequestData() const {
812 550 : db::RequestData ret;
813 :
814 550 : if (auto req = Request::getCurrent()) {
815 550 : ret.exists = true;
816 550 : ret.address = req.getInfo().useragentIp;
817 550 : ret.hostname = req.getInfo().url.host;
818 550 : ret.uri = req.getInfo().unparserUri;
819 550 : }
820 :
821 550 : return ret;
822 : }
823 :
824 3425 : void Root::initTransaction(db::Transaction &t) const {
825 3425 : if (auto req = Request::getCurrent()) {
826 2850 : t.setRole(req.getAccessRole());
827 : } else {
828 575 : t.setRole(db::AccessRoleId::Nobody);
829 3425 : }
830 :
831 3425 : if (auto serv = Host::getCurrent()) {
832 3425 : serv.initTransaction(t);
833 : }
834 3425 : }
835 :
836 100 : db::InputFile *Root::getFileFromContext(int64_t id) const {
837 100 : return InputFilter::getFileFromContext(id);
838 : }
839 :
840 3200 : int64_t Root::getUserIdFromContext() const {
841 3200 : if (auto req = Request::getCurrent()) {
842 2100 : if (auto user = req.getAuthorizedUser()) {
843 450 : return user->getObjectId();
844 : } else {
845 1650 : return req.getUserId();
846 : }
847 3200 : }
848 1100 : return 0;
849 : }
850 :
851 : }
|