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