View Javadoc
1   /*
2    * Copyright 2020-2022 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.software.os.unix.aix;
6   
7   import static oshi.software.os.OSProcess.State.INVALID;
8   import static oshi.software.os.OSService.State.RUNNING;
9   import static oshi.software.os.OSService.State.STOPPED;
10  import static oshi.util.Memoizer.defaultExpiration;
11  import static oshi.util.Memoizer.memoize;
12  
13  import java.io.File;
14  import java.util.ArrayList;
15  import java.util.HashMap;
16  import java.util.List;
17  import java.util.Map;
18  import java.util.Map.Entry;
19  import java.util.Set;
20  import java.util.function.Supplier;
21  import java.util.stream.Collectors;
22  
23  import com.sun.jna.Native;
24  import com.sun.jna.platform.unix.aix.Perfstat.perfstat_partition_config_t;
25  import com.sun.jna.platform.unix.aix.Perfstat.perfstat_process_t;
26  
27  import oshi.annotation.concurrent.ThreadSafe;
28  import oshi.driver.unix.aix.Uptime;
29  import oshi.driver.unix.aix.Who;
30  import oshi.driver.unix.aix.perfstat.PerfstatConfig;
31  import oshi.driver.unix.aix.perfstat.PerfstatProcess;
32  import oshi.jna.platform.unix.AixLibc;
33  import oshi.software.common.AbstractOperatingSystem;
34  import oshi.software.os.FileSystem;
35  import oshi.software.os.InternetProtocolStats;
36  import oshi.software.os.NetworkParams;
37  import oshi.software.os.OSProcess;
38  import oshi.software.os.OSService;
39  import oshi.software.os.OSThread;
40  import oshi.util.ExecutingCommand;
41  import oshi.util.ParseUtil;
42  import oshi.util.Util;
43  import oshi.util.tuples.Pair;
44  
45  /**
46   * AIX (Advanced Interactive eXecutive) is a series of proprietary Unix operating systems developed and sold by IBM for
47   * several of its computer platforms.
48   */
49  @ThreadSafe
50  public class AixOperatingSystem extends AbstractOperatingSystem {
51  
52      private final Supplier<perfstat_partition_config_t> config = memoize(PerfstatConfig::queryConfig);
53      private final Supplier<perfstat_process_t[]> procCpu = memoize(PerfstatProcess::queryProcesses,
54              defaultExpiration());
55  
56      private static final long BOOTTIME = querySystemBootTimeMillis() / 1000L;
57  
58      @Override
59      public String queryManufacturer() {
60          return "IBM";
61      }
62  
63      @Override
64      public Pair<String, OSVersionInfo> queryFamilyVersionInfo() {
65          perfstat_partition_config_t cfg = config.get();
66  
67          String systemName = System.getProperty("os.name");
68          String archName = System.getProperty("os.arch");
69          String versionNumber = System.getProperty("os.version");
70          if (Util.isBlank(versionNumber)) {
71              versionNumber = ExecutingCommand.getFirstAnswer("oslevel");
72          }
73          String releaseNumber = Native.toString(cfg.OSBuild);
74          if (Util.isBlank(releaseNumber)) {
75              releaseNumber = ExecutingCommand.getFirstAnswer("oslevel -s");
76          } else {
77              // strip leading date
78              int idx = releaseNumber.lastIndexOf(' ');
79              if (idx > 0 && idx < releaseNumber.length()) {
80                  releaseNumber = releaseNumber.substring(idx + 1);
81              }
82          }
83          return new Pair<>(systemName, new OSVersionInfo(versionNumber, archName, releaseNumber));
84      }
85  
86      @Override
87      protected int queryBitness(int jvmBitness) {
88          if (jvmBitness == 64) {
89              return 64;
90          }
91          // 9th bit of conf is 64-bit kernel
92          return (config.get().conf & 0x0080_0000) > 0 ? 64 : 32;
93      }
94  
95      @Override
96      public FileSystem getFileSystem() {
97          return new AixFileSystem();
98      }
99  
100     @Override
101     public InternetProtocolStats getInternetProtocolStats() {
102         return new AixInternetProtocolStats();
103     }
104 
105     @Override
106     public List<OSProcess> queryAllProcesses() {
107         return getProcessListFromProcfs(-1);
108     }
109 
110     @Override
111     public List<OSProcess> queryChildProcesses(int parentPid) {
112         List<OSProcess> allProcs = queryAllProcesses();
113         Set<Integer> descendantPids = getChildrenOrDescendants(allProcs, parentPid, false);
114         return allProcs.stream().filter(p -> descendantPids.contains(p.getProcessID())).collect(Collectors.toList());
115     }
116 
117     @Override
118     public List<OSProcess> queryDescendantProcesses(int parentPid) {
119         List<OSProcess> allProcs = queryAllProcesses();
120         Set<Integer> descendantPids = getChildrenOrDescendants(allProcs, parentPid, true);
121         return allProcs.stream().filter(p -> descendantPids.contains(p.getProcessID())).collect(Collectors.toList());
122     }
123 
124     @Override
125     public OSProcess getProcess(int pid) {
126         List<OSProcess> procs = getProcessListFromProcfs(pid);
127         if (procs.isEmpty()) {
128             return null;
129         }
130         return procs.get(0);
131     }
132 
133     private List<OSProcess> getProcessListFromProcfs(int pid) {
134         List<OSProcess> procs = new ArrayList<>();
135         // Fetch user/system times from perfstat
136         perfstat_process_t[] perfstat = procCpu.get();
137         Map<Integer, Pair<Long, Long>> cpuMap = new HashMap<>();
138         for (perfstat_process_t stat : perfstat) {
139             int statpid = (int) stat.pid;
140             if (pid < 0 || statpid == pid) {
141                 cpuMap.put(statpid, new Pair<>((long) stat.ucpu_time, (long) stat.scpu_time));
142             }
143         }
144 
145         // Keys of this map are pids
146         for (Entry<Integer, Pair<Long, Long>> entry : cpuMap.entrySet()) {
147             OSProcess proc = new AixOSProcess(entry.getKey(), entry.getValue(), procCpu, this);
148             if (proc.getState() != INVALID) {
149                 procs.add(proc);
150             }
151         }
152         return procs;
153     }
154 
155     @Override
156     public int getProcessId() {
157         return AixLibc.INSTANCE.getpid();
158     }
159 
160     @Override
161     public int getProcessCount() {
162         return procCpu.get().length;
163     }
164 
165     @Override
166     public int getThreadId() {
167         return AixLibc.INSTANCE.thread_self();
168     }
169 
170     @Override
171     public OSThread getCurrentThread() {
172         OSProcess proc = getCurrentProcess();
173         final int tid = getThreadId();
174         return proc.getThreadDetails().stream().filter(t -> t.getThreadId() == tid).findFirst()
175                 .orElse(new AixOSThread(proc.getProcessID(), tid));
176     }
177 
178     @Override
179     public int getThreadCount() {
180         long tc = 0L;
181         for (perfstat_process_t proc : procCpu.get()) {
182             tc += proc.num_threads;
183         }
184         return (int) tc;
185     }
186 
187     @Override
188     public long getSystemUptime() {
189         return System.currentTimeMillis() / 1000L - BOOTTIME;
190     }
191 
192     @Override
193     public long getSystemBootTime() {
194         return BOOTTIME;
195     }
196 
197     private static long querySystemBootTimeMillis() {
198         long bootTime = Who.queryBootTime();
199         if (bootTime >= 1000L) {
200             return bootTime;
201         }
202         return System.currentTimeMillis() - Uptime.queryUpTime();
203     }
204 
205     @Override
206     public NetworkParams getNetworkParams() {
207         return new AixNetworkParams();
208     }
209 
210     @Override
211     public List<OSService> getServices() {
212         List<OSService> services = new ArrayList<>();
213         // Get system services from lssrc command
214         /*-
215          Output:
216          Subsystem         Group            PID          Status
217             platform_agent                    2949214      active
218             cimsys                            2490590      active
219             snmpd            tcpip            2883698      active
220             syslogd          ras              2359466      active
221             sendmail         mail             3145828      active
222             portmap          portmap          2818188      active
223             inetd            tcpip            2752656      active
224             lpd              spooler                       inoperative
225                         ...
226          */
227         List<String> systemServicesInfoList = ExecutingCommand.runNative("lssrc -a");
228         if (systemServicesInfoList.size() > 1) {
229             systemServicesInfoList.remove(0); // remove header
230             for (String systemService : systemServicesInfoList) {
231                 String[] serviceSplit = ParseUtil.whitespaces.split(systemService.trim());
232                 if (systemService.contains("active")) {
233                     if (serviceSplit.length == 4) {
234                         services.add(new OSService(serviceSplit[0], ParseUtil.parseIntOrDefault(serviceSplit[2], 0),
235                                 RUNNING));
236                     } else if (serviceSplit.length == 3) {
237                         services.add(new OSService(serviceSplit[0], ParseUtil.parseIntOrDefault(serviceSplit[1], 0),
238                                 RUNNING));
239                     }
240                 } else if (systemService.contains("inoperative")) {
241                     services.add(new OSService(serviceSplit[0], 0, STOPPED));
242                 }
243             }
244         }
245         // Get installed services from /etc/rc.d/init.d
246         File dir = new File("/etc/rc.d/init.d");
247         File[] listFiles;
248         if (dir.exists() && dir.isDirectory() && (listFiles = dir.listFiles()) != null) {
249             for (File file : listFiles) {
250                 String installedService = ExecutingCommand.getFirstAnswer(file.getAbsolutePath() + " status");
251                 // Apache httpd daemon is running with PID 3997858.
252                 if (installedService.contains("running")) {
253                     services.add(new OSService(file.getName(), ParseUtil.parseLastInt(installedService, 0), RUNNING));
254                 } else {
255                     services.add(new OSService(file.getName(), 0, STOPPED));
256                 }
257             }
258         }
259         return services;
260     }
261 }