View Javadoc
1   /*
2    * Copyright 2016-2022 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.software.os.unix.freebsd;
6   
7   import java.io.File;
8   import java.nio.file.PathMatcher;
9   import java.util.ArrayList;
10  import java.util.HashMap;
11  import java.util.List;
12  import java.util.Map;
13  
14  import oshi.annotation.concurrent.ThreadSafe;
15  import oshi.software.common.AbstractFileSystem;
16  import oshi.software.os.OSFileStore;
17  import oshi.software.os.linux.LinuxOSFileStore;
18  import oshi.util.ExecutingCommand;
19  import oshi.util.FileSystemUtil;
20  import oshi.util.ParseUtil;
21  import oshi.util.platform.unix.freebsd.BsdSysctlUtil;
22  
23  /**
24   * The FreeBSD File System contains {@link oshi.software.os.OSFileStore}s which are a storage pool, device, partition,
25   * volume, concrete file system or other implementation specific means of file storage.
26   */
27  @ThreadSafe
28  public final class FreeBsdFileSystem extends AbstractFileSystem {
29  
30      public static final String OSHI_FREEBSD_FS_PATH_EXCLUDES = "oshi.os.freebsd.filesystem.path.excludes";
31      public static final String OSHI_FREEBSD_FS_PATH_INCLUDES = "oshi.os.freebsd.filesystem.path.includes";
32      public static final String OSHI_FREEBSD_FS_VOLUME_EXCLUDES = "oshi.os.freebsd.filesystem.volume.excludes";
33      public static final String OSHI_FREEBSD_FS_VOLUME_INCLUDES = "oshi.os.freebsd.filesystem.volume.includes";
34  
35      private static final List<PathMatcher> FS_PATH_EXCLUDES = FileSystemUtil
36              .loadAndParseFileSystemConfig(OSHI_FREEBSD_FS_PATH_EXCLUDES);
37      private static final List<PathMatcher> FS_PATH_INCLUDES = FileSystemUtil
38              .loadAndParseFileSystemConfig(OSHI_FREEBSD_FS_PATH_INCLUDES);
39      private static final List<PathMatcher> FS_VOLUME_EXCLUDES = FileSystemUtil
40              .loadAndParseFileSystemConfig(OSHI_FREEBSD_FS_VOLUME_EXCLUDES);
41      private static final List<PathMatcher> FS_VOLUME_INCLUDES = FileSystemUtil
42              .loadAndParseFileSystemConfig(OSHI_FREEBSD_FS_VOLUME_INCLUDES);
43  
44      @Override
45      public List<OSFileStore> getFileStores(boolean localOnly) {
46          // TODO map mount point to UUID?
47          // is /etc/fstab useful for this?
48          Map<String, String> uuidMap = new HashMap<>();
49          // Now grab dmssg output
50          String device = "";
51          for (String line : ExecutingCommand.runNative("geom part list")) {
52              if (line.contains("Name: ")) {
53                  device = line.substring(line.lastIndexOf(' ') + 1);
54              }
55              // If we aren't working with a current partition, continue
56              if (device.isEmpty()) {
57                  continue;
58              }
59              line = line.trim();
60              if (line.startsWith("rawuuid:")) {
61                  uuidMap.put(device, line.substring(line.lastIndexOf(' ') + 1));
62                  device = "";
63              }
64          }
65  
66          List<OSFileStore> fsList = new ArrayList<>();
67  
68          // Get inode usage data
69          Map<String, Long> inodeFreeMap = new HashMap<>();
70          Map<String, Long> inodeTotalMap = new HashMap<>();
71          for (String line : ExecutingCommand.runNative("df -i")) {
72              /*- Sample Output:
73              Filesystem    1K-blocks   Used   Avail Capacity iused  ifree %iused  Mounted on
74              /dev/twed0s1a   2026030 584112 1279836    31%    2751 279871    1%   /
75              */
76              if (line.startsWith("/")) {
77                  String[] split = ParseUtil.whitespaces.split(line);
78                  if (split.length > 7) {
79                      inodeFreeMap.put(split[0], ParseUtil.parseLongOrDefault(split[6], 0L));
80                      // total is used + free
81                      inodeTotalMap.put(split[0],
82                              inodeFreeMap.get(split[0]) + ParseUtil.parseLongOrDefault(split[5], 0L));
83                  }
84              }
85          }
86  
87          // Get mount table
88          for (String fs : ExecutingCommand.runNative("mount -p")) {
89              String[] split = ParseUtil.whitespaces.split(fs);
90              if (split.length < 5) {
91                  continue;
92              }
93              // 1st field is volume name
94              // 2nd field is mount point
95              // 3rd field is fs type
96              // 4th field is options
97              // other fields ignored
98              String volume = split[0];
99              String path = split[1];
100             String type = split[2];
101             String options = split[3];
102 
103             // Skip non-local drives if requested, and exclude pseudo file systems
104             if ((localOnly && NETWORK_FS_TYPES.contains(type))
105                     || !path.equals("/") && (PSEUDO_FS_TYPES.contains(type) || FileSystemUtil.isFileStoreExcluded(path,
106                             volume, FS_PATH_INCLUDES, FS_PATH_EXCLUDES, FS_VOLUME_INCLUDES, FS_VOLUME_EXCLUDES))) {
107                 continue;
108             }
109 
110             String name = path.substring(path.lastIndexOf('/') + 1);
111             // Special case for /, pull last element of volume instead
112             if (name.isEmpty()) {
113                 name = volume.substring(volume.lastIndexOf('/') + 1);
114             }
115             File f = new File(path);
116             long totalSpace = f.getTotalSpace();
117             long usableSpace = f.getUsableSpace();
118             long freeSpace = f.getFreeSpace();
119 
120             String description;
121             if (volume.startsWith("/dev") || path.equals("/")) {
122                 description = "Local Disk";
123             } else if (volume.equals("tmpfs")) {
124                 description = "Ram Disk";
125             } else if (NETWORK_FS_TYPES.contains(type)) {
126                 description = "Network Disk";
127             } else {
128                 description = "Mount Point";
129             }
130             // Match UUID
131             String uuid = uuidMap.getOrDefault(name, "");
132 
133             fsList.add(new LinuxOSFileStore(name, volume, name, path, options, uuid, "", description, type, freeSpace,
134                     usableSpace, totalSpace, inodeFreeMap.containsKey(path) ? inodeFreeMap.get(path) : 0L,
135                     inodeTotalMap.containsKey(path) ? inodeTotalMap.get(path) : 0L));
136         }
137         return fsList;
138     }
139 
140     @Override
141     public long getOpenFileDescriptors() {
142         return BsdSysctlUtil.sysctl("kern.openfiles", 0);
143     }
144 
145     @Override
146     public long getMaxFileDescriptors() {
147         return BsdSysctlUtil.sysctl("kern.maxfiles", 0);
148     }
149 
150     @Override
151     public long getMaxFileDescriptorsPerProcess() {
152         // On FreeBsd there is no process specific system-wide limit, so the general limit is returned
153         return getMaxFileDescriptors();
154     }
155 }