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