View Javadoc
1   /*
2    * Copyright 2016-2022 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.software.common;
6   
7   import static oshi.software.os.OperatingSystem.ProcessFiltering.ALL_PROCESSES;
8   import static oshi.software.os.OperatingSystem.ProcessSorting.NO_SORTING;
9   import static oshi.util.Memoizer.memoize;
10  
11  import java.util.ArrayDeque;
12  import java.util.Collection;
13  import java.util.Comparator;
14  import java.util.HashSet;
15  import java.util.List;
16  import java.util.Map;
17  import java.util.Map.Entry;
18  import java.util.Queue;
19  import java.util.Set;
20  import java.util.function.Predicate;
21  import java.util.function.Supplier;
22  import java.util.stream.Collectors;
23  
24  import com.sun.jna.Platform;
25  
26  import oshi.software.os.OSProcess;
27  import oshi.software.os.OperatingSystem;
28  import oshi.util.GlobalConfig;
29  import oshi.util.tuples.Pair;
30  
31  /**
32   * Common methods for OperatingSystem implementations
33   */
34  public abstract class AbstractOperatingSystem implements OperatingSystem {
35  
36      protected static final boolean USE_WHO_COMMAND = GlobalConfig.get(GlobalConfig.OSHI_OS_UNIX_WHOCOMMAND, false);
37  
38      private final Supplier<String> manufacturer = memoize(this::queryManufacturer);
39      private final Supplier<Pair<String, OSVersionInfo>> familyVersionInfo = memoize(this::queryFamilyVersionInfo);
40      private final Supplier<Integer> bitness = memoize(this::queryPlatformBitness);
41  
42      @Override
43      public String getManufacturer() {
44          return manufacturer.get();
45      }
46  
47      protected abstract String queryManufacturer();
48  
49      @Override
50      public String getFamily() {
51          return familyVersionInfo.get().getA();
52      }
53  
54      @Override
55      public OSVersionInfo getVersionInfo() {
56          return familyVersionInfo.get().getB();
57      }
58  
59      protected abstract Pair<String, OSVersionInfo> queryFamilyVersionInfo();
60  
61      @Override
62      public int getBitness() {
63          return bitness.get();
64      }
65  
66      private int queryPlatformBitness() {
67          if (Platform.is64Bit()) {
68              return 64;
69          }
70          // Initialize based on JVM Bitness. Individual OS implementations will test
71          // if 32-bit JVM running on 64-bit OS
72          int jvmBitness = System.getProperty("os.arch").contains("64") ? 64 : 32;
73          return queryBitness(jvmBitness);
74      }
75  
76      /**
77       * Backup OS-specific query to determine bitness if previous checks fail
78       *
79       * @param jvmBitness The bitness of the JVM
80       * @return The operating system bitness
81       */
82      protected abstract int queryBitness(int jvmBitness);
83  
84      @Override
85      public List<OSProcess> getProcesses(Predicate<OSProcess> filter, Comparator<OSProcess> sort, int limit) {
86          return queryAllProcesses().stream().filter(filter == null ? ALL_PROCESSES : filter)
87                  .sorted(sort == null ? NO_SORTING : sort).limit(limit > 0 ? limit : Long.MAX_VALUE)
88                  .collect(Collectors.toList());
89      }
90  
91      protected abstract List<OSProcess> queryAllProcesses();
92  
93      @Override
94      public List<OSProcess> getChildProcesses(int parentPid, Predicate<OSProcess> filter, Comparator<OSProcess> sort,
95              int limit) {
96          // Get this pid and its children
97          List<OSProcess> childProcs = queryChildProcesses(parentPid);
98          // Extract the parent from the list
99          OSProcess parent = childProcs.stream().filter(p -> p.getProcessID() == parentPid).findAny().orElse(null);
100         // Get the parent's start time
101         long parentStartTime = parent == null ? 0 : parent.getStartTime();
102         // Get children after parent
103         return queryChildProcesses(parentPid).stream().filter(filter == null ? ALL_PROCESSES : filter)
104                 .filter(p -> p.getProcessID() != parentPid && p.getStartTime() >= parentStartTime)
105                 .sorted(sort == null ? NO_SORTING : sort).limit(limit > 0 ? limit : Long.MAX_VALUE)
106                 .collect(Collectors.toList());
107     }
108 
109     protected abstract List<OSProcess> queryChildProcesses(int parentPid);
110 
111     @Override
112     public List<OSProcess> getDescendantProcesses(int parentPid, Predicate<OSProcess> filter,
113             Comparator<OSProcess> sort, int limit) {
114         // Get this pid and its descendants
115         List<OSProcess> descendantProcs = queryDescendantProcesses(parentPid);
116         // Extract the parent from the list
117         OSProcess parent = descendantProcs.stream().filter(p -> p.getProcessID() == parentPid).findAny().orElse(null);
118         // Get the parent's start time
119         long parentStartTime = parent == null ? 0 : parent.getStartTime();
120         // Get descendants after parent
121         return queryDescendantProcesses(parentPid).stream().filter(filter == null ? ALL_PROCESSES : filter)
122                 .filter(p -> p.getProcessID() != parentPid && p.getStartTime() >= parentStartTime)
123                 .sorted(sort == null ? NO_SORTING : sort).limit(limit > 0 ? limit : Long.MAX_VALUE)
124                 .collect(Collectors.toList());
125     }
126 
127     protected abstract List<OSProcess> queryDescendantProcesses(int parentPid);
128 
129     /**
130      * Utility method for subclasses to take a full process list as input and return the children or descendants of a
131      * particular process. The process itself is also returned to more efficiently extract its start time for filtering
132      *
133      * @param allProcs       A collection of all processes
134      * @param parentPid      The process ID whose children or descendants to return
135      * @param allDescendants If false, only gets immediate children of this process. If true, gets all descendants.
136      * @return Set of children or descendants of parentPid
137      */
138     protected static Set<Integer> getChildrenOrDescendants(Collection<OSProcess> allProcs, int parentPid,
139             boolean allDescendants) {
140         Map<Integer, Integer> parentPidMap = allProcs.stream()
141                 .collect(Collectors.toMap(OSProcess::getProcessID, OSProcess::getParentProcessID));
142         return getChildrenOrDescendants(parentPidMap, parentPid, allDescendants);
143     }
144 
145     /**
146      * Utility method for subclasses to take a map of pid to parent as input and return the children or descendants of a
147      * particular process.
148      *
149      * @param parentPidMap   a map of all processes with processID as key and parentProcessID as value
150      * @param parentPid      The process ID whose children or descendants to return
151      * @param allDescendants If false, only gets immediate children of this process. If true, gets all descendants.
152      * @return Set of children or descendants of parentPid, including the parent
153      */
154     protected static Set<Integer> getChildrenOrDescendants(Map<Integer, Integer> parentPidMap, int parentPid,
155             boolean allDescendants) {
156         // Set to hold results
157         Set<Integer> descendantPids = new HashSet<>();
158         descendantPids.add(parentPid);
159         // Queue for BFS algorithm
160         Queue<Integer> queue = new ArrayDeque<>();
161         queue.add(parentPid);
162         // Add children, repeating if recursive
163         do {
164             for (int pid : getChildren(parentPidMap, queue.poll())) {
165                 if (!descendantPids.contains(pid)) {
166                     descendantPids.add(pid);
167                     queue.add(pid);
168                 }
169             }
170         } while (allDescendants && !queue.isEmpty());
171         return descendantPids;
172     }
173 
174     private static Set<Integer> getChildren(Map<Integer, Integer> parentPidMap, int parentPid) {
175         return parentPidMap.entrySet().stream()
176                 .filter(e -> e.getValue().equals(parentPid) && !e.getKey().equals(parentPid)).map(Entry::getKey)
177                 .collect(Collectors.toSet());
178     }
179 
180     @Override
181     public String toString() {
182         StringBuilder sb = new StringBuilder();
183         sb.append(getManufacturer()).append(' ').append(getFamily()).append(' ').append(getVersionInfo());
184         return sb.toString();
185     }
186 }