1 //===-- PlatformPOSIX.cpp ---------------------------------------*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "PlatformPOSIX.h"
11
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 // Project includes
16
17 #include "lldb/Core/DataBufferHeap.h"
18 #include "lldb/Core/Log.h"
19 #include "lldb/Core/StreamString.h"
20 #include "lldb/Host/File.h"
21 #include "lldb/Host/FileSpec.h"
22 #include "lldb/Host/Host.h"
23
24 using namespace lldb;
25 using namespace lldb_private;
26
27
28 //------------------------------------------------------------------
29 /// Default Constructor
30 //------------------------------------------------------------------
PlatformPOSIX(bool is_host)31 PlatformPOSIX::PlatformPOSIX (bool is_host) :
32 Platform(is_host), // This is the local host platform
33 m_remote_platform_sp ()
34 {
35 }
36
37 //------------------------------------------------------------------
38 /// Destructor.
39 ///
40 /// The destructor is virtual since this class is designed to be
41 /// inherited from by the plug-in instance.
42 //------------------------------------------------------------------
~PlatformPOSIX()43 PlatformPOSIX::~PlatformPOSIX()
44 {
45 }
46
47 lldb_private::OptionGroupOptions*
GetConnectionOptions(lldb_private::CommandInterpreter & interpreter)48 PlatformPOSIX::GetConnectionOptions (lldb_private::CommandInterpreter& interpreter)
49 {
50 if (m_options.get() == NULL)
51 {
52 m_options.reset(new OptionGroupOptions(interpreter));
53 m_options->Append(new OptionGroupPlatformRSync());
54 m_options->Append(new OptionGroupPlatformSSH());
55 m_options->Append(new OptionGroupPlatformCaching());
56 }
57 return m_options.get();
58 }
59
60 lldb_private::Error
RunShellCommand(const char * command,const char * working_dir,int * status_ptr,int * signo_ptr,std::string * command_output,uint32_t timeout_sec)61 PlatformPOSIX::RunShellCommand (const char *command, // Shouldn't be NULL
62 const char *working_dir, // Pass NULL to use the current working directory
63 int *status_ptr, // Pass NULL if you don't want the process exit status
64 int *signo_ptr, // Pass NULL if you don't want the signal that caused the process to exit
65 std::string *command_output, // Pass NULL if you don't want the command output
66 uint32_t timeout_sec) // Timeout in seconds to wait for shell program to finish
67 {
68 if (IsHost())
69 return Host::RunShellCommand(command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec);
70 else
71 {
72 if (m_remote_platform_sp)
73 return m_remote_platform_sp->RunShellCommand(command, working_dir, status_ptr, signo_ptr, command_output, timeout_sec);
74 else
75 return Error("unable to run a remote command without a platform");
76 }
77 }
78
79 Error
MakeDirectory(const char * path,uint32_t file_permissions)80 PlatformPOSIX::MakeDirectory (const char *path, uint32_t file_permissions)
81 {
82 if (m_remote_platform_sp)
83 return m_remote_platform_sp->MakeDirectory(path, file_permissions);
84 else
85 return Platform::MakeDirectory(path ,file_permissions);
86 }
87
88 Error
GetFilePermissions(const char * path,uint32_t & file_permissions)89 PlatformPOSIX::GetFilePermissions (const char *path, uint32_t &file_permissions)
90 {
91 if (m_remote_platform_sp)
92 return m_remote_platform_sp->GetFilePermissions(path, file_permissions);
93 else
94 return Platform::GetFilePermissions(path ,file_permissions);
95 }
96
97 Error
SetFilePermissions(const char * path,uint32_t file_permissions)98 PlatformPOSIX::SetFilePermissions (const char *path, uint32_t file_permissions)
99 {
100 if (m_remote_platform_sp)
101 return m_remote_platform_sp->MakeDirectory(path, file_permissions);
102 else
103 return Platform::SetFilePermissions(path ,file_permissions);
104 }
105
106 lldb::user_id_t
OpenFile(const FileSpec & file_spec,uint32_t flags,uint32_t mode,Error & error)107 PlatformPOSIX::OpenFile (const FileSpec& file_spec,
108 uint32_t flags,
109 uint32_t mode,
110 Error &error)
111 {
112 if (IsHost())
113 return Host::OpenFile(file_spec, flags, mode, error);
114 else if (m_remote_platform_sp)
115 return m_remote_platform_sp->OpenFile(file_spec, flags, mode, error);
116 else
117 return Platform::OpenFile(file_spec, flags, mode, error);
118 }
119
120 bool
CloseFile(lldb::user_id_t fd,Error & error)121 PlatformPOSIX::CloseFile (lldb::user_id_t fd, Error &error)
122 {
123 if (IsHost())
124 return Host::CloseFile(fd, error);
125 else if (m_remote_platform_sp)
126 return m_remote_platform_sp->CloseFile(fd, error);
127 else
128 return Platform::CloseFile(fd, error);
129 }
130
131 uint64_t
ReadFile(lldb::user_id_t fd,uint64_t offset,void * dst,uint64_t dst_len,Error & error)132 PlatformPOSIX::ReadFile (lldb::user_id_t fd,
133 uint64_t offset,
134 void *dst,
135 uint64_t dst_len,
136 Error &error)
137 {
138 if (IsHost())
139 return Host::ReadFile(fd, offset, dst, dst_len, error);
140 else if (m_remote_platform_sp)
141 return m_remote_platform_sp->ReadFile(fd, offset, dst, dst_len, error);
142 else
143 return Platform::ReadFile(fd, offset, dst, dst_len, error);
144 }
145
146 uint64_t
WriteFile(lldb::user_id_t fd,uint64_t offset,const void * src,uint64_t src_len,Error & error)147 PlatformPOSIX::WriteFile (lldb::user_id_t fd,
148 uint64_t offset,
149 const void* src,
150 uint64_t src_len,
151 Error &error)
152 {
153 if (IsHost())
154 return Host::WriteFile(fd, offset, src, src_len, error);
155 else if (m_remote_platform_sp)
156 return m_remote_platform_sp->WriteFile(fd, offset, src, src_len, error);
157 else
158 return Platform::WriteFile(fd, offset, src, src_len, error);
159 }
160
161 static uint32_t
chown_file(Platform * platform,const char * path,uint32_t uid=UINT32_MAX,uint32_t gid=UINT32_MAX)162 chown_file(Platform *platform,
163 const char* path,
164 uint32_t uid = UINT32_MAX,
165 uint32_t gid = UINT32_MAX)
166 {
167 if (!platform || !path || *path == 0)
168 return UINT32_MAX;
169
170 if (uid == UINT32_MAX && gid == UINT32_MAX)
171 return 0; // pretend I did chown correctly - actually I just didn't care
172
173 StreamString command;
174 command.PutCString("chown ");
175 if (uid != UINT32_MAX)
176 command.Printf("%d",uid);
177 if (gid != UINT32_MAX)
178 command.Printf(":%d",gid);
179 command.Printf("%s",path);
180 int status;
181 platform->RunShellCommand(command.GetData(),
182 NULL,
183 &status,
184 NULL,
185 NULL,
186 10);
187 return status;
188 }
189
190 lldb_private::Error
PutFile(const lldb_private::FileSpec & source,const lldb_private::FileSpec & destination,uint32_t uid,uint32_t gid)191 PlatformPOSIX::PutFile (const lldb_private::FileSpec& source,
192 const lldb_private::FileSpec& destination,
193 uint32_t uid,
194 uint32_t gid)
195 {
196 Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
197
198 if (IsHost())
199 {
200 if (FileSpec::Equal(source, destination, true))
201 return Error();
202 // cp src dst
203 // chown uid:gid dst
204 std::string src_path (source.GetPath());
205 if (src_path.empty())
206 return Error("unable to get file path for source");
207 std::string dst_path (destination.GetPath());
208 if (dst_path.empty())
209 return Error("unable to get file path for destination");
210 StreamString command;
211 command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
212 int status;
213 RunShellCommand(command.GetData(),
214 NULL,
215 &status,
216 NULL,
217 NULL,
218 10);
219 if (status != 0)
220 return Error("unable to perform copy");
221 if (uid == UINT32_MAX && gid == UINT32_MAX)
222 return Error();
223 if (chown_file(this,dst_path.c_str(),uid,gid) != 0)
224 return Error("unable to perform chown");
225 return Error();
226 }
227 else if (m_remote_platform_sp)
228 {
229 if (GetSupportsRSync())
230 {
231 std::string src_path (source.GetPath());
232 if (src_path.empty())
233 return Error("unable to get file path for source");
234 std::string dst_path (destination.GetPath());
235 if (dst_path.empty())
236 return Error("unable to get file path for destination");
237 StreamString command;
238 if (GetIgnoresRemoteHostname())
239 {
240 if (!GetRSyncPrefix())
241 command.Printf("rsync %s %s %s",
242 GetRSyncOpts(),
243 src_path.c_str(),
244 dst_path.c_str());
245 else
246 command.Printf("rsync %s %s %s%s",
247 GetRSyncOpts(),
248 src_path.c_str(),
249 GetRSyncPrefix(),
250 dst_path.c_str());
251 }
252 else
253 command.Printf("rsync %s %s %s:%s",
254 GetRSyncOpts(),
255 src_path.c_str(),
256 GetHostname(),
257 dst_path.c_str());
258 if (log)
259 log->Printf("[PutFile] Running command: %s\n", command.GetData());
260 int retcode;
261 Host::RunShellCommand(command.GetData(),
262 NULL,
263 &retcode,
264 NULL,
265 NULL,
266 60);
267 if (retcode == 0)
268 {
269 // Don't chown a local file for a remote system
270 // if (chown_file(this,dst_path.c_str(),uid,gid) != 0)
271 // return Error("unable to perform chown");
272 return Error();
273 }
274 // if we are still here rsync has failed - let's try the slow way before giving up
275 }
276
277 if (log)
278 log->Printf ("PlatformPOSIX::PutFile(src='%s', dst='%s', uid=%u, gid=%u)",
279 source.GetPath().c_str(),
280 destination.GetPath().c_str(),
281 uid,
282 gid); // REMOVE THIS PRINTF PRIOR TO CHECKIN
283 // open
284 // read, write, read, write, ...
285 // close
286 // chown uid:gid dst
287 if (log)
288 log->Printf("[PutFile] Using block by block transfer....\n");
289
290 uint32_t source_open_options = File::eOpenOptionRead;
291 if (source.GetFileType() == FileSpec::eFileTypeSymbolicLink)
292 source_open_options |= File::eOpenoptionDontFollowSymlinks;
293
294 File source_file(source, source_open_options, lldb::eFilePermissionsUserRW);
295 Error error;
296 uint32_t permissions = source_file.GetPermissions(error);
297 if (permissions == 0)
298 permissions = lldb::eFilePermissionsFileDefault;
299
300 if (!source_file.IsValid())
301 return Error("unable to open source file");
302 lldb::user_id_t dest_file = OpenFile (destination,
303 File::eOpenOptionCanCreate | File::eOpenOptionWrite | File::eOpenOptionTruncate,
304 permissions,
305 error);
306 if (log)
307 log->Printf ("dest_file = %" PRIu64 "\n", dest_file);
308 if (error.Fail())
309 return error;
310 if (dest_file == UINT64_MAX)
311 return Error("unable to open target file");
312 lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
313 uint64_t offset = 0;
314 while (error.Success())
315 {
316 size_t bytes_read = buffer_sp->GetByteSize();
317 error = source_file.Read(buffer_sp->GetBytes(), bytes_read);
318 if (bytes_read)
319 {
320 WriteFile(dest_file, offset, buffer_sp->GetBytes(), bytes_read, error);
321 offset += bytes_read;
322 }
323 else
324 break;
325 }
326 CloseFile(dest_file, error);
327 if (uid == UINT32_MAX && gid == UINT32_MAX)
328 return error;
329 // This is remopve, don't chown a local file...
330 // std::string dst_path (destination.GetPath());
331 // if (chown_file(this,dst_path.c_str(),uid,gid) != 0)
332 // return Error("unable to perform chown");
333 return error;
334 }
335 return Platform::PutFile(source,destination,uid,gid);
336 }
337
338 lldb::user_id_t
GetFileSize(const FileSpec & file_spec)339 PlatformPOSIX::GetFileSize (const FileSpec& file_spec)
340 {
341 if (IsHost())
342 return Host::GetFileSize(file_spec);
343 else if (m_remote_platform_sp)
344 return m_remote_platform_sp->GetFileSize(file_spec);
345 else
346 return Platform::GetFileSize(file_spec);
347 }
348
349 Error
CreateSymlink(const char * src,const char * dst)350 PlatformPOSIX::CreateSymlink(const char *src, const char *dst)
351 {
352 if (IsHost())
353 return Host::Symlink(src, dst);
354 else if (m_remote_platform_sp)
355 return m_remote_platform_sp->CreateSymlink(src, dst);
356 else
357 return Platform::CreateSymlink(src, dst);
358 }
359
360 bool
GetFileExists(const FileSpec & file_spec)361 PlatformPOSIX::GetFileExists (const FileSpec& file_spec)
362 {
363 if (IsHost())
364 return file_spec.Exists();
365 else if (m_remote_platform_sp)
366 return m_remote_platform_sp->GetFileExists(file_spec);
367 else
368 return Platform::GetFileExists(file_spec);
369 }
370
371 Error
Unlink(const char * path)372 PlatformPOSIX::Unlink (const char *path)
373 {
374 if (IsHost())
375 return Host::Unlink (path);
376 else if (m_remote_platform_sp)
377 return m_remote_platform_sp->Unlink(path);
378 else
379 return Platform::Unlink(path);
380 }
381
382 lldb_private::Error
GetFile(const lldb_private::FileSpec & source,const lldb_private::FileSpec & destination)383 PlatformPOSIX::GetFile (const lldb_private::FileSpec& source /* remote file path */,
384 const lldb_private::FileSpec& destination /* local file path */)
385 {
386 Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM));
387
388 // Check the args, first.
389 std::string src_path (source.GetPath());
390 if (src_path.empty())
391 return Error("unable to get file path for source");
392 std::string dst_path (destination.GetPath());
393 if (dst_path.empty())
394 return Error("unable to get file path for destination");
395 if (IsHost())
396 {
397 if (FileSpec::Equal(source, destination, true))
398 return Error("local scenario->source and destination are the same file path: no operation performed");
399 // cp src dst
400 StreamString cp_command;
401 cp_command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str());
402 int status;
403 RunShellCommand(cp_command.GetData(),
404 NULL,
405 &status,
406 NULL,
407 NULL,
408 10);
409 if (status != 0)
410 return Error("unable to perform copy");
411 return Error();
412 }
413 else if (m_remote_platform_sp)
414 {
415 if (GetSupportsRSync())
416 {
417 StreamString command;
418 if (GetIgnoresRemoteHostname())
419 {
420 if (!GetRSyncPrefix())
421 command.Printf("rsync %s %s %s",
422 GetRSyncOpts(),
423 src_path.c_str(),
424 dst_path.c_str());
425 else
426 command.Printf("rsync %s %s%s %s",
427 GetRSyncOpts(),
428 GetRSyncPrefix(),
429 src_path.c_str(),
430 dst_path.c_str());
431 }
432 else
433 command.Printf("rsync %s %s:%s %s",
434 GetRSyncOpts(),
435 m_remote_platform_sp->GetHostname(),
436 src_path.c_str(),
437 dst_path.c_str());
438 if (log)
439 log->Printf("[GetFile] Running command: %s\n", command.GetData());
440 int retcode;
441 Host::RunShellCommand(command.GetData(),
442 NULL,
443 &retcode,
444 NULL,
445 NULL,
446 60);
447 if (retcode == 0)
448 return Error();
449 // If we are here, rsync has failed - let's try the slow way before giving up
450 }
451 // open src and dst
452 // read/write, read/write, read/write, ...
453 // close src
454 // close dst
455 if (log)
456 log->Printf("[GetFile] Using block by block transfer....\n");
457 Error error;
458 user_id_t fd_src = OpenFile (source,
459 File::eOpenOptionRead,
460 lldb::eFilePermissionsFileDefault,
461 error);
462
463 if (fd_src == UINT64_MAX)
464 return Error("unable to open source file");
465
466 uint32_t permissions = 0;
467 error = GetFilePermissions(source.GetPath().c_str(), permissions);
468
469 if (permissions == 0)
470 permissions = lldb::eFilePermissionsFileDefault;
471
472 user_id_t fd_dst = Host::OpenFile(destination,
473 File::eOpenOptionCanCreate | File::eOpenOptionWrite | File::eOpenOptionTruncate,
474 permissions,
475 error);
476
477 if (fd_dst == UINT64_MAX)
478 {
479 if (error.Success())
480 error.SetErrorString("unable to open destination file");
481 }
482
483 if (error.Success())
484 {
485 lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0));
486 uint64_t offset = 0;
487 error.Clear();
488 while (error.Success())
489 {
490 const uint64_t n_read = ReadFile (fd_src,
491 offset,
492 buffer_sp->GetBytes(),
493 buffer_sp->GetByteSize(),
494 error);
495 if (error.Fail())
496 break;
497 if (n_read == 0)
498 break;
499 if (Host::WriteFile(fd_dst,
500 offset,
501 buffer_sp->GetBytes(),
502 n_read,
503 error) != n_read)
504 {
505 if (!error.Fail())
506 error.SetErrorString("unable to write to destination file");
507 break;
508 }
509 offset += n_read;
510 }
511 }
512 // Ignore the close error of src.
513 if (fd_src != UINT64_MAX)
514 CloseFile(fd_src, error);
515 // And close the dst file descriptot.
516 if (fd_dst != UINT64_MAX && !Host::CloseFile(fd_dst, error))
517 {
518 if (!error.Fail())
519 error.SetErrorString("unable to close destination file");
520
521 }
522 return error;
523 }
524 return Platform::GetFile(source,destination);
525 }
526
527 std::string
GetPlatformSpecificConnectionInformation()528 PlatformPOSIX::GetPlatformSpecificConnectionInformation()
529 {
530 StreamString stream;
531 if (GetSupportsRSync())
532 {
533 stream.PutCString("rsync");
534 if ( (GetRSyncOpts() && *GetRSyncOpts()) ||
535 (GetRSyncPrefix() && *GetRSyncPrefix()) ||
536 GetIgnoresRemoteHostname())
537 {
538 stream.Printf(", options: ");
539 if (GetRSyncOpts() && *GetRSyncOpts())
540 stream.Printf("'%s' ",GetRSyncOpts());
541 stream.Printf(", prefix: ");
542 if (GetRSyncPrefix() && *GetRSyncPrefix())
543 stream.Printf("'%s' ",GetRSyncPrefix());
544 if (GetIgnoresRemoteHostname())
545 stream.Printf("ignore remote-hostname ");
546 }
547 }
548 if (GetSupportsSSH())
549 {
550 stream.PutCString("ssh");
551 if (GetSSHOpts() && *GetSSHOpts())
552 stream.Printf(", options: '%s' ",GetSSHOpts());
553 }
554 if (GetLocalCacheDirectory() && *GetLocalCacheDirectory())
555 stream.Printf("cache dir: %s",GetLocalCacheDirectory());
556 if (stream.GetSize())
557 return stream.GetData();
558 else
559 return "";
560 }
561
562 bool
CalculateMD5(const FileSpec & file_spec,uint64_t & low,uint64_t & high)563 PlatformPOSIX::CalculateMD5 (const FileSpec& file_spec,
564 uint64_t &low,
565 uint64_t &high)
566 {
567 if (IsHost())
568 return Platform::CalculateMD5 (file_spec, low, high);
569 if (m_remote_platform_sp)
570 return m_remote_platform_sp->CalculateMD5(file_spec, low, high);
571 return false;
572 }
573
574 lldb_private::ConstString
GetRemoteWorkingDirectory()575 PlatformPOSIX::GetRemoteWorkingDirectory()
576 {
577 if (IsRemote() && m_remote_platform_sp)
578 return m_remote_platform_sp->GetRemoteWorkingDirectory();
579 else
580 return Platform::GetRemoteWorkingDirectory();
581 }
582
583 bool
SetRemoteWorkingDirectory(const lldb_private::ConstString & path)584 PlatformPOSIX::SetRemoteWorkingDirectory(const lldb_private::ConstString &path)
585 {
586 if (IsRemote() && m_remote_platform_sp)
587 return m_remote_platform_sp->SetRemoteWorkingDirectory(path);
588 else
589 return Platform::SetRemoteWorkingDirectory(path);
590 }
591
592 void
CalculateTrapHandlerSymbolNames()593 PlatformPOSIX::CalculateTrapHandlerSymbolNames ()
594 {
595 m_trap_handlers.push_back (ConstString ("_sigtramp"));
596 }
597