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