1
2
3
4
5 package oshi.software.os.linux;
6
7 import java.io.File;
8 import java.io.IOException;
9 import java.nio.file.Files;
10 import java.nio.file.Path;
11 import java.nio.file.PathMatcher;
12 import java.nio.file.Paths;
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Locale;
17 import java.util.Map;
18
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21
22 import com.sun.jna.Native;
23 import com.sun.jna.platform.linux.LibC;
24
25 import oshi.annotation.concurrent.ThreadSafe;
26 import oshi.software.common.AbstractFileSystem;
27 import oshi.software.os.OSFileStore;
28 import oshi.util.ExecutingCommand;
29 import oshi.util.FileSystemUtil;
30 import oshi.util.FileUtil;
31 import oshi.util.ParseUtil;
32 import oshi.util.platform.linux.DevPath;
33 import oshi.util.platform.linux.ProcPath;
34
35
36
37
38
39
40 @ThreadSafe
41 public class LinuxFileSystem extends AbstractFileSystem {
42
43 private static final Logger LOG = LoggerFactory.getLogger(LinuxFileSystem.class);
44
45 public static final String OSHI_LINUX_FS_PATH_EXCLUDES = "oshi.os.linux.filesystem.path.excludes";
46 public static final String OSHI_LINUX_FS_PATH_INCLUDES = "oshi.os.linux.filesystem.path.includes";
47 public static final String OSHI_LINUX_FS_VOLUME_EXCLUDES = "oshi.os.linux.filesystem.volume.excludes";
48 public static final String OSHI_LINUX_FS_VOLUME_INCLUDES = "oshi.os.linux.filesystem.volume.includes";
49
50 private static final List<PathMatcher> FS_PATH_EXCLUDES = FileSystemUtil
51 .loadAndParseFileSystemConfig(OSHI_LINUX_FS_PATH_EXCLUDES);
52 private static final List<PathMatcher> FS_PATH_INCLUDES = FileSystemUtil
53 .loadAndParseFileSystemConfig(OSHI_LINUX_FS_PATH_INCLUDES);
54 private static final List<PathMatcher> FS_VOLUME_EXCLUDES = FileSystemUtil
55 .loadAndParseFileSystemConfig(OSHI_LINUX_FS_VOLUME_EXCLUDES);
56 private static final List<PathMatcher> FS_VOLUME_INCLUDES = FileSystemUtil
57 .loadAndParseFileSystemConfig(OSHI_LINUX_FS_VOLUME_INCLUDES);
58
59 private static final String UNICODE_SPACE = "\\040";
60
61 @Override
62 public List<OSFileStore> getFileStores(boolean localOnly) {
63
64 Map<String, String> volumeDeviceMap = new HashMap<>();
65 File devMapper = new File(DevPath.MAPPER);
66 File[] volumes = devMapper.listFiles();
67 if (volumes != null) {
68 for (File volume : volumes) {
69 try {
70 volumeDeviceMap.put(volume.getCanonicalPath(), volume.getAbsolutePath());
71 } catch (IOException e) {
72 LOG.error("Couldn't get canonical path for {}. {}", volume.getName(), e.getMessage());
73 }
74 }
75 }
76
77 Map<String, String> uuidMap = new HashMap<>();
78 File uuidDir = new File(DevPath.DISK_BY_UUID);
79 File[] uuids = uuidDir.listFiles();
80 if (uuids != null) {
81 for (File uuid : uuids) {
82 try {
83
84 String canonicalPath = uuid.getCanonicalPath();
85 uuidMap.put(canonicalPath, uuid.getName().toLowerCase(Locale.ROOT));
86 if (volumeDeviceMap.containsKey(canonicalPath)) {
87 uuidMap.put(volumeDeviceMap.get(canonicalPath), uuid.getName().toLowerCase(Locale.ROOT));
88 }
89 } catch (IOException e) {
90 LOG.error("Couldn't get canonical path for {}. {}", uuid.getName(), e.getMessage());
91 }
92 }
93 }
94
95
96 return getFileStoreMatching(null, uuidMap, localOnly);
97 }
98
99
100 static List<OSFileStore> getFileStoreMatching(String nameToMatch, Map<String, String> uuidMap) {
101 return getFileStoreMatching(nameToMatch, uuidMap, false);
102 }
103
104 private static List<OSFileStore> getFileStoreMatching(String nameToMatch, Map<String, String> uuidMap,
105 boolean localOnly) {
106 List<OSFileStore> fsList = new ArrayList<>();
107
108 Map<String, String> labelMap = queryLabelMap();
109
110
111 List<String> mounts = FileUtil.readFile(ProcPath.MOUNTS);
112 for (String mount : mounts) {
113 String[] split = mount.split(" ");
114
115
116
117
118
119
120
121 if (split.length < 6) {
122 continue;
123 }
124
125
126 String volume = split[0].replace(UNICODE_SPACE, " ");
127 String name = volume;
128 String path = split[1].replace(UNICODE_SPACE, " ");
129 if (path.equals("/")) {
130 name = "/";
131 }
132 String type = split[2];
133
134
135 if ((localOnly && NETWORK_FS_TYPES.contains(type))
136 || !path.equals("/") && (PSEUDO_FS_TYPES.contains(type) || FileSystemUtil.isFileStoreExcluded(path,
137 volume, FS_PATH_INCLUDES, FS_PATH_EXCLUDES, FS_VOLUME_INCLUDES, FS_VOLUME_EXCLUDES))) {
138 continue;
139 }
140
141 String options = split[3];
142
143
144 if (nameToMatch != null && !nameToMatch.equals(name)) {
145 continue;
146 }
147
148 String uuid = uuidMap != null ? uuidMap.getOrDefault(split[0], "") : "";
149
150 String description;
151 if (volume.startsWith(DevPath.DEV)) {
152 description = "Local Disk";
153 } else if (volume.equals("tmpfs")) {
154 description = "Ram Disk";
155 } else if (NETWORK_FS_TYPES.contains(type)) {
156 description = "Network Disk";
157 } else {
158 description = "Mount Point";
159 }
160
161
162
163 String logicalVolume = "";
164 Path link = Paths.get(volume);
165 if (link.toFile().exists() && Files.isSymbolicLink(link)) {
166 try {
167 Path slink = Files.readSymbolicLink(link);
168 Path full = Paths.get(DevPath.MAPPER + slink.toString());
169 if (full.toFile().exists()) {
170 logicalVolume = full.normalize().toString();
171 }
172 } catch (IOException e) {
173 LOG.warn("Couldn't access symbolic path {}. {}", link, e.getMessage());
174 }
175 }
176
177 long totalInodes = 0L;
178 long freeInodes = 0L;
179 long totalSpace = 0L;
180 long usableSpace = 0L;
181 long freeSpace = 0L;
182
183 try {
184 LibC.Statvfs vfsStat = new LibC.Statvfs();
185 if (0 == LibC.INSTANCE.statvfs(path, vfsStat)) {
186 totalInodes = vfsStat.f_files.longValue();
187 freeInodes = vfsStat.f_ffree.longValue();
188
189 totalSpace = vfsStat.f_blocks.longValue() * vfsStat.f_frsize.longValue();
190 usableSpace = vfsStat.f_bavail.longValue() * vfsStat.f_frsize.longValue();
191 freeSpace = vfsStat.f_bfree.longValue() * vfsStat.f_frsize.longValue();
192 } else {
193 LOG.warn("Failed to get information to use statvfs. path: {}, Error code: {}", path,
194 Native.getLastError());
195 }
196 } catch (UnsatisfiedLinkError | NoClassDefFoundError e) {
197 LOG.error("Failed to get file counts from statvfs. {}", e.getMessage());
198 }
199
200 if (totalSpace == 0L) {
201 File tmpFile = new File(path);
202 totalSpace = tmpFile.getTotalSpace();
203 usableSpace = tmpFile.getUsableSpace();
204 freeSpace = tmpFile.getFreeSpace();
205 }
206
207 fsList.add(new LinuxOSFileStore(name, volume, labelMap.getOrDefault(path, name), path, options, uuid,
208 logicalVolume, description, type, freeSpace, usableSpace, totalSpace, freeInodes, totalInodes));
209 }
210 return fsList;
211 }
212
213 private static Map<String, String> queryLabelMap() {
214 Map<String, String> labelMap = new HashMap<>();
215 for (String line : ExecutingCommand.runNative("lsblk -o mountpoint,label")) {
216 String[] split = ParseUtil.whitespaces.split(line, 2);
217 if (split.length == 2) {
218 labelMap.put(split[0], split[1]);
219 }
220 }
221 return labelMap;
222 }
223
224 @Override
225 public long getOpenFileDescriptors() {
226 return getFileDescriptors(0);
227 }
228
229 @Override
230 public long getMaxFileDescriptors() {
231 return getFileDescriptors(2);
232 }
233
234 @Override
235 public long getMaxFileDescriptorsPerProcess() {
236 return getFileDescriptorsPerProcess();
237 }
238
239
240
241
242
243
244
245
246
247 private static long getFileDescriptors(int index) {
248 String filename = ProcPath.SYS_FS_FILE_NR;
249 if (index < 0 || index > 2) {
250 throw new IllegalArgumentException("Index must be between 0 and 2.");
251 }
252 List<String> osDescriptors = FileUtil.readFile(filename);
253 if (!osDescriptors.isEmpty()) {
254 String[] splittedLine = osDescriptors.get(0).split("\\D+");
255 return ParseUtil.parseLongOrDefault(splittedLine[index], 0L);
256 }
257 return 0L;
258 }
259
260 private static long getFileDescriptorsPerProcess() {
261 return FileUtil.getLongFromFile(ProcPath.SYS_FS_FILE_MAX);
262 }
263 }