PlatformPOSIX.cpp revision 269024
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
24using namespace lldb;
25using namespace lldb_private;
26
27
28//------------------------------------------------------------------
29/// Default Constructor
30//------------------------------------------------------------------
31PlatformPOSIX::PlatformPOSIX (bool is_host) :
32Platform(is_host),  // This is the local host platform
33m_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//------------------------------------------------------------------
43PlatformPOSIX::~PlatformPOSIX()
44{
45}
46
47lldb_private::OptionGroupOptions*
48PlatformPOSIX::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
60lldb_private::Error
61PlatformPOSIX::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
79Error
80PlatformPOSIX::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
88Error
89PlatformPOSIX::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
97Error
98PlatformPOSIX::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
106lldb::user_id_t
107PlatformPOSIX::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
120bool
121PlatformPOSIX::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
131uint64_t
132PlatformPOSIX::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
146uint64_t
147PlatformPOSIX::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
161static uint32_t
162chown_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
190lldb_private::Error
191PlatformPOSIX::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
338lldb::user_id_t
339PlatformPOSIX::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
349Error
350PlatformPOSIX::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
360bool
361PlatformPOSIX::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
371Error
372PlatformPOSIX::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
382lldb_private::Error
383PlatformPOSIX::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
527std::string
528PlatformPOSIX::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
562bool
563PlatformPOSIX::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
574lldb_private::ConstString
575PlatformPOSIX::GetRemoteWorkingDirectory()
576{
577    if (IsRemote() && m_remote_platform_sp)
578        return m_remote_platform_sp->GetRemoteWorkingDirectory();
579    else
580        return Platform::GetRemoteWorkingDirectory();
581}
582
583bool
584PlatformPOSIX::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
592void
593PlatformPOSIX::CalculateTrapHandlerSymbolNames ()
594{
595    m_trap_handlers.push_back (ConstString ("_sigtramp"));
596}
597