diff options
author | Florian Zschocke <f.zschocke+git@gmail.com> | 2025-05-23 21:11:03 +0200 |
---|---|---|
committer | Florian Zschocke <f.zschocke+git@gmail.com> | 2025-06-14 01:35:24 +0200 |
commit | edcf3f4c62cc653a6fead6b1542474f8d801ef5c (patch) | |
tree | 448e9f3f8d3c7d8828a549f7f037a1684c7a5d3d /src/main/java/com/gitblit/instance/GitblitInstanceId.java | |
parent | f751da2bfe791aa96f31a1c849c2da03a8dbdab5 (diff) | |
download | gitblit-edcf3f4c62cc653a6fead6b1542474f8d801ef5c.tar.gz gitblit-edcf3f4c62cc653a6fead6b1542474f8d801ef5c.zip |
Add id for a running gitblit instance
Diffstat (limited to 'src/main/java/com/gitblit/instance/GitblitInstanceId.java')
-rw-r--r-- | src/main/java/com/gitblit/instance/GitblitInstanceId.java | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/src/main/java/com/gitblit/instance/GitblitInstanceId.java b/src/main/java/com/gitblit/instance/GitblitInstanceId.java new file mode 100644 index 00000000..652c200c --- /dev/null +++ b/src/main/java/com/gitblit/instance/GitblitInstanceId.java @@ -0,0 +1,265 @@ +package com.gitblit.instance; + +import com.gitblit.utils.FileUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.Socket; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.UUID; + +/** + * The instance id is a unique identifier for an installed Gitblit instance. + * + * This is used to track the number of Gitblit instances in the field. + * Its purpose is to gauge the popularity of Gitblit and to help + * prioritize feature requests. + * + * The instance id should be unique between different instances, even + * on the same machine. But it should stay the same between restarts of + * the same instance. It should also stay the same between upgrades of + * the same instance. Therefore, it must be stored in a file that is + * not overwritten during upgrades, once it has been created. + */ +public class GitblitInstanceId +{ + static final String STORAGE_FILE = "gbins"; + + private final Logger log = LoggerFactory.getLogger(getClass()); + + private final File idFileBase; + + private UUID id; + + + /** + * Constructor. + */ + public GitblitInstanceId() + { + this.idFileBase = null; + } + + /** + * Constructor. + */ + public GitblitInstanceId(File idFileBase) + { + this.idFileBase = idFileBase; + } + + + /** + * Get the instance id. + * + * @return the instance id. + */ + public UUID getId() { + if (this.id == null) { + load(); + } + return this.id; + } + + + /** + * Load the instance id from the file. + */ + private void load() + { + if (this.idFileBase == null) { + // Not working with stored id. + log.debug("No id file base directory specified. Generated id is not persisted."); + generate(); + return; + } + + File idFile = new File(this.idFileBase, STORAGE_FILE); + if (idFile.exists()) { + // Read the file + String uuidString = readFromFile(idFile); + + // Parse the UUID + try { + this.id = UUID.fromString(uuidString); + return; + } + catch (IllegalArgumentException e) { + log.debug("Unable to parse instance id. Will generate a new one: {}", e.getMessage(), e); + } + } + + // Generate a new instance id and persist it to disk. + generate(); + storeToFile(idFile); + } + + + private String readFromFile(File idfile) + { +// log.debug("Loading instance id from file: {}", idfile.getAbsolutePath()); + + String string = FileUtils.readContent(idfile, null).trim(); + String uuidString = string.replaceAll("\\s+",""); + return uuidString.trim(); + } + + private void storeToFile(File idfile) + { + // Make sure that the directory exists + if (!idfile.getParentFile().exists()) { + if (!idfile.getParentFile().mkdirs()) { + log.debug("Unable to create directory for instance id file: {}", idfile.getParentFile().getAbsolutePath()); + return; + } + } + + // Write the UUID to the file + String uuidString = this.id.toString(); + FileUtils.writeContent(idfile, uuidString); + } + + + /** + * Generate a new instance id and persist it to disk. + * + * UUID is variant, i.e. OSF DCE, version 8, a custom format. + * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + * date -rand-8rnd-8rnd- rand OUI + * + * The variant nibble has the variant (1) in the upper two bits as 0b10xx, + * and the lower two bits are used as a version, currently 0bxx00. + * Should the format of this UUID change, the version can be incremented + * to 0bxx01 or 0bxx10. Further increments would set the bits in the variant + * nibble to 0bxx11 and employ more bits from the next nibble for further + * differentiation. + */ + private void generate() + { + // Start with a random UUID + UUID id = UUID.randomUUID(); + long upper = id.getMostSignificantBits(); + long lower = id.getLeastSignificantBits(); + + + // Set the variant bits to 0b1000, variant 1, our version 0. + lower &= 0x0FFFFFFFFFFFFFFFL; // Clear the variant bits + lower |= 0x8000000000000000L; // Set the variant bits to 0b1000 + + // Set the version bits to 0b1000, version 8. + upper &= 0xFFFFFFFFFFFF0FFFL; // Clear the version bits + upper |= 0x0000000000008000L; // Set the version bits to 0b1000 + + + // Set the first four bytes to represent the date. + long date = System.currentTimeMillis(); + date &= 0xFFFFFFFFFFFF0000L; // Clear the last two bytes, those are only a few minutes. + date <<= 2 * 8; // We do not need the upper two bytes, that is too far into the future. + + upper &= 0x00000000FFFFFFFFL; // Clear the date bits. + upper |= date; // Set the date in the upper 32 bits. + + + // Set the OUI in the lower three bytes. + Long oui = getNodeOUI(); + if (oui != null) { + lower &= 0xFFFFFFFFFF000000L; // Clear the OUI bits. + lower |= (0x1000000L | oui); // Set the OUI in the lower three bytes. Mark as valid OUI in bit above them. + } + else { + // Mark this as an invalid OUI, i.e. random bits, by setting the bit above the OUI bits to zero. + lower &= 0xFFFFFFFFFEFFFFFFL; // Clear the valid OUI indicator bit. + } + + this.id = new UUID(upper, lower); + } + + + /** + * Get the OUI of one NIC of this host. + * + * @return null if no OUI could be detected, otherwise the OUI in the lower three bytes of a Long. + */ + private Long getNodeOUI() + { + byte[] node = null; + String logPrefix = "Unable to detect host. Use random value."; + + try { + InetAddress ipa = InetAddress.getLocalHost(); + NetworkInterface iface = NetworkInterface.getByInetAddress(ipa); + if (iface != null) { + node = iface.getHardwareAddress(); + logPrefix = "From getLocalHost:"; + } + + if (node == null) { + List<byte[]> macs = new ArrayList<>(); + List<byte[]> offmacs = new ArrayList<>(); + Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces(); + while (interfaces.hasMoreElements()) { + iface = interfaces.nextElement(); + byte[] mac = iface.getHardwareAddress(); + if (mac != null) { + if (iface.isLoopback()) { + continue; + } + if (iface.isVirtual() || iface.isPointToPoint()) { + continue; + } + if (iface.isUp()) { + macs.add(mac); + } + else { + offmacs.add(mac); + } + } + } + + if (macs.size() == 1) { + node = macs.get(0); + logPrefix = "From up iface:"; + } + else if (offmacs.size() == 1) { + node = offmacs.get(0); + logPrefix = "From down iface:"; + } + } + + if (node == null) { + Socket socket = new Socket("www.gitblit.dev", 80); + ipa = socket.getLocalAddress(); + socket.close(); + iface = NetworkInterface.getByInetAddress(ipa); + if (iface != null) { + node = iface.getHardwareAddress(); + logPrefix = "From socket:"; + } + } + + if (node == null) { + log.debug(logPrefix); + return null; + } + + if (log.isDebugEnabled()) { + log.debug("{} {}", logPrefix, String.format("%02X:%02X:%02X", node[0], node[1], node[2])); + } + + long l = (((long)node[0]) << 16) & 0xff0000; + l |= (((long)node[1]) << 8) & 0xff00; + l |= ((long)node[2]) & 0xff; + return l; + } + catch (IOException e) { + log.debug("Exception while getting OUI: {}", e.getMessage(), e); + return null; + } + } +} |