DUNE-DAQ
DUNE Trigger and Data Acquisition software
Loading...
Searching...
No Matches
kernel.cpp
Go to the documentation of this file.
1#define _OksBuildDll_
2
3#include <errno.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <unistd.h>
8#include <wchar.h>
9//#include <security/pam_appl.h>
10#include <sys/stat.h>
11#include <sys/wait.h>
12
13#include <algorithm>
14#include <filesystem>
15#include <fstream>
16#include <sstream>
17#include <stdexcept>
18#include <vector>
19#include <mutex>
20#include <chrono>
21#include <cstring>
22#include <ctime>
23
24#include "ers/ers.hpp"
25#include "logging/Logging.hpp"
26
27#include "okssystem/Host.hpp"
28#include "okssystem/User.hpp"
30
31//#include <daq_tokens/verify.h>
32
33#include "oks/kernel.hpp"
34#include "oks/xml.hpp"
35#include "oks/file.hpp"
36#include "oks/attribute.hpp"
37#include "oks/relationship.hpp"
38#include "oks/method.hpp"
39#include "oks/class.hpp"
40#include "oks/object.hpp"
41#include "oks/profiler.hpp"
42#include "oks/pipeline.hpp"
43#include "oks/cstring.hpp"
44
45#include "oks_utils.hpp"
46
47namespace dunedaq {
48namespace oks {
49
51
52static std::mutex s_get_cwd_mutex;
53
56char * OksKernel::s_cwd = nullptr;
57
60
62
63
65 {
66 public:
68 {
69 std::lock_guard scoped_lock(s_git_folders_mutex);
70
71 for (const auto &x : s_git_folders)
72 remove(x);
73
74 s_git_folders.clear();
75 }
76
77 static void
78 remove(const std::string &path)
79 {
80 try
81 {
82 std::filesystem::remove_all(path);
83 }
84 catch (std::exception &ex)
85 {
86 Oks::error_msg("OksKernel::~OksKernel") << "cannot remove user repository \"" << path << "\"" << ex.what() << std::endl;
87 }
88 }
89
90 void
91 insert(const std::string& path)
92 {
93 std::lock_guard scoped_lock(s_git_folders_mutex);
94 s_git_folders.insert(path);
95 }
96
97 void
98 erase(const std::string& path)
99 {
100 std::lock_guard scoped_lock(s_git_folders_mutex);
101 s_git_folders.erase(path);
102 }
103
104 private:
105
107 std::set<std::string> s_git_folders;
108 };
109
111
112
113const std::string
115{
116 char buffer[1024];
117 buffer[0] = 0;
118 return std::string(strerror_r(error, buffer, 1024));
119}
120
121
123 kernel,
124 SetGroupIdFailed,
125 "cannot set group ID " << id << " for the file \'" << file << "\': chown() failed with code " << code << " , reason = \'" << why << '\'',
126 ((long)id)
127 ((const char *)file)
128 ((int)code)
129 ((std::string)why)
130 )
131
133 kernel,
135 "Found unresolved reference(s):\n" << which,
136 ((std::string)which)
137 )
138
140 kernel,
141 ClassAlreadyDefined,
142 "class \"" << name << "\" is defined in files \"" << f1 << "\" and \"" << f2 << "\"",
143 ((std::string)name)
144 ((std::string)f1)
145 ((std::string)f2)
146 )
147
149 kernel,
150 InternalError,
151 "internal error: " << text,
152 ((std::string)text)
153 )
154
155 std::string
156 CanNotOpenFile::fill(const char * prefix, const std::string& name, const std::string& reason) noexcept
157 {
158 std::string result(prefix);
159 result += "(): ";
160
161 if(name.empty()) return (result + "file name is empty");
162
163 result += "cannot load file \'" + name + '\'' ;
164
165 if(!reason.empty()) {
166 result += " because:\n" + reason;
167 }
168
169 return result;
170 }
171
172 std::string
173 FailedLoadFile::fill(const std::string& what, const std::string& name, const std::string& reason) noexcept
174 {
175 return (std::string("Failed to load ") + what + " \"" + name + "\" because:\n" + reason);
176 }
177
178 std::string
179 FailedReloadFile::fill(const std::string& names, const std::string& reason) noexcept
180 {
181 return (std::string("Failed to re-load file(s) ") + names + " because:\n" + reason);
182 }
183
184 std::string
185 RepositoryOperationFailed::fill(const char * op, const std::string& reason) noexcept
186 {
187 return (std::string("Failed to ") + op + " file(s) because:\n" + reason);
188 }
189
190 std::string
191 CanNotCreateFile::fill(const char * prefix, const char * what, const std::string& name, const std::string& reason) noexcept
192 {
193 return std::string(prefix) + "(): failed to create new " + what + " \'" + name + "\' because:\n" + reason;
194 }
195
196 std::string
197 CanNotCreateRepositoryDir::fill(const char * prefix, const std::string& name) noexcept
198 {
199 return std::string(prefix) + "(): failed to create user repository dir, mkdtemp(\'" + name + "\') failed: " + strerror(errno);
200 }
201
202 std::string
203 CanNotWriteToFile::fill(const char * prefix, const char * item, const std::string& name, const std::string& reason) noexcept
204 {
205 return (std::string(prefix) + "(): failed to write " + item + " to \'" + name + "\' because:\n" + reason);
206 }
207
208 std::string
209 CanNotBackupFile::fill(const std::string& name, const std::string& reason) noexcept
210 {
211 return (std::string("Failed to make a backup file of \'") + name + "\' because:\n" + reason);
212 }
213
214 std::string
215 CanNotSetActiveFile::fill(const char * item, const std::string& name, const std::string& reason) noexcept
216 {
217 return (std::string("Failed to set active ") + item + " file \'" + name + "\' because:\n" + reason);
218 }
219
220 std::string
221 CanNotSetFile::fill(const OksClass * c, const OksObject * o, const OksFile& file, const std::string& reason) noexcept
222 {
223 std::ostringstream s;
224 s << "Failed to move ";
225 if(c) s << "class " << c->get_name();
226 else s << "object " << o;
227 s << " to file \'" << file.get_full_file_name() << "\' because:\n" << reason;
228 return s.str();
229 }
230
231 std::string
232 CannotAddClass::fill(const OksClass &c, const std::string& reason) noexcept
233 {
234 return (std::string("Cannot add class \'") + c.get_name() + "\' because:\n" + reason);
235 }
236
237 std::string
238 CannotResolvePath::fill(const std::string& path, int error_code) noexcept
239 {
240 std::ostringstream text;
241 text << "realpath(\'" << path << "\') has failed with code " << error_code << ": \'" << strerror(error_code) << '\'';
242 return text.str();
243 }
244
245
246 static const std::string&
247 get_temporary_dir()
248 {
249 static std::string s_dir;
250 static std::once_flag flag;
251
252 std::call_once(flag, []()
253 {
254 try
255 {
256 if (!getenv("OKS_GIT_NO_VAR_DIR"))
257 {
258 OksSystem::User myself;
259
260 s_dir = "/var/run/user/";
261 s_dir.append(std::to_string(myself.identity()));
262
263 if (std::filesystem::is_directory(s_dir) == false)
264 {
265 TLOG_DEBUG(1) << "directory " << s_dir << " does not exist";
266 s_dir.clear();
267 }
268 else
269 {
270 const std::filesystem::space_info si = std::filesystem::space(s_dir);
271 const double available = static_cast<double>(si.available) / static_cast<double>(si.capacity);
272
273 if (available < 0.5)
274 {
275 TLOG_DEBUG(1) << "usage of " << s_dir << " is " << static_cast<int>((1.0 - available) * 100.0) << "%, will use standard temporary path";
276 s_dir.clear();
277 }
278 }
279 }
280
281 if (s_dir.empty())
282 s_dir = std::filesystem::temp_directory_path().string();
283 }
284 catch (std::exception& ex)
285 {
286 Oks::error_msg("OksKernel::OksKernel") << "cannot get temporary directory path: " << ex.what() << std::endl;
287 s_dir = "/tmp";
288 }
289 }
290 );
291
292 return s_dir;
293 }
294
295
296const char *
298{
299 return "862f2957270";
300}
301
302
303const std::string&
305{
306 static std::once_flag flag;
307
308 std::call_once(flag, []()
309 {
310 if (const char * s = getenv("TDAQ_DB_REPOSITORY"))
311 {
312 std::string rep(s);
313
314 if (!std::all_of(rep.begin(), rep.end(), [](char c) { return std::isspace(c); }))
315 {
316 const char * p = getenv("OKS_GIT_PROTOCOL");
317
318 if (p && !*p)
319 p = nullptr;
320
321 Oks::Tokenizer t(rep, "|");
322 std::string token;
323
324 while (t.next(token) && p_repository_root.empty())
325 {
326 if (p)
327 {
328 if (token.find(p) == 0)
329 {
330 p_repository_root = token;
331 }
332 }
333 else
334 {
335 p_repository_root = token;
336 }
337 }
338
339 if (p_repository_root.empty())
340 Oks::error_msg("OksKernel::OksKernel") << "cannot find OKS_GIT_PROTOCOL=\"" << p << "\" in TDAQ_DB_REPOSITORY=\"" << rep << '\"' << std::endl;
341 }
342 }
343 }
344 );
345
346 return p_repository_root;
347}
348
349const std::string&
351{
352 static std::once_flag flag;
353
354 std::call_once(flag, []()
355 {
356 if (const char * s = getenv("OKS_REPOSITORY_MAPPING_DIR"))
357 {
360 }
361 }
362 );
363
365}
366
368
369const std::string&
371{
373 {
374 OksKernel * k(const_cast<OksKernel*>(this));
376 if(const char * s = getenv("TDAQ_DB_USER_REPOSITORY")) {
377 if(s[0] != 0) {
379 }
380 }
382 }
383 }
384
386}
387
388void
389OksKernel::set_user_repository_root(const std::string& path, const std::string& version)
390{
391 if(get_repository_root().empty()) {
392 TLOG_DEBUG( 1) << "Failed to set user-repository-root:\n\tcaused by: repository-root is not set" ;
394 return;
395 }
396
397 if(path.empty()) {
399 return;
400 }
401
402 std::string s;
403 s = path;
404
405 try {
406 Oks::real_path(s, false);
407 }
408 catch(exception& ex) {
409 TLOG_DEBUG( 1) << "Failed to set user-repository-root = \'" << s << "\':\n\tcaused by: " << ex.what() ;
410 }
411
412 for(std::string::size_type idx = s.size()-1; idx > 0 && s[idx] == '/' ; idx--) {
413 s.erase(idx);
414 }
415
416 p_allow_repository = true;
417
420
421 // is used for backup restore by RDB
422 if (!version.empty())
424}
425
427
428std::string&
430{
431 static std::string s_host_name;
432 static std::once_flag flag;
433
434 std::call_once(flag, []()
435 {
437
438 if (s_host_name.empty())
439 s_host_name = "unknown.host";
440 }
441 );
442
443 return s_host_name;
444}
445
446std::string&
448{
449 static std::string s_domain_name;
450 static std::once_flag flag;
451
452 std::call_once(flag, []()
453 {
454 std::string::size_type idx = get_host_name().find('.');
455
456 if (idx != std::string::npos)
457 s_domain_name = get_host_name().substr(idx+1);
458
459 if (s_domain_name.empty())
460 s_domain_name = "unknown.domain";
461 }
462 );
463
464 return s_domain_name;
465}
466
467std::string&
469{
470 static std::string s_user_name;
471 static std::once_flag flag;
472
473 std::call_once(flag, []()
474 {
475 OksSystem::User myself;
476 s_user_name = myself.name_safe();
477
478 if (s_user_name.empty())
479 s_user_name = "unknown.user";
480 }
481 );
482
483 return s_user_name;
484}
485
486
488
489static long
490get_file_length(std::ifstream& f)
491{
492 long file_length = 0;
493
494 // get size of file and check that it is not empty
495
496 f.seekg(0, std::ios::end);
497
498#if __GNUC__ >= 3
499 file_length = static_cast<std::streamoff>(f.tellg());
500#else
501 file_length = f.tellg();
502#endif
503
504 f.seekg(0, std::ios::beg);
505
506 return file_length;
507}
508
509 // makes name of function with parameters for methods working with files
510
511std::string
512make_fname(const char * f, size_t f_len, const std::string& file, bool * p, const OksFile * const * fh)
513{
514 const char _prefix [] = "OksKernel::";
515 std::string fname(_prefix, (sizeof(_prefix)-1));
516
517 fname.append(f, f_len);
518
519 const char _x1 [] = "(file \'";
520 fname.append(_x1, (sizeof(_x1)-1));
521 fname.append(file);
522 fname.push_back('\'');
523
524 // add boolean parameter
525
526 if(p) {
527 if(*p) {
528 const char _true_str [] = ", true";
529 fname.append(_true_str, (sizeof(_true_str)-1));
530 }
531 else {
532 const char _false_str [] = ", false";
533 fname.append(_false_str, (sizeof(_false_str)-1));
534 }
535 }
536
537 // add file handler parameter
538
539 if(fh && *fh) {
540 const char _x2 [] = ", included by file \'";
541 fname.append(_x2, (sizeof(_x2)-1));
542 fname.append((*fh)->get_full_file_name());
543 fname.push_back('\'');
544 }
545
546 fname.push_back(')');
547
548 return fname;
549}
550
551 //
552 // Prototypes for error and warning messages
553 //
554
555std::ostream&
556Oks::error_msg(const char *msg)
557{
558 std::cerr << "ERROR [" << msg << "]:\n"; return std::cerr;
559}
560
561
562std::ostream&
563Oks::warning_msg(const char *msg)
564{
565 std::cerr << "WARNING [" << msg << "]:\n"; return std::cerr;
566}
567
568
569void
571{
572 std::string::size_type pos = 0; // position of tested string index
573 std::string::size_type p_start = 0; // begining of variable
574 std::string::size_type p_end = 0; // begining of variable
575
576 while(
577 ((p_start = s.find("$(", pos)) != std::string::npos) &&
578 ((p_end = s.find(")", p_start + 2)) != std::string::npos)
579 ) {
580 std::string var(s, p_start + 2, p_end - p_start - 2);
581
582 char * env = getenv(var.c_str());
583
584 if(env) {
585 s.replace(p_start, p_end - p_start + 1, env);
586 }
587
588 pos = p_start + 1;
589 }
590}
591
592
593bool
594Oks::real_path(std::string& path, bool ignore_errors)
595{
596 char resolved_name[PATH_MAX];
597
598 if(realpath(path.c_str(), resolved_name) != 0) {
599 path = resolved_name;
600 return true;
601 }
602 else {
603 if(ignore_errors) {
604 TLOG_DEBUG( 3) << "realpath(\'" << path << "\') has failed with code " << errno << ": \'" << strerror(errno) << '\'' ;
605 return false;
606 }
607 else {
608 throw CannotResolvePath(path, errno);
609 }
610 }
611}
612
613
614unsigned long OksKernel::p_count = 0;
615const char OksNameTable::symbols[] = "0123456789"
616 "abcdefghijklmnoprstuvwxyz"
617 "ABCDEFGHIJKLMNOPRSTUVWXYZ";
618
619
622{
623 static const size_t slen(sizeof(symbols)-1);
624 static const size_t slen2(slen*slen);
625
626 char b[16], *buf = b;
627
628 count++;
629
630 *buf++ = symbols[count%slen];
631
632 if((size_t)count >= slen) {
633 *buf++ = symbols[(count/slen)%slen];
634
635 if((size_t)count >= slen2) {
636 *buf++ = symbols[(count/slen2)%slen];
637 }
638 }
639
640 *buf = '\0';
641
642 return new OksString(b);
643}
644
645
647{
648 if (p_aliases.empty())
649 return;
650
651 for (auto& i : p_aliases)
652 {
653 if (OksString *s = i.second->class_name)
654 delete s;
655
656 delete i.second;
657 delete const_cast<OksString *>(i.first);
658 }
659
660 p_aliases.clear();
661}
662
663
664std::string
665OksKernel::get_tmp_file(const std::string& file)
666{
667 unsigned int j = 0;
668 std::string s2;
669 std::ostringstream s;
670
671 while(j++ < 1000000) {
672 s.str("");
673 s << file << '.' << get_user_name() << ':' << get_host_name() << ':' << getpid() << ':' << j;
674
675 s2 = s.str();
676
677 std::ofstream f(s2.c_str(), std::ios::in);
678
679 if(!f) return s2;
680 }
681
682 std::string s3(file);
683 s3 += ".tmp";
684
685 return s3;
686}
687
688const char *
690{
691 std::lock_guard scoped_lock(s_get_cwd_mutex);
692
693 if (!s_cwd)
694 {
695 errno = 0;
696 long size = pathconf(".", _PC_PATH_MAX);
697 if (errno)
698 {
699 Oks::error_msg("OksKernel::OksKernel") << "pathconf(\".\", _PC_PATH_MAX) has failed with code " << errno << ": \'" << strerror(errno) << '\'' << std::endl;
700 }
701 else
702 {
703 if (size == -1)
704 {
705 size = PATH_MAX;
706 }
707 s_cwd = new char[size];
708 if (!getcwd(s_cwd, (size_t) size))
709 {
710 Oks::error_msg("OksKernel::OksKernel") << "getcwd() has failed with code " << errno << ": \'" << strerror(errno) << '\'' << std::endl;
711 delete[] s_cwd;
712 s_cwd = 0;
713 }
714 }
715
716 if (!s_cwd)
717 {
718 s_cwd = new char[2];
719 s_cwd[0] = '/';
720 s_cwd[1] = 0;
721 }
722
723 TLOG_DEBUG(1) << "Current working dir: \'" << s_cwd << '\'';
724 }
725
726 return s_cwd;
727}
728
729/******************************************************************************/
730/***************************** OKS Kernel Class *****************************/
731/******************************************************************************/
732
733OksKernel::OksKernel(bool sm, bool vm, bool tm, bool allow_repository, const char * version, std::string branch_name) :
734 p_silence (sm),
735 p_verbose (vm),
736 p_profiling (tm),
737 p_allow_repository (allow_repository),
738 p_allow_duplicated_classes (true),
739 p_allow_duplicated_objects (false),
740 p_test_duplicated_objects_via_inheritance (false),
741 p_user_repository_root_inited (false),
742 p_user_repository_root_created (false),
743 p_active_schema (nullptr),
744 p_active_data (nullptr),
745 p_close_all (false),
746 profiler (nullptr),
747 p_create_object_notify_fn (nullptr),
748 p_create_object_notify_param (nullptr),
749 p_change_object_notify_fn (nullptr),
750 p_change_object_notify_param (nullptr),
751 p_delete_object_notify_fn (nullptr),
752 p_delete_object_notify_param (nullptr)
753{
754 OSK_VERBOSE_REPORT("Enter OksKernel::OksKernel(" << sm << ", " << vm << ", " << tm << ')')
755
756 struct __InitFromEnv__ {
757 const char * name;
758 bool& value;
759 } vars[] = {
760 {"OKS_KERNEL_VERBOSE", p_verbose },
761 {"OKS_KERNEL_SILENCE", p_silence },
762 {"OKS_KERNEL_PROFILING", p_profiling },
763 {"OKS_KERNEL_ALLOW_REPOSITORY", p_allow_repository },
764 {"OKS_KERNEL_ALLOW_DUPLICATED_CLASSES", p_allow_duplicated_classes },
765 {"OKS_KERNEL_ALLOW_DUPLICATED_OBJECTS", p_allow_duplicated_objects },
766 {"OKS_KERNEL_TEST_DUPLICATED_OBJECTS_VIA_INHERITANCE", p_test_duplicated_objects_via_inheritance },
767 {"OKS_KERNEL_SKIP_STRING_RANGE", p_skip_string_range }
768 };
769
770 for(unsigned int i = 0; i < sizeof(vars) / sizeof(__InitFromEnv__); ++i) {
771 if(char * s = getenv(vars[i].name)) {
772 vars[i].value = (strcmp(s, "no")) ? true : false;
773 }
774 }
775
776 {
777 const char * oks_db_root = getenv("OKS_DB_ROOT");
778
779 if (oks_db_root == nullptr || *oks_db_root == 0)
780 oks_db_root = getenv("DUNEDAQ_DB_PATH");
781 else
783
784 OSK_VERBOSE_REPORT("database root = \'" << oks_db_root << "\' (as defined by the OKS_DB_ROOT or DUNEDAQ_DB_PATH)");
785
786
787 // if not defined, set oks_db_root to empty string (to avoid std::string constructor crash when string is 0)
788
789 if (oks_db_root == nullptr)
790 oks_db_root = "";
791
792
793 // temporal set
794
795 Oks::Tokenizer t(oks_db_root, ":");
796 std::string token;
797
798 while (t.next(token))
800 }
801
802
803
804
805 {
806 std::unique_lock lock(p_kernel_mutex);
807
808 if(!p_count++) {
812 }
813
814 get_cwd();
815 }
816
817 {
818 static std::once_flag flag;
819
820 std::call_once(flag, []()
821 {
822 if (char * s = getenv("OKS_KERNEL_THREADS_POOL_SIZE"))
823 {
824 if (*s != '\0')
825 {
826 p_threads_pool_size = atoi(s);
827 if (p_threads_pool_size < 1)
829 }
830 }
831
833 {
834 errno = 0;
835 p_threads_pool_size = sysconf(_SC_NPROCESSORS_ONLN);
836 if (p_threads_pool_size == -1 && !errno)
837 {
838 Oks::error_msg("OksKernel::OksKernel()") << " sysconf(_SC_NPROCESSORS_ONLN) has failed with code " << errno << ": \'" << strerror(errno) << '\'' << std::endl;
840 }
841 }
842
845
846 TLOG_DEBUG(2) << "Threads pool size: " << p_threads_pool_size;
847 }
848 );
849
850 }
851
852 // process OKS server URL settings if any
853 if (p_allow_repository && !get_repository_root().empty())
854 {
855 if (get_user_repository_root().empty())
856 {
857 // parse specific version option if any
858 std::string param, val;
859
860 if (!version)
861 version = getenv("TDAQ_DB_VERSION");
862
863 if (version)
864 {
865 if(*version)
866 {
867 if(const char * value = strchr(version, ':'))
868 {
869 param.assign(version, value-version);
870 val.assign(value+1);
871 }
872 else
873 Oks::error_msg("OksKernel::OksKernel") << "bad version value \"" << version << "\" expecting parameter:value format (check TDAQ_DB_VERSION variable)" << std::endl;
874 }
875 }
876
877 try
878 {
879 std::string tmp_dirname = OksKernel::create_user_repository_dir();
880
881 set_user_repository_root(tmp_dirname);
884
885 if (branch_name.empty())
886 if (const char *var = getenv("TDAQ_DB_BRANCH"))
887 branch_name = var;
888
889 k_checkout_repository(param, val, branch_name);
890 }
891 catch(exception& ex)
892 {
893 Oks::error_msg("OksKernel::OksKernel") << "cannot check out user repository: " << ex.what() << std::endl;
894 }
895 }
896 else
897 {
898 if(!p_silence)
899 {
900 try
901 {
902 std::lock_guard lock(p_parallel_out_mutex);
903 std::cout << "attach to repository \"" << get_user_repository_root() << "\" version " << read_repository_version() << std::endl;
904 }
905 catch(exception& ex)
906 {
907 Oks::error_msg("OksKernel::OksKernel") << "cannot read user repository version: " << ex.what() << std::endl;
908 }
909 }
910 }
911 }
912
913#ifndef ERS_NO_DEBUG
914 if(p_profiling)
915 profiler = (OksProfiler *) new OksProfiler();
916#endif
917}
918
919OksKernel::OksKernel(const OksKernel& src, bool copy_repository) :
920 p_silence (src.p_silence),
921 p_verbose (src.p_verbose),
922 p_profiling (src.p_profiling),
923 p_allow_repository (src.p_allow_repository),
924 p_allow_duplicated_classes (src.p_allow_duplicated_classes),
925 p_allow_duplicated_objects (src.p_allow_duplicated_objects),
926 p_test_duplicated_objects_via_inheritance (src.p_test_duplicated_objects_via_inheritance),
927 p_user_repository_root (src.p_user_repository_root),
928 p_user_repository_root_inited (src.p_user_repository_root_inited),
929 p_user_repository_root_created (false),
930 p_repository_version (src.p_repository_version),
931 p_repository_checkout_ts (src.p_repository_checkout_ts),
932 p_repository_update_ts (src.p_repository_update_ts),
933 p_active_schema (nullptr),
934 p_active_data (nullptr),
935 p_close_all (false),
936 profiler (nullptr),
937 p_create_object_notify_fn (nullptr),
938 p_create_object_notify_param (nullptr),
939 p_change_object_notify_fn (nullptr),
940 p_change_object_notify_param (nullptr),
941 p_delete_object_notify_fn (nullptr),
942 p_delete_object_notify_param (nullptr)
943{
944
945 {
946 std::unique_lock lock(p_kernel_mutex);
947 p_count++;
948 }
949
950 if (copy_repository && p_user_repository_root_inited)
951 {
952 try
953 {
959 }
960 catch(exception& ex)
961 {
962 Oks::error_msg("OksKernel::OksKernel") << "cannot copy user repository: " << ex.what() << std::endl;
964 throw;
965 }
966
968 }
969
970
971 // copy repository directories
972 for (const auto& i : src.p_repository_dirs)
973 p_repository_dirs.push_back(i);
974
975
976 // copy schema and data files
977 {
978 const OksFile::Map * src_files[2] = { &src.p_schema_files, &src.p_data_files };
979 OksFile::Map * dst_files[2] = { &p_schema_files, &p_data_files };
980
981 for (int i = 0; i < 2; ++i)
982 {
983 for (const auto& j : *src_files[i])
984 {
985 OksFile * f = new OksFile(*j.second);
986 f->p_kernel = this;
987 (*dst_files[i])[&f->p_full_name] = f;
988 }
989 }
990 }
991
992 // search a class in map is not efficient, if there are many such operations; use array with class index for fast search
993 OksClass ** c_table = new OksClass * [src.p_classes.size()];
994 unsigned int idx(0);
995
996
997 // copy classes: first iteration to define class names and simple properties
998 for(const auto& i : src.p_classes) {
999 const OksClass& src_c(*i.second);
1000 OksClass * c = new OksClass (src_c.p_name, src_c.p_description, src_c.p_abstract, nullptr, src_c.p_transient); // pass 0 kernel to avoid class insertion
1001
1002 c->p_kernel = this;
1003 p_classes[c->get_name().c_str()] = c;
1004
1005 const_cast<OksClass&>(src_c).p_id = idx++;
1006 c->p_id = src_c.p_id;
1007 c_table[c->p_id] = c;
1008
1009 c->p_abstract = src_c.p_abstract;
1010 c->p_to_be_deleted = src_c.p_to_be_deleted;
1011 c->p_instance_size = src_c.p_instance_size;
1012 c->p_file = (*p_schema_files.find(&src_c.p_file->p_full_name)).second;
1013 c->p_objects = (src_c.p_objects ? new OksObject::Map() : nullptr);
1014
1015
1016 // copy super-classes
1017 if (const std::list<std::string *> * scls = src_c.p_super_classes)
1018 {
1019 c->p_super_classes = new std::list<std::string *>();
1020
1021 for (const auto& j : *scls)
1022 c->p_super_classes->push_back(new std::string(*j));
1023 }
1024
1025
1026 // copy direct attributes
1027 if (const std::list<OksAttribute *> * attrs = src_c.p_attributes)
1028 {
1029 c->p_attributes = new std::list<OksAttribute *>();
1030
1031 for (const auto& j : *attrs)
1032 {
1033 OksAttribute * a = new OksAttribute(j->p_name, c);
1034
1035 a->p_range = j->p_range;
1036 a->p_data_type = j->p_data_type;
1037 a->p_multi_values = j->p_multi_values;
1038 a->p_no_null = j->p_no_null;
1039 a->p_init_value = j->p_init_value;
1040 a->p_init_data = j->p_init_data;
1041 a->p_format = j->p_format;
1042 a->p_description = j->p_description;
1043
1044 if (j->p_enumerators)
1045 a->p_enumerators = new std::vector<std::string>(*j->p_enumerators);
1046
1047 c->p_attributes->push_back(a);
1048 }
1049 }
1050
1051
1052 // copy direct relationships
1053 if (const std::list<OksRelationship *> * rels = src_c.p_relationships)
1054 {
1055 c->p_relationships = new std::list<OksRelationship *>();
1056
1057 for (const auto& j : *rels)
1058 {
1059 OksRelationship * r = new OksRelationship(j->p_name, c);
1060
1061 r->p_rclass = j->p_rclass;
1062 r->p_low_cc = j->p_low_cc;
1063 r->p_high_cc = j->p_high_cc;
1064 r->p_composite = j->p_composite;
1065 r->p_exclusive = j->p_exclusive;
1066 r->p_dependent = j->p_dependent;
1067 r->p_description = j->p_description;
1068
1069 c->p_relationships->push_back(r);
1070 }
1071 }
1072
1073
1074 // copy direct methods
1075 if (const std::list<OksMethod *> * mets = src_c.p_methods)
1076 {
1077 c->p_methods = new std::list<OksMethod *>();
1078
1079 for (const auto& j : *mets)
1080 {
1081 OksMethod * m = new OksMethod(j->p_name, j->p_description, c);
1082
1083 if (const std::list<OksMethodImplementation *> * impls = j->p_implementations)
1084 {
1085 m->p_implementations = new std::list<OksMethodImplementation *>();
1086
1087 for (const auto& x : *impls)
1088 m->p_implementations->push_back(new OksMethodImplementation(x->get_language(), x->get_prototype(), x->get_body(), m));
1089 }
1090
1091 c->p_methods->push_back(m);
1092 }
1093 }
1094 }
1095
1096
1097 // copy classes: second iteration to resolve pointers to classes
1098
1099 for (const auto& i : src.p_classes)
1100 {
1101 OksClass * c = c_table[i.second->p_id];
1102
1103 // link p_class_type of relationship
1104 if (const std::list<OksRelationship *> * rels = i.second->p_relationships)
1105 {
1106 for (const auto& j : *rels)
1107 c->find_relationship(j->get_name())->p_class_type = (j->p_class_type ? c_table[j->p_class_type->p_id] : nullptr);
1108 }
1109
1110 // copy all super- and sub- classes
1111 if (const OksClass::FList * spcls = i.second->p_all_super_classes)
1112 {
1113 c->p_all_super_classes = new OksClass::FList();
1114
1115 for (const auto& j : *spcls)
1116 c->p_all_super_classes->push_back(c_table[j->p_id]);
1117 }
1118
1119 if (const OksClass::FList * sbcls = i.second->p_all_sub_classes)
1120 {
1121 c->p_all_sub_classes = new OksClass::FList();
1122
1123 for (const auto& j : *sbcls)
1124 c->p_all_sub_classes->push_back(c_table[j->p_id]);
1125 }
1126
1127 // create oks object layout information
1128 c->p_data_info = new OksDataInfo::Map();
1129 size_t instance_size = 0;
1130
1131 // copy all attributes
1132 if (const std::list<OksAttribute *> * attrs = i.second->p_all_attributes)
1133 {
1134 c->p_all_attributes = new std::list<OksAttribute *>();
1135
1136 for (const auto& j : *attrs)
1137 {
1138 OksAttribute * a = (c_table[j->p_class->p_id])->find_direct_attribute(j->get_name());
1139 c->p_all_attributes->push_back(a);
1140 (*c->p_data_info)[a->get_name()] = new OksDataInfo(instance_size++, a);
1141 }
1142 }
1143
1144 // copy all relationships
1145 if (const std::list<OksRelationship *> * rels = i.second->p_all_relationships)
1146 {
1147 c->p_all_relationships = new std::list<OksRelationship *>();
1148
1149 for (const auto& j : *rels)
1150 {
1151 OksRelationship * r = (c_table[j->p_class->p_id])->find_direct_relationship(j->get_name());
1152 c->p_all_relationships->push_back(r);
1153 (*c->p_data_info)[r->get_name()] = new OksDataInfo(instance_size++, r);
1154 }
1155 }
1156
1157 // copy all methods
1158 if (const std::list<OksMethod *> * mets = i.second->p_all_methods)
1159 {
1160 c->p_all_methods = new std::list<OksMethod *>();
1161
1162 for (const auto& j : *mets)
1163 c->p_all_methods->push_back((c_table[j->p_class->p_id])->find_direct_method(j->get_name()));
1164 }
1165
1166 // copy inheritance hierarchy
1167 if (const std::vector<OksClass *> * ih = i.second->p_inheritance_hierarchy)
1168 {
1169 c->p_inheritance_hierarchy = new std::vector<OksClass *>();
1170 c->p_inheritance_hierarchy->reserve(ih->size());
1171
1172 for (const auto& j : *ih)
1173 c->p_inheritance_hierarchy->push_back(c_table[j->p_id]);
1174 }
1175 }
1176
1177
1178 // copy objects
1179
1180 if(!src.p_objects.empty()) {
1181
1182 // search an object in a class is not efficien, if there are many such operations
1183 // use array with class index for fast search
1184
1185 OksObject ** o_table = new OksObject * [src.p_objects.size()];
1186 idx = 0;
1187
1188 // first iteration: create objects with attributes
1189
1190 for(OksObject::Set::const_iterator i = src.p_objects.begin(); i != src.p_objects.end(); ++i, ++idx) {
1191 OksObject * src_o(*i);
1192 src_o->p_user_data = reinterpret_cast<void *>(idx);
1193
1194 OksClass * c = c_table[src_o->uid.class_id->p_id];
1195
1196 OksObject * o = new OksObject(
1197 c,
1198 src_o->uid.object_id,
1199 src_o->p_user_data,
1200 src_o->p_int32_id,
1201 src_o->p_duplicated_object_id_idx,
1202 (*p_data_files.find(&src_o->file->p_full_name)).second
1203 );
1204
1205 o_table[idx] = o;
1206 (*c->p_objects)[&o->uid.object_id] = o;
1207 p_objects.insert(o);
1208
1209 if(size_t num_of_attrs = c->number_of_all_attributes()) {
1210 const OksData * src_data = src_o->data;
1211 OksData * dst_data = o->data;
1212
1213 std::list<OksAttribute *>::const_iterator ia_dst = c->all_attributes()->begin();
1214 std::list<OksAttribute *>::const_iterator ia_src = src_o->uid.class_id->all_attributes()->begin();
1215
1216 for(size_t j = 0; j < num_of_attrs; ++j) {
1217 *dst_data = *src_data;
1218
1219 OksAttribute * a_dst(*ia_dst);
1220
1221 // process in a special way CLASS type (reference on class)
1222
1223 if(a_dst->get_data_type() == OksData::class_type) {
1224 if(a_dst->get_is_multi_values() == false) {
1225 dst_data->data.CLASS = c_table[src_data->data.CLASS->p_id];
1226 }
1227 else {
1228 OksData::List::iterator li_dst = dst_data->data.LIST->begin();
1229 OksData::List::iterator li_src = src_data->data.LIST->begin();
1230
1231 while(li_dst != dst_data->data.LIST->end()) {
1232 (*li_dst)->data.CLASS = c_table[(*li_src)->data.CLASS->p_id];
1233 ++li_dst;
1234 ++li_src;
1235 }
1236 }
1237 }
1238
1239 // process in a special way ENUM type (reference on attribute's data)
1240
1241 else if(a_dst->get_data_type() == OksData::enum_type) {
1242 OksAttribute * a_src(*ia_src);
1243 const std::string * p_enumerators_first(&((*(a_src->p_enumerators))[0]));
1244
1245 if(a_dst->get_is_multi_values() == false) {
1246 unsigned long dx = src_data->data.ENUMERATION - p_enumerators_first;
1247 dst_data->data.ENUMERATION = &((*(a_dst->p_enumerators))[dx]);
1248 }
1249 else {
1250 OksData::List::iterator li_dst = dst_data->data.LIST->begin();
1251 OksData::List::iterator li_src = src_data->data.LIST->begin();
1252
1253 while(li_dst != dst_data->data.LIST->end()) {
1254 unsigned long dx = (*li_src)->data.ENUMERATION - p_enumerators_first;
1255 (*li_dst)->data.ENUMERATION = &((*(a_dst->p_enumerators))[dx]);
1256 ++li_dst;
1257 ++li_src;
1258 }
1259 }
1260 }
1261
1262 src_data++;
1263 dst_data++;
1264
1265 ia_dst++;
1266 ia_src++;
1267 }
1268 }
1269 }
1270
1271
1272 // second iteration: link relationships of objects
1273
1274 for(const auto& src_o : src.p_objects) {
1275 OksClass * c = c_table[src_o->uid.class_id->p_id];
1276 OksObject * o(o_table[reinterpret_cast<unsigned long>(src_o->p_user_data)]);
1277
1278 if(size_t num_of_rels = c->number_of_all_relationships()) {
1279 const OksData * src_data(src_o->data + c->number_of_all_attributes());
1280 OksData * dst_data(o->data + c->number_of_all_attributes());
1281
1282 for(size_t j = 0; j < num_of_rels; ++j) {
1283 dst_data->type = src_data->type;
1284 switch(src_data->type) {
1285 case OksData::list_type:
1286 dst_data->data.LIST = new OksData::List();
1287 for(const auto& x : *src_data->data.LIST) {
1288 OksData *d = new OksData();
1289
1290 if(x->type == OksData::object_type)
1291 {
1292 if(const OksObject * o2 = x->data.OBJECT)
1293 {
1294 d->Set(o_table[reinterpret_cast<unsigned long>(o2->p_user_data)]);
1295 }
1296 else
1297 {
1298 d->Set((OksObject *)nullptr);
1299 }
1300 }
1301 else if(x->type == OksData::uid_type)
1302 {
1303 d->Set(c_table[x->data.UID.class_id->p_id], *x->data.UID.object_id);
1304 }
1305 else if(x->type == OksData::uid2_type)
1306 {
1307 d->Set(*x->data.UID2.class_id, *x->data.UID2.object_id);
1308 }
1309 else
1310 {
1311 ers::error(kernel::InternalError(ERS_HERE, "unexpected data type in relationship list"));
1312 }
1313
1314 dst_data->data.LIST->push_back(d);
1315 }
1316 break;
1317
1318 case OksData::object_type:
1319 if(const OksObject * o2 = src_data->data.OBJECT) {
1320 dst_data->data.OBJECT = o_table[reinterpret_cast<unsigned long>(o2->p_user_data)];
1321 }
1322 else {
1323 dst_data->data.OBJECT = 0;
1324 }
1325 break;
1326
1327 case OksData::uid_type:
1328 dst_data->data.UID.class_id = c_table[src_data->data.UID.class_id->p_id];
1329 dst_data->data.UID.object_id = new OksString(*src_data->data.UID.object_id);
1330 break;
1331
1332 case OksData::uid2_type:
1333 dst_data->data.UID2.class_id = new OksString(*src_data->data.UID2.class_id);
1334 dst_data->data.UID2.object_id = new OksString(*src_data->data.UID2.object_id);
1335 break;
1336
1337 default:
1338 ers::error(kernel::InternalError(ERS_HERE, "unexpected data type in relationship"));
1339 break;
1340 }
1341
1342 src_data++;
1343 dst_data++;
1344 }
1345 }
1346
1347 if (const std::list<OksRCR *> * src_rcrs = src_o->p_rcr)
1348 {
1349 o->p_rcr = new std::list<OksRCR *>();
1350
1351 for (const auto& j : *src_rcrs)
1352 o->p_rcr->push_back(
1353 new OksRCR(
1354 o_table[reinterpret_cast<unsigned long>(j->obj->p_user_data)],
1355 (c_table[j->relationship->p_class->p_id])->find_direct_relationship(j->relationship->get_name())
1356 )
1357 );
1358 }
1359 else
1360 {
1361 o->p_rcr = nullptr;
1362 }
1363 }
1364
1365 delete [] o_table;
1366 }
1367
1368 delete [] c_table;
1369
1370 // rename all files
1371 if (copy_repository && p_user_repository_root_inited)
1372 {
1373 OksFile::Map * files_map[2] = { &p_schema_files, &p_data_files };
1374
1375 for (int i = 0; i < 2; ++i)
1376 {
1377 std::vector<OksFile *> files;
1378
1379 for (auto& f : *files_map[i])
1380 files.push_back(f.second);
1381
1382 files_map[i]->clear();
1383
1384 for (auto& f : files)
1385 {
1386 std::string name = p_user_repository_root;
1387 name.push_back('/');
1388 name.append(f->get_repository_name());
1389 f->rename(name);
1390 (*files_map[i])[&f->p_full_name] = f;
1391 }
1392 }
1393 }
1394}
1395
1396
1397std::string
1398OksKernel::insert_repository_dir(const std::string& dir, bool push_back)
1399{
1400 std::unique_lock lock(p_kernel_mutex);
1401
1402 std::string s(dir);
1403 Oks::substitute_variables(s);
1404
1405 try {
1406 Oks::real_path(s, false);
1407
1408 if(std::find(p_repository_dirs.begin(), p_repository_dirs.end(), s) == p_repository_dirs.end()) {
1409 if(push_back) p_repository_dirs.push_back(s); else p_repository_dirs.push_front(s);
1410 if(p_verbose) {
1411 std::cout << " * push " << (push_back ? "back" : "front") << " repository search directory \'" << s << "\'\n";
1412 }
1413 return s;
1414 }
1415 }
1416 catch(exception& ex) {
1417 TLOG_DEBUG( 1) << "Cannot insert repository dir \'" << dir << "\':\n\tcaused by: " << ex ;
1418 }
1419
1420 return "";
1421}
1422
1423void
1424OksKernel::remove_repository_dir(const std::string& dir)
1425{
1426 std::unique_lock lock(p_kernel_mutex);
1427
1428 std::list<std::string>::iterator i = std::find(p_repository_dirs.begin(), p_repository_dirs.end(), dir);
1429 if(i != p_repository_dirs.end()) {
1430 if(p_verbose) {
1431 std::cout << " * remove repository search directory \'" << dir << "\'\n";
1432 }
1433 p_repository_dirs.erase(i);
1434 }
1435}
1436
1437
1438void
1439OksKernel::set_profiling_mode(const bool b)
1440{
1441 if(p_profiling == b) return;
1442
1443 p_profiling = b;
1444
1445#ifndef ERS_NO_DEBUG
1446 if(p_profiling == true) {
1447 if(!profiler) profiler = new OksProfiler();
1448 }
1449 else {
1450 if(profiler) {
1451 delete profiler;
1452 profiler = 0;
1453 }
1454 }
1455#endif
1456}
1457
1458
1459OksKernel::~OksKernel()
1460{
1461 {
1462 OSK_PROFILING(OksProfiler::KernelDestructor, this)
1463 OSK_VERBOSE_REPORT("ENTER OksKernel::~OksKernel()")
1464
1465 close_all_data();
1466 close_all_schema();
1467
1468 std::unique_lock lock(p_kernel_mutex);
1469
1470 p_classes.erase(p_classes.begin(), p_classes.end());
1471
1472 --p_count;
1473
1474 if(p_count == 0) {
1475 std::lock_guard scoped_lock(s_get_cwd_mutex);
1476 delete [] s_cwd;
1477 s_cwd = nullptr;
1478 }
1479
1480 remove_user_repository_dir();
1481 }
1482
1483#ifndef ERS_NO_DEBUG
1484 if(p_profiling) std::cout << *profiler << std::endl;
1485#endif
1486
1487 OSK_VERBOSE_REPORT("LEAVE OksKernel::~OksKernel()")
1488}
1489
1490
1491std::ostream&
1492operator<<(std::ostream& s, OksKernel& k)
1493{
1494 OSK_PROFILING(OksProfiler::KernelOperatorOut, &k)
1495
1496 s << "OKS KERNEL DUMP:\n" << "OKS VERSION: " << k.GetVersion() << std::endl;
1497
1498 if (!k.p_schema_files.empty())
1499 {
1500 s << " Loaded schema:\n";
1501
1502 for (const auto& i : k.p_schema_files)
1503 s << " " << i.second->get_full_file_name() << std::endl;
1504
1505 if (!k.p_data_files.empty())
1506 {
1507 s << " Loaded data:\n";
1508
1509 for (const auto& j : k.p_data_files)
1510 s << " " << j.second->get_full_file_name() << std::endl;
1511 }
1512 else
1513 s << " No loaded data files\n";
1514
1515 if (!k.p_classes.empty())
1516 {
1517 s << " The classes:\n";
1518
1519 for (const auto& j : k.p_classes)
1520 s << *j.second;
1521 }
1522 }
1523 else
1524 s << " No loaded schema files\n";
1525
1526 s << "END OF OKS KERNEL DUMP.\n";
1527
1528 return s;
1529}
1530
1531bool
1532OksKernel::check_read_only(OksFile *fp)
1533{
1534 static std::string suffix;
1535 static std::once_flag flag;
1536
1537 std::call_once(flag, []()
1538 {
1539 std::ostringstream s;
1540 s << '-' << get_user_name() << ':' << get_host_name() << ':' << getpid() << std::ends;
1541 suffix = s.str();
1542 }
1543 );
1544
1545 std::string s(fp->p_full_name);
1546 s.append(suffix);
1547
1548 {
1549 static std::mutex p_check_read_only_mutex;
1550 std::lock_guard scoped_lock(p_check_read_only_mutex);
1551
1552 {
1553 std::ofstream f(s.c_str(), std::ios::out);
1554 fp->p_is_read_only = (!f.good());
1555 }
1556
1557 TLOG_DEBUG( 3) << "read-only test on file \"" << s << "\" returns " << fp->p_is_read_only ;
1558
1559 if(!fp->p_is_read_only) {
1560 unlink(s.c_str());
1561 }
1562
1563 return fp->p_is_read_only;
1564 }
1565}
1566
1567OksFile *
1568OksKernel::create_file_info(const std::string& short_path, const std::string& full_path)
1569{
1570 OksFile * file_h = 0;
1571
1572 {
1573 std::shared_ptr<std::ifstream> f(new std::ifstream(full_path.c_str()));
1574
1575 if(f->good()) {
1576 std::shared_ptr<OksXmlInputStream> xmls(new OksXmlInputStream(f));
1577 file_h = new OksFile(xmls, short_path, full_path, this);
1578 }
1579 }
1580
1581 // check file access permissions
1582
1583 if(file_h) {
1584 check_read_only(file_h);
1585 }
1586
1587 return file_h;
1588}
1589
1590 // The function tests file existence, touches the file,
1591 // prints out warnings/errors in case of problems
1592 // and finally prints out info message.
1593
1594static void
1595test_file_existence(const std::string& file_name, bool silence, const std::string& fname, const char * msg)
1596{
1597 // test file existence
1598
1599 bool file_exists = false;
1600
1601 {
1602 struct stat buf;
1603 if(stat(file_name.c_str(), &buf) == 0) {
1604 if(!silence) {
1605 Oks::warning_msg(fname) << " File \"" << file_name << "\" already exists\n";
1606 }
1607 file_exists = true;
1608 }
1609 }
1610
1611
1612 // test file permissions
1613
1614 {
1615 std::ofstream f(file_name.c_str());
1616
1617 if(!f) {
1618 if(!silence) {
1619 if(file_exists) {
1620 throw std::runtime_error("cannot open file in write mode");
1621 }
1622 else {
1623 throw std::runtime_error("cannot create file");
1624 }
1625 }
1626 }
1627 }
1628
1629 if(!silence)
1630 std::cout << "Creating new " << msg << " file \"" << file_name << "\"..." << std::endl;
1631}
1632
1633
1634inline std::string
1635mk_name_and_test(const std::string& name, const char * test, size_t test_len)
1636{
1637 const char _s1_str[] = " - \'";
1638 const char _s2_str[] = "\' tested as ";
1639
1640 return ((std::string(_s1_str, sizeof(_s1_str)-1) + name).append(_s2_str, sizeof(_s2_str)-1)).append(test, test_len);
1641}
1642
1643
1644#define TEST_PATH_TOKEN(path, file, msg) \
1645std::string token(path); \
1646token.push_back('/'); \
1647token.append(file); \
1648Oks::substitute_variables(token); \
1649if(Oks::real_path(token, true)) { \
1650 TLOG_DEBUG(2) << fname << " returns \'" << token << "\' (filename relative to " << msg << " database repository directory)"; \
1651 return token; \
1652} \
1653else { \
1654 const char _test_name[] = "relative to database repository directory"; \
1655 tested_files.push_back(mk_name_and_test(token, _test_name, sizeof(_test_name)-1)); \
1656}
1657
1658
1659std::string
1660OksKernel::get_file_path(const std::string& s, const OksFile * file_h, bool strict_paths) const
1661{
1662 strict_paths &= p_use_strict_repository_paths;
1663
1664 const char _fname[] = "get_file_path";
1665 std::string fname;
1666
1667 TLOG_DEBUG(2) << "ENTER " << make_fname(_fname, sizeof(_fname)-1, s, nullptr, &file_h);
1668
1669 std::list<std::string> tested_files;
1670
1671 // check absolute path or path relative to working directory
1672 bool is_absolute_path = false;
1673
1674 std::string s2(s);
1675 Oks::substitute_variables(s2);
1676 fname = make_fname(_fname, sizeof(_fname) - 1, s2, nullptr, &file_h);
1677
1678 // test if file has an absolute path
1679 if (s2[0] == '/')
1680 is_absolute_path = true;
1681
1682 // continue only if the file is an absolute path or it is not included
1683 if (is_absolute_path || file_h == 0)
1684 {
1685 if (!is_absolute_path && s_cwd && *s_cwd)
1686 {
1687 std::string s3 = s_cwd;
1688 s3.push_back('/');
1689 s2 = s3 + s2;
1690 }
1691
1692 if (Oks::real_path(s2, true))
1693 {
1694 if (!get_user_repository_root().empty() && strict_paths)
1695 {
1696 if (!get_repository_mapping_dir().empty() && s2.find(get_repository_mapping_dir()) == 0)
1697 {
1698 s2.erase(0, get_repository_mapping_dir().size()+1);
1699 TEST_PATH_TOKEN(get_user_repository_root(), s2, "user")
1700 }
1701 else if (s2.find(get_user_repository_root()) == 0)
1702 {
1703 // presumably from explicitly set TDAQ_DB_USER_REPOSITORY created externally
1704 TLOG_DEBUG(2) << fname << " returns external \'" << s2 << "\' (an absolute filename or relative to CWD=\'" << s_cwd << "\')";
1705 return s2;
1706 }
1707
1708 std::ostringstream text;
1709 text << fname << " file does not belong to oks git repository\n"
1710 "provide file repository name, or unset TDAQ_DB_REPOSITORY and try again with the DUNEDAQ_DB_PATH environment variable";
1711 throw std::runtime_error(text.str().c_str());
1712 }
1713 else
1714 {
1715 TLOG_DEBUG(2) << fname << " returns \'" << s2 << "\' (an absolute filename or relative to CWD=\'" << s_cwd << "\')";
1716 return s2;
1717 }
1718 }
1719 else
1720 {
1721 if (is_absolute_path)
1722 {
1723 const char _test_name[] = "an absolute file name";
1724 tested_files.push_back(mk_name_and_test(s2, _test_name, sizeof(_test_name) - 1));
1725 }
1726 else
1727 {
1728 const char _test_name[] = "relative to current working directory";
1729 tested_files.push_back(mk_name_and_test(s2, _test_name, sizeof(_test_name) - 1));
1730 }
1731 }
1732 }
1733
1734 if( !get_user_repository_root().empty())
1735 {
1736 TEST_PATH_TOKEN(get_user_repository_root(), s, "user")
1737 }
1738
1739 if(get_user_repository_root().empty() || !p_use_strict_repository_paths)
1740 {
1741 // check paths relative to OKS database root directories
1742 if (!is_absolute_path)
1743 for (auto & i : p_repository_dirs)
1744 {
1745 TEST_PATH_TOKEN(i, s, "DUNEDAQ_DB_PATH")
1746 }
1747
1748 // check non-absolute path relative to parent file if any
1749 if (file_h && !is_absolute_path)
1750 {
1751 std::string s2(file_h->get_full_file_name());
1752 std::string::size_type pos = s2.find_last_of('/');
1753
1754 if (pos != std::string::npos)
1755 {
1756 s2.erase(pos + 1);
1757 s2.append(s);
1758 Oks::substitute_variables(s2);
1759
1760 if (Oks::real_path(s2, true))
1761 {
1762 TLOG_DEBUG(2) << fname << " returns \'" << s2 << "\' (filename relative to parent file)";
1763 return s2;
1764 }
1765 else
1766 {
1767 const char _test_name[] = "relative to parent file";
1768 tested_files.push_back(mk_name_and_test(s2, _test_name, sizeof(_test_name) - 1));
1769 }
1770 }
1771 }
1772 }
1773
1774 TLOG_DEBUG(2) << fname << " throw exception (file was not found)";
1775
1776 std::ostringstream text;
1777 text << fname << " found no readable file among " << tested_files.size() << " tested:\n";
1778 for (auto & i : tested_files)
1779 text << i << std::endl;
1780
1781 throw std::runtime_error(text.str().c_str());
1782}
1783
1784
1785 // user-allowed method
1786
1787OksFile *
1788OksKernel::load_file(const std::string& short_file_name, bool bind)
1789{
1790 std::unique_lock lock(p_kernel_mutex);
1791 return k_load_file(short_file_name, bind, 0, 0);
1792}
1793
1794
1795 // kernel method
1796
1797OksFile *
1798OksKernel::k_load_file(const std::string& short_file_name, bool bind, const OksFile * parent_h, OksPipeline * pipeline)
1799{
1800 const char _fname[] = "k_load_file";
1801 std::string fname = make_fname(_fname, sizeof(_fname)-1, short_file_name, &bind, &parent_h);
1802
1803 OSK_VERBOSE_REPORT("ENTER " << fname)
1804
1805 std::string full_file_name;
1806
1807 try {
1808 full_file_name = get_file_path(short_file_name, parent_h);
1809 }
1810 catch (std::exception & e) {
1811 throw CanNotOpenFile("k_load_file", short_file_name, e.what());
1812 }
1813
1814 try {
1815
1816 // check if the file is already loaded
1817
1818 OksFile::Map::const_iterator i = p_schema_files.find(&full_file_name);
1819 if(i != p_schema_files.end()) return i->second->check_parent(parent_h);
1820
1821 i = p_data_files.find(&full_file_name);
1822 if(i != p_data_files.end()) return i->second->check_parent(parent_h);
1823
1824 std::shared_ptr<std::ifstream> f(new std::ifstream(full_file_name.c_str()));
1825
1826 if(f->good()) {
1827 long file_length(get_file_length(*f));
1828
1829 if(!file_length) {
1830 throw std::runtime_error("k_load_file(): file is empty");
1831 }
1832
1833 // read file header and decide what to load
1834
1835 std::shared_ptr<OksXmlInputStream> xmls(new OksXmlInputStream(f));
1836
1837 OksFile * file_h = new OksFile(xmls, short_file_name, full_file_name, this);
1838
1839 if(file_h->p_oks_format.size() == 6 && cmp_str6n(file_h->p_oks_format.c_str(), "schema")) {
1840 k_load_schema(file_h, xmls, parent_h);
1841 }
1842 else {
1843 char format;
1844
1845 if(file_h->p_oks_format.size() == 4 && cmp_str4n(file_h->p_oks_format.c_str(), "data"))
1846 format = 'n';
1847 else if(file_h->p_oks_format.size() == 8 && cmp_str8n(file_h->p_oks_format.c_str(), "extended"))
1848 format = 'X';
1849 else if(file_h->p_oks_format.size() == 7 && cmp_str7n(file_h->p_oks_format.c_str(), "compact"))
1850 format = 'c';
1851 else {
1852 delete file_h;
1853 throw std::runtime_error("k_load_file(): failed to parse header");
1854 }
1855
1856 k_load_data(file_h, format, xmls, file_length, bind, parent_h, pipeline);
1857 }
1858
1859 OSK_VERBOSE_REPORT("LEAVE " << fname)
1860
1861 return file_h;
1862 }
1863 else {
1864 throw std::runtime_error("k_load_file(): cannot open file");
1865 }
1866 }
1867 catch (exception & e) {
1868 throw FailedLoadFile("file", full_file_name, e);
1869 }
1870 catch (std::exception & e) {
1871 throw FailedLoadFile("file", full_file_name, e.what());
1872 }
1873 catch (...) {
1874 throw FailedLoadFile("file", full_file_name, "caught unknown exception");
1875 }
1876}
1877
1878
1879void
1880OksKernel::k_load_includes(const OksFile& f, OksPipeline * pipeline)
1881{
1882 for(std::list<std::string>::const_iterator i = f.p_list_of_include_files.begin(); i != f.p_list_of_include_files.end(); ++i) {
1883 if(!(*i).empty()) {
1884 //if(f.get_repository() != OksFile::NoneRepository) {
1885 if(!get_user_repository_root().empty()) {
1886 if((*i)[0] == '/') {
1887 throw FailedLoadFile("include", *i, "inclusion of files with absolute pathname (like \"/foo/bar\") is not allowed by repository files; include files relative to repository root (like \"foo/bar\")");
1888 }
1889 else if((*i)[0] == '.') {
1890 throw FailedLoadFile("include", *i, "inclusion of files with file-related relative pathnames (like \"../foo/bar\" or \"./bar\") is not supported by repository files; include files relative to repository root (like \"foo/bar\")");
1891 }
1892 else if((*i).find('/') == std::string::npos) {
1893 throw FailedLoadFile("include", *i, "inclusion of files with file-related local pathnames (like \"bar\") is not supported by repository files; include files relative to repository root (like \"foo/bar\")");
1894 }
1895 }
1896
1897 try {
1898 k_load_file(*i, false, &f, pipeline);
1899 }
1900 catch (exception & e) {
1901 throw FailedLoadFile("include", *i, e);
1902 }
1903 catch (std::exception & e) {
1904 throw FailedLoadFile("include", *i, e.what());
1905 }
1906 }
1907 else {
1908 throw FailedLoadFile("include", *i, "empty filename");
1909 }
1910 }
1911}
1912
1913void
1914OksKernel::get_includes(const std::string& file_name, std::set< std::string >& includes, bool use_repository_name)
1915{
1916 try {
1917 std::shared_ptr<std::ifstream> f(new std::ifstream(file_name.c_str()));
1918
1919 if(!f->good()) {
1920 throw std::runtime_error("get_includes(): cannot open file");
1921 }
1922
1923 std::shared_ptr<OksXmlInputStream> xmls(new OksXmlInputStream(f));
1924 OksFile fp(xmls, file_name, file_name, this);
1925
1926 if(fp.p_list_of_include_files.size()) {
1927 for(std::list<std::string>::iterator i = fp.p_list_of_include_files.begin(); i != fp.p_list_of_include_files.end(); ++i) {
1928 includes.insert(use_repository_name ? get_file_path(*i, &fp).substr(get_user_repository_root().size()+1) : get_file_path(*i, &fp));
1929 }
1930 }
1931
1932 f->close();
1933 }
1934 catch (exception & e) {
1935 throw (FailedLoadFile("file", file_name, e));
1936 }
1937 catch (std::exception & e) {
1938 throw (FailedLoadFile("file", file_name, e.what()));
1939 }
1940}
1941
1942bool
1943OksKernel::test_parent(OksFile * file, OksFile::IMap::iterator& i)
1944{
1945 OksFile * parent(i->first);
1946 OksFile::Set& includes(i->second);
1947
1948 if(includes.find(file) != includes.end()) {
1949 if(file->p_included_by != parent) {
1950 TLOG_DEBUG( 1 ) << "new parent of file " << file->get_full_file_name() << " is " << parent->get_full_file_name() ;
1951 file->p_included_by = parent;
1952 }
1953
1954 return true;
1955 }
1956
1957 return false;
1958}
1959
1960void
1961OksKernel::k_close_dangling_includes()
1962{
1963 // build inclusion graph
1964
1965 OksFile::IMap igraph;
1966
1967 for(OksFile::Map::iterator i = p_schema_files.begin(); i != p_schema_files.end(); ++i) {
1968 for(std::list<std::string>::const_iterator x = i->second->p_list_of_include_files.begin(); x != i->second->p_list_of_include_files.end(); ++x) {
1969 std::string f = get_file_path(*x, i->second);
1970 OksFile::Map::const_iterator it = p_schema_files.find(&f);
1971 if(it != p_schema_files.end()) {
1972 igraph[i->second].insert(it->second);
1973 }
1974 else {
1975 std::cerr << "cannot find schema " << *x << " included by " << i->second->get_full_file_name() << std::endl;
1976 }
1977 }
1978 }
1979
1980 for(OksFile::Map::iterator i = p_data_files.begin(); i != p_data_files.end(); ++i) {
1981 for(std::list<std::string>::const_iterator x = i->second->p_list_of_include_files.begin(); x != i->second->p_list_of_include_files.end(); ++x) {
1982 std::string f = get_file_path(*x, i->second);
1983 OksFile::Map::const_iterator it = p_data_files.find(&f);
1984 if(it != p_data_files.end()) {
1985 igraph[i->second].insert(it->second);
1986 }
1987 else {
1988 it = p_schema_files.find(&f);
1989
1990 if(it != p_schema_files.end()) {
1991 igraph[i->second].insert(it->second);
1992 }
1993 else {
1994 std::cerr << "cannot find file " << *x << " included by " << i->second->get_full_file_name() << std::endl;
1995 }
1996 }
1997 }
1998 }
1999
2000
2001 const size_t num_of_schema_files(schema_files().size());
2002
2003
2004 // calculate files to be closed
2005
2006 while(true) {
2007 std::list<OksFile *> schema_files, data_files; // files to be closed
2008
2009 OksFile::Map * files[2] = {&p_schema_files, &p_data_files};
2010 std::list<OksFile *> to_be_closed[2];
2011
2012 for(int c = 0; c < 2; ++c) {
2013 for(OksFile::Map::iterator i = files[c]->begin(); i != files[c]->end(); ++i) {
2014 if(OksFile * p = const_cast<OksFile *>(i->second->p_included_by)) {
2015 bool found_parent = false;
2016
2017 // check, the parent is valid
2018
2019 if(igraph.find(p) != igraph.end()) {
2020 OksFile::IMap::iterator j = igraph.find(p);
2021 if(test_parent(i->second, j)) {
2022 continue;
2023 }
2024 }
2025
2026 TLOG_DEBUG( 1 ) << "the parent of file " << i->second->get_full_file_name() << " is not valid" ;
2027
2028
2029 // try to find new parent
2030
2031 for(OksFile::IMap::iterator j = igraph.begin(); j != igraph.end(); ++j) {
2032 if(test_parent(i->second, j)) {
2033 found_parent = true;
2034 break;
2035 }
2036 }
2037
2038 if(found_parent == false) {
2039 TLOG_DEBUG( 1 ) << "the file " << i->second->get_full_file_name() << " is not included by any other file and will be closed" ;
2040 to_be_closed[c].push_back(i->second);
2041 }
2042 }
2043 }
2044 }
2045
2046 int num = 0;
2047
2048 for(std::list<OksFile *>::const_iterator i = to_be_closed[1].begin(); i != to_be_closed[1].end(); ++i) {
2049 num += (*i)->p_list_of_include_files.size();
2050 k_close_data(*i, true);
2051 igraph.erase(*i);
2052 }
2053
2054 for(std::list<OksFile *>::const_iterator i = to_be_closed[0].begin(); i != to_be_closed[0].end(); ++i) {
2055 num += (*i)->p_list_of_include_files.size();
2056 k_close_schema(*i);
2057 igraph.erase(*i);
2058 }
2059
2060 if(num > 0) {
2061 TLOG_DEBUG( 1 ) << "go into recursive call, number of potentially closed includes is " << num ;
2062 }
2063 else {
2064 TLOG_DEBUG( 1 ) << "break loop";
2065 break;
2066 }
2067 }
2068
2069 if(num_of_schema_files != schema_files().size()) {
2070 TLOG_DEBUG( 1 ) << "rebuild classes since number of schema files has been changed from " << num_of_schema_files << " to " << schema_files().size() ;
2071 registrate_all_classes();
2072 }
2073}
2074
2075
2076bool
2077OksKernel::k_preload_includes(OksFile * fp, std::set<OksFile *>& new_files_h, bool allow_schema_extension)
2078{
2079 bool found_include_changes(false);
2080
2081 try {
2082 std::shared_ptr<std::ifstream> file(new std::ifstream(fp->get_full_file_name().c_str()));
2083
2084 if(!file->good()) {
2085 throw std::runtime_error(std::string("k_preload_includes(): cannot open file \"") + fp->get_full_file_name() + '\"');
2086 }
2087
2088 // keep included files
2089
2090 std::set<std::string> included;
2091
2092 if(fp->p_list_of_include_files.size()) {
2093 for(std::list<std::string>::iterator i = fp->p_list_of_include_files.begin(); i != fp->p_list_of_include_files.end(); ++i) {
2094 included.insert(*i);
2095 }
2096 }
2097
2098 // get size of file and check that it is not empty
2099
2100 file->seekg(0, std::ios::end);
2101
2102 long file_length = static_cast<std::streamoff>(file->tellg());
2103
2104 if(file_length == 0) {
2105 throw std::runtime_error(std::string("k_preload_includes(): file \"") + fp->get_full_file_name() + "\" is empty");
2106 }
2107
2108 file->seekg(0, std::ios::beg);
2109
2110 std::shared_ptr<OksXmlInputStream> xmls(new OksXmlInputStream(file));
2111
2112 {
2113 OksFile fp2(xmls, fp->get_short_file_name(), fp->get_full_file_name(), this);
2114 fp2.p_included_by = fp->p_included_by;
2115 p_preload_file_info[fp] = new OksFile(*fp);
2116 *fp = fp2;
2117
2118 if(fp->p_oks_format.empty()) {
2119 throw std::runtime_error(std::string("k_preload_includes(): failed to read header of file \"") + fp->get_full_file_name() + '\"');
2120 }
2121 else if(fp->p_oks_format == "schema") {
2122 TLOG_DEBUG(2) << "skip reload of schema file \'" << fp->get_full_file_name() << '\'';
2123 return false;
2124 }
2125 else if(fp->p_oks_format != "data" && fp->p_oks_format != "extended" && fp->p_oks_format != "compact") {
2126 throw std::runtime_error(std::string("k_preload_includes(): file \"") + fp->get_full_file_name() + "\" is not valid oks file");
2127 }
2128 }
2129
2130 if(fp->p_list_of_include_files.size()) {
2131 TLOG_DEBUG(3) << "check \'include\' of the file \'" << fp->get_full_file_name() << '\'';
2132
2133 if(included.size() != fp->p_list_of_include_files.size()) found_include_changes = true;
2134
2135 for(std::list<std::string>::iterator i = fp->p_list_of_include_files.begin(); i != fp->p_list_of_include_files.end(); ++i) {
2136 std::set<std::string>::const_iterator x = included.find(*i);
2137 if(x != included.end()) {
2138 TLOG_DEBUG(3) << "include \'" << *i << "\' already exists, skip...";
2139 }
2140 else {
2141 TLOG_DEBUG(3) << "the file \'" << *i << "\' was not previously included by \'" << fp->get_full_file_name() << '\'';
2142
2143 found_include_changes = true;
2144
2145 std::string full_file_name;
2146
2147 try {
2148 full_file_name = get_file_path(*i, fp);
2149 }
2150 catch (std::exception & e) {
2151 throw CanNotOpenFile("k_preload_includes", *i, e.what());
2152 }
2153
2154 OksFile::Map::const_iterator j = p_schema_files.find(&full_file_name);
2155 if(j != p_schema_files.end()) {
2156 TLOG_DEBUG(3) << "the include \'" << *i << "\' is already loaded schema file \'" << j->first << '\'';
2157 continue;
2158 }
2159
2160 j = p_data_files.find(&full_file_name);
2161 if(j != p_data_files.end()) {
2162 TLOG_DEBUG(3) << "the include \'" << *i << "\' is already loaded data file \'" << j->first << '\'';
2163 continue;
2164 }
2165
2166 if(OksFile * f = create_file_info(*i, full_file_name)) {
2167 if(f->p_oks_format == "schema") {
2168 std::string new_schema_full_file_name = f->get_full_file_name();
2169 delete f;
2170 if(p_schema_files.find(&new_schema_full_file_name) != p_schema_files.end()) {
2171 TLOG_DEBUG(3) << "the include \'" << *i << "\' is a schema file, that was already loaded";
2172 continue;
2173 }
2174 else {
2175 if(allow_schema_extension) {
2176 TLOG_DEBUG(3) << "the include \'" << *i << "\' is new schema file, loading...";
2177 k_load_schema(*i, fp);
2178 continue;
2179 }
2180 else {
2181 std::ostringstream text;
2182 text << "k_preload_includes(): include of new schema file (\'" << *i << "\') is not allowed on data reload";
2183 throw std::runtime_error(text.str().c_str());
2184 }
2185 }
2186 }
2187 else if(f->p_oks_format == "data" || f->p_oks_format == "extended" || f->p_oks_format == "compact") {
2188 TLOG_DEBUG(3) << "the include \'" << *i << "\' is new data file, pre-loading...";
2189 add_data_file(f);
2190 p_preload_added_files.push_back(f);
2191 new_files_h.insert(f);
2192 f->p_list_of_include_files.clear();
2193 if(k_preload_includes(f, new_files_h, allow_schema_extension)) found_include_changes = true;
2194 f->p_included_by = fp;
2195 f->update_status_of_file();
2196 }
2197 else {
2198 delete f;
2199 std::ostringstream text;
2200 text << "k_preload_includes(): failed to parse header of included \'" << full_file_name << "\' file";
2201 throw std::runtime_error(text.str().c_str());
2202 }
2203 }
2204 else {
2205 throw std::runtime_error("k_load_file(): cannot open file");
2206 }
2207 }
2208 }
2209 }
2210 }
2211 catch (exception & e) {
2212 throw FailedLoadFile("data file", fp->get_full_file_name(), e);
2213 }
2214 catch (std::exception & e) {
2215 throw FailedLoadFile("data file", fp->get_full_file_name(), e.what());
2216 }
2217
2218 return found_include_changes;
2219}
2220
2221
2222/******************************************************************************/
2223
2224std::string
2225OksKernel::create_user_repository_dir()
2226{
2227 if (const char * path = getenv("TDAQ_DB_USER_REPOSITORY_PATH"))
2228 return path;
2229
2230 // create user repository directory if necessary
2231 std::string user_repo;
2232
2233 if (const char * user_repo_base = getenv("TDAQ_DB_USER_REPOSITORY_ROOT"))
2234 user_repo = user_repo_base;
2235 else
2236 user_repo = get_temporary_dir();
2237
2238 user_repo.push_back('/');
2239 if (const char * user_repo_pattern = getenv("TDAQ_DB_USER_REPOSITORY_PATTERN"))
2240 user_repo.append(user_repo_pattern);
2241 else
2242 user_repo.append("oks.XXXXXX");
2243
2244 std::unique_ptr<char[]> dir_template(new char[user_repo.size() + 1]);
2245 strcpy(dir_template.get(), user_repo.c_str());
2246
2247 if (char * tmp_dirname = mkdtemp(dir_template.get()))
2248 {
2249 return tmp_dirname;
2250 }
2251
2252 throw CanNotCreateRepositoryDir("make_user_repository_dir", user_repo);
2253}
2254
2255
2256OksFile *
2257OksKernel::find_file(const std::string &s, const OksFile::Map& files) const
2258{
2259 try {
2260 std::string at = get_file_path(s, nullptr);
2261 OksFile::Map::const_iterator i = files.find(&at);
2262 if(i != files.end()) return (*i).second;
2263 }
2264 catch (...) {
2265 // return 0;
2266 }
2267
2268 // search files included as relative to parent
2269 for(const auto& f : files)
2270 {
2271 if (s == f.second->get_short_file_name())
2272 {
2273 return f.second;
2274 }
2275 }
2276
2277 return nullptr;
2278}
2279
2280OksFile *
2281OksKernel::find_schema_file(const std::string &s) const
2282{
2283 return find_file(s, p_schema_files);
2284}
2285
2286OksFile *
2287OksKernel::find_data_file(const std::string &s) const
2288{
2289 return find_file(s, p_data_files);
2290}
2291
2292/******************************************************************************/
2293
2294 // user-allowed method
2295
2296OksFile *
2297OksKernel::load_schema(const std::string& short_file_name, const OksFile * parent_h)
2298{
2299 std::unique_lock lock(p_kernel_mutex);
2300 return k_load_schema(short_file_name, parent_h);
2301}
2302
2303 // kernel method
2304
2305OksFile *
2306OksKernel::k_load_schema(const std::string& short_file_name, const OksFile * parent_h)
2307{
2308 const char _fname[] = "k_load_schema";
2309 std::string fname = make_fname(_fname, sizeof(_fname)-1, short_file_name, nullptr, &parent_h);
2310
2311 OSK_VERBOSE_REPORT("ENTER " << fname)
2312
2313 std::string full_file_name;
2314
2315 try {
2316 full_file_name = get_file_path(short_file_name, parent_h);
2317 }
2318 catch (std::exception & e) {
2319 throw CanNotOpenFile("k_load_schema", short_file_name, e.what());
2320 }
2321
2322 try {
2323 OksFile * fp = find_schema_file(full_file_name);
2324
2325 if(fp != 0) {
2326 if(p_verbose) {
2327 std::lock_guard lock(p_parallel_out_mutex);
2328 Oks::warning_msg(fname) << " The file was already loaded\n";
2329 }
2330 return fp->check_parent(parent_h);
2331 }
2332
2333 {
2334 std::shared_ptr<std::ifstream> f(new std::ifstream(full_file_name.c_str()));
2335
2336 if(!f->good()) {
2337 throw std::runtime_error("k_load_schema(): cannot open file");
2338 }
2339
2340 std::shared_ptr<OksXmlInputStream> xmls(new OksXmlInputStream(f));
2341
2342 fp = new OksFile(xmls, short_file_name, full_file_name, this);
2343
2344 if(fp->p_oks_format.empty()) {
2345 throw std::runtime_error("k_load_schema(): failed to read header of file");
2346 }
2347 else if(fp->p_oks_format != "schema") {
2348 throw std::runtime_error("k_load_schema(): file is not oks schema file");
2349 }
2350
2351 k_load_schema(fp, xmls, parent_h);
2352
2353 OSK_VERBOSE_REPORT("LEAVE " << fname)
2354
2355 return fp;
2356 }
2357 }
2358 catch (FailedLoadFile &) {
2359 throw; //forward
2360 }
2361 catch (exception & e) {
2362 throw FailedLoadFile("schema file", full_file_name, e);
2363 }
2364 catch (std::exception & e) {
2365 throw FailedLoadFile("schema file", full_file_name, e.what());
2366 }
2367 catch (...) {
2368 throw FailedLoadFile("schema file", full_file_name, "caught unknown exception");
2369 }
2370}
2371
2372
2373void
2374OksKernel::k_load_schema(OksFile * fp, std::shared_ptr<OksXmlInputStream> xmls, const OksFile * parent_h)
2375{
2376 OSK_PROFILING(OksProfiler::KernelLoadSchema, this)
2377
2378 fp->p_included_by = parent_h;
2379 check_read_only(fp);
2380
2381 try {
2382 if(!p_silence) {
2383 std::lock_guard lock(p_parallel_out_mutex);
2384 std::cout << (parent_h ? " * loading " : "Loading ") << fp->p_number_of_items << " classes from file \"" << fp->get_full_file_name() << "\"...\n";
2385 if(parent_h == 0 && fp->get_full_file_name() != fp->get_short_file_name()) {
2386 std::cout << "(non-fully-qualified filename was \"" << fp->get_short_file_name() << "\")\n";
2387 }
2388 }
2389
2390 k_load_includes(*fp, 0);
2391
2392 add_schema_file(fp);
2393
2394 std::list<OksClass *> set;
2395
2396 while(true) {
2397 OksClass *c = 0;
2398
2399 try {
2400 c = new OksClass(*xmls, this);
2401 }
2402 catch(EndOfXmlStream &) {
2403 delete c;
2404 break;
2405 }
2406
2407 if(c->get_name().empty()) {
2408 throw std::runtime_error("k_load_schema(): failed to read a class");
2409 }
2410
2411 OksClass::Map::const_iterator ic = p_classes.find(c->get_name().c_str());
2412 if(ic != p_classes.end()) {
2413 bool are_different = (*ic->second != *c);
2414
2415 c->p_transient = true;
2416 delete c;
2417 c = ic->second;
2418 if(p_allow_duplicated_classes && !are_different) {
2419 static bool ers_report = (getenv("OKS_KERNEL_ERS_REPORT_DUPLICATED_CLASSES") != nullptr);
2420 if(ers_report) {
2421 ers::warning(kernel::ClassAlreadyDefined(ERS_HERE,c->get_name(),fp->get_full_file_name(),c->p_file->get_full_file_name()));
2422 }
2423 else {
2424 const char _fname[] = "k_load_schema";
2425 std::lock_guard lock(p_parallel_out_mutex);
2426 Oks::warning_msg(make_fname(_fname, sizeof(_fname)-1, fp->get_full_file_name(), nullptr, &parent_h))
2427 << " Class \"" << c->get_name() << "\" was already loaded from file \'" << c->get_file()->get_full_file_name() << "\'\n";
2428 }
2429 }
2430 else {
2431 std::stringstream text;
2432 text << (are_different ? "different " : "") << "class \"" << c->get_name() << "\" was already loaded from file \'" << c->get_file()->get_full_file_name() << '\'';
2433 throw std::runtime_error(text.str().c_str());
2434 }
2435 }
2436 else {
2437 {
2438 std::unique_lock lock(p_schema_mutex); // protect schema and all objects from changes
2439 p_classes[c->get_name().c_str()] = c;
2440 }
2441 c->p_file = fp;
2442 if(OksClass::create_notify_fn) set.push_back(c);
2443 }
2444 }
2445
2446 fp->p_size = xmls->get_position();
2447
2448 registrate_all_classes(true);
2449
2450 if(OksClass::create_notify_fn)
2451 for(std::list<OksClass *>::iterator i2 = set.begin(); i2 != set.end(); ++i2)
2452 (*OksClass::create_notify_fn)(*i2);
2453
2454 if(OksClass::change_notify_fn) {
2455 std::set<OksClass *> set2;
2456
2457 for(std::list<OksClass *>::iterator i2 = set.begin(); i2 != set.end(); ++i2) {
2458 OksClass * c = *i2;
2459 if(c->p_all_sub_classes && !c->p_all_sub_classes->empty()) {
2460 for(OksClass::FList::iterator i3 = c->p_all_sub_classes->begin(); i3 != c->p_all_sub_classes->end(); ++i3) {
2461 if(set2.find(*i3) == set2.end()) {
2462 (*OksClass::change_notify_fn)(*i3, OksClass::ChangeSuperClassesList, (const void *)(&(c->p_name)));
2463 set2.insert(*i3);
2464 }
2465 }
2466 }
2467 }
2468 }
2469
2471 }
2472 catch (exception & e) {
2473 throw FailedLoadFile("schema file", fp->get_full_file_name(), e);
2474 }
2475 catch (std::exception & e) {
2476 throw FailedLoadFile("schema file", fp->get_full_file_name(), e.what());
2477 }
2478 catch (...) {
2479 throw FailedLoadFile("schema file", fp->get_full_file_name(), "caught unknown exception");
2480 }
2481}
2482
2483
2484OksFile *
2485OksKernel::new_schema(const std::string& s)
2486{
2487 const char _fname[] = "new_schema";
2488 std::string fname = make_fname(_fname, sizeof(_fname)-1, s, nullptr, nullptr);
2489 OSK_VERBOSE_REPORT("ENTER " << fname)
2490
2491 if(!s.length()) {
2492 throw CanNotOpenFile("new_schema", s, "file name is empty");
2493 }
2494
2495 std::string file_name(s);
2496
2497 try {
2498
2499 Oks::substitute_variables(file_name);
2500
2501 test_file_existence(file_name, p_silence, fname, "schema");
2502
2503 file_name = get_file_path(s, nullptr, false);
2504
2505 std::unique_lock lock(p_kernel_mutex);
2506
2507 if(find_schema_file(file_name) != 0) {
2508 throw std::runtime_error("the file is already loaded");
2509 }
2510
2511 OksFile * file_h = new OksFile(file_name, "", "", "schema", this);
2512
2513 file_h->p_short_name = s;
2514
2515 k_set_active_schema(file_h);
2516
2517 add_schema_file(p_active_schema);
2518
2519 registrate_all_classes(true);
2520
2521 }
2522 catch (std::exception & e) {
2523 throw CanNotCreateFile("new_schema", "schema file", file_name, e.what());
2524 }
2525 catch (...) {
2526 throw CanNotCreateFile("new_schema", "schema file", file_name, "caught unknown exception");
2527 }
2528
2529 OSK_VERBOSE_REPORT("LEAVE " << fname)
2530
2531 return p_active_schema;
2532}
2533
2534
2535 // user-allowed method
2536
2537void
2538OksKernel::save_schema(OksFile * pf, bool force, OksFile * fh)
2539{
2540 std::shared_lock lock(p_kernel_mutex);
2541 k_save_schema(pf, force, fh);
2542}
2543
2544
2545void
2546OksKernel::save_schema(OksFile * file_h, bool force, const OksClass::Map & classes)
2547{
2548 std::shared_lock lock(p_kernel_mutex);
2549 k_save_schema(file_h, force, 0, &classes);
2550}
2551
2552
2553void
2554OksKernel::backup_schema(OksFile * pf, const char * suffix)
2555{
2556 std::shared_lock lock(p_kernel_mutex);
2557
2558 OksFile f(
2559 pf->get_full_file_name() + suffix,
2560 pf->get_logical_name(),
2561 pf->get_type(),
2562 pf->get_oks_format(),
2563 this
2564 );
2565
2566 f.p_created_by = pf->p_created_by;
2567 f.p_creation_time = pf->p_creation_time;
2568 f.p_created_on = pf->p_created_on;
2569 f.p_list_of_include_files = pf->p_list_of_include_files;
2570
2571 if(!p_silence) {
2572 std::cout << "Making backup of schema file \"" << pf->p_full_name << "\"...\n";
2573 }
2574
2575 bool silence = p_silence;
2576
2577 p_silence = true;
2578
2579 try {
2580 k_save_schema(&f, true, pf);
2581 }
2582 catch(exception& ex) {
2583 p_silence = silence;
2584 throw CanNotBackupFile(pf->p_full_name, ex);
2585 }
2586
2587 p_silence = silence;
2588}
2589
2590 // kernel method
2591
2592void
2593OksKernel::k_save_schema(OksFile * pf, bool force, OksFile * fh, const OksClass::Map * classes)
2594{
2595 const char _fname[] = "k_save_schema";
2596 std::string fname = make_fname(_fname, sizeof(_fname)-1, pf->p_full_name, nullptr, nullptr);
2597
2598 OSK_PROFILING(OksProfiler::KernelSaveSchema, this)
2599 OSK_VERBOSE_REPORT("ENTER " << fname)
2600
2601 std::string tmp_file_name;
2602
2603 if(!fh) fh = pf;
2604
2605 try {
2606
2607 // lock the file, if it is not locked already
2608
2609 if(pf->is_locked() == false) {
2610 pf->lock();
2611 }
2612
2613
2614 // calculate number of classes, going into the schema file
2615
2616 size_t numberOfClasses = 0;
2617
2618 if(classes) {
2619 numberOfClasses = classes->size();
2620 }
2621 else if(!p_classes.empty()) {
2622 for(OksClass::Map::iterator i = p_classes.begin(); i != p_classes.end(); ++i) {
2623 if(i->second->p_file == fh) numberOfClasses++;
2624 }
2625 }
2626
2627
2628 // create temporal file to store the schema
2629
2630 tmp_file_name = get_tmp_file(pf->p_full_name);
2631
2632 {
2633 std::ofstream f(tmp_file_name.c_str());
2634 f.exceptions ( std::ostream::failbit | std::ostream::badbit );
2635
2636 if(!f) {
2637 std::ostringstream text;
2638 text << "cannot create temporal file \'" << tmp_file_name << "\' to save schema";
2639 throw std::runtime_error(text.str().c_str());
2640 }
2641 else {
2642 if(!p_silence)
2643 std::cout << "Saving " << numberOfClasses << " classes to schema file \"" << pf->p_full_name << "\"...\n";
2644 }
2645
2646
2647 OksXmlOutputStream xmls(f);
2648
2649
2650 // write oks xml file header
2651
2652 pf->p_number_of_items = numberOfClasses;
2653 pf->p_oks_format = "schema";
2654
2655 pf->write(xmls);
2656
2657
2658 // write oks classes
2659
2660 std::ostringstream errors;
2661 bool found_errors = false;
2662
2663 if (classes)
2664 {
2665 for (auto & i : *classes)
2666 {
2667 found_errors |= i.second->check_relationships(errors, false);
2668 i.second->save(xmls);
2669 }
2670 }
2671 else
2672 {
2673 for (auto & i : p_classes)
2674 {
2675 if (i.second->p_file == fh)
2676 {
2677 found_errors |= i.second->check_relationships(errors, false);
2678 i.second->save(xmls);
2679 }
2680 }
2681 }
2682
2683 if(found_errors)
2684 {
2685 kernel::BindError ex(ERS_HERE, errors.str());
2686
2687 if(force == false)
2688 {
2689 throw std::runtime_error(ex.what());
2690 }
2691 else if(p_silence == false)
2692 {
2693 ers::warning(ex);
2694 }
2695 }
2696
2697 // close oks xml file
2698
2699 xmls.put_last_tag("oks-schema", sizeof("oks-schema")-1);
2700
2701
2702 // flush the buffers
2703
2704 f.close();
2705 }
2706
2707 if(rename(tmp_file_name.c_str(), pf->p_full_name.c_str())) {
2708 std::ostringstream text;
2709 text << "cannot rename \'" << tmp_file_name << "\' to \'" << pf->p_full_name << '\'';
2710 throw std::runtime_error(text.str().c_str());
2711 }
2712
2713 tmp_file_name.erase(0);
2714
2715 if(pf != p_active_schema) {
2716 try {
2717 pf->unlock();
2718 }
2719 catch(exception& ex) {
2720 throw std::runtime_error(ex.what());
2721 }
2722 }
2723 pf->p_is_updated = false;
2725
2726 }
2727 catch (exception & ex) {
2728 if(pf != p_active_schema) { try { pf->unlock();} catch(...) {} }
2729 if(!tmp_file_name.empty()) { unlink(tmp_file_name.c_str()); }
2730 throw CanNotWriteToFile("k_save_schema", "schema file", pf->p_full_name, ex);
2731 }
2732 catch (std::exception & ex) {
2733 if(pf != p_active_schema) { try { pf->unlock();} catch(...) {} }
2734 if(!tmp_file_name.empty()) { unlink(tmp_file_name.c_str()); }
2735 throw CanNotWriteToFile("k_save_schema", "schema file", pf->p_full_name, ex.what());
2736 }
2737
2738 OSK_VERBOSE_REPORT("LEAVE " << fname)
2739}
2740
2741
2742 // note: the file name is used as a key in the map
2743 // to rename a file it is necessary to remove file from map, change name and insert it back
2744
2745void
2746OksKernel::k_rename_schema(OksFile * pf, const std::string& short_name, const std::string& long_name)
2747{
2748 remove_schema_file(pf);
2749 pf->rename(short_name, long_name);
2750 add_schema_file(pf);
2751}
2752
2753
2754void
2755OksKernel::save_as_schema(const std::string& new_name, OksFile * pf)
2756{
2757 const char _fname[] = "save_as_schema";
2758 std::string fname = make_fname(_fname, sizeof(_fname)-1, new_name, nullptr, &pf);
2759 OSK_VERBOSE_REPORT("ENTER " << fname)
2760
2761 try {
2762
2763 if(!new_name.length()) {
2764 throw std::runtime_error("the new filename is empty");
2765 }
2766
2767 std::string old_short_name = pf->p_short_name;
2768 std::string old_full_name = pf->p_full_name;
2769
2770 std::unique_lock lock(p_kernel_mutex);
2771
2772 k_rename_schema(pf, new_name, new_name);
2773
2774 try {
2775 k_save_schema(pf);
2776 }
2777 catch (...) {
2778 k_rename_schema(pf, old_short_name, old_full_name);
2779 throw;
2780 }
2781
2782 }
2783 catch (exception & ex) {
2784 throw CanNotWriteToFile("k_save_as_schema", "schema file", pf->p_full_name, ex);
2785 }
2786 catch (std::exception & ex) {
2787 throw CanNotWriteToFile("k_save_as_schema", "schema file", pf->p_full_name, ex.what());
2788 }
2789
2790 OSK_VERBOSE_REPORT("LEAVE " << fname)
2791}
2792
2793void
2794OksKernel::save_all_schema()
2795{
2796 OSK_VERBOSE_REPORT("ENTER OksKernel::save_all_schema()")
2797
2798 {
2799 std::shared_lock lock(p_kernel_mutex);
2800
2801 for(OksFile::Map::iterator i = p_schema_files.begin(); i != p_schema_files.end(); ++i) {
2802 if(check_read_only(i->second) == false) {
2803 k_save_schema(i->second);
2804 }
2805 else {
2806 TLOG_DEBUG(2) << "skip read-only schema file \'" << *(i->first) << '\'';
2807 }
2808
2809 }
2810
2811 }
2812
2813 OSK_VERBOSE_REPORT("LEAVE OksKernel::save_all_schema()")
2814}
2815
2816
2817 // user-allowed method
2818
2819void
2820OksKernel::close_schema(OksFile * pf)
2821{
2822 std::unique_lock lock(p_kernel_mutex);
2823 k_close_schema(pf);
2824}
2825
2826 // kernel method
2827
2828void
2829OksKernel::k_close_schema(OksFile * pf)
2830{
2831 OSK_PROFILING(OksProfiler::KernelCloseSchema, this)
2832
2833 if(!pf) {
2834 TLOG_DEBUG(1) << "enter for file (null)";
2835 return;
2836 }
2837 else {
2838 TLOG_DEBUG(2) << "enter for file " << pf->p_full_name;
2839 }
2840
2841 if(p_active_schema == pf) p_active_schema = 0;
2842
2843 try {
2844 pf->unlock();
2845 }
2846 catch(exception& ex) {
2847 Oks::error_msg("OksKernel::k_close_schema()") << ex.what() << std::endl;
2848 }
2849
2850 if(!p_silence)
2851 std::cout << (pf->p_included_by ? " * c" : "C") << "lose OKS schema \"" << pf->p_full_name << "\"..." << std::endl;
2852
2853 if(!p_classes.empty()) {
2854
2855 //
2856 // The fastest way to delete classes is delete
2857 // them in order of superclasses descreasing
2858 // because this reduces amount of work to restructure
2859 // child classes when their parent was deleted and
2860 // there will be no dangling classes
2861 //
2862
2863 std::set<OksClass *> sorted;
2864
2865 for(OksClass::Map::iterator i = p_classes.begin(); i != p_classes.end(); ++i) {
2866 OksClass *c = i->second;
2867 if(pf == c->p_file) sorted.insert(c);
2868 c->p_to_be_deleted = true;
2869 }
2870
2871 for(std::set<OksClass *>::iterator j = sorted.begin(); j != sorted.end(); ++j) {
2872 delete *j;
2873 }
2874 }
2875
2876 remove_schema_file(pf);
2877 delete pf;
2878
2879 TLOG_DEBUG(4) << "exit for file " << (void *)pf;
2880}
2881
2882void
2883OksKernel::close_all_schema()
2884{
2885 TLOG_DEBUG(4) << "enter";
2886
2887 {
2888 std::unique_lock lock(p_kernel_mutex);
2889
2890 for(OksClass::Map::iterator i = p_classes.begin(); i != p_classes.end(); ++i) {
2891 i->second->p_to_be_deleted = true;
2892 }
2893
2894 while(!p_schema_files.empty()) {
2895 k_close_schema(p_schema_files.begin()->second);
2896 }
2897 }
2898
2899 TLOG_DEBUG(4) << "exit";
2900}
2901
2902void
2903OksKernel::set_active_schema(OksFile * f)
2904{
2905 std::unique_lock lock(p_kernel_mutex);
2906 k_set_active_schema(f);
2907}
2908
2909void
2910OksKernel::k_set_active_schema(OksFile * f)
2911{
2912 TLOG_DEBUG(4) << "enter for file " << (void *)f;
2913
2914 // check if active schema is different from given file
2915
2916 if(p_active_schema == f) return;
2917
2918
2919 // unlock current active schema, if it was saved
2920
2921 if(p_active_schema && p_active_schema->is_updated() == false) {
2922 try {
2923 p_active_schema->unlock();
2924 }
2925 catch(exception& ex) {
2926 throw CanNotSetActiveFile("schema", f->get_full_file_name(), ex);
2927 }
2928 }
2929
2930
2931 // exit, if file is (null)
2932
2933 if(!f) {
2934 p_active_schema = 0;
2935 return;
2936 }
2937
2938 try {
2939 f->lock();
2940 p_active_schema = f;
2941 }
2942 catch(exception& ex) {
2943 throw CanNotSetActiveFile("schema", f->get_full_file_name(), ex);
2944 }
2945
2946 TLOG_DEBUG(4) << "exit for file " << (void *)f;
2947}
2948
2949std::list<OksClass *> *
2950OksKernel::create_list_of_schema_classes(OksFile * pf) const
2951{
2952 std::string fname("OksKernel::create_list_of_schema_classes(");
2953 fname.append(pf->get_full_file_name());
2954 fname.push_back('\'');
2955
2956 OSK_PROFILING(OksProfiler::KernelCreateSchemaClassList, this)
2957 OSK_VERBOSE_REPORT("ENTER " << fname)
2958
2959 std::list<OksClass *> * clist = nullptr;
2960
2961 if(!p_classes.empty()) {
2962 for(OksClass::Map::const_iterator i = p_classes.begin(); i != p_classes.end(); ++i) {
2963 if(pf == i->second->p_file) {
2964 if(!clist && !(clist = new std::list<OksClass *>())) {
2965 return 0;
2966 }
2967
2968 clist->push_back(i->second);
2969 }
2970 }
2971 }
2972
2973 OSK_VERBOSE_REPORT("LEAVE " << fname)
2974
2975 return clist;
2976}
2977
2978/******************************************************************************/
2979
2980static void
2981create_updated_lists(const OksFile::Map& files, std::list<OksFile *> ** ufs, std::list<OksFile *> ** rfs, OksFile::FileStatus wu, OksFile::FileStatus wr)
2982{
2983 *ufs = 0; // updated files
2984 *rfs = 0; // removed files
2985
2986 for(OksFile::Map::const_iterator i = files.begin(); i != files.end(); ++i) {
2987 OksFile::FileStatus fs = i->second->get_status_of_file();
2988
2989 if(fs == wu /* i.e. Which Updated */ ) {
2990 if(*ufs == 0) { *ufs = new std::list<OksFile *>(); }
2991 (*ufs)->push_back(i->second);
2992 }
2993 else if(fs == wr /* i.e. Which Removed */ ) {
2994 if(*rfs == 0) { *rfs = new std::list<OksFile *>(); }
2995 (*rfs)->push_back(i->second);
2996 }
2997 }
2998}
2999
3000void
3001OksKernel::create_lists_of_updated_schema_files(std::list<OksFile *> ** ufs, std::list<OksFile *> ** rfs) const
3002{
3003 std::shared_lock lock(p_kernel_mutex);
3004
3005 create_updated_lists(p_schema_files, ufs, rfs, OksFile::FileModified, OksFile::FileRemoved);
3006}
3007
3008void
3009OksKernel::create_lists_of_updated_data_files(std::list<OksFile *> ** ufs, std::list<OksFile *> ** rfs) const
3010{
3011 std::shared_lock lock(p_kernel_mutex);
3012
3013 create_updated_lists(p_data_files, ufs, rfs, OksFile::FileModified, OksFile::FileRemoved);
3014}
3015
3016void
3017OksKernel::get_modified_files(std::set<OksFile *>& mfs, std::set<OksFile *>& rfs, const std::string& version)
3018{
3019 std::list<OksFile *> * files[] =
3020 { nullptr, nullptr, nullptr, nullptr };
3021
3022 std::set<OksFile *> * sets[] =
3023 { &mfs, &rfs };
3024
3025 create_lists_of_updated_data_files(&files[0], &files[1]);
3026 create_lists_of_updated_schema_files(&files[2], &files[3]);
3027
3028 for (int i = 0; i < 4; ++i)
3029 if (std::list<OksFile *> * f = files[i])
3030 {
3031 while (!f->empty())
3032 {
3033 sets[i % 2]->insert(f->front());
3034 f->pop_front();
3035 }
3036 delete f;
3037 }
3038
3039 if (!get_user_repository_root().empty() && !version.empty())
3040 for (const auto& file_name : get_repository_versions_diff(get_repository_version(), version))
3041 {
3042 if (OksFile * f = find_data_file(file_name))
3043 sets[0]->insert(f);
3044 else if (OksFile * f = find_schema_file(file_name))
3045 sets[0]->insert(f);
3046 }
3047}
3048
3049/******************************************************************************/
3050
3051
3052 void LoadErrors::add_parents(std::string& text, const OksFile * file, std::set<const OksFile *>& parents)
3053 {
3054 if(file) {
3055 if(parents.insert(file).second == false) {
3056 text += "(ignoring circular dependency between included files...)\n";
3057 }
3058 else {
3059 add_parents(text, file->get_parent(), parents);
3060 text += "file \'";
3061 text += file->get_full_file_name();
3062 text += "\' includes:\n";
3063 }
3064 }
3065 }
3066
3067 void LoadErrors::add_error(const OksFile& file, std::exception& ex)
3068 {
3069 std::string text;
3070 std::set<const OksFile *> parents;
3071 add_parents(text, file.get_parent(), parents);
3072
3073 bool has_no_parents(text.empty());
3074
3075 text += "file \'";
3076 text += file.get_full_file_name();
3077 text += (has_no_parents ? "\' has problem:\n" : "\' that has problem:\n");
3078 text += ex.what();
3079
3080 std::lock_guard lock(p_mutex);
3081 m_errors.push_back(text);
3082 }
3083
3084 std::string LoadErrors::get_text()
3085 {
3086 std::lock_guard lock(p_mutex);
3087
3088 {
3089 std::ostringstream s;
3090
3091 if(m_errors.size() == 1) {
3092 s << "Found 1 error parsing OKS data:\n" << *m_errors.begin();
3093 }
3094 else {
3095 s << "Found " << m_errors.size() << " errors parsing OKS data:";
3096 int j = 1;
3097 for(std::list<std::string>::const_iterator i = m_errors.begin(); i != m_errors.end(); ++i) {
3098 s << "\nERROR [" << j++ << "] ***: " << *i;
3099 }
3100 }
3101
3102 m_error_string = s.str();
3103 }
3104
3105 return m_error_string;
3106 }
3107
3108
3109
3111{
3112 public:
3113
3114 OksLoadObjectsJob( OksKernel * kernel, OksFile * fp, std::shared_ptr<OksXmlInputStream> xmls, char format)
3115 : m_kernel (kernel),
3116 m_fp (fp),
3117 m_xmls (xmls),
3118 m_format (format)
3119 { ; }
3120
3121
3122 void run()
3123 {
3124 try {
3125 OksAliasTable alias_table;
3126 ReadFileParams read_params( m_fp, *m_xmls, ((m_format == 'X') ? 0 : &alias_table), m_kernel, m_format, 0 );
3127
3128 m_fp->p_number_of_items = 0;
3129
3130 try {
3131 while(OksObject::read(read_params)) {
3132 m_fp->p_number_of_items++;
3133 }
3134 }
3135 catch(FailedCreateObject & ex) {
3136 m_kernel->p_load_errors.add_error(*m_fp, ex);
3137 return;
3138 }
3139
3140 m_fp->p_size = m_xmls->get_position();
3141 }
3142 catch (std::exception& ex) {
3143 m_kernel->p_load_errors.add_error(*m_fp, ex);
3144 }
3145 }
3146
3147
3148 private:
3149
3152 std::shared_ptr<OksXmlInputStream> m_xmls;
3154
3155
3156 // protect usage of copy constructor and assignment operator
3157
3158 private:
3159
3162
3163};
3164
3165/******************************************************************************/
3166
3167static bool _find_file(const OksFile::Map & files, const OksFile * f)
3168{
3169 for(OksFile::Map::const_iterator i = files.begin(); i != files.end(); ++i) {
3170 if(i->second == f) return true;
3171 }
3172 return false;
3173}
3174
3175void
3176ReloadObjects::put(OksObject * o)
3177{
3178 map_str_t<OksObject *> *& c = data[o->uid.class_id];
3179 if(!c) c = new map_str_t<OksObject *>();
3180 (*c)[o->GetId()] = o;
3181}
3182
3183OksObject *
3184ReloadObjects::pop(const OksClass* c, const std::string& id)
3185{
3186 std::map< const OksClass *, map_str_t<OksObject *> * >::iterator i = data.find(c);
3187 if(i != data.end()) {
3188 map_str_t<OksObject *>::iterator j = i->second->find(id);
3189 if(j != i->second->end()) {
3190 OksObject * o = j->second;
3191 i->second->erase(j);
3192 if(i->second->empty()) {
3193 delete i->second;
3194 data.erase(i);
3195 }
3196 return o;
3197 }
3198 }
3199
3200 return 0;
3201}
3202
3203ReloadObjects::~ReloadObjects()
3204{
3205 for(std::map< const OksClass *, map_str_t<OksObject *> * >::iterator i = data.begin(); i != data.end(); ++i) {
3206 delete i->second;
3207 }
3208}
3209
3210void
3211OksKernel::reload_data(std::set<OksFile *>& files_h, bool allow_schema_extension)
3212{
3213 std::map<OksFile *, std::vector<std::string> > included;
3214
3215 std::set<OksFile *>::const_iterator i;
3216
3217 bool check_includes(false);
3218 bool found_schema_files(false);
3219 std::string file_names;
3220
3221 std::unique_lock lock(p_kernel_mutex);
3222
3223 for(std::set<OksFile *>::const_iterator fi = files_h.begin(); fi != files_h.end();) {
3224 if(_find_file(p_schema_files, *fi)) {
3225 found_schema_files = true;
3226 }
3227 else if(!_find_file(p_data_files, *fi)) {
3228 Oks::error_msg("OksKernel::reload_data") << "file " << (void *)(*fi) << " is not OKS data or schema file, skip..." << std::endl;
3229 files_h.erase(fi++);
3230 continue;
3231 }
3232
3233 if(fi != files_h.begin()) file_names.append(", ");
3234 file_names.push_back('\"');
3235 file_names.append((*fi)->get_full_file_name());
3236 file_names.push_back('\"');
3237 ++fi;
3238 }
3239
3240 try {
3241
3242 // throw exception on schema_files
3243
3244 if(found_schema_files) {
3245 throw std::runtime_error("Reload of modified schema files is not supported");
3246 }
3247
3248 // exit, if there are no files to reload (e.g. dangling refs.)
3249
3250 if(files_h.empty()) return;
3251
3252
3253 // unlock any locked file
3254 // build container of objects which can be updated (and removed, if not reloaded)
3255
3256 ReloadObjects reload_objects;
3257
3258 for(i = files_h.begin(); i != files_h.end(); ++i) {
3259 if((*i)->is_locked()) {
3260 if(p_active_data == (*i)) { p_active_data = 0; }
3261 (*i)->unlock();
3262 }
3263
3264 for(OksObject::Set::const_iterator oi = p_objects.begin(); oi != p_objects.end(); ++oi) {
3265 if((*oi)->file == *i) reload_objects.put(*oi);
3266 }
3267 }
3268
3269
3270 // preload includes of modified files
3271
3272 {
3273 std::set<OksFile *> new_files;
3274
3275 for(i = files_h.begin(); i != files_h.end(); ++i) {
3276 try {
3277 check_includes |= k_preload_includes(*i, new_files, allow_schema_extension);
3278 }
3279 catch(...) {
3280 restore_preload_file_info();
3281 throw;
3282 }
3283 }
3284
3285 clear_preload_file_info();
3286
3287 if(!new_files.empty()) {
3288 TLOG_DEBUG(2) << new_files.size() << " new files will be loaded";
3289 }
3290
3291 for(i = new_files.begin(); i != new_files.end(); ++i) {
3292 files_h.insert(*i);
3293 }
3294 }
3295
3296 // build list of files to be closed:
3297 // close files which are no more referenced by others
3298 // this may happen if there are changes in the list of includes
3299
3300 std::set<OksFile *> files_to_be_closed;
3301
3302 if(check_includes) {
3303 size_t num_of_closing_files = 0;
3304
3305 while(true) {
3306
3307 // build list of good (i.e. "included") files
3308 // rebuild the list, if at least one file was excluded
3309
3310 std::map<std::string, OksFile *> good_files;
3311
3312 for(OksFile::Map::iterator j = p_data_files.begin(); j != p_data_files.end(); ++j) {
3313 if(files_to_be_closed.find(j->second) == files_to_be_closed.end()) {
3314 for(std::list<std::string>::iterator l = j->second->p_list_of_include_files.begin(); l != j->second->p_list_of_include_files.end(); ++l) {
3315 try {
3316 good_files[get_file_path(*l, j->second)] = j->second;
3317 }
3318 catch (...) {
3319 }
3320 }
3321 }
3322 }
3323
3324 for(OksFile::Map::iterator i = p_data_files.begin(); i != p_data_files.end(); ) {
3325 if(i->second->p_included_by && files_to_be_closed.find(i->second) == files_to_be_closed.end()) {
3326 TLOG_DEBUG(3) << "check the file \'" << i->second->get_full_file_name() << "\' is included";
3327 const std::string& s = i->second->get_full_file_name();
3328 OksFile * f2 = i->second;
3329 ++i;
3330
3331 f2->p_included_by = 0;
3332
3333 // search for a file which could include given one
3334
3335 std::map<std::string, OksFile *>::const_iterator j = good_files.find(s);
3336
3337 if(j != good_files.end()) {
3338 f2->p_included_by = j->second;
3339 }
3340 else {
3341 files_to_be_closed.insert(f2);
3342
3343 for(OksObject::Set::const_iterator oi = p_objects.begin(); oi != p_objects.end(); ++oi) {
3344 if((*oi)->file == f2) {
3345 reload_objects.put(*oi);
3346 }
3347 }
3348
3349 if (files_h.erase(f2)) {
3350 TLOG_DEBUG(2) << "skip reload of updated file \'" << f2->get_full_file_name() << " since it will be closed";
3351 }
3352
3353 TLOG_DEBUG(3) << "file \'" << f2->get_full_file_name() << " will be closed";
3354 }
3355 }
3356 else {
3357 ++i;
3358 }
3359 }
3360
3361 if(num_of_closing_files == files_to_be_closed.size()) {
3362 break;
3363 }
3364 else {
3365 num_of_closing_files = files_to_be_closed.size();
3366 }
3367 }
3368 }
3369 else {
3370 TLOG_DEBUG(2) << "no changes in the list of includes";
3371 }
3372
3373 // remove exclusive RCRs (will be restored when read, if object was not changed)
3374
3375 {
3376 for(std::map< const OksClass *, map_str_t<OksObject *> * >::const_iterator cx = reload_objects.data.begin(); cx != reload_objects.data.end(); ++cx) {
3377 const OksClass * c(cx->first);
3378 if(c->p_all_relationships && !c->p_all_relationships->empty()) {
3379 const unsigned int atts_num(c->number_of_all_attributes());
3380 for(map_str_t<OksObject *>::const_iterator j = cx->second->begin(); j != cx->second->end(); ++j) {
3381 OksObject * obj = j->second;
3382 OksData * d(obj->data + atts_num);
3383
3384 for(std::list<OksRelationship *>::iterator i = c->p_all_relationships->begin(); i != c->p_all_relationships->end(); ++i, ++d) {
3385 OksRelationship * r = *i;
3386 if(r->get_is_composite() && r->get_is_exclusive()) {
3387 if(d->type == OksData::object_type && d->data.OBJECT) {
3388 if(!is_dangling(d->data.OBJECT)) {
3389 d->data.OBJECT->remove_RCR(obj, r);
3390 }
3391 }
3392 else if(d->type == OksData::list_type && d->data.LIST) {
3393 for(OksData::List::iterator i2 = d->data.LIST->begin(); i2 != d->data.LIST->end(); ++i2) {
3394 OksData *d2(*i2);
3395 if(d2->type == OksData::object_type && d2->data.OBJECT) {
3396 if(!is_dangling(d2->data.OBJECT)) {
3397 d2->data.OBJECT->remove_RCR(obj, r);
3398 }
3399 }
3400 }
3401 }
3402 }
3403 }
3404 }
3405 }
3406 }
3407 }
3408
3409 // need to allow "duplicated_objects_via_inheritance_mode", since the objects with the same ID can be created and removed
3410
3411 bool duplicated_objs_mode = get_test_duplicated_objects_via_inheritance_mode();
3412 set_test_duplicated_objects_via_inheritance_mode(false);
3413
3414
3415 // read objects
3416
3417 for(i = files_h.begin(); i != files_h.end(); ++i) {
3418 std::shared_ptr<std::ifstream> f(new std::ifstream((*i)->get_full_file_name().c_str()));
3419
3420 std::shared_ptr<OksXmlInputStream> xmls(new OksXmlInputStream(f));
3421
3422 OksFile fp(xmls, (*i)->get_short_file_name(), (*i)->get_full_file_name(), this);
3423 char format = (fp.p_oks_format == "data" ? 'n' : ((fp.p_oks_format == "extended") ? 'X': 'c'));
3424
3425 if(!p_silence) {
3426 std::lock_guard lock(p_parallel_out_mutex);
3427 std::cout << " * reading data file \"" << (*i)->get_full_file_name() << "\"..." << std::endl;
3428 }
3429
3430 {
3431 OksAliasTable alias_table;
3432 ReadFileParams read_params( *i, *xmls, ((format == 'X') ? 0 : &alias_table), this, format, &reload_objects );
3433
3434 try {
3435 while(OksObject::read(read_params)) { ; }
3436 }
3437 catch(FailedCreateObject & ex) {
3438 set_test_duplicated_objects_via_inheritance_mode(duplicated_objs_mode);
3439 throw ex;
3440 }
3441 }
3442
3443 (*i)->update_status_of_file();
3444 (*i)->p_is_updated = false;
3445 }
3446
3447 set_test_duplicated_objects_via_inheritance_mode(duplicated_objs_mode);
3448
3449
3450 // remove objects which were not re-read
3451
3452 OksObject::FSet oset;
3453
3454 {
3455 for(std::map< const OksClass *, map_str_t<OksObject *> * >::const_iterator cx = reload_objects.data.begin(); cx != reload_objects.data.end(); ++cx) {
3456 for(map_str_t<OksObject *>::const_iterator ox = cx->second->begin(); ox != cx->second->end(); ++ox) {
3457 oset.insert(ox->second);
3458 }
3459 }
3460
3461#ifndef ERS_NO_DEBUG
3462 if(ers::debug_level() >= 3) {
3463 std::ostringstream text;
3464 text << "there are " << oset.size() << " removed objects:\n";
3465
3466 for(OksObject::FSet::iterator x = oset.begin(); x != oset.end(); ++x) {
3467 text << " - object " << *x << std::endl;
3468 TLOG_DEBUG(3) << text.str();
3469 }
3470 }
3471#endif
3472
3473 {
3474 OksObject::FSet refs;
3475 unbind_all_rels(oset, refs);
3476 if(p_change_object_notify_fn) {
3477 for(OksObject::FSet::const_iterator x2 = refs.begin(); x2 != refs.end(); ++x2) {
3478 TLOG_DEBUG(3) << "*** add object " << *x2 << " to the list of updated *** ";
3479 (*p_change_object_notify_fn)(*x2, p_change_object_notify_param);
3480 }
3481 }
3482 }
3483 }
3484
3485 for(OksObject::FSet::iterator ox = oset.begin(); ox != oset.end(); ++ox) {
3486 OksObject * o = *ox;
3487
3488 if(is_dangling(o)) {
3489 TLOG_DEBUG(4) << "skip dangling object " << (void *)o;
3490 continue;
3491 }
3492
3493 TLOG_DEBUG(3) << "*** remove non-reloaded object " << o << " => " << (void *)o << " ***";
3494
3495 delete o;
3496 }
3497
3498
3499 for(std::set<OksFile *>::const_iterator x = files_to_be_closed.begin(); x != files_to_be_closed.end(); ++x) {
3500 k_close_data(*x, true);
3501 }
3502
3503 k_bind_objects();
3504
3505
3506 // check that created objects do not have duplicated IDs within inheritance hierarchy
3507
3508 if (duplicated_objs_mode == true)
3509 {
3510 for (auto& o : reload_objects.created)
3511 {
3512 o->check_ids();
3513 }
3514 }
3515 }
3516 catch (exception & e) {
3517 throw FailedReloadFile(file_names, e);
3518 }
3519 catch (std::exception & e) {
3520 throw FailedReloadFile(file_names, e.what());
3521 }
3522}
3523
3524void
3525OksKernel::clear_preload_file_info()
3526{
3527 for(std::map<const OksFile *, OksFile *>::iterator i = p_preload_file_info.begin(); i != p_preload_file_info.end(); ++i) {
3528 delete i->second;
3529 }
3530
3531 p_preload_file_info.clear();
3532 p_preload_added_files.clear();
3533}
3534
3535void
3536OksKernel::restore_preload_file_info()
3537{
3538 for(std::vector<OksFile *>::iterator j = p_preload_added_files.begin(); j != p_preload_added_files.end(); ++j) {
3539 TLOG_DEBUG( 1 ) << "remove file " << (*j)->get_full_file_name() ;
3540 remove_data_file(*j);
3541 delete *j;
3542 }
3543
3544 for(OksFile::Map::iterator i = p_data_files.begin(); i != p_data_files.end(); ++i) {
3545 std::map<const OksFile *, OksFile *>::iterator j = p_preload_file_info.find(i->second);
3546 if(j != p_preload_file_info.end()) {
3547 TLOG_DEBUG( 1 ) << "restore file " << i->second->get_full_file_name() ;
3548 (*i->second) = (*j->second);
3549 }
3550 }
3551
3552 clear_preload_file_info();
3553}
3554
3555
3556 // user-allowed method
3557
3558OksFile *
3559OksKernel::load_data(const std::string& short_file_name, bool bind)
3560{
3561 std::unique_lock lock(p_kernel_mutex);
3562 return k_load_data(short_file_name, bind, 0, 0);
3563}
3564
3565
3566 // kernel method
3567
3568OksFile *
3569OksKernel::k_load_data(const std::string& short_file_name, bool bind, const OksFile * parent_h, OksPipeline * pipeline)
3570{
3571 const char _fname[] = "k_load_data";
3572 std::string fname = make_fname(_fname, sizeof(_fname)-1, short_file_name, &bind, &parent_h);
3573
3574 OSK_VERBOSE_REPORT("ENTER " << fname)
3575
3576 std::string full_file_name;
3577
3578 try {
3579 full_file_name = get_file_path(short_file_name, parent_h);
3580 }
3581 catch (std::exception & e) {
3582 throw CanNotOpenFile("k_load_data", short_file_name, e.what());
3583 }
3584
3585 try {
3586
3587 OksFile *fp = find_data_file(full_file_name);
3588
3589 if(fp != 0) {
3590 if(p_verbose) {
3591 std::lock_guard lock(p_parallel_out_mutex);
3592 Oks::warning_msg(fname) << " The file \'" << full_file_name << "\' was already loaded";
3593 }
3594 return fp->check_parent(parent_h);
3595 }
3596
3597 {
3598 std::shared_ptr<std::ifstream> f(new std::ifstream(full_file_name.c_str()));
3599
3600 if(!f->good()) {
3601 throw std::runtime_error("k_load_data(): cannot open file");
3602 }
3603
3604 long file_length(get_file_length(*f));
3605
3606 if(!file_length) {
3607 throw std::runtime_error("k_load_data(): file is empty");
3608 }
3609
3610 std::shared_ptr<OksXmlInputStream> xmls(new OksXmlInputStream(f));
3611
3612 fp = new OksFile(xmls, short_file_name, full_file_name, this);
3613
3614 if(fp->p_oks_format.empty()) {
3615 throw std::runtime_error("k_load_data(): failed to read header of file");
3616 }
3617
3618 char format;
3619
3620 if(fp->p_oks_format.size() == 4 && cmp_str4n(fp->p_oks_format.c_str(), "data"))
3621 format = 'n';
3622 else if(fp->p_oks_format.size() == 8 && cmp_str8n(fp->p_oks_format.c_str(), "extended"))
3623 format = 'X';
3624 else if(fp->p_oks_format.size() == 7 && cmp_str7n(fp->p_oks_format.c_str(), "compact"))
3625 format = 'c';
3626 else {
3627 throw std::runtime_error("k_load_data(): file is not an oks data file");
3628 }
3629
3630 k_load_data(fp, format, xmls, file_length, bind, parent_h, pipeline);
3631
3632 OSK_VERBOSE_REPORT("LEAVE " << fname)
3633
3634 return fp;
3635 }
3636 }
3637 catch (FailedLoadFile&) {
3638 throw;
3639 }
3640 catch (exception & e) {
3641 throw (FailedLoadFile("data file", full_file_name, e));
3642 }
3643 catch (std::exception & e) {
3644 throw (FailedLoadFile("data file", full_file_name, e.what()));
3645 }
3646}
3647
3648
3649void
3650OksKernel::k_load_data(OksFile * fp, char format, std::shared_ptr<OksXmlInputStream> xmls, long file_length, bool bind, const OksFile * parent_h, OksPipeline * pipeline)
3651{
3652 OSK_PROFILING(OksProfiler::KernelLoadData, this)
3653
3654 fp->p_included_by = parent_h;
3655 check_read_only(fp);
3656
3657 try {
3658 if(!p_silence) {
3659 std::lock_guard lock(p_parallel_out_mutex);
3660 std::cout << (parent_h ? " * r" : "R") << "eading data file \"" << fp->get_full_file_name()
3661 << "\" in " << (format == 'n' ? "normal" : (format == 'X' ? "extended" : "compact")) << " format (" << file_length << " bytes)...\n";
3662 if(parent_h == 0 && fp->get_full_file_name() != fp->get_short_file_name()) {
3663 std::cout << "(non-fully-qualified filename was \"" << fp->get_short_file_name() << "\")\n";
3664 }
3665 }
3666
3668 add_data_file(fp);
3669
3670 {
3671 std::unique_ptr<OksPipeline> pipeline_guard(pipeline ? 0 : new OksPipeline(p_threads_pool_size));
3672
3673 OksPipeline * m_pipeline;
3674
3675 if(pipeline) {
3676 m_pipeline = pipeline;
3677 }
3678 else {
3679 m_pipeline = pipeline_guard.get();
3680 p_load_errors.clear();
3681 }
3682
3683 k_load_includes(*fp, m_pipeline);
3684
3685
3686 if(p_threads_pool_size > 1) {
3687 m_pipeline->addJob( new OksLoadObjectsJob( this, fp, xmls, format) );
3688 }
3689 else {
3690 OksLoadObjectsJob job(this, fp, xmls, format);
3691 job.run();
3692 }
3693 }
3694
3695 if(!pipeline) {
3696 if(!p_load_errors.is_empty()) {
3697 throw (FailedLoadFile("data file", fp->get_full_file_name(), p_load_errors.get_text()));
3698 }
3699
3700 if(bind) {
3701 k_bind_objects();
3702 }
3703 }
3704 }
3705 catch (FailedLoadFile&) {
3706 throw;
3707 }
3708 catch (exception& e) {
3709 throw (FailedLoadFile("data file", fp->get_full_file_name(), e));
3710 }
3711 catch (std::exception& e) {
3712 throw (FailedLoadFile("data file", fp->get_full_file_name(), e.what()));
3713 }
3714}
3715
3716
3717OksFile *
3718OksKernel::new_data(const std::string& s, const std::string& logical_name, const std::string& type)
3719{
3720 const char _fname[] = "new_data";
3721 std::string fname = make_fname(_fname, sizeof(_fname)-1, s, nullptr, nullptr);
3722 OSK_VERBOSE_REPORT("ENTER " << fname)
3723
3724 if(!s.length()) {
3725 throw CanNotOpenFile("new_data", s, "file name is empty");
3726 }
3727
3728 std::string file_name(s);
3729
3730 try {
3731
3732 Oks::substitute_variables(file_name);
3733
3734 test_file_existence(file_name, p_silence, fname, "data");
3735
3736 file_name = get_file_path(s, nullptr, false);
3737
3738 std::unique_lock lock(p_kernel_mutex);
3739
3740 if(find_data_file(file_name) != 0) {
3741 throw std::runtime_error("the file is already loaded");
3742 }
3743
3744 OksFile * file_h = new OksFile(file_name, logical_name, type, "data", this);
3745
3746 file_h->p_short_name = s;
3747
3748 k_set_active_data(file_h);
3749
3750 add_data_file(p_active_data);
3751
3752 }
3753 catch (exception & e) {
3754 throw CanNotCreateFile("new_data", "data file", file_name, e);
3755 }
3756 catch (std::exception & e) {
3757 throw CanNotCreateFile("new_data", "data file", file_name, e.what());
3758 }
3759
3760 OSK_VERBOSE_REPORT("LEAVE " << fname)
3761
3762 return p_active_data;
3763}
3764
3765
3766void
3767OksKernel::add_data_file(OksFile * f)
3768{
3769 p_data_files[&f->p_full_name] = f;
3770}
3771
3772void
3773OksKernel::add_schema_file(OksFile * f)
3774{
3775 p_schema_files[&f->p_full_name] = f;
3776}
3777
3778void
3779OksKernel::remove_data_file(OksFile * f)
3780{
3781 p_data_files.erase(&f->p_full_name);
3782}
3783
3784void
3785OksKernel::remove_schema_file(OksFile * f)
3786{
3787 p_schema_files.erase(&f->p_full_name);
3788}
3789
3790
3791 // user-allowed method
3792
3793void
3794OksKernel::save_data(OksFile * pf, bool ignoreBadObjects, OksFile * true_file_h, bool force_defaults)
3795{
3796 std::shared_lock lock(p_kernel_mutex);
3797 k_save_data(pf, ignoreBadObjects, true_file_h, nullptr, force_defaults);
3798}
3799
3800void
3801OksKernel::save_data(OksFile * file_h, const OksObject::FSet& objects)
3802{
3803 std::shared_lock lock(p_kernel_mutex);
3804 k_save_data(file_h, false, 0, &objects);
3805}
3806
3807void
3808OksKernel::backup_data(OksFile * pf, const char * suffix)
3809{
3810 std::shared_lock lock(p_kernel_mutex);
3811
3812 OksFile f(
3813 pf->get_full_file_name() + suffix,
3814 pf->get_logical_name(),
3815 pf->get_type(),
3816 pf->get_oks_format(),
3817 this
3818 );
3819
3820 f.p_created_by = pf->p_created_by;
3821 f.p_creation_time = pf->p_creation_time;
3822 f.p_created_on = pf->p_created_on;
3823 f.p_list_of_include_files = pf->p_list_of_include_files;
3824
3825 if(!p_silence) {
3826 std::lock_guard lock(p_parallel_out_mutex);
3827 std::cout << "Making backup of data file \"" << pf->p_full_name << "\"...\n";
3828 }
3829
3830 bool silence = p_silence;
3831
3832 p_silence = true;
3833
3834 try {
3835 k_save_data(&f, true, pf);
3836 }
3837 catch(exception& ex) {
3838 p_silence = silence;
3839 throw CanNotBackupFile(pf->p_full_name, ex);
3840 }
3841
3842 p_silence = silence;
3843}
3844
3845
3846 // kernel method
3847
3848void
3849OksKernel::k_save_data(OksFile * pf, bool ignoreBadObjects, OksFile * fh, const OksObject::FSet * objects, bool force_defaults)
3850{
3851 const char _fname[] = "k_save_data";
3852 std::string fname = make_fname(_fname, sizeof(_fname)-1, pf->p_full_name, nullptr, nullptr);
3853
3854 OSK_PROFILING(OksProfiler::KernelSaveData, this)
3855 OSK_VERBOSE_REPORT("ENTER " << fname)
3856
3857 std::string tmp_file_name;
3858
3859 if(!fh) fh = pf;
3860
3861 try {
3862
3863 // calculate number of objects in file and check objects
3864
3865 size_t numberOfObjects = 0;
3866
3867 if(objects) {
3868 numberOfObjects = objects->size();
3869
3870 if(!ignoreBadObjects) {
3871 std::string errors;
3872
3873 for(OksObject::FSet::const_iterator i = objects->begin(); i != objects->end(); ++i) {
3874 errors += (*i)->report_dangling_references();
3875 }
3876
3877 if(!errors.empty()) {
3878 std::ostringstream text;
3879 text << "the file contains objects with dangling references:\n" << errors;
3880 throw std::runtime_error(text.str().c_str());
3881 }
3882 }
3883 }
3884 else {
3885 // get list of all includes
3886
3887 std::set<OksFile *> includes;
3888 pf->get_all_include_files(this, includes);
3889
3890 OSK_VERBOSE_REPORT("Test consistency of objects in file \"" << pf->p_full_name << '\"')
3891
3892 bool found_bad_object = false;
3893
3894 for(OksObject::Set::iterator i = p_objects.begin(); i != p_objects.end(); ++i) {
3895 if((*i)->file == fh) {
3896 numberOfObjects++;
3897 if(!ignoreBadObjects || !p_silence) {
3898 if((*i)->is_consistent(includes, "WARNING") == false) {
3899 found_bad_object = true;
3900 }
3901 if((*i)->is_duplicated() == true) {
3902 if(!p_silence) {
3903 Oks::error_msg(fname) << " The file contains duplicated object " << *i << std::endl;
3904 }
3905 found_bad_object = true;
3906 }
3907 }
3908 }
3909 }
3910
3911 if(found_bad_object && ignoreBadObjects == false) {
3912 throw std::runtime_error("the file contains inconsistent/duplicated objects or misses includes");
3913 }
3914 }
3915
3916
3917 // lock the file, if it is not locked already
3918
3919 if(pf->is_locked() == false) {
3920 pf->lock();
3921 }
3922
3923
3924 // check if it possible to open the non-existent file in write mode
3925
3926 {
3927 // check first if file already exists
3928
3929 std::fstream f(pf->p_full_name.c_str(), std::ios::in);
3930
3931 if(!f) {
3932
3933 // no check for non-existent file
3934
3935 std::fstream f2(pf->p_full_name.c_str(), std::ios::out);
3936
3937 if(!f2) {
3938 std::ostringstream text;
3939 text << "cannot open file \'" << pf->p_full_name << "\' for writing";
3940 throw std::runtime_error(text.str().c_str());
3941 }
3942 }
3943 }
3944
3945
3946 tmp_file_name = get_tmp_file(pf->p_full_name);
3947
3948 long file_len = 0;
3949
3950 {
3951 std::ofstream f(tmp_file_name.c_str());
3952
3953 f.exceptions ( std::ostream::failbit | std::ostream::badbit );
3954
3955 if(!f) {
3956 std::ostringstream text;
3957 text << "cannot create temporal file \'" << tmp_file_name << "\' to save data";
3958 throw std::runtime_error(text.str().c_str());
3959 }
3960 else {
3961 if(!p_silence) {
3962 std::lock_guard lock(p_parallel_out_mutex);
3963 std::cout << "Saving " << numberOfObjects << " objects " << (force_defaults ? "with enforced default values " : "") << "to data file \"" << pf->p_full_name << "\"...\n";
3964 }
3965 }
3966
3967
3968 // set header parameters
3969
3970 OksXmlOutputStream xmls(f);
3971
3972 pf->p_number_of_items = numberOfObjects;
3973 pf->p_oks_format = "data";
3974
3975 pf->write(xmls);
3976
3977 if(!p_objects.empty()) {
3978 for(OksClass::Map::const_iterator i = p_classes.begin(); i != p_classes.end() && f.good(); ++i) {
3979 OksObject::SMap sorted;
3980
3981 for(OksObject::Map::const_iterator j = i->second->p_objects->begin(); j != i->second->p_objects->end(); ++j) {
3982 if(j->second->file == fh || (objects && (objects->find(j->second) != objects->end()))) {
3983 sorted[j->first] = j->second;
3984 }
3985 }
3986
3987 for(OksObject::SMap::iterator j = sorted.begin(); j != sorted.end(); ++j) {
3988 j->second->put(xmls, force_defaults);
3989 xmls.put_raw('\n');
3990 }
3991 }
3992 }
3993
3994 xmls.put_last_tag("oks-data", sizeof("oks-data")-1);
3995 file_len = f.tellp();
3996
3997 f.close();
3998 }
3999
4000
4001 // check that the written file is OK
4002 // FIXME: can be removed later, if exceptions work well enough
4003
4004 {
4005 long written_len = 0;
4006
4007 std::shared_ptr<std::ifstream> f(new std::ifstream(tmp_file_name.c_str()));
4008
4009 if(*f) {
4010 f->seekg(0, std::ios::end);
4011 written_len = static_cast<std::streamoff>(f->tellg());
4012 }
4013
4014 if(written_len != file_len) {
4015 unlink(tmp_file_name.c_str());
4016 std::ostringstream text;
4017 text << "write error in file \'" << tmp_file_name << "\': " << written_len << " bytes been written instead of " << file_len;
4018 throw std::runtime_error(text.str().c_str());
4019 }
4020 }
4021
4022
4023 // remember file's mode
4024
4025 struct stat buf;
4026 if(int code = stat(pf->p_full_name.c_str(), &buf)) {
4027 std::ostringstream text;
4028 text << "cannot get information about file \'" << pf->p_full_name << "\': stat() failed with code " << code << ", reason = \'" << strerror(errno) << '\'';
4029 throw std::runtime_error(text.str().c_str());
4030 }
4031
4032
4033 // rename temporal file
4034
4035 if(int code = rename(tmp_file_name.c_str(), pf->p_full_name.c_str())) {
4036 std::ostringstream text;
4037 text << "cannot rename file \'" << tmp_file_name << "\' to \'" << pf->p_full_name << "\': rename() failed with code " << code << ", reason = \'" << strerror(errno) << '\'';
4038 throw std::runtime_error(text.str().c_str());
4039 }
4040
4041 tmp_file_name.erase(0);
4042
4043
4044 if(pf != p_active_data) {
4045 try {
4046 pf->unlock();
4047 }
4048 catch(exception& ex) {
4049 throw std::runtime_error(ex.what());
4050 }
4051 }
4052 pf->p_is_updated = false;
4054
4055
4056 // get mode for new file
4057
4058 struct stat buf2;
4059 if(int code = stat(pf->p_full_name.c_str(), &buf2)) {
4060 std::ostringstream text;
4061 text << "cannot get information about file \'" << pf->p_full_name << "\': stat() failed with code " << code << ", reason = \'" << strerror(errno) << '\'';
4062 throw std::runtime_error(text.str().c_str());
4063 }
4064
4065
4066 // set file's mode if needed
4067
4068 if(buf.st_mode != buf2.st_mode) {
4069 if(int code = chmod(pf->p_full_name.c_str(), buf.st_mode) != 0) {
4070 std::ostringstream text;
4071 text << "cannot set protection mode for file \'" << pf->p_full_name << "\': chmod() failed with code " << code << ", reason = \'" << strerror(errno) << '\'';
4072 throw std::runtime_error(text.str().c_str());
4073 }
4074 }
4075
4076 // set file's group if needed (in case of problems report error, but do not throw exception)
4077
4078 if(buf.st_gid != buf2.st_gid) {
4079 if(int code = chown(pf->p_full_name.c_str(), (gid_t)(-1), buf.st_gid) != 0) {
4080 ers::warning(kernel::SetGroupIdFailed(ERS_HERE, buf.st_gid, pf->p_full_name.c_str(), code, strerror(errno)));
4081 }
4082 }
4083
4084 }
4085
4086 catch (exception & e) {
4087 if(pf != p_active_data) { try { pf->unlock();} catch(...) {} }
4088 if(!tmp_file_name.empty()) { unlink(tmp_file_name.c_str()); }
4089 throw CanNotWriteToFile("k_save_data", "data file", pf->p_full_name, e);
4090 }
4091 catch (std::exception & e) {
4092 if(pf != p_active_data) { try { pf->unlock();} catch(...) {} }
4093 if(!tmp_file_name.empty()) { unlink(tmp_file_name.c_str()); }
4094 throw CanNotWriteToFile("k_save_data", "data file", pf->p_full_name, e.what());
4095 }
4096 catch (...) {
4097 if(pf != p_active_data) { try { pf->unlock();} catch(...) {} }
4098 if(!tmp_file_name.empty()) { unlink(tmp_file_name.c_str()); }
4099 throw CanNotWriteToFile("k_save_data", "data file", pf->p_full_name, "unknown");
4100 }
4101
4102 OSK_VERBOSE_REPORT("LEAVE " << fname)
4103}
4104
4105
4106 // note: the file name is used as a key in the map
4107 // to rename a file it is necessary to remove file from map, change name and insert it back
4108
4109void
4110OksKernel::k_rename_data(OksFile * pf, const std::string& short_name, const std::string& long_name)
4111{
4112 remove_data_file(pf);
4113 pf->rename(short_name, long_name);
4114 add_data_file(pf);
4115}
4116
4117void
4118OksKernel::save_as_data(const std::string& new_name, OksFile * pf)
4119{
4120 const char _fname[] = "save_as_data";
4121 std::string fname = make_fname(_fname, sizeof(_fname)-1, new_name, nullptr, &pf);
4122 OSK_VERBOSE_REPORT("ENTER " << fname)
4123
4124 try {
4125
4126 if(!new_name.length()) {
4127 throw std::runtime_error("the new filename is empty");
4128 }
4129
4130 std::unique_lock lock(p_kernel_mutex);
4131
4132 std::string old_short_name = pf->p_short_name;
4133 std::string old_full_name = pf->p_full_name;
4134 std::string old_format = pf->p_oks_format;
4135
4136 k_rename_data(pf, new_name, new_name);
4137
4138 try {
4139 k_save_data(pf);
4140 }
4141 catch (...) {
4142 k_rename_data(pf, old_short_name, old_full_name);
4143 pf->p_oks_format = old_format;
4144 throw;
4145 }
4146
4147 }
4148 catch (exception & ex) {
4149 throw CanNotWriteToFile("k_save_as_data", "data file", new_name, ex);
4150 }
4151 catch (std::exception & ex) {
4152 throw CanNotWriteToFile("k_save_as_data", "data file", new_name, ex.what());
4153 }
4154
4155 OSK_VERBOSE_REPORT("LEAVE " << fname)
4156}
4157
4158
4159void
4160OksKernel::save_all_data(bool force_defaults)
4161{
4162 TLOG_DEBUG(4) << "enter";
4163
4164 {
4165 std::shared_lock lock(p_kernel_mutex);
4166
4167 for(OksFile::Map::iterator i = p_data_files.begin(); i != p_data_files.end(); ++i) {
4168 if(check_read_only(i->second) == false) {
4169 k_save_data(i->second, false, nullptr, nullptr, force_defaults);
4170 }
4171 else {
4172 TLOG_DEBUG(2) << "skip read-only data file \'" << *(i->first) << '\'';
4173 }
4174 }
4175 }
4176
4177 TLOG_DEBUG(4) << "exit";
4178}
4179
4180 // user-allowed method
4181
4182void
4183OksKernel::close_data(OksFile * fp, bool unbind)
4184{
4185 std::unique_lock lock(p_kernel_mutex);
4186 k_close_data(fp, unbind);
4187}
4188
4189 // kernel method
4190
4191void
4192OksKernel::k_close_data(OksFile * fp, bool unbind)
4193{
4194 const char _fname[] = "k_close_data";
4195 std::string fname = make_fname(_fname, sizeof(_fname)-1, fp->p_full_name, &unbind, nullptr);
4196
4197 OSK_PROFILING(OksProfiler::KernelCloseData, this)
4198 OSK_VERBOSE_REPORT("ENTER " << fname)
4199
4200 try {
4201 fp->unlock();
4202 }
4203 catch(exception& ex) {
4204 Oks::error_msg(fname) << ex.what() << std::endl;
4205 }
4206
4207 if(!p_silence) {
4208 std::lock_guard lock(p_parallel_out_mutex);
4209 std::cout << (fp->p_included_by ? " * c" : "C") << "lose OKS data \"" << fp->p_full_name << "\"..." << std::endl;
4210 }
4211
4212 if(unbind) {
4213 for(OksObject::Set::const_iterator i = p_objects.begin(); i != p_objects.end(); ++i) {
4214 OksObject *o = *i;
4215 if(o->file != fp) o->unbind_file(fp);
4216 }
4217 }
4218
4219 if(p_close_all == false) {
4220 std::list<OksObject *> * olist = create_list_of_data_objects(fp);
4221
4222 if(olist) {
4223 while(!olist->empty()) {
4224 OksObject *o = olist->front();
4225 olist->pop_front();
4226 if(!is_dangling(o)) delete o;
4227 }
4228
4229 delete olist;
4230 }
4231 }
4232
4233 remove_data_file(fp);
4234 delete fp;
4235
4236 if(p_active_data == fp) p_active_data = 0;
4237
4238 OSK_VERBOSE_REPORT("LEAVE " << fname)
4239}
4240
4241
4242void
4243OksKernel::close_all_data()
4244{
4245 TLOG_DEBUG(4) << "enter";
4246
4247 {
4248 std::unique_lock lock(p_kernel_mutex);
4249
4250 p_close_all = true;
4251
4252 while(!p_data_files.empty()) {
4253 k_close_data(p_data_files.begin()->second, false);
4254 }
4255
4256 if(!p_objects.empty()) {
4257 for(OksObject::Set::iterator i = p_objects.begin(); i != p_objects.end(); ++i) {
4258 delete *i;
4259 }
4260
4261 p_objects.clear();
4262
4263 for(OksClass::Map::const_iterator i = p_classes.begin(); i != p_classes.end(); ++i) {
4264 if(i->second->p_objects) {
4265 delete i->second->p_objects;
4266 i->second->p_objects = 0;
4267 }
4268 }
4269 }
4270
4271 p_close_all = false;
4272 }
4273
4274 TLOG_DEBUG(4) << "exit";
4275}
4276
4277void
4278OksKernel::set_active_data(OksFile * fp)
4279{
4280 std::unique_lock lock(p_kernel_mutex);
4281 k_set_active_data(fp);
4282}
4283
4284void
4285OksKernel::k_set_active_data(OksFile * fp)
4286{
4287 TLOG_DEBUG(4) << "enter for file " << (void *)fp;
4288
4289
4290 // check if active data is different from given file
4291
4292 if(p_active_data == fp) return;
4293
4294
4295 // first unlock current active data
4296
4297 if(p_active_data && p_active_data->is_updated() == false) {
4298 try {
4299 p_active_data->unlock();
4300 }
4301 catch(exception& ex) {
4302 throw CanNotSetActiveFile("data", fp->get_full_file_name(), ex);
4303 }
4304 }
4305
4306
4307 // exit, if file is (null)
4308
4309 if(!fp) {
4310 p_active_data = 0;
4311 return;
4312 }
4313
4314 try {
4315 fp->lock();
4316 p_active_data = fp;
4317 }
4318 catch(exception& ex) {
4319 throw CanNotSetActiveFile("data", fp->get_full_file_name(), ex);
4320 }
4321
4322 TLOG_DEBUG(4) << "leave for file " << (void *)fp;
4323}
4324
4325
4326std::list<OksObject *> *
4327OksKernel::create_list_of_data_objects(OksFile * fp) const
4328{
4329 const char _fname[] = "create_list_of_data_objects";
4330 std::string fname = make_fname(_fname, sizeof(_fname)-1, fp->get_full_file_name(), nullptr, nullptr);
4331
4332 OSK_PROFILING(OksProfiler::KernelCreateDataObjectList, this)
4333 OSK_VERBOSE_REPORT("ENTER " << fname)
4334
4335
4336 std::list<OksObject *> * olist = new std::list<OksObject *>();
4337
4338 if(!p_objects.empty()) {
4339 for(OksObject::Set::const_iterator i = p_objects.begin(); i != p_objects.end(); ++i) {
4340 if((*i)->file == fp) olist->push_back(*i);
4341 }
4342 }
4343
4344 if(olist->empty()) {
4345 delete olist;
4346 olist = 0;
4347 }
4348
4349
4350 OSK_VERBOSE_REPORT("LEAVE " << fname)
4351
4352 return olist;
4353}
4354
4355
4356/******************************************************************************/
4357
4358 // user-allowed method
4359
4360void
4361OksKernel::bind_objects()
4362{
4363 std::unique_lock lock(p_kernel_mutex);
4364 k_bind_objects();
4365}
4366
4367void
4368OksKernel::k_bind_objects()
4369{
4370 TLOG_DEBUG(4) << "enter";
4371
4372 p_bind_objects_status.clear();
4373
4374 if(!p_objects.empty()) {
4375 for(OksObject::Set::iterator i = p_objects.begin(); i != p_objects.end(); ++i) {
4376 try {
4377 (*i)->bind_objects();
4378 }
4379 catch(ObjectBindError& ex) {
4380 if(ex.p_is_error) {
4381 throw;
4382 }
4383 else {
4384 const std::string error_text(strchr(ex.what(), '\n')+1);
4385 if(!p_bind_objects_status.empty()) p_bind_objects_status.push_back('\n');
4386 p_bind_objects_status.append(error_text);
4387
4388 TLOG_DEBUG(1) << error_text;
4389 }
4390 }
4391 }
4392 }
4393
4394 if(!p_silence && !p_bind_objects_status.empty()) {
4395 ers::warning(kernel::BindError(ERS_HERE, p_bind_objects_status));
4396 }
4397
4398 TLOG_DEBUG(4) << "exit with status:\n" << p_bind_objects_status;
4399}
4400
4401
4402void
4403OksKernel::unbind_all_rels(const OksObject::FSet& rm_objs, OksObject::FSet& updated) const
4404{
4405 const OksClass::Map& all_classes(classes());
4406
4407 for(OksClass::Map::const_iterator i = all_classes.begin(); i != all_classes.end(); ++i) {
4408 OksClass * c(i->second);
4409 if(const OksObject::Map * objs = i->second->objects()) {
4410 for(OksObject::Map::const_iterator j = objs->begin(); j != objs->end(); ++j) {
4411 OksObject *o(j->second);
4412 unsigned short l1 = c->number_of_all_attributes();
4413 unsigned short l2 = l1 + c->number_of_all_relationships();
4414
4415 while(l1 < l2) {
4416 OksDataInfo odi(l1, (OksRelationship *)0);
4417 OksData * d(o->GetRelationshipValue(&odi));
4418
4419 if(d->type == OksData::object_type) {
4420 if(rm_objs.find(d->data.OBJECT) != rm_objs.end()) {
4421 updated.insert(o);
4422 const OksClass * __c(d->data.OBJECT->GetClass());
4423 const OksString& __id(d->data.OBJECT->GetId());
4424 d->Set(__c,__id);
4425 TLOG_DEBUG(5) << "set relationship of " << o << ": " << *d;
4426 }
4427 }
4428 else if(d->type == OksData::list_type) {
4429 for(OksData::List::const_iterator li = d->data.LIST->begin(); li != d->data.LIST->end(); ++li) {
4430 OksData * lid(*li);
4431 if(lid->type == OksData::object_type) {
4432 if(rm_objs.find(lid->data.OBJECT) != rm_objs.end()) {
4433 updated.insert(o);
4434 const OksClass * __c(lid->data.OBJECT->GetClass());
4435 const OksString& __id(lid->data.OBJECT->GetId());
4436 lid->Set(__c,__id);
4437 TLOG_DEBUG(5) << "set relationship of " << o << ": " << *d;
4438 }
4439 }
4440 }
4441 }
4442 ++l1;
4443 }
4444 }
4445 }
4446 }
4447}
4448
4449
4450OksClass *
4451OksKernel::find_class(const char * name) const
4452{
4453 OksClass::Map::const_iterator i = p_classes.find(name);
4454 return (i != p_classes.end() ? i->second : 0);
4455}
4456
4457
4458void
4459OksKernel::k_add(OksClass *c)
4460{
4461 TLOG_DEBUG(4) << "enter for class \"" << c->get_name() << '\"';
4462
4463 if(p_active_schema == 0) {
4464 throw CannotAddClass(*c, "no active schema");
4465 }
4466
4467 if(p_classes.find(c->get_name().c_str()) != p_classes.end()) {
4468 throw CannotAddClass(*c, "class already exists");
4469 }
4470
4471 c->p_kernel = this;
4472 c->p_file = p_active_schema;
4473 c->p_file->p_is_updated = true;
4474
4475 p_classes[c->get_name().c_str()] = c;
4476
4477 try {
4478 registrate_all_classes(false);
4479 }
4480 catch(exception& ex) {
4481 throw CannotAddClass(*c, ex);
4482 }
4483
4484 if(OksClass::create_notify_fn) (*OksClass::create_notify_fn)(c);
4485}
4486
4487void
4488OksKernel::k_remove(OksClass *c)
4489{
4490 TLOG_DEBUG(4) << "enter for class \"" << c->get_name() << '\"';
4491
4492 if(OksClass::delete_notify_fn) (*OksClass::delete_notify_fn)(c);
4493
4494 p_classes.erase(c->get_name().c_str());
4495}
4496
4497
4498void
4499OksKernel::registrate_all_classes(bool skip_registered)
4500{
4501 std::unique_lock lock(p_schema_mutex); // protect schema and all objects from changes
4502
4503 if(size_t num_of_classes = p_classes.size()) {
4504 OksClass ** table = new OksClass * [num_of_classes];
4505 size_t array_size = num_of_classes * sizeof(OksClass*) / sizeof(wchar_t); // is used by wmemset() to init above array with NULLs
4506
4507 OksClass::Map::iterator i = p_classes.begin();
4508 unsigned long idx(0);
4509
4510 for(; i != p_classes.end(); ++i) i->second->registrate_class(skip_registered);
4511
4512 // create lists of sub-classes
4513 // note: do not use unscalable OksClass::create_sub_classes()
4514 for(i = p_classes.begin(); i != p_classes.end(); ++i) {
4515 OksClass * c(i->second);
4516
4517 if(!c->p_all_sub_classes) c->p_all_sub_classes = new OksClass::FList();
4518 else c->p_all_sub_classes->clear();
4519
4520 c->p_id = idx++;
4521 }
4522
4523 for(i = p_classes.begin(); i != p_classes.end(); ++i) {
4524 OksClass * c(i->second);
4525 if(const OksClass::FList * scl = c->p_all_super_classes) {
4526 for(OksClass::FList::const_iterator j = scl->begin(); j != scl->end(); ++j) {
4527 (*j)->p_all_sub_classes->push_back(c);
4528 }
4529 }
4530 }
4531
4532 if(get_test_duplicated_objects_via_inheritance_mode() && !get_allow_duplicated_objects_mode()) {
4533 for(i = p_classes.begin(); i != p_classes.end(); ++i) {
4534 OksClass *c(i->second);
4535 if(!c->get_is_abstract()) {
4536 if(const OksClass::FList * spc = c->all_super_classes()) {
4537 if(!spc->empty()) {
4538 wmemset(reinterpret_cast<wchar_t *>(table), 0, array_size); // [re-]set table by NULLs
4539 for(OksClass::FList::const_iterator j1 = spc->begin(); j1 != spc->end(); ++j1) {
4540 if(const OksClass::FList * sbc = (*j1)->all_sub_classes()) {
4541 for(OksClass::FList::const_iterator j2 = sbc->begin(); j2 != sbc->end(); ++j2) {
4542 OksClass *c2(*j2);
4543 if((c2 != c) && !c2->get_is_abstract()) {
4544 table[c2->p_id] = c2;
4545 }
4546 }
4547 }
4548 }
4549
4550 unsigned int count(0);
4551 for(unsigned int x = 0; x < num_of_classes; ++x) {
4552 if(table[x]) count++;
4553 }
4554
4555 if(count) {
4556 std::vector<OksClass *> * cih;
4557 if(!c->p_inheritance_hierarchy) {
4558 cih = c->p_inheritance_hierarchy = new std::vector<OksClass *>();
4559 }
4560 else {
4561 cih = c->p_inheritance_hierarchy;
4562 cih->clear();
4563 }
4564
4565 cih->reserve(count);
4566
4567 for(unsigned int x = 0; x < num_of_classes; ++x) {
4568 if(table[x]) cih->push_back(table[x]);
4569 }
4570 }
4571 }
4572 }
4573 }
4574 }
4575
4576#ifndef ERS_NO_DEBUG
4577 if(ers::debug_level() >= 2) {
4578 std::ostringstream s;
4579
4580 for(i = p_classes.begin(); i != p_classes.end(); ++i) {
4581 if(std::vector<OksClass *> * cis = i->second->p_inheritance_hierarchy) {
4582 s << " - class \'" << i->second->get_name() << "\' shares IDs with " << cis->size() << " classes: ";
4583 OksClass::Set sorted;
4584 for(std::vector<OksClass *>::const_iterator j = cis->begin(); j != cis->end(); ++j) {
4585 sorted.insert(*j);
4586 }
4587 for(OksClass::Set::const_iterator j = sorted.begin(); j != sorted.end(); ++j) {
4588 if(j != sorted.begin()) s << ", ";
4589 s << '\'' << (*j)->get_name() << '\'';
4590 }
4591 s << std::endl;
4592 }
4593 }
4594
4595 TLOG_DEBUG(2) << "Schema inheritance hierarchy used to test objects with equal IDs:\n" << s.str();
4596 }
4597#endif
4598 }
4599
4600 delete [] table;
4601
4602 k_check_bind_classes_status();
4603 }
4604}
4605
4606bool
4607OksKernel::is_dangling(OksObject *o) const
4608{
4609 OSK_PROFILING(OksProfiler::KernelIsDanglingObject, this)
4610 OSK_VERBOSE_REPORT("Enter OksKernel::is_dangling(OksObject *)")
4611
4612 bool not_found;
4613
4614 {
4615 std::lock_guard lock(p_objects_mutex);
4616 not_found = (p_objects.find(o) == p_objects.end());
4617 }
4618
4619 if(p_verbose) {
4620 std::string fname("OksKernel::is_dangling(");
4621
4622 if(not_found)
4623 fname += "?object?) returns true";
4624 else {
4625 fname += '\"';
4626 fname += o->GetId();
4627 fname += "\") returns false";
4628 }
4629
4630 OSK_VERBOSE_REPORT("LEAVE " << fname.c_str())
4631 }
4632
4633 return not_found;
4634}
4635
4636
4637bool
4638OksKernel::is_dangling(OksClass *c) const
4639{
4640 OSK_PROFILING(OksProfiler::KernelIsDanglingClass, this)
4641 OSK_VERBOSE_REPORT("Enter OksKernel::is_dangling(OksClass *)")
4642
4643 bool not_found = true;
4644
4645 {
4646 if(!p_classes.empty()) {
4647 for(OksClass::Map::const_iterator i = p_classes.begin(); i != p_classes.end(); ++i) {
4648 if(i->second == c) {
4649 not_found = false;
4650 break;
4651 }
4652 }
4653 }
4654 }
4655
4656 if(p_verbose) {
4657 std::string fname("OksKernel::is_dangling(");
4658
4659 if(not_found)
4660 fname += "?class?) returns true";
4661 else {
4662 fname += '\"';
4663 fname += c->get_name();
4664 fname += "\") returns false";
4665 }
4666
4667 OSK_VERBOSE_REPORT("LEAVE " << fname.c_str())
4668 }
4669
4670 return not_found;
4671}
4672
4673
4674void
4675OksKernel::get_all_classes(const std::vector<std::string>& names_in, ClassSet& classes_out) const
4676{
4677 for(std::vector<std::string>::const_iterator i = names_in.begin(); i != names_in.end(); ++i) {
4678 OksClass * c = find_class(*i);
4679 if(c != 0 && classes_out.find(c) == classes_out.end()) {
4680 classes_out.insert(c);
4681 if(const OksClass::FList * sc = c->all_sub_classes()) {
4682 for(OksClass::FList::const_iterator j = sc->begin(); j != sc->end(); ++j) {
4683 classes_out.insert(*j);
4684 }
4685 }
4686 }
4687 }
4688}
4689
4690
4691 // keep information about repository root
4692 // throw exception if TDAQ_DB_REPOSITORY or TDAQ_DB_USER_REPOSITORY are not defined
4693
4695
4696 ReposDirs(const char * op, const char * cwd, OksKernel * kernel);
4697 ~ReposDirs();
4698
4699 const char * p_repository_root;
4703 const char * p_dir;
4704
4705 static std::mutex p_mutex;
4706};
4707
4708
4709std::mutex ReposDirs::p_mutex;
4710
4711
4712ReposDirs::ReposDirs(const char * op, const char * cwd, OksKernel * kernel)
4713{
4714 p_repository_root = OksKernel::get_repository_root().c_str();
4715 if(OksKernel::get_repository_root().empty()) {
4716 throw RepositoryOperationFailed(op, "the repository-root is not set (check environment variable TDAQ_DB_REPOSITORY)");
4717 }
4718 else {
4719 p_repos_dir_len = strlen(p_repository_root);
4720 }
4721
4722 p_user_repository_root = kernel->get_user_repository_root().c_str();
4723 if(!*p_user_repository_root) {
4724 throw RepositoryOperationFailed(op, "the user-repository-root is not set (check environment variable TDAQ_DB_USER_REPOSITORY)");
4725 }
4726 else {
4727 p_user_dir_len = strlen(p_user_repository_root);
4728 }
4729
4730 p_mutex.lock();
4731
4732 if(strcmp(cwd, p_user_repository_root)) {
4733 if(int result = chdir(p_user_repository_root)) {
4734 p_mutex.unlock();
4735 std::ostringstream text;
4736 text << "chdir (\'" << p_user_repository_root << "\') failed with code " << result << ": " << strerror(errno);
4737 throw RepositoryOperationFailed(op, text.str());
4738 }
4739 p_dir = cwd;
4740 TLOG_DEBUG( 2 ) << "change cwd: \"" << p_user_repository_root << '\"' ;
4741 }
4742 else {
4743 p_dir = 0;
4744 TLOG_DEBUG( 2 ) << "cwd: \"" << p_user_repository_root << "\" is equal to target dir" ;
4745 }
4746}
4747
4748ReposDirs::~ReposDirs()
4749{
4750 if(p_dir) {
4751 if(int result = chdir(p_dir)) {
4752 Oks::error_msg("ReposDirs::~ReposDirs()")
4753 << "chdir (\'" << p_dir << "\') failed with code " << result << ": " << strerror(errno) << std::endl;
4754 }
4755 TLOG_DEBUG( 2 ) << "restore cwd: \"" << p_dir << '\"' ;
4756 }
4757
4758 p_mutex.unlock();
4759}
4760
4761
4762
4763 // keep information and delete output file on exit
4764
4766
4767 CommandOutput(const char * command_name, const OksKernel * k, std::string& cmd);
4769
4770 const std::string& file_name() const {return p_file_name;}
4771 std::string cat2str() const;
4772 std::string last_str() const;
4773
4774 void check_command_status(int status);
4775
4776 std::string p_file_name;
4777 std::string& p_command;
4778 const char * p_command_name;
4779
4780};
4781
4782CommandOutput::CommandOutput(const char *command_name, const OksKernel *k, std::string &cmd) :
4783p_command(cmd), p_command_name(command_name)
4784{
4785 p_file_name = get_temporary_dir() + '/' + command_name + '.' + std::to_string(getpid()) + ':' + std::to_string(reinterpret_cast<std::uintptr_t>(k)) + ".txt";
4786
4787 p_command.append(" &>");
4788 p_command.append(p_file_name);
4789}
4790
4792{
4793 unlink(p_file_name.c_str());
4794}
4795
4796std::string
4798{
4799 std::ifstream f(p_file_name.c_str());
4800 std::string out;
4801
4802 if(f) {
4803 std::filebuf * buf = f.rdbuf();
4804 while(true) {
4805 char c = buf->sbumpc();
4806 if(c == EOF) break;
4807 else out += c;
4808 }
4809 }
4810
4811 return out;
4812}
4813
4814std::string
4816{
4817 std::ifstream f(p_file_name.c_str());
4818 std::string lastline;
4819
4820 if (f.is_open())
4821 {
4822 f.seekg(-1, std::ios_base::end);
4823 if (f.peek() == '\n')
4824 {
4825 f.seekg(-1, std::ios_base::cur);
4826 for (int i = f.tellg(); i >= 0; i--)
4827 {
4828 if (f.peek() == '\n')
4829 {
4830 f.get();
4831 break;
4832 }
4833 f.seekg(i, std::ios_base::beg);
4834 }
4835 }
4836
4837 getline(f, lastline);
4838 }
4839 return lastline;
4840}
4841
4842
4843void
4845{
4846 if (status == -1)
4847 {
4848 std::ostringstream text;
4849 text << "cannot execute command \'" << p_command << "\': " << strerror(errno);
4850 throw RepositoryOperationFailed(p_command_name, text.str());
4851 }
4852 else if (int error_code = WEXITSTATUS(status))
4853 {
4854 std::ostringstream text;
4855 text << p_command.substr(0, p_command.find_first_of(' ')) << " exit with error code " << error_code << ", output:\n" << cat2str();
4856 throw RepositoryOperationFailed(p_command_name, text.str());
4857 }
4858}
4859
4860
4861void
4862OksKernel::k_rename_repository_file(OksFile * file_h, const std::string& new_name)
4863{
4864 if (file_h->p_oks_format == "schema")
4865 {
4866 remove_schema_file(file_h);
4867 file_h->rename(new_name);
4868 add_schema_file(file_h);
4869 }
4870 else
4871 {
4872 remove_data_file(file_h);
4873 file_h->rename(new_name);
4874 add_data_file(file_h);
4875 }
4876}
4877
4878
4879void
4880OksKernel::k_checkout_repository(const std::string& param, const std::string& val, const std::string& branch_name)
4881{
4882 if (get_repository_root().empty())
4883 throw RepositoryOperationFailed("checkout", "the repository root is not set");
4884
4885 ReposDirs reps("checkout", s_cwd, this);
4886
4887 std::string cmd("oks-checkout.sh");
4888
4889 if(get_verbose_mode()) { cmd += " -v"; }
4890
4891 cmd.append(" -u ");
4892 cmd.append(get_user_repository_root());
4893
4894 if (!param.empty())
4895 {
4896 cmd.append(" --");
4897 cmd.append(param);
4898 cmd.push_back(' ');
4899 cmd.push_back('\"');
4900 cmd.append(val);
4901 cmd.push_back('\"');
4902 }
4903
4904 if (!branch_name.empty())
4905 {
4906 cmd.append(" -b ");
4907 cmd.append(branch_name);
4908 }
4909
4910 auto start_usage = std::chrono::steady_clock::now();
4911
4912 if (!p_silence)
4913 {
4914 std::lock_guard lock(p_parallel_out_mutex);
4915 log_timestamp() << "[OKS checkout] => " << cmd << std::endl;
4916 }
4917
4918 CommandOutput cmd_out("oks-checkout", this, cmd);
4919 cmd_out.check_command_status(system(cmd.c_str()));
4920
4922
4923 if(!p_silence)
4924 {
4925 std::lock_guard lock(p_parallel_out_mutex);
4926 std::cout << cmd_out.cat2str();
4927 log_timestamp() << "[OKS checkout] => done in " << std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now()-start_usage).count() / 1000. << " ms" << std::endl;
4928 }
4929
4930 static std::string version_prefix("checkout oks version ");
4931 std::string version = cmd_out.last_str();
4932 std::string::size_type pos = version.find(version_prefix);
4933
4934 if (pos == 0)
4935 p_repository_version = version.substr(version_prefix.size());
4936 else
4937 throw RepositoryOperationFailed("checkout", "cannot read oks version");
4938}
4939
4940void
4950
4951void
4957
4958void
4959OksKernel::k_copy_repository(const std::string& source, const std::string& destination)
4960{
4961 std::string cmd("oks-copy.sh");
4962
4963 if (get_verbose_mode())
4964 cmd.append(" -v");
4965
4966 cmd.append(" -s ");
4967 cmd.append(source);
4968
4969 cmd.append(" -d ");
4970 cmd.append(destination);
4971
4972 cmd.append(" -c ");
4973 cmd.append(get_repository_version());
4974
4975 auto start_usage = std::chrono::steady_clock::now();
4976
4977 if(!p_silence) {
4978 std::lock_guard lock(p_parallel_out_mutex);
4979 log_timestamp() << "[OKS copy] => " << cmd << std::endl;
4980 }
4981
4982 CommandOutput cmd_out("oks-copy", this, cmd);
4983 cmd_out.check_command_status(system(cmd.c_str()));
4984
4985 if(!p_silence) {
4986 std::lock_guard lock(p_parallel_out_mutex);
4987 std::cout << cmd_out.cat2str();
4988 log_timestamp() << "[OKS copy] => done in " << std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now()-start_usage).count() / 1000. << " ms" << std::endl;
4989 }
4990}
4991
4992
4993void
4994OksKernel::update_repository(const std::string& param, const std::string& val, RepositoryUpdateType update_type)
4995{
4996 std::unique_lock lock(p_kernel_mutex);
4997
4998 if (OksKernel::get_repository_root().empty())
4999 throw RepositoryOperationFailed("update", "the repository-root is not set (check environment variable TDAQ_DB_REPOSITORY)");
5000
5001 if (get_user_repository_root().empty())
5002 throw RepositoryOperationFailed("update", "the user-repository-root is not set (check environment variable TDAQ_DB_USER_REPOSITORY)");
5003
5004 std::string cmd("oks-update.sh");
5005
5006 if (get_verbose_mode())
5007 cmd.append(" -v");
5008
5009 cmd.append(" -u ");
5010 cmd.append(get_user_repository_root());
5011
5012 if (update_type == OksKernel::DiscardChanges)
5013 cmd.append(" --force");
5014 else if (update_type == OksKernel::MergeChanges)
5015 cmd.append(" --merge");
5016
5017 if (!param.empty())
5018 {
5019 cmd.append(" --");
5020 cmd.append(param);
5021 cmd.push_back(' ');
5022 cmd.push_back('\"');
5023 cmd.append(val);
5024 cmd.push_back('\"');
5025 }
5026
5027 auto start_usage = std::chrono::steady_clock::now();
5028
5029 if (!p_silence) {
5030 std::lock_guard lock(p_parallel_out_mutex);
5031 log_timestamp() << "[OKS update] => " << cmd << std::endl;
5032 }
5033
5034 CommandOutput cmd_out("oks-update", this, cmd);
5035 cmd_out.check_command_status(system(cmd.c_str()));
5036
5037 p_repository_update_ts = std::time(0);
5038
5039 static std::string version_prefix("update oks version ");
5040 std::string version = cmd_out.last_str();
5041 std::string::size_type pos = version.find(version_prefix);
5042
5043 if(pos == 0)
5044 p_repository_version = version.substr(version_prefix.size());
5045 else
5046 throw RepositoryOperationFailed("commit", "cannot read oks version");
5047
5048 if (!p_silence)
5049 {
5050 std::lock_guard lock(p_parallel_out_mutex);
5051 std::cout << cmd_out.cat2str();
5052 log_timestamp() << "[OKS update] => done in " << std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now()-start_usage).count() / 1000. << " ms" << std::endl;
5053 }
5054}
5055
5056void
5057OksKernel::commit_repository(const std::string& comments, const std::string& credentials)
5058{
5059 std::unique_lock lock(p_kernel_mutex);
5060
5061 if (OksKernel::get_repository_root().empty())
5062 throw RepositoryOperationFailed("commit", "the repository-root is not set (check environment variable TDAQ_DB_REPOSITORY)");
5063
5064 if (get_user_repository_root().empty())
5065 throw RepositoryOperationFailed("commit", "the user-repository-root is not set (check environment variable TDAQ_DB_USER_REPOSITORY)");
5066
5067 std::string cmd("oks-commit.sh");
5068
5069 if (get_verbose_mode())
5070 cmd.append(" -v");
5071
5072 cmd.append(" -u ");
5073 cmd.append(get_user_repository_root());
5074
5075 if(comments.empty() || std::all_of(comments.begin(), comments.end(), [](char c) { return std::isspace(c); }))
5076 throw RepositoryOperationFailed("commit", "the commit message may not be empty");
5077
5078 const std::string log_file_name(get_temporary_dir() + '/' + "oks-commit-msg." + std::to_string(getpid()) + ':' + std::to_string(reinterpret_cast<std::uintptr_t>(this)) + ".txt");
5079
5080 try
5081 {
5082 std::ofstream f(log_file_name);
5083
5084 f.exceptions(std::ostream::failbit | std::ostream::badbit);
5085
5086 if (!f)
5087 throw std::runtime_error("create message file failed");
5088
5089 f << comments;
5090 f.close();
5091 }
5092 catch (const std::exception& ex)
5093 {
5094 std::ostringstream ss;
5095 ss << "cannot write to file \'" << log_file_name << "\' to save commit message: " << ex.what();
5096 throw RepositoryOperationFailed("commit", ss.str());
5097 }
5098
5099 cmd.append(" -f \'");
5100 cmd.append(log_file_name);
5101 cmd.push_back('\'');
5102
5103 // if (!credentials.empty())
5104 // {
5105 // try
5106 // {
5107 // auto token = daq::tokens::verify(credentials);
5108
5109 // const std::string author = token.get_subject();
5110
5111 // std::string email;
5112
5113 // if (token.has_payload_claim("email"))
5114 // email = token.get_payload_claim("email").as_string();
5115
5116 // if (email.empty())
5117 // email = author + '@' + get_domain_name();
5118
5119 // OksSystem::User user(author);
5120
5121 // cmd.append(" -n \'");
5122 // cmd.append(user.real_name());
5123 // cmd.append("\' -e \'");
5124 // cmd.append(email);
5125 // cmd.push_back('\'');
5126
5127 // static std::once_flag flag;
5128
5129 // std::call_once(flag, []()
5130 // {
5131 // const char * opt = " -o SendEnv=OKS_COMMIT_USER";
5132
5133 // std::string val;
5134
5135 // if (const char * s = getenv("GIT_SSH_COMMAND"))
5136 // {
5137 // if (strstr(s, opt) == nullptr)
5138 // val = s;
5139 // }
5140 // else
5141 // {
5142 // val = "ssh";
5143 // }
5144
5145 // if (!val.empty())
5146 // {
5147 // val.append(opt);
5148 // setenv("GIT_SSH_COMMAND", val.c_str(), 1);
5149 // }
5150 // }
5151 // );
5152
5153 // setenv("OKS_COMMIT_USER", credentials.c_str(), 1);
5154 // }
5155 // catch(const ers::Issue& ex)
5156 // {
5157 // std::ostringstream text;
5158 // text << '\t' << ex;
5159 // throw RepositoryOperationFailed("commit", text.str());
5160 // }
5161 // }
5162
5163 auto start_usage = std::chrono::steady_clock::now();
5164
5165 if (!p_silence)
5166 {
5167 std::lock_guard lock(p_parallel_out_mutex);
5168 log_timestamp() << "[OKS commit] => " << cmd << std::endl;
5169 }
5170
5171 CommandOutput cmd_out("oks-commit", this, cmd);
5172
5173 int status = system(cmd.c_str());
5174
5175 unlink(log_file_name.c_str());
5176
5177 if (!credentials.empty())
5178 unsetenv("OKS_COMMIT_USER");
5179
5180 cmd_out.check_command_status(status);
5181
5182 p_repository_update_ts = std::time(0);
5183
5184 if (!p_silence)
5185 {
5186 std::lock_guard lock(p_parallel_out_mutex);
5187 std::cout << cmd_out.cat2str();
5188 log_timestamp() << "[OKS commit] => done in " << std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now()-start_usage).count() / 1000. << " ms" << std::endl;
5189 }
5190
5191 static std::string version_prefix("commit oks version ");
5192 std::string version = cmd_out.last_str();
5193 std::string::size_type pos = version.find(version_prefix);
5194
5195 if(pos == 0)
5196 p_repository_version = version.substr(version_prefix.size());
5197 else
5198 throw RepositoryOperationFailed("commit", "cannot read oks version");
5199}
5200
5201void
5202OksKernel::tag_repository(const std::string& tag)
5203{
5204 std::unique_lock lock(p_kernel_mutex);
5205
5206 if (OksKernel::get_repository_root().empty())
5207 throw RepositoryOperationFailed("tag", "the repository-root is not set (check environment variable TDAQ_DB_REPOSITORY)");
5208
5209 if (get_user_repository_root().empty())
5210 throw RepositoryOperationFailed("tag", "the user-repository-root is not set (check environment variable TDAQ_DB_USER_REPOSITORY)");
5211
5212 std::string cmd("oks-tag.sh");
5213
5214 if (get_verbose_mode())
5215 cmd.append(" -v");
5216
5217 cmd.append(" -u ");
5218 cmd.append(get_user_repository_root());
5219
5220 if(tag.empty() || std::all_of(tag.begin(), tag.end(), [](char c) { return std::isspace(c); }))
5221 throw RepositoryOperationFailed("tag", "the tag may not be empty");
5222
5223 cmd.append(" -t \'");
5224 cmd.append(tag);
5225 cmd.append("\' -c \'");
5226 cmd.append(get_repository_version());
5227 cmd.push_back('\'');
5228
5229 auto start_usage = std::chrono::steady_clock::now();
5230
5231 if (!p_silence)
5232 {
5233 std::lock_guard lock(p_parallel_out_mutex);
5234 log_timestamp() << "[OKS tag] => " << cmd << std::endl;
5235 }
5236
5237 CommandOutput cmd_out("oks-tag", this, cmd);
5238 cmd_out.check_command_status(system(cmd.c_str()));
5239
5240 if (!p_silence)
5241 {
5242 std::lock_guard lock(p_parallel_out_mutex);
5243 std::cout << cmd_out.cat2str();
5244 log_timestamp() << "[OKS tag] => done in " << std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now()-start_usage).count() / 1000. << " ms" << std::endl;
5245 }
5246}
5247
5248std::list<std::string>
5249OksKernel::get_repository_versions_diff(const std::string& sha1, const std::string& sha2)
5250{
5251 std::unique_lock lock(p_kernel_mutex);
5252
5253 if (OksKernel::get_repository_root().empty())
5254 throw RepositoryOperationFailed("diff", "the repository-root is not set (check environment variable TDAQ_DB_REPOSITORY)");
5255
5256 if (get_user_repository_root().empty())
5257 throw RepositoryOperationFailed("diff", "the user-repository-root is not set (check environment variable TDAQ_DB_USER_REPOSITORY)");
5258
5259 std::string cmd("oks-diff.sh");
5260
5261 if (get_verbose_mode())
5262 cmd.append(" -v");
5263
5264 cmd.append(" -u ");
5265 cmd.append(get_user_repository_root());
5266
5267 if(!sha1.empty() && !sha2.empty())
5268 {
5269 cmd.append(" --sha ");
5270 cmd.append(sha1);
5271 cmd.push_back(' ');
5272 cmd.append(sha2);
5273 }
5274 else
5275 {
5276 cmd.append(" --unmerged");
5277 }
5278
5279 CommandOutput cmd_out("oks-diff", this, cmd);
5280 cmd_out.check_command_status(system(cmd.c_str()));
5281
5282 std::string output = cmd_out.cat2str();
5283
5284 if(!p_silence) {
5285 std::lock_guard lock(p_parallel_out_mutex);
5286 std::cout << output << "[OKS get_diff] => done" << std::endl;
5287 }
5288
5289 std::istringstream s(output);
5290
5291 std::string line;
5292 bool found = false;
5293
5294 const char * diff_pattern = "git diff --name-only ";
5295
5296 while (std::getline(s, line))
5297 if (line.find(diff_pattern) == 0)
5298 {
5299 found = true;
5300 break;
5301 }
5302
5303 if (found == false)
5304 {
5305 std::ostringstream text;
5306 text << "cannot find \"" << diff_pattern << "\" pattern in output of oks-diff.sh:\n" << output;
5307 throw RepositoryOperationFailed("get_diff", text.str());
5308 }
5309
5310 std::list<std::string> result;
5311
5312 while (std::getline(s, line))
5313 result.push_back(line);
5314
5315 return result;
5316}
5317
5318static bool
5319check_relevant(const std::set<std::string>& loaded_files, const OksRepositoryVersion& ver)
5320{
5321 for (const auto& x : ver.m_files)
5322 if (loaded_files.find(x) != loaded_files.end())
5323 return true;
5324
5325 return false;
5326}
5327
5328std::vector<OksRepositoryVersion>
5329OksKernel::get_repository_versions(bool skip_irrelevant, const std::string& command_line)
5330{
5331 std::string cmd("oks-log.sh");
5332
5333 if (get_verbose_mode())
5334 cmd.append(" -v");
5335
5336 cmd.append(" -u ");
5337 cmd.append(get_user_repository_root());
5338
5339 cmd.append(command_line);
5340
5341 CommandOutput cmd_out("oks-log", this, cmd);
5342 cmd_out.check_command_status(system(cmd.c_str()));
5343
5344 std::string output = cmd_out.cat2str();
5345
5346 std::istringstream s(output);
5347
5348 std::string line;
5349 bool found = false;
5350
5351 while (std::getline(s, line))
5352 if (line.find("git log ") == 0)
5353 {
5354 found = true;
5355 break;
5356 }
5357
5358 if (found == false)
5359 {
5360 std::ostringstream text;
5361 text << "cannot find \"git log\" pattern in output of oks-log.sh:\n" << output;
5362 throw RepositoryOperationFailed("log", text.str());
5363 }
5364
5365 std::set<std::string> loaded_files;
5366
5367 if (skip_irrelevant)
5368 {
5369 std::size_t len = get_user_repository_root().size()+1;
5370
5371 for (const auto& x : p_schema_files)
5372 loaded_files.emplace(x.first->substr(len));
5373
5374 for (const auto& x : p_data_files)
5375 loaded_files.emplace(x.first->substr(len));
5376 }
5377
5378 std::vector<OksRepositoryVersion> vec;
5380
5381 found = false;
5382
5383 while (std::getline(s, line))
5384 {
5385 if (found == false)
5386 {
5387 ver.clear();
5388
5389 std::string::size_type idx1 = 0;
5390 std::string::size_type idx2 = line.find('|');
5391
5392 if (idx2 != std::string::npos)
5393 {
5394 ver.m_commit_hash = line.substr(idx1, idx2);
5395 idx1 = idx2+1;
5396 idx2 = line.find('|', idx1);
5397
5398 if (idx2 != std::string::npos)
5399 {
5400 ver.m_user = line.substr(idx1, idx2-idx1);
5401 idx1 = idx2+1;
5402 idx2 = line.find('|', idx1);
5403
5404 if (idx2 != std::string::npos)
5405 {
5406 ver.m_date = std::stol(line.substr(idx1, idx2-idx1));
5407 ver.m_comment = line.substr(idx2+1);
5408 }
5409 }
5410 }
5411
5412
5413 if (!ver.m_comment.empty())
5414 found = true;
5415 else
5416 {
5417 std::ostringstream text;
5418 text << "unexpected line \"" << line << "\" in output of oks-log.sh:\n" << output;
5419 throw RepositoryOperationFailed("log", text.str());
5420 }
5421 }
5422 else
5423 {
5424 if (line.empty())
5425 {
5426 found = false;
5427
5428 if (skip_irrelevant && check_relevant(loaded_files, ver) == false)
5429 continue;
5430
5431 vec.emplace_back(ver);
5432 }
5433 else
5434 {
5435 ver.m_files.push_back(line);
5436 }
5437 }
5438 }
5439
5440 if (found)
5441 {
5442 if (skip_irrelevant == false || check_relevant(loaded_files, ver))
5443 vec.emplace_back(ver);
5444 }
5445
5446 return vec;
5447}
5448
5449std::vector<OksRepositoryVersion>
5450OksKernel::get_repository_versions_by_hash(bool skip_irrelevant, const std::string& since, const std::string& until)
5451{
5452 std::string command_line;
5453
5454 if(!since.empty() && !until.empty())
5455 {
5456 command_line.push_back(' ');
5457 command_line.append(since);
5458 command_line.append("..");
5459 command_line.append(until);
5460 }
5461 else if(!since.empty())
5462 {
5463 command_line.push_back(' ');
5464 command_line.append(since);
5465 command_line.append("..origin/master");
5466 }
5467 else if(!until.empty())
5468 {
5469 command_line.append(" ..");
5470 command_line.append(until);
5471 }
5472
5473 return get_repository_versions(skip_irrelevant, command_line);
5474}
5475
5476static std::string
5477replace_datetime_spaces(const std::string& in)
5478{
5479 std::string out(in);
5480 std::replace(out.begin(), out.end(), ' ', 'T');
5481 return out;
5482}
5483
5484
5485std::vector<OksRepositoryVersion>
5486OksKernel::get_repository_versions_by_date(bool skip_irrelevant, const std::string& since, const std::string& until)
5487{
5488 std::string command_line;
5489
5490 if(!since.empty())
5491 {
5492 command_line.append(" --since ");
5493 command_line.append(replace_datetime_spaces(since));
5494 }
5495
5496 if(!until.empty())
5497 {
5498 command_line.append(" --until ");
5499 command_line.append(replace_datetime_spaces(until));
5500 }
5501
5502 command_line.append(" origin/master");
5503
5504 return get_repository_versions(skip_irrelevant, command_line);
5505}
5506
5507
5508std::string
5510{
5511 std::unique_lock lock(p_kernel_mutex);
5512
5513 if (OksKernel::get_repository_root().empty())
5514 throw RepositoryOperationFailed("status", "the repository-root is not set (check environment variable TDAQ_DB_REPOSITORY)");
5515
5516 if (get_user_repository_root().empty())
5517 throw RepositoryOperationFailed("status", "the user-repository-root is not set (check environment variable TDAQ_DB_USER_REPOSITORY)");
5518
5519 std::string cmd("oks-version.sh");
5520
5521 cmd.append(" -u ");
5522 cmd.append(get_user_repository_root());
5523
5524 CommandOutput cmd_out("oks-status", this, cmd);
5525 cmd_out.check_command_status(system(cmd.c_str()));
5526
5527 std::string output = cmd_out.cat2str();
5528
5529 static std::string version_prefix("oks version ");
5530 std::string version = cmd_out.last_str();
5531 std::string::size_type pos = version.find(version_prefix);
5532
5533 if (pos == 0)
5534 p_repository_version = version.substr(version_prefix.size());
5535 else
5536 {
5537 std::ostringstream text;
5538 text << "cannot read oks version from oks-version.sh output: \"" << output << '\"';
5539 throw RepositoryOperationFailed("get version", text.str().c_str());
5540 }
5541
5542 return p_repository_version;
5543}
5544
5545
5546void OksKernel::get_updated_repository_files(std::set<std::string>& updated, std::set<std::string>& added, std::set<std::string>& removed)
5547{
5548 std::unique_lock lock(p_kernel_mutex);
5549
5550 if (OksKernel::get_repository_root().empty())
5551 throw RepositoryOperationFailed("status", "the repository-root is not set (check environment variable TDAQ_DB_REPOSITORY)");
5552
5553 if (get_user_repository_root().empty())
5554 throw RepositoryOperationFailed("status", "the user-repository-root is not set (check environment variable TDAQ_DB_USER_REPOSITORY)");
5555
5556 std::string cmd("oks-status.sh");
5557
5558 cmd.append(" -u ");
5559 cmd.append(get_user_repository_root());
5560
5561 CommandOutput cmd_out("oks-status", this, cmd);
5562 cmd_out.check_command_status(system(cmd.c_str()));
5563
5564 std::string output = cmd_out.cat2str();
5565
5566 if(p_verbose) {
5567 std::lock_guard lock(p_parallel_out_mutex);
5568 std::cout << output << "[OKS get_status] => done" << std::endl;
5569 }
5570
5571 std::istringstream s(output);
5572
5573 std::string line;
5574 bool found = false;
5575
5576 const char * diff_pattern = "git status --porcelain";
5577
5578 while (std::getline(s, line))
5579 if (line.find(diff_pattern) == 0)
5580 {
5581 found = true;
5582 break;
5583 }
5584
5585 if (found == false)
5586 {
5587 std::ostringstream text;
5588 text << "cannot find \"" << diff_pattern << "\" pattern in output of oks-status.sh:\n" << output;
5589 throw RepositoryOperationFailed("status", text.str());
5590 }
5591
5592 std::list<std::string> result;
5593
5594 while (std::getline(s, line))
5595 {
5596 if (line.find(" D ") == 0)
5597 removed.insert(line.substr(3));
5598 else if (line.find(" M ") == 0)
5599 updated.insert(line.substr(3));
5600 else if (line.find("?? ") == 0)
5601 added.insert(line.substr(3));
5602 else
5603 {
5604 std::ostringstream text;
5605 text << "unexpected output \"" << line << "\" in output of oks-status.sh:\n" << output;
5606 throw RepositoryOperationFailed("status", text.str());
5607 }
5608 }
5609}
5610
5611
5612// /* PAM authentication */
5613
5614// struct creds_pam_t {
5615// const char * user;
5616// const char * pwd;
5617// };
5618
5619// static int
5620// get_credentials_pam (int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
5621// {
5622// struct creds_pam_t * creds = (struct creds_pam_t *)appdata_ptr;
5623// struct pam_response * buf = (struct pam_response *)(malloc(num_msg * sizeof (struct pam_response))); /* released by PAM */
5624
5625// for(int k = 0; k < num_msg; ++ k) {
5626// const size_t STRING_SIZE = 1024;
5627// char * out;
5628// int error = 0;
5629
5630// buf[k].resp = (char*)malloc(STRING_SIZE);
5631// out = buf[k].resp;
5632// out[0] = 0;
5633
5634// if(msg[k]->msg) {
5635// if(!strcmp(msg[k]->msg, "login:") || !strcmp(msg[k]->msg, "login: ")) {
5636// TLOG_DEBUG( 1 ) << "process \'login\' PAM credential of user " << creds->user ;
5637// strncpy(out, creds->user, STRING_SIZE);
5638// }
5639// else if(!strcmp(msg[k]->msg, "Password: ") || !strcmp(msg[k]->msg, "LDAP Password: ")) {
5640// TLOG_DEBUG( 1 ) << "process \'password\' PAM credential of user " << creds->user ;
5641// strncpy (out, creds->pwd, STRING_SIZE);
5642// }
5643// else {
5644// error = 1;
5645// }
5646// }
5647
5648// if(error != 0) {
5649// std::cerr << "ERROR: unexpected request in process PAM credential: " << msg[k]->msg << std::endl;
5650// for (int j = 0; j <= k; ++ j) {
5651// free(buf[k].resp);
5652// }
5653// free(buf);
5654// return PAM_CONV_ERR;
5655// }
5656
5657// out[STRING_SIZE - 1] = 0;
5658// buf[k].resp_retcode = 0;
5659// }
5660
5661// *resp = buf;
5662
5663// return PAM_SUCCESS;
5664// }
5665
5666
5667// void
5668// OksKernel::validate_credentials(const char * user, const char * passwd)
5669// {
5670// if(!user || !*user) {
5671// throw oks::AuthenticationFailure("user name is not given");
5672// }
5673
5674// if(!passwd || !*passwd) {
5675// throw oks::AuthenticationFailure("user password is not given");
5676// }
5677
5678// struct creds_pam_t creds = {user, passwd};
5679// struct pam_conv conv = {get_credentials_pam, &creds};
5680
5681// const char *service = "system-auth";
5682// pam_handle_t * pamh = 0;
5683
5684// int retval = pam_start(service, 0, &conv, &pamh);
5685// if (retval != PAM_SUCCESS) {
5686// std::ostringstream text;
5687// text << "pam_start() failed for service \'" << service << "\': " << pam_strerror (pamh, retval);
5688// throw oks::AuthenticationFailure(text.str());
5689// }
5690
5691// retval = pam_authenticate (pamh, 0);
5692// if (retval == PAM_SUCCESS) {
5693// TLOG_DEBUG( 1 ) << "user " << user << " was authenticated" ;
5694// }
5695// else {
5696// std::ostringstream text;
5697// text << "failed to authenticate user " << user << ": " << pam_strerror (pamh, retval);
5698// throw oks::AuthenticationFailure(text.str());
5699// }
5700
5701// retval = pam_end (pamh,retval);
5702// if (retval != PAM_SUCCESS) {
5703// std::ostringstream text;
5704// text << "pam_end() failed: " << pam_strerror (pamh, retval);
5705// throw oks::AuthenticationFailure(text.str());
5706// }
5707// }
5708
5709void
5711{
5712 std::ostringstream out;
5713
5714 for (auto & c : p_classes)
5715 c.second->check_relationships(out, true);
5716
5717 p_bind_classes_status = out.str();
5718}
5719
5720
5721std::ostream&
5723{
5724 auto now(std::chrono::system_clock::now());
5725 auto time_since_epoch(now.time_since_epoch());
5726 auto seconds_since_epoch(std::chrono::duration_cast<std::chrono::seconds>(time_since_epoch));
5727
5728 std::time_t now_t(std::chrono::system_clock::to_time_t(std::chrono::system_clock::time_point(seconds_since_epoch)));
5729
5730 char buff[128];
5731 std::size_t len = std::strftime(buff, 128 - 16, "%Y-%b-%d %H:%M:%S.", std::localtime(&now_t));
5732 sprintf(buff+len, "%03d ", (int)(std::chrono::duration_cast<std::chrono::milliseconds>(time_since_epoch).count() - seconds_since_epoch.count()*1000));
5733
5734 std::ostream& s (severity <= Warning ? std::cerr : std::cout);
5735
5736 s << buff << (severity == Error ? "ERROR " : severity == Warning ? "WARNING " : severity == Debug ? "DEBUG " : "");
5737
5738 return s;
5739}
5740
5741} // namespace oks
5742} // namespace dunedaq
std::ostream & operator<<(std::ostream &stream, const OksSystem::File &file)
Definition File.cpp:852
#define ERS_DECLARE_ISSUE(namespace_name, class_name, message, attributes)
#define ERS_HERE
static const std::string & full_local_name()
fully qualified local host name
Definition Host.cpp:211
static const LocalHost * instance()
pointer to the singleton local host
Definition Host.cpp:221
uid_t identity() const
user-id
Definition User.cpp:96
const std::string & name_safe() const
username - no exception
Definition User.cpp:204
Failed backup file.
Definition kernel.hpp:227
static std::string fill(const std::string &name, const std::string &reason) noexcept
Cannot create file.
Definition kernel.hpp:157
static std::string fill(const char *prefix, const char *item, const std::string &name, const std::string &reason) noexcept
static std::string fill(const char *prefix, const std::string &name) noexcept
Cannot open file.
Definition kernel.hpp:135
static std::string fill(const char *prefix, const std::string &name, const std::string &reason) noexcept
Failed to set active file.
Definition kernel.hpp:250
static std::string fill(const char *item, const std::string &name, const std::string &reason) noexcept
static std::string fill(const OksClass *c, const OksObject *o, const OksFile &file, const std::string &reason) noexcept
Failed write to file.
Definition kernel.hpp:201
static std::string fill(const char *prefix, const char *item, const std::string &name, const std::string &reason) noexcept
Failed add new class.
Definition kernel.hpp:302
static std::string fill(const OksClass &c, const std::string &reason) noexcept
Failed resolve path.
Definition kernel.hpp:345
static std::string fill(const std::string &path, int error_code) noexcept
Cannot load file.
Definition kernel.hpp:56
static std::string fill(const std::string &item, const std::string &name, const std::string &reason) noexcept
Cannot re-load files.
Definition kernel.hpp:82
static std::string fill(const std::string &names, const std::string &reason) noexcept
static void remove(const std::string &path)
Definition kernel.cpp:78
void erase(const std::string &path)
Definition kernel.cpp:98
std::set< std::string > s_git_folders
Definition kernel.cpp:107
void insert(const std::string &path)
Definition kernel.cpp:91
The OKS class.
Definition class.hpp:200
bool get_is_abstract() const noexcept
Definition class.hpp:384
std::set< OksClass *, SortByName > Set
Definition class.hpp:222
unsigned int p_id
Definition class.hpp:950
static NotifyFN create_notify_fn
Definition class.hpp:966
static NotifyFN delete_notify_fn
Definition class.hpp:968
static ChangeNotifyFN change_notify_fn
Definition class.hpp:967
std::list< OksClass *, boost::fast_pool_allocator< OksClass * > > FList
Definition class.hpp:235
std::map< const char *, OksClass *, SortStr > Map
Definition class.hpp:233
Provides interface to the OKS XML schema and data files.
Definition file.hpp:340
std::unordered_map< OksFile *, Set, oks::hash_file_ptr, oks::equal_file_ptr > IMap
Definition file.hpp:370
std::string p_oks_format
Definition file.hpp:744
const std::string & get_logical_name() const
Definition file.hpp:566
void unlock()
Unlock OKS file.
Definition file.cpp:1143
void get_all_include_files(const OksKernel *kernel, std::set< OksFile * > &out)
Get all include files.
Definition file.cpp:1466
void lock()
Lock OKS file.
Definition file.cpp:1012
void write(OksXmlOutputStream &)
Definition file.cpp:767
std::string p_created_by
Definition file.hpp:747
std::unordered_set< OksFile *, oks::hash_file_ptr, oks::equal_file_ptr > Set
Definition file.hpp:369
bool is_locked() const
Return lock status of OKS file.
Definition file.hpp:616
std::string p_short_name
Definition file.hpp:739
const std::string & get_type() const
Definition file.hpp:567
const std::string & get_short_file_name() const
Definition file.hpp:518
std::list< std::string > p_list_of_include_files
Definition file.hpp:758
boost::posix_time::ptime p_creation_time
Definition file.hpp:748
void update_status_of_file(bool update_local=true, bool update_repository=true)
Update status of file.
Definition file.cpp:1391
std::string p_full_name
Definition file.hpp:740
const std::string & get_oks_format() const
Definition file.hpp:568
OksFile * check_parent(const OksFile *parent_h)
Set given parent, if this file is not yet included.
Definition file.cpp:1183
const OksFile * p_included_by
Definition file.hpp:763
const std::string & get_full_file_name() const
Definition file.hpp:523
std::string p_created_on
Definition file.hpp:749
void rename(const std::string &short_name, const std::string &full_name)
Definition file.cpp:1449
std::map< const std::string *, OksFile *, SortByName > Map
Definition file.hpp:367
Provides interface to the OKS kernel.
Definition kernel.hpp:577
static const std::string & get_repository_mapping_dir()
Get OKS repository name.
Definition kernel.cpp:350
const std::string & get_user_repository_root() const
Get user OKS repository root.
Definition kernel.cpp:370
static std::string & get_host_name()
Get hostname of given process.
Definition kernel.cpp:429
static std::string & get_user_name()
Get username of given process.
Definition kernel.cpp:468
std::list< std::string > get_repository_versions_diff(const std::string &sha1, const std::string &sha2)
Definition kernel.cpp:5249
static char * s_cwd
Definition kernel.hpp:2063
void k_rename_repository_file(OksFile *file_h, const std::string &new_name)
Definition kernel.cpp:4862
static bool p_use_strict_repository_paths
Definition kernel.hpp:2028
void update_repository(const std::string &hash_val, RepositoryUpdateType update_type)
Update user repository files from origin by hash.
Definition kernel.hpp:1608
static std::mutex p_parallel_out_mutex
Definition kernel.hpp:2046
std::string p_repository_version
Definition kernel.hpp:2035
void k_check_bind_classes_status() const noexcept
Definition kernel.cpp:5710
static std::string p_repository_root
Definition kernel.hpp:2030
std::time_t p_repository_checkout_ts
Definition kernel.hpp:2036
OksClass::Map p_classes
Definition kernel.hpp:2066
std::map< std::string, T > map_str_t
Definition kernel.hpp:586
void set_user_repository_root(const std::string &path, const std::string &version="")
Set user OKS repository root.
Definition kernel.cpp:389
void add_data_file(OksFile *)
Definition kernel.cpp:3767
OksFile::Map p_schema_files
Definition kernel.hpp:2050
OksProfiler * profiler
Definition kernel.hpp:2070
bool get_verbose_mode() const
Get status of verbose mode. The method returns true, if the verbose mode is switched 'On'.
Definition kernel.hpp:665
std::string create_user_repository_dir()
Definition kernel.cpp:2225
OksKernel(bool silence_mode=false, bool verbose_mode=false, bool profiling_mode=false, bool allow_repository=true, const char *version=nullptr, std::string branch_name="")
Constructor to build OksKernel object.
Definition kernel.cpp:733
std::list< std::string > p_repository_dirs
Definition kernel.hpp:2061
void remove_user_repository_dir()
Definition kernel.cpp:4941
std::vector< OksRepositoryVersion > get_repository_versions(bool skip_irrelevant, const std::string &command_line)
Return repository versions.
Definition kernel.cpp:5329
std::vector< OksRepositoryVersion > get_repository_versions_by_date(bool skip_irrelevant=true, const std::string &since="", const std::string &until="")
Return repository versions between timestamps.
Definition kernel.cpp:5486
static std::string p_repository_mapping_dir
Definition kernel.hpp:2031
std::time_t p_repository_update_ts
Definition kernel.hpp:2037
static int p_threads_pool_size
Definition kernel.hpp:2074
void tag_repository(const std::string &tag)
Tag current state of repository.
Definition kernel.cpp:5202
void remove_schema_file(OksFile *)
Definition kernel.cpp:3785
void get_updated_repository_files(std::set< std::string > &updated, std::set< std::string > &added, std::set< std::string > &removed)
Get repository modified schema files.
Definition kernel.cpp:5546
void unset_repository_created()
Set repository created flag to false to avoid created repository removal in destructor;.
Definition kernel.cpp:4952
void k_copy_repository(const std::string &source, const std::string &destination)
Definition kernel.cpp:4959
friend class OksFile
Definition kernel.hpp:579
std::string p_bind_classes_status
Definition kernel.hpp:2077
static unsigned long p_count
Definition kernel.hpp:2068
std::string read_repository_version()
Read and return current repository version.
Definition kernel.cpp:5509
std::string p_user_repository_root
Definition kernel.hpp:2032
static bool p_skip_string_range
Definition kernel.hpp:2027
static const std::string & get_repository_root()
Get OKS repository root.
Definition kernel.cpp:304
void commit_repository(const std::string &comments, const std::string &credentials="")
Commit user modifications into repository.
Definition kernel.cpp:5057
const std::string & get_repository_version()
Definition kernel.hpp:936
static std::string get_tmp_file(const std::string &file_name)
Generates temporal file name.
Definition kernel.cpp:665
static const char * get_cwd()
Definition kernel.cpp:689
std::vector< OksRepositoryVersion > get_repository_versions_by_hash(bool skip_irrelevant=true, const std::string &sha1="", const std::string &sha2="")
Return repository versions between hash keys.
Definition kernel.cpp:5450
std::shared_mutex p_kernel_mutex
Definition kernel.hpp:2042
bool p_test_duplicated_objects_via_inheritance
Definition kernel.hpp:2025
void add_schema_file(OksFile *)
Definition kernel.cpp:3773
static const char * GetVersion()
Get OKS version. The method returns string containing CVS tag and date of OKS build.
Definition kernel.cpp:297
void remove_data_file(OksFile *)
Definition kernel.cpp:3779
static std::string & get_domain_name()
Get domain name of host.
Definition kernel.cpp:447
std::string insert_repository_dir(const std::string &dir, bool push_back=true)
Insert repository search directory.
Definition kernel.cpp:1398
void k_checkout_repository(const std::string &param, const std::string &val, const std::string &branch)
Check out repository files into local user directory.
Definition kernel.cpp:4880
OksFile::Map p_data_files
Definition kernel.hpp:2051
OksObject describes instance of OksClass.
Definition object.hpp:836
struct dunedaq::oks::OksObject::OksUid uid
std::unordered_map< const std::string *, OksObject *, oks::hash_str, oks::equal_str > Map
Definition object.hpp:865
OksData * GetRelationshipValue(const std::string &) const
Get value of relationship by name.
Definition object.cpp:2004
std::unordered_set< OksObject *, oks::hash_obj_ptr, oks::equal_obj_ptr > FSet
Definition object.hpp:866
std::map< const std::string *, OksObject *, SortById > SMap
Definition object.hpp:860
void unbind_file(const OksFile *)
Definition object.cpp:2825
const OksClass * GetClass() const
Definition object.hpp:990
void remove_RCR(OksObject *, const OksRelationship *) noexcept
Definition object.cpp:2602
const std::string & GetId() const
Definition object.hpp:995
void addJob(OksJob *job)
Definition pipeline.cpp:98
Class OKS string.
Definition object.hpp:369
void put_last_tag(const char *, size_t len)
Definition xml.cpp:204
String tokenizer.
Definition defs.hpp:58
const std::string next()
Definition time.cpp:189
static void substitute_variables(std::string &)
Definition kernel.cpp:570
static std::ostream & warning_msg(const char *)
Definition kernel.cpp:563
static bool real_path(std::string &, bool ignore_errors)
Definition kernel.cpp:594
static std::ostream & error_msg(const char *)
Definition kernel.cpp:556
Cannot commit, checkout or release files.
Definition kernel.hpp:108
static std::string fill(const char *op, const std::string &reason) noexcept
virtual const char * what() const noexcept
#define OSK_VERBOSE_REPORT(MSG)
Definition defs.hpp:88
#define OSK_PROFILING(FID, K)
Definition defs.hpp:97
static int64_t now()
#define TEST_PATH_TOKEN(path, file, msg)
Definition kernel.cpp:1644
#define TLOG_DEBUG(lvl,...)
Definition Logging.hpp:112
std::string const reason(ers::Issue const &)
bool cmp_str8n(const char *s1, const char s2[9])
Definition cstring.hpp:65
const std::string strerror(int error)
Convert C error number to string.
Definition kernel.cpp:114
static void create_updated_lists(const OksFile::Map &files, std::list< OksFile * > **ufs, std::list< OksFile * > **rfs, OksFile::FileStatus wu, OksFile::FileStatus wr)
Definition kernel.cpp:2981
bool cmp_str7n(const char *s1, const char s2[8])
Definition cstring.hpp:57
std::string make_fname(const char *f, size_t f_len, const std::string &file, bool *p, const OksFile *const *fh)
Definition kernel.cpp:512
static std::mutex s_get_cwd_mutex
Definition kernel.cpp:52
static bool check_relevant(const std::set< std::string > &loaded_files, const OksRepositoryVersion &ver)
Definition kernel.cpp:5319
std::ostream & log_timestamp(__LogSeverity__ severity=Log)
Definition kernel.cpp:5722
static std::string replace_datetime_spaces(const std::string &in)
Definition kernel.cpp:5477
static GitFoldersHolder s_git_folders
Definition kernel.cpp:110
static long get_file_length(std::ifstream &f)
Definition kernel.cpp:490
std::unordered_set< const OksClass *, oks::hash_class_ptr, oks::equal_class_ptr > ClassSet
Definition object.hpp:820
static void test_file_existence(const std::string &file_name, bool silence, const std::string &fname, const char *msg)
Definition kernel.cpp:1595
std::string mk_name_and_test(const std::string &name, const char *test, size_t test_len)
Definition kernel.cpp:1635
static bool _find_file(const OksFile::Map &files, const OksFile *f)
Definition kernel.cpp:3167
bool cmp_str6n(const char *s1, const char s2[6])
Definition cstring.hpp:49
bool cmp_str4n(const char *s1, const char s2[4])
Definition cstring.hpp:33
Including Qt Headers.
FELIX Initialization std::string initerror FELIX queue timed out
FELIX Initialization std::string initerror FELIX queue timed std::string queuename Unexpected chunk size
int debug_level()
Definition ers.hpp:66
void warning(const Issue &issue)
Definition ers.hpp:115
void error(const Issue &issue)
Definition ers.hpp:81
Definition __init__.py:1
void check_command_status(int status)
Definition kernel.cpp:4844
std::string last_str() const
Definition kernel.cpp:4815
std::string cat2str() const
Definition kernel.cpp:4797
const std::string & file_name() const
Definition kernel.cpp:4770
The struct OksAliasTable is used to support aliases.
Definition kernel.hpp:446
Struct OKS data information.
Definition object.hpp:342
the structure to pass common parameters to various read() methods of OksData and OksObject class
Definition object.hpp:449
void Set(int8_t c)
Definition object.hpp:543
union dunedaq::oks::OksData::Data data
enum dunedaq::oks::OksData::Type type
std::shared_ptr< OksXmlInputStream > m_xmls
Definition kernel.cpp:3152
OksLoadObjectsJob(OksKernel *kernel, OksFile *fp, std::shared_ptr< OksXmlInputStream > xmls, char format)
Definition kernel.cpp:3114
OksLoadObjectsJob(const OksLoadObjectsJob &)
OksLoadObjectsJob & operator=(const OksLoadObjectsJob &)
static const char symbols[]
Definition kernel.hpp:420
std::vector< std::string > m_files
Definition kernel.hpp:515
std::map< std::string, T > map_str_t
Definition oks_utils.hpp:34
void put(OksObject *obj)
Definition kernel.cpp:3176
std::vector< OksObject * > created
Definition oks_utils.hpp:37
std::map< const OksClass *, map_str_t< OksObject * > * > data
Definition oks_utils.hpp:36
const char * p_user_repository_root
Definition kernel.cpp:4700
const char * p_repository_root
Definition kernel.cpp:4699
static std::mutex p_mutex
Definition kernel.cpp:4705
Factory couldn t std::string alg_name Invalid configuration error
Definition Issues.hpp:34