1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/linux_util.h"
12 #include <sys/types.h>
17 #include "base/command_line.h"
18 #include "base/files/file_util.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/memory/singleton.h"
21 #include "base/process/launch.h"
22 #include "base/strings/string_util.h"
23 #include "base/synchronization/lock.h"
27 // Not needed for OS_CHROMEOS.
29 enum LinuxDistroState
{
30 STATE_DID_NOT_CHECK
= 0,
31 STATE_CHECK_STARTED
= 1,
32 STATE_CHECK_FINISHED
= 2,
35 // Helper class for GetLinuxDistro().
36 class LinuxDistroHelper
{
38 // Retrieves the Singleton.
39 static LinuxDistroHelper
* GetInstance() {
40 return Singleton
<LinuxDistroHelper
>::get();
43 // The simple state machine goes from:
44 // STATE_DID_NOT_CHECK -> STATE_CHECK_STARTED -> STATE_CHECK_FINISHED.
45 LinuxDistroHelper() : state_(STATE_DID_NOT_CHECK
) {}
46 ~LinuxDistroHelper() {}
48 // Retrieve the current state, if we're in STATE_DID_NOT_CHECK,
49 // we automatically move to STATE_CHECK_STARTED so nobody else will
51 LinuxDistroState
State() {
52 base::AutoLock
scoped_lock(lock_
);
53 if (STATE_DID_NOT_CHECK
== state_
) {
54 state_
= STATE_CHECK_STARTED
;
55 return STATE_DID_NOT_CHECK
;
60 // Indicate the check finished, move to STATE_CHECK_FINISHED.
61 void CheckFinished() {
62 base::AutoLock
scoped_lock(lock_
);
63 DCHECK_EQ(STATE_CHECK_STARTED
, state_
);
64 state_
= STATE_CHECK_FINISHED
;
69 LinuxDistroState state_
;
71 #endif // if defined(OS_LINUX)
77 // Account for the terminating null character.
78 static const int kDistroSize
= 128 + 1;
80 // We use this static string to hold the Linux distro info. If we
81 // crash, the crash handler code will send this in the crash dump.
82 char g_linux_distro
[kDistroSize
] =
83 #if defined(OS_CHROMEOS)
85 #elif defined(OS_ANDROID)
87 #else // if defined(OS_LINUX)
91 std::string
GetLinuxDistro() {
92 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
93 return g_linux_distro
;
94 #elif defined(OS_LINUX)
95 LinuxDistroHelper
* distro_state_singleton
= LinuxDistroHelper::GetInstance();
96 LinuxDistroState state
= distro_state_singleton
->State();
97 if (STATE_CHECK_FINISHED
== state
)
98 return g_linux_distro
;
99 if (STATE_CHECK_STARTED
== state
)
100 return "Unknown"; // Don't wait for other thread to finish.
101 DCHECK_EQ(state
, STATE_DID_NOT_CHECK
);
102 // We do this check only once per process. If it fails, there's
103 // little reason to believe it will work if we attempt to run
104 // lsb_release again.
105 std::vector
<std::string
> argv
;
106 argv
.push_back("lsb_release");
107 argv
.push_back("-d");
109 base::GetAppOutput(CommandLine(argv
), &output
);
110 if (output
.length() > 0) {
111 // lsb_release -d should return: Description:<tab>Distro Info
112 const char field
[] = "Description:\t";
113 if (output
.compare(0, strlen(field
), field
) == 0) {
114 SetLinuxDistro(output
.substr(strlen(field
)));
117 distro_state_singleton
->CheckFinished();
118 return g_linux_distro
;
125 void SetLinuxDistro(const std::string
& distro
) {
126 std::string trimmed_distro
;
127 base::TrimWhitespaceASCII(distro
, base::TRIM_ALL
, &trimmed_distro
);
128 base::strlcpy(g_linux_distro
, trimmed_distro
.c_str(), kDistroSize
);
131 pid_t
FindThreadIDWithSyscall(pid_t pid
, const std::string
& expected_data
,
132 bool* syscall_supported
) {
134 snprintf(buf
, sizeof(buf
), "/proc/%d/task", pid
);
136 if (syscall_supported
!= NULL
)
137 *syscall_supported
= false;
139 DIR* task
= opendir(buf
);
141 DLOG(WARNING
) << "Cannot open " << buf
;
145 std::vector
<pid_t
> tids
;
147 while ((dent
= readdir(task
))) {
149 const unsigned long int tid_ul
= strtoul(dent
->d_name
, &endptr
, 10);
150 if (tid_ul
== ULONG_MAX
|| *endptr
)
152 tids
.push_back(tid_ul
);
156 scoped_ptr
<char[]> syscall_data(new char[expected_data
.length()]);
157 for (std::vector
<pid_t
>::const_iterator
158 i
= tids
.begin(); i
!= tids
.end(); ++i
) {
159 const pid_t current_tid
= *i
;
160 snprintf(buf
, sizeof(buf
), "/proc/%d/task/%d/syscall", pid
, current_tid
);
161 int fd
= open(buf
, O_RDONLY
);
164 if (syscall_supported
!= NULL
)
165 *syscall_supported
= true;
166 bool read_ret
= ReadFromFD(fd
, syscall_data
.get(), expected_data
.length());
171 if (0 == strncmp(expected_data
.c_str(), syscall_data
.get(),
172 expected_data
.length())) {