Mercurial > hg > orthanc
comparison Core/Toolbox.cpp @ 2143:fd5875662670
creation of namespace SystemToolbox
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Wed, 09 Nov 2016 16:54:23 +0100 |
parents | a260a8ad83f1 |
children | 84d1d392a9ab |
comparison
equal
deleted
inserted
replaced
2142:5a8840920121 | 2143:fd5875662670 |
---|---|
34 #include "Toolbox.h" | 34 #include "Toolbox.h" |
35 | 35 |
36 #include "OrthancException.h" | 36 #include "OrthancException.h" |
37 #include "Logging.h" | 37 #include "Logging.h" |
38 | 38 |
39 #include <boost/algorithm/string/replace.hpp> | |
40 #include <boost/lexical_cast.hpp> | |
41 #include <boost/locale.hpp> | |
42 #include <boost/uuid/sha1.hpp> | |
43 | |
39 #include <string> | 44 #include <string> |
40 #include <stdint.h> | 45 #include <stdint.h> |
41 #include <string.h> | 46 #include <string.h> |
42 #include <boost/filesystem.hpp> | |
43 #include <boost/filesystem/fstream.hpp> | |
44 #include <boost/uuid/sha1.hpp> | |
45 #include <boost/lexical_cast.hpp> | |
46 #include <algorithm> | 47 #include <algorithm> |
47 #include <ctype.h> | 48 #include <ctype.h> |
48 | 49 |
49 #if BOOST_HAS_DATE_TIME == 1 | 50 #if BOOST_HAS_REGEX == 1 |
50 #include <boost/date_time/posix_time/posix_time.hpp> | 51 # include <boost/regex.hpp> |
51 #endif | 52 #endif |
52 | 53 |
53 #if BOOST_HAS_REGEX == 1 | 54 #if BOOST_HAS_LOCALE != 1 |
54 #include <boost/regex.hpp> | 55 # error Since version 0.7.6, Orthanc entirely relies on boost::locale |
55 #endif | 56 #endif |
56 | |
57 #if defined(_WIN32) | |
58 #include <windows.h> | |
59 #include <process.h> // For "_spawnvp()" and "_getpid()" | |
60 #else | |
61 #include <unistd.h> // For "execvp()" | |
62 #include <sys/wait.h> // For "waitpid()" | |
63 #endif | |
64 | |
65 #if defined(__APPLE__) && defined(__MACH__) | |
66 #include <mach-o/dyld.h> /* _NSGetExecutablePath */ | |
67 #include <limits.h> /* PATH_MAX */ | |
68 #endif | |
69 | |
70 #if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) | |
71 #include <limits.h> /* PATH_MAX */ | |
72 #include <signal.h> | |
73 #include <unistd.h> | |
74 #endif | |
75 | |
76 #if BOOST_HAS_LOCALE != 1 | |
77 #error Since version 0.7.6, Orthanc entirely relies on boost::locale | |
78 #endif | |
79 | |
80 #include <boost/locale.hpp> | |
81 | |
82 | 57 |
83 #if ORTHANC_ENABLE_MD5 == 1 | 58 #if ORTHANC_ENABLE_MD5 == 1 |
84 # include "../Resources/ThirdParty/md5/md5.h" | 59 # include "../Resources/ThirdParty/md5/md5.h" |
85 #endif | 60 #endif |
86 | |
87 | 61 |
88 #if ORTHANC_ENABLE_BASE64 == 1 | 62 #if ORTHANC_ENABLE_BASE64 == 1 |
89 # include "../Resources/ThirdParty/base64/base64.h" | 63 # include "../Resources/ThirdParty/base64/base64.h" |
90 #endif | 64 #endif |
91 | 65 |
108 // http://stackoverflow.com/a/1626302 | 82 // http://stackoverflow.com/a/1626302 |
109 | 83 |
110 extern "C" | 84 extern "C" |
111 { | 85 { |
112 #ifdef WIN32 | 86 #ifdef WIN32 |
113 #include <rpc.h> | 87 # include <rpc.h> |
114 #else | 88 #else |
115 #include <uuid/uuid.h> | 89 # include <uuid/uuid.h> |
116 #endif | 90 #endif |
117 } | 91 } |
118 | 92 |
119 | 93 |
120 #if ORTHANC_ENABLE_PUGIXML == 1 | 94 #if ORTHANC_ENABLE_PUGIXML == 1 |
121 #include "ChunkedBuffer.h" | 95 # include "ChunkedBuffer.h" |
122 #include <pugixml.hpp> | 96 # include <pugixml.hpp> |
123 #endif | 97 #endif |
124 | 98 |
125 | 99 |
126 namespace Orthanc | 100 namespace Orthanc |
127 { | 101 { |
1317 return false; | 1291 return false; |
1318 } | 1292 } |
1319 | 1293 |
1320 return IsUuid(str.substr(0, 36)); | 1294 return IsUuid(str.substr(0, 36)); |
1321 } | 1295 } |
1322 | |
1323 | |
1324 #if ORTHANC_SANDBOXED == 0 | |
1325 | |
1326 static bool finish_; | |
1327 static ServerBarrierEvent barrierEvent_; | |
1328 | |
1329 #if defined(_WIN32) | |
1330 static BOOL WINAPI ConsoleControlHandler(DWORD dwCtrlType) | |
1331 { | |
1332 // http://msdn.microsoft.com/en-us/library/ms683242(v=vs.85).aspx | |
1333 finish_ = true; | |
1334 return true; | |
1335 } | |
1336 #else | |
1337 static void SignalHandler(int signal) | |
1338 { | |
1339 if (signal == SIGHUP) | |
1340 { | |
1341 barrierEvent_ = ServerBarrierEvent_Reload; | |
1342 } | |
1343 | |
1344 finish_ = true; | |
1345 } | |
1346 #endif | |
1347 | |
1348 | |
1349 static ServerBarrierEvent ServerBarrierInternal(const bool* stopFlag) | |
1350 { | |
1351 #if defined(_WIN32) | |
1352 SetConsoleCtrlHandler(ConsoleControlHandler, true); | |
1353 #else | |
1354 signal(SIGINT, SignalHandler); | |
1355 signal(SIGQUIT, SignalHandler); | |
1356 signal(SIGTERM, SignalHandler); | |
1357 signal(SIGHUP, SignalHandler); | |
1358 #endif | |
1359 | |
1360 // Active loop that awakens every 100ms | |
1361 finish_ = false; | |
1362 barrierEvent_ = ServerBarrierEvent_Stop; | |
1363 while (!(*stopFlag || finish_)) | |
1364 { | |
1365 Toolbox::USleep(100 * 1000); | |
1366 } | |
1367 | |
1368 #if defined(_WIN32) | |
1369 SetConsoleCtrlHandler(ConsoleControlHandler, false); | |
1370 #else | |
1371 signal(SIGINT, NULL); | |
1372 signal(SIGQUIT, NULL); | |
1373 signal(SIGTERM, NULL); | |
1374 signal(SIGHUP, NULL); | |
1375 #endif | |
1376 | |
1377 return barrierEvent_; | |
1378 } | |
1379 | |
1380 | |
1381 ServerBarrierEvent SystemToolbox::ServerBarrier(const bool& stopFlag) | |
1382 { | |
1383 return ServerBarrierInternal(&stopFlag); | |
1384 } | |
1385 | |
1386 | |
1387 ServerBarrierEvent SystemToolbox::ServerBarrier() | |
1388 { | |
1389 const bool stopFlag = false; | |
1390 return ServerBarrierInternal(&stopFlag); | |
1391 } | |
1392 | |
1393 | |
1394 static std::streamsize GetStreamSize(std::istream& f) | |
1395 { | |
1396 // http://www.cplusplus.com/reference/iostream/istream/tellg/ | |
1397 f.seekg(0, std::ios::end); | |
1398 std::streamsize size = f.tellg(); | |
1399 f.seekg(0, std::ios::beg); | |
1400 | |
1401 return size; | |
1402 } | |
1403 | |
1404 | |
1405 void SystemToolbox::ReadFile(std::string& content, | |
1406 const std::string& path) | |
1407 { | |
1408 if (!IsRegularFile(path)) | |
1409 { | |
1410 LOG(ERROR) << std::string("The path does not point to a regular file: ") << path; | |
1411 throw OrthancException(ErrorCode_RegularFileExpected); | |
1412 } | |
1413 | |
1414 boost::filesystem::ifstream f; | |
1415 f.open(path, std::ifstream::in | std::ifstream::binary); | |
1416 if (!f.good()) | |
1417 { | |
1418 throw OrthancException(ErrorCode_InexistentFile); | |
1419 } | |
1420 | |
1421 std::streamsize size = GetStreamSize(f); | |
1422 content.resize(size); | |
1423 if (size != 0) | |
1424 { | |
1425 f.read(reinterpret_cast<char*>(&content[0]), size); | |
1426 } | |
1427 | |
1428 f.close(); | |
1429 } | |
1430 | |
1431 | |
1432 bool SystemToolbox::ReadHeader(std::string& header, | |
1433 const std::string& path, | |
1434 size_t headerSize) | |
1435 { | |
1436 if (!IsRegularFile(path)) | |
1437 { | |
1438 LOG(ERROR) << std::string("The path does not point to a regular file: ") << path; | |
1439 throw OrthancException(ErrorCode_RegularFileExpected); | |
1440 } | |
1441 | |
1442 boost::filesystem::ifstream f; | |
1443 f.open(path, std::ifstream::in | std::ifstream::binary); | |
1444 if (!f.good()) | |
1445 { | |
1446 throw OrthancException(ErrorCode_InexistentFile); | |
1447 } | |
1448 | |
1449 bool full = true; | |
1450 | |
1451 { | |
1452 std::streamsize size = GetStreamSize(f); | |
1453 if (size <= 0) | |
1454 { | |
1455 headerSize = 0; | |
1456 full = false; | |
1457 } | |
1458 else if (static_cast<size_t>(size) < headerSize) | |
1459 { | |
1460 headerSize = size; // Truncate to the size of the file | |
1461 full = false; | |
1462 } | |
1463 } | |
1464 | |
1465 header.resize(headerSize); | |
1466 if (headerSize != 0) | |
1467 { | |
1468 f.read(reinterpret_cast<char*>(&header[0]), headerSize); | |
1469 } | |
1470 | |
1471 f.close(); | |
1472 | |
1473 return full; | |
1474 } | |
1475 | |
1476 | |
1477 void SystemToolbox::WriteFile(const void* content, | |
1478 size_t size, | |
1479 const std::string& path) | |
1480 { | |
1481 boost::filesystem::ofstream f; | |
1482 f.open(path, std::ofstream::out | std::ofstream::binary); | |
1483 if (!f.good()) | |
1484 { | |
1485 throw OrthancException(ErrorCode_CannotWriteFile); | |
1486 } | |
1487 | |
1488 if (size != 0) | |
1489 { | |
1490 f.write(reinterpret_cast<const char*>(content), size); | |
1491 | |
1492 if (!f.good()) | |
1493 { | |
1494 f.close(); | |
1495 throw OrthancException(ErrorCode_FileStorageCannotWrite); | |
1496 } | |
1497 } | |
1498 | |
1499 f.close(); | |
1500 } | |
1501 | |
1502 | |
1503 void SystemToolbox::WriteFile(const std::string& content, | |
1504 const std::string& path) | |
1505 { | |
1506 WriteFile(content.size() > 0 ? content.c_str() : NULL, | |
1507 content.size(), path); | |
1508 } | |
1509 | |
1510 | |
1511 void SystemToolbox::RemoveFile(const std::string& path) | |
1512 { | |
1513 if (boost::filesystem::exists(path)) | |
1514 { | |
1515 if (IsRegularFile(path)) | |
1516 { | |
1517 boost::filesystem::remove(path); | |
1518 } | |
1519 else | |
1520 { | |
1521 throw OrthancException(ErrorCode_RegularFileExpected); | |
1522 } | |
1523 } | |
1524 } | |
1525 | |
1526 | |
1527 uint64_t SystemToolbox::GetFileSize(const std::string& path) | |
1528 { | |
1529 try | |
1530 { | |
1531 return static_cast<uint64_t>(boost::filesystem::file_size(path)); | |
1532 } | |
1533 catch (boost::filesystem::filesystem_error&) | |
1534 { | |
1535 throw OrthancException(ErrorCode_InexistentFile); | |
1536 } | |
1537 } | |
1538 | |
1539 | |
1540 void SystemToolbox::MakeDirectory(const std::string& path) | |
1541 { | |
1542 if (boost::filesystem::exists(path)) | |
1543 { | |
1544 if (!boost::filesystem::is_directory(path)) | |
1545 { | |
1546 throw OrthancException(ErrorCode_DirectoryOverFile); | |
1547 } | |
1548 } | |
1549 else | |
1550 { | |
1551 if (!boost::filesystem::create_directories(path)) | |
1552 { | |
1553 throw OrthancException(ErrorCode_MakeDirectory); | |
1554 } | |
1555 } | |
1556 } | |
1557 | |
1558 | |
1559 bool SystemToolbox::IsExistingFile(const std::string& path) | |
1560 { | |
1561 return boost::filesystem::exists(path); | |
1562 } | |
1563 | |
1564 | |
1565 #if defined(_WIN32) | |
1566 static std::string GetPathToExecutableInternal() | |
1567 { | |
1568 // Yes, this is ugly, but there is no simple way to get the | |
1569 // required buffer size, so we use a big constant | |
1570 std::vector<char> buffer(32768); | |
1571 /*int bytes =*/ GetModuleFileNameA(NULL, &buffer[0], static_cast<DWORD>(buffer.size() - 1)); | |
1572 return std::string(&buffer[0]); | |
1573 } | |
1574 | |
1575 #elif defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__FreeBSD__) | |
1576 static std::string GetPathToExecutableInternal() | |
1577 { | |
1578 std::vector<char> buffer(PATH_MAX + 1); | |
1579 ssize_t bytes = readlink("/proc/self/exe", &buffer[0], buffer.size() - 1); | |
1580 if (bytes == 0) | |
1581 { | |
1582 throw OrthancException(ErrorCode_PathToExecutable); | |
1583 } | |
1584 | |
1585 return std::string(&buffer[0]); | |
1586 } | |
1587 | |
1588 #elif defined(__APPLE__) && defined(__MACH__) | |
1589 static std::string GetPathToExecutableInternal() | |
1590 { | |
1591 char pathbuf[PATH_MAX + 1]; | |
1592 unsigned int bufsize = static_cast<int>(sizeof(pathbuf)); | |
1593 | |
1594 _NSGetExecutablePath( pathbuf, &bufsize); | |
1595 | |
1596 return std::string(pathbuf); | |
1597 } | |
1598 | |
1599 #else | |
1600 #error Support your platform here | |
1601 #endif | |
1602 | |
1603 | |
1604 std::string SystemToolbox::GetPathToExecutable() | |
1605 { | |
1606 boost::filesystem::path p(GetPathToExecutableInternal()); | |
1607 return boost::filesystem::absolute(p).string(); | |
1608 } | |
1609 | |
1610 | |
1611 std::string SystemToolbox::GetDirectoryOfExecutable() | |
1612 { | |
1613 boost::filesystem::path p(GetPathToExecutableInternal()); | |
1614 return boost::filesystem::absolute(p.parent_path()).string(); | |
1615 } | |
1616 | |
1617 | |
1618 void SystemToolbox::ExecuteSystemCommand(const std::string& command, | |
1619 const std::vector<std::string>& arguments) | |
1620 { | |
1621 // Convert the arguments as a C array | |
1622 std::vector<char*> args(arguments.size() + 2); | |
1623 | |
1624 args.front() = const_cast<char*>(command.c_str()); | |
1625 | |
1626 for (size_t i = 0; i < arguments.size(); i++) | |
1627 { | |
1628 args[i + 1] = const_cast<char*>(arguments[i].c_str()); | |
1629 } | |
1630 | |
1631 args.back() = NULL; | |
1632 | |
1633 int status; | |
1634 | |
1635 #if defined(_WIN32) | |
1636 // http://msdn.microsoft.com/en-us/library/275khfab.aspx | |
1637 status = static_cast<int>(_spawnvp(_P_OVERLAY, command.c_str(), &args[0])); | |
1638 | |
1639 #else | |
1640 int pid = fork(); | |
1641 | |
1642 if (pid == -1) | |
1643 { | |
1644 // Error in fork() | |
1645 #if ORTHANC_ENABLE_LOGGING == 1 | |
1646 LOG(ERROR) << "Cannot fork a child process"; | |
1647 #endif | |
1648 | |
1649 throw OrthancException(ErrorCode_SystemCommand); | |
1650 } | |
1651 else if (pid == 0) | |
1652 { | |
1653 // Execute the system command in the child process | |
1654 execvp(command.c_str(), &args[0]); | |
1655 | |
1656 // We should never get here | |
1657 _exit(1); | |
1658 } | |
1659 else | |
1660 { | |
1661 // Wait for the system command to exit | |
1662 waitpid(pid, &status, 0); | |
1663 } | |
1664 #endif | |
1665 | |
1666 if (status != 0) | |
1667 { | |
1668 #if ORTHANC_ENABLE_LOGGING == 1 | |
1669 LOG(ERROR) << "System command failed with status code " << status; | |
1670 #endif | |
1671 | |
1672 throw OrthancException(ErrorCode_SystemCommand); | |
1673 } | |
1674 } | |
1675 | |
1676 | |
1677 int SystemToolbox::GetProcessId() | |
1678 { | |
1679 #if defined(_WIN32) | |
1680 return static_cast<int>(_getpid()); | |
1681 #else | |
1682 return static_cast<int>(getpid()); | |
1683 #endif | |
1684 } | |
1685 | |
1686 | |
1687 bool SystemToolbox::IsRegularFile(const std::string& path) | |
1688 { | |
1689 namespace fs = boost::filesystem; | |
1690 | |
1691 try | |
1692 { | |
1693 if (fs::exists(path)) | |
1694 { | |
1695 fs::file_status status = fs::status(path); | |
1696 return (status.type() == boost::filesystem::regular_file || | |
1697 status.type() == boost::filesystem::reparse_file); // Fix BitBucket issue #11 | |
1698 } | |
1699 } | |
1700 catch (fs::filesystem_error&) | |
1701 { | |
1702 } | |
1703 | |
1704 return false; | |
1705 } | |
1706 | |
1707 | |
1708 FILE* SystemToolbox::OpenFile(const std::string& path, | |
1709 FileMode mode) | |
1710 { | |
1711 #if defined(_WIN32) | |
1712 // TODO Deal with special characters by converting to the current locale | |
1713 #endif | |
1714 | |
1715 const char* m; | |
1716 switch (mode) | |
1717 { | |
1718 case FileMode_ReadBinary: | |
1719 m = "rb"; | |
1720 break; | |
1721 | |
1722 case FileMode_WriteBinary: | |
1723 m = "wb"; | |
1724 break; | |
1725 | |
1726 default: | |
1727 throw OrthancException(ErrorCode_ParameterOutOfRange); | |
1728 } | |
1729 | |
1730 return fopen(path.c_str(), m); | |
1731 } | |
1732 | |
1733 | |
1734 #if BOOST_HAS_DATE_TIME == 1 | |
1735 std::string SystemToolbox::GetNowIsoString() | |
1736 { | |
1737 boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); | |
1738 return boost::posix_time::to_iso_string(now); | |
1739 } | |
1740 | |
1741 void SystemToolbox::GetNowDicom(std::string& date, | |
1742 std::string& time) | |
1743 { | |
1744 boost::posix_time::ptime now = boost::posix_time::second_clock::local_time(); | |
1745 tm tm = boost::posix_time::to_tm(now); | |
1746 | |
1747 char s[32]; | |
1748 sprintf(s, "%04d%02d%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); | |
1749 date.assign(s); | |
1750 | |
1751 // TODO milliseconds | |
1752 sprintf(s, "%02d%02d%02d.%06d", tm.tm_hour, tm.tm_min, tm.tm_sec, 0); | |
1753 time.assign(s); | |
1754 } | |
1755 #endif | |
1756 | |
1757 | |
1758 #endif /* ORTHANC_SANDBOXED */ | |
1759 } | 1296 } |