View Javadoc
1   /*
2    * Copyright 2025 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.software.os.unix.aix;
6   
7   import oshi.software.os.ApplicationInfo;
8   import oshi.util.Constants;
9   import oshi.util.ExecutingCommand;
10  import oshi.util.ParseUtil;
11  
12  import java.util.ArrayList;
13  import java.util.LinkedHashSet;
14  import java.util.List;
15  import java.util.Set;
16  import java.util.Map;
17  import java.util.LinkedHashMap;
18  import java.util.regex.Pattern;
19  
20  public final class AixInstalledApps {
21  
22      private static final Pattern COLON_PATTERN = Pattern.compile(":");
23  
24      private AixInstalledApps() {
25      }
26  
27      public static List<ApplicationInfo> queryInstalledApps() {
28          // https://www.ibm.com/docs/en/aix/7.1.0?topic=l-lslpp-command
29          List<String> output = ExecutingCommand.runNative("lslpp -Lc");
30          return parseAixAppInfo(output);
31      }
32  
33      private static List<ApplicationInfo> parseAixAppInfo(List<String> lines) {
34          Set<ApplicationInfo> appInfoSet = new LinkedHashSet<>();
35          String architecture = System.getProperty("os.arch");
36          boolean isFirstLine = true;
37          for (String line : lines) {
38              if (isFirstLine) {
39                  isFirstLine = false;
40                  continue; // Skip the first line as it consists column names
41              }
42              /*
43               * Sample output: (1) devices.chrp.IBM.lhca:devices.chrp.IBM.lhca.rte:7.1.5.30: : :C:F:Infiniband Logical
44               * HCA Runtime Environment: : : : : : :0:0:/:1837 (2) bash:bash-5.0.18-1:5.0.18-1: : :C:R:The GNU Bourne
45               * Again shell (bash) version 5.0.18: :/bin/rpm -e bash: : : : :0: :(none):Fri Sep 18 15:53:11 2020
46               */
47              // split by the colon character
48              String[] parts = COLON_PATTERN.split(line, -1); // -1 to keep empty fields
49              String name = ParseUtil.getStringValueOrUnknown(parts[0]);
50              if (name.equals(Constants.UNKNOWN)) {
51                  continue;
52              }
53              String version = ParseUtil.getStringValueOrUnknown(parts[2]);
54              String vendor = Constants.UNKNOWN; // lslpp command does not provide vendor info, hence, assigning as
55              // unknown
56              // Build Date is of two formats YYWW and EEE MMM dd HH:mm:ss yyyy
57              String buildDate = ParseUtil.getStringValueOrUnknown(parts[17]);
58              long timestamp = 0;
59              if (!buildDate.equals(Constants.UNKNOWN)) {
60                  if (buildDate.matches("\\d{4}")) {
61                      // Convert to ISO week date string (e.g., 1125 -> 2011-W25-2 for Monday)
62                      String isoWeekString = "20" + buildDate.substring(0, 2) + "-W" + buildDate.substring(2) + "-2";
63                      timestamp = ParseUtil.parseDateToEpoch(isoWeekString, "YYYY-'W'ww-e");
64                  } else {
65                      timestamp = ParseUtil.parseDateToEpoch(buildDate, "EEE MMM dd HH:mm:ss yyyy");
66                  }
67              }
68              String description = ParseUtil.getStringValueOrUnknown(parts[7].trim());
69              String installPath = ParseUtil.getStringValueOrUnknown(parts[16].trim());
70              Map<String, String> additionalInfo = new LinkedHashMap<>();
71              additionalInfo.put("architecture", architecture);
72              additionalInfo.put("description", description);
73              additionalInfo.put("installPath", installPath);
74              ApplicationInfo app = new ApplicationInfo(name, version, vendor, timestamp, additionalInfo);
75              appInfoSet.add(app);
76          }
77  
78          return new ArrayList<>(appInfoSet);
79      }
80  }