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