1 /* $Id: nst_server.cpp 589351 2019-07-10 13:39:06Z satskyse $
2 * ===========================================================================
3 *
4 * PUBLIC DOMAIN NOTICE
5 * National Center for Biotechnology Information
6 *
7 * This software/database is a "United States Government Work" under the
8 * terms of the United States Copyright Act. It was written as part of
9 * the author's official duties as a United States Government employee and
10 * thus cannot be copyrighted. This software/database is freely available
11 * to the public for use. The National Library of Medicine and the U.S.
12 * Government have not placed any restriction on its use or reproduction.
13 *
14 * Although all reasonable efforts have been taken to ensure the accuracy
15 * and reliability of the software and data, the NLM and the U.S.
16 * Government do not and cannot warrant the performance or results that
17 * may be obtained by using this software or data. The NLM and the U.S.
18 * Government disclaim all warranties, express or implied, including
19 * warranties of performance, merchantability or fitness for any particular
20 * purpose.
21 *
22 * Please cite the author in any work or product based on this material.
23 *
24 * ===========================================================================
25 *
26 * Authors: Denis Vakatov
27 *
28 * File Description: NetStorage threaded server
29 *
30 */
31
32 #include <ncbi_pch.hpp>
33 #include <corelib/resource_info.hpp>
34
35 #include "nst_server.hpp"
36 #include "nst_service_thread.hpp"
37 #include "nst_config.hpp"
38 #include "nst_constants.hpp"
39
40
41 USING_NCBI_SCOPE;
42
43
44 CNetStorageServer * CNetStorageServer::sm_netstorage_server = NULL;
45 const string kCrashFlagFileName("CRASH_FLAG");
46
47
CNetStorageServer()48 CNetStorageServer::CNetStorageServer()
49 : m_Port(0),
50 m_HostNetAddr(CSocketAPI::gethostbyname(kEmptyStr)),
51 m_Shutdown(false),
52 m_SigNum(0),
53 m_Log(default_log),
54 m_LogTimingNSTAPI(default_log_timing_nst_api),
55 m_LogTimingClientSocket(default_log_timing_client_socket),
56 m_SessionID("s" + x_GenerateGUID()),
57 m_NetworkTimeout(0),
58 m_DataPath("./data." + NStr::NumericToString(m_Port)),
59 m_StartTime(CNSTPreciseTime::Current()),
60 m_AnybodyCanReconfigure(false),
61 m_NeedDecryptCacheReset(false),
62 m_LastDecryptCacheReset(0.0)
63 {
64 sm_netstorage_server = this;
65 }
66
67
~CNetStorageServer()68 CNetStorageServer::~CNetStorageServer()
69 {}
70
71
72 void
AddDefaultListener(IServer_ConnectionFactory * factory)73 CNetStorageServer::AddDefaultListener(IServer_ConnectionFactory * factory)
74 {
75 AddListener(factory, m_Port);
76 }
77
78
79 CJsonNode
SetParameters(const SNetStorageServerParameters & params,bool reconfigure)80 CNetStorageServer::SetParameters(
81 const SNetStorageServerParameters & params,
82 bool reconfigure)
83 {
84 if (!reconfigure) {
85 CServer::SetParameters(params);
86
87 m_Port = params.port;
88 m_Log = params.log;
89 m_LogTimingNSTAPI = params.log_timing_nst_api;
90 m_LogTimingClientSocket = params.log_timing_client_socket;
91 m_NetworkTimeout = params.network_timeout;
92 m_AdminClientNames = x_GetAdminClientNames(params.admin_client_names);
93 m_DataPath = params.data_path;
94 return CJsonNode::NewNullNode();
95 }
96
97 // Here: it is reconfiguration.
98 // Need to consider:
99 // - log
100 // - admin names
101 // - max connections
102 // - network timeout
103
104 set<string> new_admins = x_GetAdminClientNames(
105 params.admin_client_names);
106 vector<string> added;
107 vector<string> deleted;
108
109 {
110 CFastMutexGuard guard(m_AdminClientNamesLock);
111
112 // Compare the old and the new sets of administrators
113 set_difference(new_admins.begin(), new_admins.end(),
114 m_AdminClientNames.begin(), m_AdminClientNames.end(),
115 inserter(added, added.begin()));
116 set_difference(m_AdminClientNames.begin(), m_AdminClientNames.end(),
117 new_admins.begin(), new_admins.end(),
118 inserter(deleted, deleted.begin()));
119
120 m_AdminClientNames = new_admins;
121 }
122
123 SServer_Parameters current_params;
124 CServer::GetParameters(¤t_params);
125
126 if (m_Log == params.log &&
127 m_LogTimingNSTAPI == params.log_timing_nst_api &&
128 m_LogTimingClientSocket == params.log_timing_client_socket &&
129 m_NetworkTimeout == params.network_timeout &&
130 m_DataPath == params.data_path &&
131 current_params.max_connections == params.max_connections &&
132 added.empty() &&
133 deleted.empty())
134 return CJsonNode::NewNullNode();
135
136 // Here: there is a difference
137 CJsonNode diff = CJsonNode::NewObjectNode();
138
139 if (m_Log != params.log) {
140 CJsonNode values = CJsonNode::NewObjectNode();
141
142 values.SetByKey("Old", CJsonNode::NewBooleanNode(m_Log));
143 values.SetByKey("New", CJsonNode::NewBooleanNode(params.log));
144 diff.SetByKey("log", values);
145
146 m_Log = params.log;
147 }
148 if (m_LogTimingNSTAPI != params.log_timing_nst_api) {
149 CJsonNode values = CJsonNode::NewObjectNode();
150
151 values.SetByKey("Old", CJsonNode::NewBooleanNode(m_LogTimingNSTAPI));
152 values.SetByKey("New", CJsonNode::NewBooleanNode(
153 params.log_timing_nst_api));
154 diff.SetByKey("log_timing_nst_api", values);
155
156 m_LogTimingNSTAPI = params.log_timing_nst_api;
157 }
158 if (m_LogTimingClientSocket != params.log_timing_client_socket) {
159 CJsonNode values = CJsonNode::NewObjectNode();
160
161 values.SetByKey("Old", CJsonNode::NewBooleanNode(
162 m_LogTimingClientSocket));
163 values.SetByKey("New", CJsonNode::NewBooleanNode(
164 params.log_timing_client_socket));
165 diff.SetByKey("log_timing_client_socket", values);
166
167 m_LogTimingClientSocket = params.log_timing_client_socket;
168 }
169
170 if (m_NetworkTimeout != params.network_timeout) {
171 CJsonNode values = CJsonNode::NewObjectNode();
172
173 values.SetByKey("Old", CJsonNode::NewIntegerNode(m_NetworkTimeout));
174 values.SetByKey("New", CJsonNode::NewIntegerNode(
175 params.network_timeout));
176 diff.SetByKey("network_timeout", values);
177
178 m_NetworkTimeout = params.network_timeout;
179 }
180
181 if (m_DataPath != params.data_path) {
182 CJsonNode values = CJsonNode::NewObjectNode();
183
184 values.SetByKey("Old", CJsonNode::NewStringNode(m_DataPath));
185 values.SetByKey("New", CJsonNode::NewStringNode(params.data_path));
186 diff.SetByKey("data_path", values);
187
188 m_DataPath = params.data_path;
189 }
190
191 if (current_params.max_connections != params.max_connections) {
192 CJsonNode values = CJsonNode::NewObjectNode();
193
194 values.SetByKey("Old", CJsonNode::NewIntegerNode(
195 current_params.max_connections));
196 values.SetByKey("New", CJsonNode::NewIntegerNode(
197 params.max_connections));
198 diff.SetByKey("max_connections", values);
199
200 current_params.max_connections = params.max_connections;
201 CServer::SetParameters(current_params);
202 }
203
204 if (!added.empty() || !deleted.empty())
205 diff.SetByKey("admin_client_name", x_diffInJson(added, deleted));
206
207 return diff;
208 }
209
210
ShutdownRequested(void)211 bool CNetStorageServer::ShutdownRequested(void)
212 {
213 return m_Shutdown;
214 }
215
216
SetShutdownFlag(int signum)217 void CNetStorageServer::SetShutdownFlag(int signum)
218 {
219 if (!m_Shutdown) {
220 m_Shutdown = true;
221 m_SigNum = signum;
222 }
223 }
224
225
GetInstance(void)226 CNetStorageServer * CNetStorageServer::GetInstance(void)
227 {
228 return sm_netstorage_server;
229 }
230
231
Exit()232 void CNetStorageServer::Exit()
233 {}
234
235
236 // Resets the decrypt keys cache if necessary
ResetDecryptCacheIfNeed(void)237 void CNetStorageServer::ResetDecryptCacheIfNeed(void)
238 {
239 const CNSTPreciseTime k_ResetInterval(100.0);
240
241 if (m_NeedDecryptCacheReset) {
242 CNSTPreciseTime current = CNSTPreciseTime::Current();
243 if ((current - m_LastDecryptCacheReset) >= k_ResetInterval) {
244 CNcbiEncrypt::Reload();
245 m_LastDecryptCacheReset = current;
246 }
247 }
248 }
249
250
RegisterNetStorageAPIDecryptError(const string & message)251 void CNetStorageServer::RegisterNetStorageAPIDecryptError(
252 const string & message)
253 {
254 m_NeedDecryptCacheReset = true;
255 RegisterAlert(eDecryptInNetStorageAPI, message);
256 }
257
258
ReportNetStorageAPIDecryptSuccess(void)259 void CNetStorageServer::ReportNetStorageAPIDecryptSuccess(void)
260 {
261 m_NeedDecryptCacheReset = false;
262 }
263
264
x_GenerateGUID(void) const265 string CNetStorageServer::x_GenerateGUID(void) const
266 {
267 // Naive implementation of the unique identifier.
268 Int8 pid = CCurrentProcess::GetPid();
269 Int8 current_time = time(0);
270
271 return NStr::NumericToString((pid << 32) | current_time);
272 }
273
274
275 void
UpdateBackendConfiguration(const IRegistry & reg,vector<string> & config_warnings)276 CNetStorageServer::UpdateBackendConfiguration(const IRegistry & reg,
277 vector<string> & config_warnings)
278 {
279 m_BackendConfiguration = NSTGetBackendConfiguration(reg, this,
280 config_warnings);
281 }
282
283
284 CJsonNode
GetBackendConfDiff(const CJsonNode & conf) const285 CNetStorageServer::GetBackendConfDiff(const CJsonNode & conf) const
286 {
287 vector<string> added;
288 vector<string> deleted;
289 vector<string> common;
290 vector<string> changed;
291
292 for (CJsonIterator it = m_BackendConfiguration.Iterate(); it; ++it) {
293 string key = it.GetKey();
294
295 if (conf.HasKey(key))
296 common.push_back(key);
297 else
298 deleted.push_back(key);
299 }
300
301 for (CJsonIterator it = conf.Iterate(); it; ++it) {
302 string key = it.GetKey();
303
304 if (!m_BackendConfiguration.HasKey(key))
305 added.push_back(key);
306 }
307
308 // We do not detect what specifically changed in the certain service
309 // configuration. We detect only the fact a certain service has changed.
310 for (vector<string>::const_iterator k = common.begin();
311 k != common.end(); ++k) {
312 string old_params = m_BackendConfiguration.GetByKey(*k).Repr();
313 string new_params = conf.GetByKey(*k).Repr();
314
315 if (old_params != new_params)
316 changed.push_back(*k);
317 }
318
319 // Test if any changes detected
320 if (added.empty() && deleted.empty() && changed.empty())
321 return CJsonNode::NewNullNode();
322
323 // Here: need to build a dictionary with changes
324 CJsonNode diff = CJsonNode::NewObjectNode();
325
326 if (!added.empty()) {
327 CJsonNode added_services = CJsonNode::NewArrayNode();
328 for (vector<string>::const_iterator k = added.begin();
329 k != added.end(); ++k)
330 added_services.AppendString(*k);
331 diff.SetByKey("AddedServices", added_services);
332 }
333
334 if (!deleted.empty()) {
335 CJsonNode deleted_services = CJsonNode::NewArrayNode();
336 for (vector<string>::const_iterator k = deleted.begin();
337 k != deleted.end(); ++k)
338 deleted_services.AppendString(*k);
339 diff.SetByKey("DeletedServices", deleted_services);
340 }
341
342 if (!changed.empty()) {
343 CJsonNode changed_services = CJsonNode::NewArrayNode();
344 for (vector<string>::const_iterator k = changed.begin();
345 k != changed.end(); ++k)
346 changed_services.AppendString(*k);
347 diff.SetByKey("ChangedServices", changed_services);
348 }
349
350 return diff;
351 }
352
353
IsAdminClientName(const string & name) const354 bool CNetStorageServer::IsAdminClientName(const string & name) const
355 {
356 set<string>::const_iterator found;
357 CFastMutexGuard guard(m_AdminClientNamesLock);
358
359 found = m_AdminClientNames.find(name);
360 return found != m_AdminClientNames.end();
361 }
362
363
364 set<string>
x_GetAdminClientNames(const string & client_names)365 CNetStorageServer::x_GetAdminClientNames(const string & client_names)
366 {
367 vector<string> admins;
368 NStr::Split(client_names, ";, ", admins,
369 NStr::fSplit_MergeDelimiters | NStr::fSplit_Truncate);
370 return set<string>(admins.begin(), admins.end());
371 }
372
373
AcknowledgeAlert(const string & id,const string & user)374 enum EAlertAckResult CNetStorageServer::AcknowledgeAlert(const string & id,
375 const string & user)
376 {
377 return m_Alerts.Acknowledge(id, user);
378 }
379
380
AcknowledgeAlert(EAlertType alert_type,const string & user)381 enum EAlertAckResult CNetStorageServer::AcknowledgeAlert(EAlertType alert_type,
382 const string & user)
383 {
384 return m_Alerts.Acknowledge(alert_type, user);
385 }
386
387
RegisterAlert(EAlertType alert_type,const string & message)388 void CNetStorageServer::RegisterAlert(EAlertType alert_type,
389 const string & message)
390 {
391 m_Alerts.Register(alert_type, message);
392 }
393
394
SerializeAlerts(void) const395 CJsonNode CNetStorageServer::SerializeAlerts(void) const
396 {
397 return m_Alerts.Serialize();
398 }
399
400
GetDb(void)401 CNSTDatabase & CNetStorageServer::GetDb(void)
402 {
403 if (m_Db.get())
404 return *m_Db;
405
406 m_Db.reset(new CNSTDatabase(this));
407 return *m_Db;
408 }
409
410
411 EServiceMetadataPresence
InMetadataServices(const string & service) const412 CNetStorageServer::InMetadataServices(const string & service) const
413 {
414 return m_MetadataServiceRegistry.IsKnown(service);
415 }
416
417
GetServiceTTL(const string & service,TNSTDBValue<CTimeSpan> & ttl) const418 bool CNetStorageServer::GetServiceTTL(const string & service,
419 TNSTDBValue<CTimeSpan> & ttl) const
420 {
421 return m_MetadataServiceRegistry.GetTTL(service, ttl);
422 }
423
424
425 bool
GetServiceProlongOnRead(const string & service,const TNSTDBValue<Int8> & individual_obj_ttl,TNSTDBValue<CTimeSpan> & prolong_on_read) const426 CNetStorageServer::GetServiceProlongOnRead(
427 const string & service,
428 const TNSTDBValue<Int8> & individual_obj_ttl,
429 TNSTDBValue<CTimeSpan> & prolong_on_read) const
430 {
431 return m_MetadataServiceRegistry.GetProlongOnRead(
432 service, individual_obj_ttl, prolong_on_read);
433 }
434
435
436 bool
GetServiceProlongOnWrite(const string & service,const TNSTDBValue<Int8> & individual_obj_ttl,TNSTDBValue<CTimeSpan> & prolong_on_write) const437 CNetStorageServer::GetServiceProlongOnWrite(
438 const string & service,
439 const TNSTDBValue<Int8> & individual_obj_ttl,
440 TNSTDBValue<CTimeSpan> & prolong_on_write) const
441 {
442 return m_MetadataServiceRegistry.GetProlongOnWrite(
443 service, individual_obj_ttl, prolong_on_write);
444 }
445
446
447 bool
GetServiceProperties(const string & service,CNSTServiceProperties & service_props) const448 CNetStorageServer::GetServiceProperties(const string & service,
449 CNSTServiceProperties & service_props) const
450 {
451 return m_MetadataServiceRegistry.GetServiceProperties(service,
452 service_props);
453 }
454
455
456 CJsonNode
ReadMetadataConfiguration(const IRegistry & reg)457 CNetStorageServer::ReadMetadataConfiguration(const IRegistry & reg)
458 {
459 return m_MetadataServiceRegistry.ReadConfiguration(reg);
460 }
461
462
463 CJsonNode
SerializeMetadataInfo(void) const464 CNetStorageServer::SerializeMetadataInfo(void) const
465 {
466 return m_MetadataServiceRegistry.Serialize();
467 }
468
469
470 void
RunServiceThread(void)471 CNetStorageServer::RunServiceThread(void)
472 {
473 m_ServiceThread.Reset(new CNetStorageServiceThread(*this));
474 m_ServiceThread->Run();
475 }
476
477
478 void
StopServiceThread(void)479 CNetStorageServer::StopServiceThread(void)
480 {
481 if (!m_ServiceThread.Empty()) {
482 m_ServiceThread->RequestStop();
483 m_ServiceThread->Join();
484 m_ServiceThread.Reset(0);
485 }
486 }
487
488
489 CJsonNode
x_diffInJson(const vector<string> & added,const vector<string> & deleted) const490 CNetStorageServer::x_diffInJson(const vector<string> & added,
491 const vector<string> & deleted) const
492 {
493 CJsonNode diff = CJsonNode::NewObjectNode();
494
495 if (!added.empty()) {
496 CJsonNode add_node = CJsonNode::NewArrayNode();
497 for (vector<string>::const_iterator k = added.begin();
498 k != added.end(); ++k)
499 add_node.AppendString(*k);
500 diff.SetByKey("Added", add_node);
501 }
502
503 if (!deleted.empty()) {
504 CJsonNode del_node = CJsonNode::NewArrayNode();
505 for (vector<string>::const_iterator k = deleted.begin();
506 k != deleted.end(); ++k)
507 del_node.AppendString(*k);
508 diff.SetByKey("Deleted", del_node);
509 }
510
511 return diff;
512 }
513
514
x_GetCrashFlagFileName(void) const515 string CNetStorageServer::x_GetCrashFlagFileName(void) const
516 {
517 return CFile::MakePath(m_DataPath, kCrashFlagFileName);
518 }
519
520
x_DoesCrashFlagFileExist(void) const521 bool CNetStorageServer::x_DoesCrashFlagFileExist(void) const
522 {
523 return CFile(x_GetCrashFlagFileName()).Exists();
524 }
525
526
CheckStartAfterCrash(void)527 void CNetStorageServer::CheckStartAfterCrash(void)
528 {
529 if (x_DoesCrashFlagFileExist()) {
530 // There could be two reasons:
531 // - start after crash
532 // - previous instance could not remove the file by some reasons, e.g.
533 // due to changed permissions
534 RegisterAlert(eStartAfterCrash,
535 "The server did not stop gracefully last time");
536
537 // Try to remove the file to check the permissions and re-create it
538 string error = RemoveCrashFlagFile();
539 if (!error.empty()) {
540 ERR_POST(error);
541 RegisterAlert(eCrashSignalFileCreation, error);
542 return;
543 }
544
545 error = CreateCrashFlagFile();
546 if (!error.empty()) {
547 ERR_POST(error);
548 RegisterAlert(eCrashSignalFileCreation, error);
549 return;
550 }
551
552 // Here: the crash flag file has been successfully re-created
553 return;
554 }
555
556 // Here: the crash flag file does not exist.
557 // Check that the directory exists
558 bool dir_created = true;
559 if (!CDirEntry(m_DataPath).Exists()) {
560 string msg = "The data path directory " + m_DataPath +
561 " does not exist. It is OK if it is a first time "
562 "server start. Otherwise someone may have removed the "
563 "directory and this may mask the previous instance "
564 "crash";
565 RegisterAlert(eDataPathAbsence, msg);
566 ERR_POST(msg);
567
568 // Create the directory recursively
569 try {
570 CDir dir(m_DataPath);
571 if (dir.CreatePath() == false) {
572 msg = "Error creating the data path directory " + m_DataPath;
573 RegisterAlert(eCrashSignalFileCreation, msg);
574 ERR_POST(msg);
575 dir_created = false;
576 }
577 } catch (const exception & ex) {
578 msg = "Error while creating the data path directory (" +
579 m_DataPath + "): " + ex.what();
580 RegisterAlert(eCrashSignalFileCreation, msg);
581 ERR_POST(msg);
582 dir_created = false;
583 } catch (...) {
584 msg = "Unknown error while creating the data path directory (" +
585 m_DataPath + ")";
586 RegisterAlert(eCrashSignalFileCreation, msg);
587 ERR_POST(msg);
588 dir_created = false;
589 }
590 }
591
592 // Create the flag file
593 if (dir_created) {
594 string error = CreateCrashFlagFile();
595 if (!error.empty()) {
596 ERR_POST(error);
597 RegisterAlert(eCrashSignalFileCreation, error);
598 }
599 }
600 }
601
602
CreateCrashFlagFile(void)603 string CNetStorageServer::CreateCrashFlagFile(void)
604 {
605 try {
606 CFile crash_file(x_GetCrashFlagFileName());
607 if (!crash_file.Exists()) {
608 CFileIO f;
609 f.Open(x_GetCrashFlagFileName(),
610 CFileIO_Base::eCreate,
611 CFileIO_Base::eReadWrite);
612 f.Close();
613 }
614 } catch (const exception & ex) {
615 return "Error while creating the start-after-crash detection "
616 "file (" + x_GetCrashFlagFileName() + "): " + ex.what();
617 } catch (...) {
618 return "Unknown error while creating the start-after-crash "
619 "detection file (" + x_GetCrashFlagFileName() + ").";
620 }
621 return kEmptyStr;
622 }
623
624
RemoveCrashFlagFile(void)625 string CNetStorageServer::RemoveCrashFlagFile(void)
626 {
627 try {
628 CFile crash_file(x_GetCrashFlagFileName());
629 if (crash_file.Exists())
630 crash_file.Remove();
631 } catch (const exception & ex) {
632 return "Error while removing the start-after-crash detection "
633 "file (" + x_GetCrashFlagFileName() + "): " + ex.what() +
634 "\nWhen the server restarts with the same data path it will "
635 "set the 'StartAfterCrash' alert";
636 } catch (...) {
637 return "Unknown error while removing the start-after-crash detection "
638 "file (" + x_GetCrashFlagFileName() + "). When the server "
639 "restarts with the same data path it will set the "
640 "'StartAfterCrash' alert";
641 }
642 return kEmptyStr;
643 }
644
645