1 // Copyright 2017 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "handler/mac/file_limit_annotation.h"
16 
17 #include <dirent.h>
18 #include <errno.h>
19 #include <string.h>
20 #include <sys/resource.h>
21 #include <sys/sysctl.h>
22 #include <sys/types.h>
23 
24 #include <string>
25 
26 #include "base/format_macros.h"
27 #include "base/stl_util.h"
28 #include "base/strings/stringprintf.h"
29 #include "client/crashpad_info.h"
30 #include "client/simple_string_dictionary.h"
31 #include "util/posix/scoped_dir.h"
32 
33 namespace crashpad {
34 
35 namespace {
36 
37 // rv is the return value from sysctl() or sysctlbyname(), and value and size
38 // are the pointers passed as oldp and oldlenp. If sysctl() failed, the returned
39 // string will be "E" followed by the error number. If there was a size
40 // mismatch, the returned string will be "Z" followed by the size indicated by
41 // sysctl(). Normally, a string representation of *value will be returned.
FormatFromSysctl(int rv,const int * value,const size_t * size)42 std::string FormatFromSysctl(int rv, const int* value, const size_t* size) {
43   if (rv != 0) {
44     return base::StringPrintf("E%d", errno);
45   }
46   if (*size != sizeof(*value)) {
47     return base::StringPrintf("Z%zu", *size);
48   }
49   return base::StringPrintf("%d", *value);
50 }
51 
52 // Counts the number of open file descriptors in the process and returns it as a
53 // string. This /dev/fd and the value returned will include the open file
54 // descriptor for that directory. If opendir() fails, the returned string will
55 // be "E" followed by the error number. If readdir() fails, it will be "R"
56 // followed by the error number.
CountOpenFileDescriptors()57 std::string CountOpenFileDescriptors() {
58   DIR* dir = opendir("/dev/fd");
59   if (!dir) {
60     return base::StringPrintf("E%d", errno);
61   }
62 
63   ScopedDIR dir_owner(dir);
64 
65   dirent* entry;
66   int count = 0;
67   while ((errno = 0, entry = readdir(dir)) != nullptr) {
68     const char* entry_name = entry->d_name;
69     if (strcmp(entry_name, ".") == 0 || strcmp(entry_name, "..") == 0) {
70       continue;
71     }
72 
73     ++count;
74   }
75 
76   if (errno != 0) {
77     return base::StringPrintf("R%d", errno);
78   }
79 
80   return base::StringPrintf("%d", count);
81 }
82 
83 // Returns a string for |limit|, or "inf" if |limit| is RLIM_INFINITY.
StringForRLim(rlim_t limit)84 std::string StringForRLim(rlim_t limit) {
85   if (limit == RLIM_INFINITY) {
86     return std::string("inf");
87   }
88 
89   return base::StringPrintf("%" PRIu64, limit);
90 }
91 
92 }  // namespace
93 
RecordFileLimitAnnotation()94 void RecordFileLimitAnnotation() {
95   CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();
96   SimpleStringDictionary* simple_annotations =
97       crashpad_info->simple_annotations();
98   if (!simple_annotations) {
99     simple_annotations = new SimpleStringDictionary();
100     crashpad_info->set_simple_annotations(simple_annotations);
101   }
102 
103   int value;
104   size_t size = sizeof(value);
105   std::string num_files = FormatFromSysctl(
106       sysctlbyname("kern.num_files", &value, &size, nullptr, 0), &value, &size);
107 
108   int mib[] = {CTL_KERN, KERN_MAXFILES};
109   size = sizeof(value);
110   std::string max_files = FormatFromSysctl(
111       sysctl(mib, base::size(mib), &value, &size, nullptr, 0), &value, &size);
112 
113   std::string open_files = CountOpenFileDescriptors();
114 
115   rlimit limit;
116   std::string nofile;
117   if (getrlimit(RLIMIT_NOFILE, &limit) != 0) {
118     nofile = base::StringPrintf("E%d,E%d", errno, errno);
119   } else {
120     nofile =
121         StringForRLim(limit.rlim_cur) + "," + StringForRLim(limit.rlim_max);
122   }
123 
124   std::string annotation = base::StringPrintf("%s,%s,%s,%s",
125                                               num_files.c_str(),
126                                               max_files.c_str(),
127                                               open_files.c_str(),
128                                               nofile.c_str());
129   simple_annotations->SetKeyValue("file-limits", annotation.c_str());
130 }
131 
132 }  // namespace crashpad
133