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/path_service.h"
22 #include "base/process/launch.h"
23 #include "base/strings/string_util.h"
24 #include "base/synchronization/lock.h"
28 // Not needed for OS_CHROMEOS.
30 enum LinuxDistroState
{
31 STATE_DID_NOT_CHECK
= 0,
32 STATE_CHECK_STARTED
= 1,
33 STATE_CHECK_FINISHED
= 2,
36 // Helper class for GetLinuxDistro().
37 class LinuxDistroHelper
{
39 // Retrieves the Singleton.
40 static LinuxDistroHelper
* GetInstance() {
41 return Singleton
<LinuxDistroHelper
>::get();
44 // The simple state machine goes from:
45 // STATE_DID_NOT_CHECK -> STATE_CHECK_STARTED -> STATE_CHECK_FINISHED.
46 LinuxDistroHelper() : state_(STATE_DID_NOT_CHECK
) {}
47 ~LinuxDistroHelper() {}
49 // Retrieve the current state, if we're in STATE_DID_NOT_CHECK,
50 // we automatically move to STATE_CHECK_STARTED so nobody else will
52 LinuxDistroState
State() {
53 base::AutoLock
scoped_lock(lock_
);
54 if (STATE_DID_NOT_CHECK
== state_
) {
55 state_
= STATE_CHECK_STARTED
;
56 return STATE_DID_NOT_CHECK
;
61 // Indicate the check finished, move to STATE_CHECK_FINISHED.
62 void CheckFinished() {
63 base::AutoLock
scoped_lock(lock_
);
64 DCHECK_EQ(STATE_CHECK_STARTED
, state_
);
65 state_
= STATE_CHECK_FINISHED
;
70 LinuxDistroState state_
;
72 #endif // if defined(OS_LINUX)
78 // Account for the terminating null character.
79 static const int kDistroSize
= 128 + 1;
81 // We use this static string to hold the Linux distro info. If we
82 // crash, the crash handler code will send this in the crash dump.
83 char g_linux_distro
[kDistroSize
] =
84 #if defined(OS_CHROMEOS)
86 #elif defined(OS_ANDROID)
88 #else // if defined(OS_LINUX)
92 std::string
GetLinuxDistro() {
93 #if defined(OS_CHROMEOS) || defined(OS_ANDROID)
94 return g_linux_distro
;
95 #elif defined(OS_LINUX)
96 LinuxDistroHelper
* distro_state_singleton
= LinuxDistroHelper::GetInstance();
97 LinuxDistroState state
= distro_state_singleton
->State();
98 if (STATE_CHECK_FINISHED
== state
)
99 return g_linux_distro
;
100 if (STATE_CHECK_STARTED
== state
)
101 return "Unknown"; // Don't wait for other thread to finish.
102 DCHECK_EQ(state
, STATE_DID_NOT_CHECK
);
103 // We do this check only once per process. If it fails, there's
104 // little reason to believe it will work if we attempt to run
105 // lsb_release again.
106 std::vector
<std::string
> argv
;
107 argv
.push_back("lsb_release");
108 argv
.push_back("-d");
110 base::GetAppOutput(CommandLine(argv
), &output
);
111 if (output
.length() > 0) {
112 // lsb_release -d should return: Description:<tab>Distro Info
113 const char field
[] = "Description:\t";
114 if (output
.compare(0, strlen(field
), field
) == 0) {
115 SetLinuxDistro(output
.substr(strlen(field
)));
118 distro_state_singleton
->CheckFinished();
119 return g_linux_distro
;
126 void SetLinuxDistro(const std::string
& distro
) {
127 std::string trimmed_distro
;
128 base::TrimWhitespaceASCII(distro
, base::TRIM_ALL
, &trimmed_distro
);
129 base::strlcpy(g_linux_distro
, trimmed_distro
.c_str(), kDistroSize
);
132 pid_t
FindThreadIDWithSyscall(pid_t pid
, const std::string
& expected_data
,
133 bool* syscall_supported
) {
135 snprintf(buf
, sizeof(buf
), "/proc/%d/task", pid
);
137 if (syscall_supported
!= NULL
)
138 *syscall_supported
= false;
140 DIR* task
= opendir(buf
);
142 DLOG(WARNING
) << "Cannot open " << buf
;
146 std::vector
<pid_t
> tids
;
148 while ((dent
= readdir(task
))) {
150 const unsigned long int tid_ul
= strtoul(dent
->d_name
, &endptr
, 10);
151 if (tid_ul
== ULONG_MAX
|| *endptr
)
153 tids
.push_back(tid_ul
);
157 scoped_ptr
<char[]> syscall_data(new char[expected_data
.length()]);
158 for (std::vector
<pid_t
>::const_iterator
159 i
= tids
.begin(); i
!= tids
.end(); ++i
) {
160 const pid_t current_tid
= *i
;
161 snprintf(buf
, sizeof(buf
), "/proc/%d/task/%d/syscall", pid
, current_tid
);
162 int fd
= open(buf
, O_RDONLY
);
165 if (syscall_supported
!= NULL
)
166 *syscall_supported
= true;
167 bool read_ret
= ReadFromFD(fd
, syscall_data
.get(), expected_data
.length());
172 if (0 == strncmp(expected_data
.c_str(), syscall_data
.get(),
173 expected_data
.length())) {