2 Copyright (C) 2012-2021 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
23 #include "compose.hpp"
24 #include "dcpomatic_log.h"
26 #include <dcp/raw_convert.h>
27 DCPOMATIC_DISABLE_WARNINGS
28 #include <libxml++/libxml++.h>
29 DCPOMATIC_ENABLE_WARNINGS
30 #include <boost/algorithm/string.hpp>
39 using boost::optional;
42 Drive::Drive (string xml)
45 doc.read_string (xml);
46 _device = doc.string_child("Device");
47 for (auto i: doc.node_children("MountPoint")) {
48 _mount_points.push_back (i->content());
50 _size = doc.number_child<uint64_t>("Size");
51 _vendor = doc.optional_string_child("Vendor");
52 _model = doc.optional_string_child("Model");
57 Drive::as_xml () const
60 auto root = doc.create_root_node ("Drive");
61 root->add_child("Device")->add_child_text(_device);
62 for (auto i: _mount_points) {
63 root->add_child("MountPoint")->add_child_text(i.string());
65 root->add_child("Size")->add_child_text(dcp::raw_convert<string>(_size));
67 root->add_child("Vendor")->add_child_text(*_vendor);
70 root->add_child("Model")->add_child_text(*_model);
73 return doc.write_to_string("UTF-8");
78 Drive::description () const
81 snprintf(gb, 64, "%.1f", _size / 1000000000.0);
88 if (name.size() > 0) {
89 name += " " + *_model;
94 if (name.size() == 0) {
98 return String::compose(_("%1 (%2 GB) [%3]"), name, gb, _device);
103 Drive::log_summary () const
106 for (auto i: _mount_points) {
107 mp += i.string() + ",";
112 mp = mp.substr (0, mp.length() - 1);
115 return String::compose(
116 "Device %1 mounted on %2 size %3 vendor %4 model %5",
117 _device, mp, _size, _vendor.get_value_or("[none]"), _model.get_value_or("[none]")
123 /* This is in _common so we can use it in unit tests */
124 optional<OSXMediaPath>
125 analyse_osx_media_path (string path)
127 if (path.find("/IOHDIXController") != string::npos) {
128 /* This is a disk image, so we completely ignore it */
129 LOG_DISK_NC("Ignoring this as it seems to be a disk image");
134 vector<string> parts;
135 split(parts, path, boost::is_any_of("/"));
136 std::copy(parts.begin() + 1, parts.end(), back_inserter(mp.parts));
138 if (!parts.empty() && parts[0] == "IODeviceTree:") {
140 if (mp.parts.size() < 2) {
141 /* Later we expect at least 2 parts in a IODeviceTree */
142 LOG_DISK_NC("Ignoring this as it has a strange media path");
145 } else if (!parts.empty() && parts[0] == "IOService:") {
155 /* Take soem OSXDisk objects, representing disks that `DARegisterDiskAppearedCallback` told us about,
156 * and find those drives that we could write a DCP to. The drives returned are "real" (not synthesized)
157 * and are whole disks (not partitions). They may be mounted, or contain mounted partitions, in which
158 * their mounted() method will return true.
161 osx_disks_to_drives (vector<OSXDisk> disks)
163 using namespace boost::algorithm;
165 /* Mark disks containing mounted partitions as themselves mounted */
166 for (auto& i: disks) {
170 for (auto& j: disks) {
171 if (!j.mount_points.empty() && starts_with(j.device, i.device)) {
172 LOG_DISK("Marking %1 as mounted because %2 is", i.device, j.device);
173 std::copy(j.mount_points.begin(), j.mount_points.end(), back_inserter(i.mount_points));
178 /* Mark containers of mounted synths as themselves mounted */
179 for (auto& i: disks) {
180 if (i.media_path.real) {
181 for (auto& j: disks) {
182 if (!j.media_path.real && !j.mount_points.empty()) {
183 /* i is real, j is a mounted synth; if we see the first two parts
184 * of i anywhere in j we assume they are related and so i shares
188 find(j.media_path.parts.begin(), j.media_path.parts.end(), i.media_path.parts[0]) != j.media_path.parts.end() &&
189 find(j.media_path.parts.begin(), j.media_path.parts.end(), i.media_path.parts[1]) != j.media_path.parts.end()) {
190 LOG_DISK("Marking %1 as mounted because %2 is (found %3 and %4)", i.device, j.device, i.media_path.parts[0], i.media_path.parts[1]);
191 std::copy(j.mount_points.begin(), j.mount_points.end(), back_inserter(i.mount_points));
198 vector<Drive> drives;
199 for (auto const& i: disks) {
200 if (i.whole && i.media_path.real) {
201 drives.push_back(Drive(i.device, i.mount_points, i.size, i.vendor, i.model));
202 LOG_DISK_NC(drives.back().log_summary());