Add DCPOMATIC_DISK define and don't build stuff needing boost dll library if we don...
[dcpomatic.git] / src / lib / cross_linux.cc
1 /*
2     Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
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.
10
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.
15
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/>.
18
19 */
20
21 #include "cross.h"
22 #include "compose.hpp"
23 #include "log.h"
24 #include "dcpomatic_log.h"
25 #include "config.h"
26 #include "exceptions.h"
27 #include <dcp/raw_convert.h>
28 #include <glib.h>
29 extern "C" {
30 #include <libavformat/avio.h>
31 }
32 #include <boost/algorithm/string.hpp>
33 #include <boost/foreach.hpp>
34 #ifdef DCPOMATIC_DISK
35 #include <boost/dll/runtime_symbol_info.hpp>
36 #endif
37 #include <unistd.h>
38 #include <mntent.h>
39 #include <sys/types.h>
40 #include <ifaddrs.h>
41 #include <netinet/in.h>
42 #include <arpa/inet.h>
43 #include <fstream>
44
45 #include "i18n.h"
46
47 using std::pair;
48 using std::list;
49 using std::ifstream;
50 using std::string;
51 using std::wstring;
52 using std::make_pair;
53 using std::vector;
54 using std::cerr;
55 using std::cout;
56 using std::runtime_error;
57 using boost::shared_ptr;
58 using boost::optional;
59
60 /** @param s Number of seconds to sleep for */
61 void
62 dcpomatic_sleep_seconds (int s)
63 {
64         sleep (s);
65 }
66
67 void
68 dcpomatic_sleep_milliseconds (int ms)
69 {
70         usleep (ms * 1000);
71 }
72
73 /** @return A string of CPU information (model name etc.) */
74 string
75 cpu_info ()
76 {
77         string info;
78
79         /* This use of ifstream is ok; the filename can never
80            be non-Latin
81         */
82         ifstream f ("/proc/cpuinfo");
83         while (f.good ()) {
84                 string l;
85                 getline (f, l);
86                 if (boost::algorithm::starts_with (l, "model name")) {
87                         string::size_type const c = l.find (':');
88                         if (c != string::npos) {
89                                 info = l.substr (c + 2);
90                         }
91                 }
92         }
93
94         return info;
95 }
96
97 boost::filesystem::path
98 shared_path ()
99 {
100         char const * p = getenv ("DCPOMATIC_LINUX_SHARE_PREFIX");
101         if (p) {
102                 return p;
103         }
104         return boost::filesystem::canonical (LINUX_SHARE_PREFIX);
105 }
106
107 void
108 run_ffprobe (boost::filesystem::path content, boost::filesystem::path out)
109 {
110         string ffprobe = "ffprobe \"" + content.string() + "\" 2> \"" + out.string() + "\"";
111         LOG_GENERAL (N_("Probing with %1"), ffprobe);
112         system (ffprobe.c_str ());
113 }
114
115 list<pair<string, string> >
116 mount_info ()
117 {
118         list<pair<string, string> > m;
119
120         FILE* f = setmntent ("/etc/mtab", "r");
121         if (!f) {
122                 return m;
123         }
124
125         while (true) {
126                 struct mntent* mnt = getmntent (f);
127                 if (!mnt) {
128                         break;
129                 }
130
131                 m.push_back (make_pair (mnt->mnt_dir, mnt->mnt_type));
132         }
133
134         endmntent (f);
135
136         return m;
137 }
138
139 boost::filesystem::path
140 openssl_path ()
141 {
142         return "dcpomatic2_openssl";
143 }
144
145 #ifdef DCPOMATIC_DISK
146 boost::filesystem::path
147 disk_writer_path ()
148 {
149         return boost::dll::program_location().parent_path() / "dcpomatic2_disk_writer";
150 }
151 #endif
152
153 /* Apparently there is no way to create an ofstream using a UTF-8
154    filename under Windows.  We are hence reduced to using fopen
155    with this wrapper.
156 */
157 FILE *
158 fopen_boost (boost::filesystem::path p, string t)
159 {
160         return fopen (p.c_str(), t.c_str ());
161 }
162
163 int
164 dcpomatic_fseek (FILE* stream, int64_t offset, int whence)
165 {
166         return fseek (stream, offset, whence);
167 }
168
169 void
170 Waker::nudge ()
171 {
172
173 }
174
175 Waker::Waker ()
176 {
177
178 }
179
180 Waker::~Waker ()
181 {
182
183 }
184
185 void
186 start_tool (boost::filesystem::path dcpomatic, string executable, string)
187 {
188         boost::filesystem::path batch = dcpomatic.parent_path() / executable;
189
190         pid_t pid = fork ();
191         if (pid == 0) {
192                 int const r = system (batch.string().c_str());
193                 exit (WEXITSTATUS (r));
194         }
195 }
196
197 void
198 start_batch_converter (boost::filesystem::path dcpomatic)
199 {
200         start_tool (dcpomatic, "dcpomatic2_batch", "DCP-o-matic\\ 2\\ Batch\\ Converter.app");
201 }
202
203 void
204 start_player (boost::filesystem::path dcpomatic)
205 {
206         start_tool (dcpomatic, "dcpomatic2_player", "DCP-o-matic\\ 2\\ Player.app");
207 }
208
209 uint64_t
210 thread_id ()
211 {
212         return (uint64_t) pthread_self ();
213 }
214
215 int
216 avio_open_boost (AVIOContext** s, boost::filesystem::path file, int flags)
217 {
218         return avio_open (s, file.c_str(), flags);
219 }
220
221
222 boost::filesystem::path
223 home_directory ()
224 {
225                 return getenv("HOME");
226 }
227
228 string
229 command_and_read (string cmd)
230 {
231         FILE* pipe = popen (cmd.c_str(), "r");
232         if (!pipe) {
233                 throw runtime_error ("popen failed");
234         }
235
236         string result;
237         char buffer[128];
238         try {
239                 while (fgets(buffer, sizeof(buffer), pipe)) {
240                         result += buffer;
241                 }
242         } catch (...) {
243                 pclose (pipe);
244                 throw;
245         }
246
247         pclose (pipe);
248         return result;
249 }
250
251 /** @return true if this process is a 32-bit one running on a 64-bit-capable OS */
252 bool
253 running_32_on_64 ()
254 {
255         /* I'm assuming nobody does this on Linux */
256         return false;
257 }
258
259 vector<Drive>
260 get_drives ()
261 {
262         vector<Drive> drives;
263
264         using namespace boost::filesystem;
265         list<string> mounted_devices;
266         std::ifstream f("/proc/mounts");
267         string line;
268         while (f.good()) {
269                 getline(f, line);
270                 vector<string> bits;
271                 boost::algorithm::split (bits, line, boost::is_any_of(" "));
272                 if (bits.size() > 0 && boost::algorithm::starts_with(bits[0], "/dev/")) {
273                         mounted_devices.push_back(bits[0]);
274                         LOG_DISK("Mounted device %1", bits[0]);
275                 }
276         }
277
278         for (directory_iterator i = directory_iterator("/sys/block"); i != directory_iterator(); ++i) {
279                 string const name = i->path().filename().string();
280                 path device_type_file("/sys/block/" + name + "/device/type");
281                 optional<string> device_type;
282                 if (exists(device_type_file)) {
283                         device_type = dcp::file_to_string (device_type_file);
284                         boost::trim(*device_type);
285                 }
286                 /* Device type 5 is "SCSI_TYPE_ROM" in blkdev.h; seems usually to be a CD/DVD drive */
287                 if (!boost::algorithm::starts_with(name, "loop") && (!device_type || *device_type != "5")) {
288                         uint64_t const size = dcp::raw_convert<uint64_t>(dcp::file_to_string(*i / "size")) * 512;
289                         if (size == 0) {
290                                 continue;
291                         }
292                         bool mounted = false;
293                         optional<string> vendor;
294                         try {
295                                 vendor = dcp::file_to_string("/sys/block/" + name + "/device/vendor");
296                                 boost::trim(*vendor);
297                         } catch (...) {}
298                         optional<string> model;
299                         try {
300                                 model = dcp::file_to_string("/sys/block/" + name + "/device/model");
301                                 boost::trim(*model);
302                         } catch (...) {}
303                         BOOST_FOREACH (string j, mounted_devices) {
304                                 if (boost::algorithm::starts_with(j, "/dev/" + name)) {
305                                         mounted = true;
306                                 }
307                         }
308                         drives.push_back(Drive("/dev/" + i->path().filename().string(), size, mounted, vendor, model));
309                         LOG_DISK("Block device %1 size %2 %3 vendor %4 model %5", name, size, mounted ? "mounted" : "not mounted", vendor.get_value_or("[none]"), model.get_value_or("[none]"));
310                 }
311         }
312
313         return drives;
314 }
315
316 void
317 unprivileged ()
318 {
319         uid_t ruid, euid, suid;
320         if (getresuid(&ruid, &euid, &suid) == -1) {
321                 cerr << "getresuid() failed.\n";
322                 exit (EXIT_FAILURE);
323         }
324         seteuid (ruid);
325 }
326
327 PrivilegeEscalator::~PrivilegeEscalator ()
328 {
329         unprivileged ();
330 }
331
332 PrivilegeEscalator::PrivilegeEscalator ()
333 {
334         seteuid (0);
335 }
336
337 boost::filesystem::path
338 config_path ()
339 {
340         boost::filesystem::path p;
341         p /= g_get_user_config_dir ();
342         p /= "dcpomatic2";
343         return p;
344 }