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 //------------------------------------------------------------------ 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 //------------------------------------------------------------------ 43 PlatformPOSIX::~PlatformPOSIX() 44 { 45 } 46 47 lldb_private::OptionGroupOptions* 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 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 uint32_t 80 PlatformPOSIX::MakeDirectory (const std::string &path, 81 mode_t mode) 82 { 83 if (IsHost()) 84 { 85 return Host::MakeDirectory (path.c_str(), mode); 86 } 87 if (IsRemote() && m_remote_platform_sp) 88 return m_remote_platform_sp->MakeDirectory(path, mode); 89 return Platform::MakeDirectory(path,mode); 90 } 91 92 lldb::user_id_t 93 PlatformPOSIX::OpenFile (const FileSpec& file_spec, 94 uint32_t flags, 95 mode_t mode, 96 Error &error) 97 { 98 if (IsHost()) 99 { 100 return Host::OpenFile(file_spec, flags, mode, error); 101 } 102 if (IsRemote() && m_remote_platform_sp) 103 return m_remote_platform_sp->OpenFile(file_spec, flags, mode, error); 104 return Platform::OpenFile(file_spec, flags, mode, error); 105 } 106 107 bool 108 PlatformPOSIX::CloseFile (lldb::user_id_t fd, Error &error) 109 { 110 if (IsHost()) 111 { 112 return Host::CloseFile(fd, error); 113 } 114 if (IsRemote() && m_remote_platform_sp) 115 return m_remote_platform_sp->CloseFile(fd, error); 116 return Platform::CloseFile(fd, error); 117 } 118 119 uint64_t 120 PlatformPOSIX::ReadFile (lldb::user_id_t fd, 121 uint64_t offset, 122 void *dst, 123 uint64_t dst_len, 124 Error &error) 125 { 126 if (IsHost()) 127 { 128 return Host::ReadFile(fd, offset, dst, dst_len, error); 129 } 130 if (IsRemote() && m_remote_platform_sp) 131 return m_remote_platform_sp->ReadFile(fd, offset, dst, dst_len, error); 132 return Platform::ReadFile(fd, offset, dst, dst_len, error); 133 } 134 135 uint64_t 136 PlatformPOSIX::WriteFile (lldb::user_id_t fd, 137 uint64_t offset, 138 const void* src, 139 uint64_t src_len, 140 Error &error) 141 { 142 if (IsHost()) 143 { 144 return Host::WriteFile(fd, offset, src, src_len, error); 145 } 146 if (IsRemote() && m_remote_platform_sp) 147 return m_remote_platform_sp->WriteFile(fd, offset, src, src_len, error); 148 149 return Platform::WriteFile(fd, offset, src, src_len, error); 150 } 151 152 static uint32_t 153 chown_file(Platform *platform, 154 const char* path, 155 uint32_t uid = UINT32_MAX, 156 uint32_t gid = UINT32_MAX) 157 { 158 if (!platform || !path || *path == 0) 159 return UINT32_MAX; 160 161 if (uid == UINT32_MAX && gid == UINT32_MAX) 162 return 0; // pretend I did chown correctly - actually I just didn't care 163 164 StreamString command; 165 command.PutCString("chown "); 166 if (uid != UINT32_MAX) 167 command.Printf("%d",uid); 168 if (gid != UINT32_MAX) 169 command.Printf(":%d",gid); 170 command.Printf("%s",path); 171 int status; 172 platform->RunShellCommand(command.GetData(), 173 NULL, 174 &status, 175 NULL, 176 NULL, 177 10); 178 return status; 179 } 180 181 lldb_private::Error 182 PlatformPOSIX::PutFile (const lldb_private::FileSpec& source, 183 const lldb_private::FileSpec& destination, 184 uint32_t uid, 185 uint32_t gid) 186 { 187 if (IsHost()) 188 { 189 if (FileSpec::Equal(source, destination, true)) 190 return Error(); 191 // cp src dst 192 // chown uid:gid dst 193 std::string src_path (source.GetPath()); 194 if (src_path.empty()) 195 return Error("unable to get file path for source"); 196 std::string dst_path (destination.GetPath()); 197 if (dst_path.empty()) 198 return Error("unable to get file path for destination"); 199 StreamString command; 200 command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str()); 201 int status; 202 RunShellCommand(command.GetData(), 203 NULL, 204 &status, 205 NULL, 206 NULL, 207 10); 208 if (status != 0) 209 return Error("unable to perform copy"); 210 if (uid == UINT32_MAX && gid == UINT32_MAX) 211 return Error(); 212 if (chown_file(this,dst_path.c_str(),uid,gid) != 0) 213 return Error("unable to perform chown"); 214 return Error(); 215 } 216 else if (IsRemote() && m_remote_platform_sp) 217 { 218 if (GetSupportsRSync()) 219 { 220 std::string src_path (source.GetPath()); 221 if (src_path.empty()) 222 return Error("unable to get file path for source"); 223 std::string dst_path (destination.GetPath()); 224 if (dst_path.empty()) 225 return Error("unable to get file path for destination"); 226 StreamString command; 227 if (GetIgnoresRemoteHostname()) 228 { 229 if (!GetRSyncPrefix()) 230 command.Printf("rsync %s %s %s", 231 GetRSyncOpts(), 232 src_path.c_str(), 233 dst_path.c_str()); 234 else 235 command.Printf("rsync %s %s %s%s", 236 GetRSyncOpts(), 237 src_path.c_str(), 238 GetRSyncPrefix(), 239 dst_path.c_str()); 240 } 241 else 242 command.Printf("rsync %s %s %s:%s", 243 GetRSyncOpts(), 244 src_path.c_str(), 245 GetHostname(), 246 dst_path.c_str()); 247 Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); 248 if (log) 249 log->Printf("[PutFile] Running command: %s\n", command.GetData()); 250 int retcode; 251 Host::RunShellCommand(command.GetData(), 252 NULL, 253 &retcode, 254 NULL, 255 NULL, 256 60); 257 if (retcode == 0) 258 { 259 // Don't chown a local file for a remote system 260 // if (chown_file(this,dst_path.c_str(),uid,gid) != 0) 261 // return Error("unable to perform chown"); 262 return Error(); 263 } 264 // if we are still here rsync has failed - let's try the slow way before giving up 265 } 266 // open 267 // read, write, read, write, ... 268 // close 269 // chown uid:gid dst 270 Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); 271 if (log) 272 log->Printf("[PutFile] Using block by block transfer....\n"); 273 File source_file(source, File::eOpenOptionRead, File::ePermissionsUserRW); 274 if (!source_file.IsValid()) 275 return Error("unable to open source file"); 276 Error error; 277 lldb::user_id_t dest_file = OpenFile (destination, 278 File::eOpenOptionCanCreate | File::eOpenOptionWrite | File::eOpenOptionTruncate, 279 File::ePermissionsUserRWX | File::ePermissionsGroupRX | File::ePermissionsWorldRX, 280 error); 281 if (log) 282 log->Printf ("dest_file = %" PRIu64 "\n", dest_file); 283 if (error.Fail()) 284 return error; 285 if (dest_file == UINT64_MAX) 286 return Error("unable to open target file"); 287 lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0)); 288 uint64_t offset = 0; 289 while (error.Success()) 290 { 291 size_t bytes_read = buffer_sp->GetByteSize(); 292 error = source_file.Read(buffer_sp->GetBytes(), bytes_read); 293 if (bytes_read) 294 { 295 WriteFile(dest_file, offset, buffer_sp->GetBytes(), bytes_read, error); 296 offset += bytes_read; 297 } 298 else 299 break; 300 } 301 CloseFile(dest_file, error); 302 if (uid == UINT32_MAX && gid == UINT32_MAX) 303 return error; 304 // This is remopve, don't chown a local file... 305 // std::string dst_path (destination.GetPath()); 306 // if (chown_file(this,dst_path.c_str(),uid,gid) != 0) 307 // return Error("unable to perform chown"); 308 return error; 309 } 310 return Platform::PutFile(source,destination,uid,gid); 311 } 312 313 lldb::user_id_t 314 PlatformPOSIX::GetFileSize (const FileSpec& file_spec) 315 { 316 if (IsHost()) 317 { 318 return Host::GetFileSize(file_spec); 319 } 320 if (IsRemote() && m_remote_platform_sp) 321 return m_remote_platform_sp->GetFileSize(file_spec); 322 return Platform::GetFileSize(file_spec); 323 } 324 325 bool 326 PlatformPOSIX::GetFileExists (const FileSpec& file_spec) 327 { 328 if (IsHost()) 329 { 330 return file_spec.Exists(); 331 } 332 if (IsRemote() && m_remote_platform_sp) 333 return m_remote_platform_sp->GetFileExists(file_spec); 334 return Platform::GetFileExists(file_spec); 335 } 336 337 uint32_t 338 PlatformPOSIX::GetFilePermissions (const lldb_private::FileSpec &file_spec, 339 lldb_private::Error &error) 340 { 341 if (IsHost()) 342 { 343 return File::GetPermissions(file_spec.GetPath().c_str(), error); 344 } 345 if (IsRemote() && m_remote_platform_sp) 346 return m_remote_platform_sp->GetFilePermissions(file_spec, error); 347 return Platform::GetFilePermissions(file_spec, error); 348 349 } 350 351 352 lldb_private::Error 353 PlatformPOSIX::GetFile (const lldb_private::FileSpec& source /* remote file path */, 354 const lldb_private::FileSpec& destination /* local file path */) 355 { 356 // Check the args, first. 357 std::string src_path (source.GetPath()); 358 if (src_path.empty()) 359 return Error("unable to get file path for source"); 360 std::string dst_path (destination.GetPath()); 361 if (dst_path.empty()) 362 return Error("unable to get file path for destination"); 363 if (IsHost()) 364 { 365 if (FileSpec::Equal(source, destination, true)) 366 return Error("local scenario->source and destination are the same file path: no operation performed"); 367 // cp src dst 368 StreamString cp_command; 369 cp_command.Printf("cp %s %s", src_path.c_str(), dst_path.c_str()); 370 int status; 371 RunShellCommand(cp_command.GetData(), 372 NULL, 373 &status, 374 NULL, 375 NULL, 376 10); 377 if (status != 0) 378 return Error("unable to perform copy"); 379 return Error(); 380 } 381 else if (IsRemote() && m_remote_platform_sp) 382 { 383 if (GetSupportsRSync()) 384 { 385 StreamString command; 386 if (GetIgnoresRemoteHostname()) 387 { 388 if (!GetRSyncPrefix()) 389 command.Printf("rsync %s %s %s", 390 GetRSyncOpts(), 391 src_path.c_str(), 392 dst_path.c_str()); 393 else 394 command.Printf("rsync %s %s%s %s", 395 GetRSyncOpts(), 396 GetRSyncPrefix(), 397 src_path.c_str(), 398 dst_path.c_str()); 399 } 400 else 401 command.Printf("rsync %s %s:%s %s", 402 GetRSyncOpts(), 403 m_remote_platform_sp->GetHostname(), 404 src_path.c_str(), 405 dst_path.c_str()); 406 Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); 407 if (log) 408 log->Printf("[GetFile] Running command: %s\n", command.GetData()); 409 int retcode; 410 Host::RunShellCommand(command.GetData(), 411 NULL, 412 &retcode, 413 NULL, 414 NULL, 415 60); 416 if (retcode == 0) 417 return Error(); 418 // If we are here, rsync has failed - let's try the slow way before giving up 419 } 420 // open src and dst 421 // read/write, read/write, read/write, ... 422 // close src 423 // close dst 424 Log *log(GetLogIfAnyCategoriesSet(LIBLLDB_LOG_PLATFORM)); 425 if (log) 426 log->Printf("[GetFile] Using block by block transfer....\n"); 427 Error error; 428 user_id_t fd_src = OpenFile (source, 429 File::eOpenOptionRead, 430 File::ePermissionsDefault, 431 error); 432 433 if (fd_src == UINT64_MAX) 434 return Error("unable to open source file"); 435 436 uint32_t permissions = GetFilePermissions(source, error); 437 438 if (permissions == 0) 439 permissions = File::ePermissionsDefault; 440 441 user_id_t fd_dst = Host::OpenFile(destination, 442 File::eOpenOptionCanCreate | File::eOpenOptionWrite | File::eOpenOptionTruncate, 443 permissions, 444 error); 445 446 if (fd_dst == UINT64_MAX) 447 { 448 if (error.Success()) 449 error.SetErrorString("unable to open destination file"); 450 } 451 452 if (error.Success()) 453 { 454 lldb::DataBufferSP buffer_sp(new DataBufferHeap(1024, 0)); 455 uint64_t offset = 0; 456 error.Clear(); 457 while (error.Success()) 458 { 459 const uint64_t n_read = ReadFile (fd_src, 460 offset, 461 buffer_sp->GetBytes(), 462 buffer_sp->GetByteSize(), 463 error); 464 if (error.Fail()) 465 break; 466 if (n_read == 0) 467 break; 468 if (Host::WriteFile(fd_dst, 469 offset, 470 buffer_sp->GetBytes(), 471 n_read, 472 error) != n_read) 473 { 474 if (!error.Fail()) 475 error.SetErrorString("unable to write to destination file"); 476 break; 477 } 478 offset += n_read; 479 } 480 } 481 // Ignore the close error of src. 482 if (fd_src != UINT64_MAX) 483 CloseFile(fd_src, error); 484 // And close the dst file descriptot. 485 if (fd_dst != UINT64_MAX && !Host::CloseFile(fd_dst, error)) 486 { 487 if (!error.Fail()) 488 error.SetErrorString("unable to close destination file"); 489 490 } 491 return error; 492 } 493 return Platform::GetFile(source,destination); 494 } 495 496 std::string 497 PlatformPOSIX::GetPlatformSpecificConnectionInformation() 498 { 499 StreamString stream; 500 if (GetSupportsRSync()) 501 { 502 stream.PutCString("rsync"); 503 if ( (GetRSyncOpts() && *GetRSyncOpts()) || 504 (GetRSyncPrefix() && *GetRSyncPrefix()) || 505 GetIgnoresRemoteHostname()) 506 { 507 stream.Printf(", options: "); 508 if (GetRSyncOpts() && *GetRSyncOpts()) 509 stream.Printf("'%s' ",GetRSyncOpts()); 510 stream.Printf(", prefix: "); 511 if (GetRSyncPrefix() && *GetRSyncPrefix()) 512 stream.Printf("'%s' ",GetRSyncPrefix()); 513 if (GetIgnoresRemoteHostname()) 514 stream.Printf("ignore remote-hostname "); 515 } 516 } 517 if (GetSupportsSSH()) 518 { 519 stream.PutCString("ssh"); 520 if (GetSSHOpts() && *GetSSHOpts()) 521 stream.Printf(", options: '%s' ",GetSSHOpts()); 522 } 523 if (GetLocalCacheDirectory() && *GetLocalCacheDirectory()) 524 stream.Printf("cache dir: %s",GetLocalCacheDirectory()); 525 if (stream.GetSize()) 526 return stream.GetData(); 527 else 528 return ""; 529 } 530 531 bool 532 PlatformPOSIX::CalculateMD5 (const FileSpec& file_spec, 533 uint64_t &low, 534 uint64_t &high) 535 { 536 if (IsHost()) 537 return Platform::CalculateMD5 (file_spec, low, high); 538 if (m_remote_platform_sp) 539 return m_remote_platform_sp->CalculateMD5(file_spec, low, high); 540 return false; 541 } 542