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(&current_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