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