View Javadoc
1   /*
2    * Copyright 2020-2022 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.driver.windows.registry;
6   
7   import java.util.ArrayList;
8   import java.util.List;
9   
10  import org.slf4j.Logger;
11  import org.slf4j.LoggerFactory;
12  
13  import com.sun.jna.platform.win32.Advapi32Util;
14  import com.sun.jna.platform.win32.Advapi32Util.Account;
15  import com.sun.jna.platform.win32.Advapi32Util.InfoKey;
16  import com.sun.jna.platform.win32.Win32Exception;
17  import com.sun.jna.platform.win32.WinNT;
18  import com.sun.jna.platform.win32.WinReg;
19  import com.sun.jna.platform.win32.WinReg.HKEY;
20  
21  import oshi.annotation.concurrent.ThreadSafe;
22  import oshi.software.os.OSSession;
23  
24  /**
25   * Utility to read session data from HKEY_USERS
26   */
27  @ThreadSafe
28  public final class HkeyUserData {
29  
30      private static final String PATH_DELIMITER = "\\";
31      private static final String DEFAULT_DEVICE = "Console";
32      private static final String VOLATILE_ENV_SUBKEY = "Volatile Environment";
33      private static final String CLIENTNAME = "CLIENTNAME";
34      private static final String SESSIONNAME = "SESSIONNAME";
35  
36      private static final Logger LOG = LoggerFactory.getLogger(HkeyUserData.class);
37  
38      private HkeyUserData() {
39      }
40  
41      public static List<OSSession> queryUserSessions() {
42          List<OSSession> sessions = new ArrayList<>();
43          for (String sidKey : Advapi32Util.registryGetKeys(WinReg.HKEY_USERS)) {
44              if (!sidKey.startsWith(".") && !sidKey.endsWith("_Classes")) {
45                  try {
46                      Account a = Advapi32Util.getAccountBySid(sidKey);
47                      String name = a.name;
48                      String device = DEFAULT_DEVICE;
49                      String host = a.domain; // temporary default
50                      long loginTime = 0;
51                      String keyPath = sidKey + PATH_DELIMITER + VOLATILE_ENV_SUBKEY;
52                      if (Advapi32Util.registryKeyExists(WinReg.HKEY_USERS, keyPath)) {
53                          HKEY hKey = Advapi32Util.registryGetKey(WinReg.HKEY_USERS, keyPath, WinNT.KEY_READ).getValue();
54                          // InfoKey write time is user login time
55                          InfoKey info = Advapi32Util.registryQueryInfoKey(hKey, 0);
56                          loginTime = info.lpftLastWriteTime.toTime();
57                          for (String subKey : Advapi32Util.registryGetKeys(hKey)) {
58                              String subKeyPath = keyPath + PATH_DELIMITER + subKey;
59                              // Check for session and client name
60                              if (Advapi32Util.registryValueExists(WinReg.HKEY_USERS, subKeyPath, SESSIONNAME)) {
61                                  String session = Advapi32Util.registryGetStringValue(WinReg.HKEY_USERS, subKeyPath,
62                                          SESSIONNAME);
63                                  if (!session.isEmpty()) {
64                                      device = session;
65                                  }
66                              }
67                              if (Advapi32Util.registryValueExists(WinReg.HKEY_USERS, subKeyPath, CLIENTNAME)) {
68                                  String client = Advapi32Util.registryGetStringValue(WinReg.HKEY_USERS, subKeyPath,
69                                          CLIENTNAME);
70                                  if (!client.isEmpty() && !DEFAULT_DEVICE.equals(client)) {
71                                      host = client;
72                                  }
73                              }
74                          }
75                          Advapi32Util.registryCloseKey(hKey);
76                      }
77                      sessions.add(new OSSession(name, device, loginTime, host));
78                  } catch (Win32Exception ex) {
79                      LOG.warn("Error querying SID {} from registry: {}", sidKey, ex.getMessage());
80                  }
81              }
82          }
83          return sessions;
84      }
85  }