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 }