1 /* 2 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.servicetag; 27 28 import java.io.*; 29 import java.util.ArrayList; 30 import java.util.Date; 31 import java.util.HashSet; 32 import java.util.List; 33 import java.util.Properties; 34 import java.util.Set; 35 36 import static com.sun.servicetag.Util.*; 37 import static com.sun.servicetag.RegistrationDocument.*; 38 39 /** 40 * A service tag registry is a XML-based registry containing 41 * the list of {@link ServiceTag service tags} installed in the system. 42 * The {@code Registry} class provides interfaces 43 * to add, remove, update, and get a service tag from a service tag 44 * registry. 45 * This {@code Registry} class may not be supported 46 * on all systems. The {@link #isSupported} method 47 * can be called to determine if it is supported. 48 * <p> 49 * A registry may implement restrictions to only allow certain users 50 * to {@link #updateServiceTag update} and 51 * to {@link #removeServiceTag remove} a service tag record. Typically, 52 * only the owner of the service tag, the owner of the registry 53 * and superuser are authorized to update or remove a service tag in 54 * the registry. 55 * 56 * @see <a href="https://sn-tools.central.sun.com/twiki/bin/view/ServiceTags/ServiceTagDevGuideHelper"> 57 * Service Tag User Guide</a> 58 */ 59 public class Registry { 60 61 private static final String STCLIENT_SOLARIS = "/usr/bin/stclient"; 62 private static final String STCLIENT_LINUX = "/opt/sun/servicetag/bin/stclient"; 63 // stclient exit value (see sthelper.h) 64 private static final int ST_ERR_NOT_AUTH = 245; 65 private static final int ST_ERR_REC_NOT_FOUND = 225; 66 67 // The stclient output has to be an exported interface 68 private static final String INSTANCE_URN_DESC = "Product instance URN="; 69 private static boolean initialized = false; 70 private static File stclient = null; 71 private static String stclientPath = null; 72 private static Registry registry = new Registry(); 73 74 // System properties for testing 75 private static String SVCTAG_STCLIENT_CMD = "servicetag.stclient.cmd"; 76 private static String SVCTAG_STHELPER_SUPPORTED = "servicetag.sthelper.supported"; 77 78 private Registry() { 79 } 80 81 private synchronized static String getSTclient() { 82 if (!initialized) { 83 // Initialization to determine the platform's stclient pathname 84 String os = System.getProperty("os.name"); 85 if (os.equals("SunOS")) { 86 stclient = new File(STCLIENT_SOLARIS); 87 } else if (os.equals("Linux")) { 88 stclient = new File(STCLIENT_LINUX); 89 } else if (os.startsWith("Windows")) { 90 stclient = getWindowsStClientFile(); 91 } else { 92 if (isVerbose()) { 93 System.out.println("Running on unsupported platform"); 94 } 95 } 96 initialized = true; 97 } 98 99 boolean supportsHelperClass = true; // default 100 if (System.getProperty(SVCTAG_STHELPER_SUPPORTED) != null) { 101 // the system property always overrides the default setting 102 supportsHelperClass = Boolean.getBoolean(SVCTAG_STHELPER_SUPPORTED); 103 } 104 105 if (!supportsHelperClass) { 106 // disable system registry 107 return null; 108 } 109 110 // This is only used for testing 111 String path = System.getProperty(SVCTAG_STCLIENT_CMD); 112 if (path != null) { 113 return path; 114 } 115 116 // com.sun.servicetag package has to be compiled with JDK 5 as well 117 // JDK 5 doesn't support the File.canExecute() method. 118 // Risk not checking isExecute() for the stclient command is very low. 119 if (stclientPath == null && stclient != null && stclient.exists()) { 120 stclientPath = stclient.getAbsolutePath(); 121 } 122 return stclientPath; 123 } 124 125 /** 126 * Returns the system service tag registry. The {@code Registry} class 127 * may not be supported on some platforms; use the {@link #isSupported} 128 * method to determine if it is supported. 129 * 130 * @return the {@code Registry} object for the system service tag registry. 131 * 132 * @throws UnsupportedOperationException if the {@code Registry} class is 133 * not supported. 134 */ 135 public static Registry getSystemRegistry() { 136 if (isSupported()) { 137 return registry; 138 } else { 139 throw new UnsupportedOperationException("Registry class is not supported"); 140 } 141 } 142 143 /** 144 * Returns {@code true} if the {@code Registry} class is supported on this system. 145 * 146 * @return {@code true} if the {@code Registry} class is supported; 147 * otherwise, return {@code false}. 148 */ 149 public static synchronized boolean isSupported() { 150 return getSTclient() != null; 151 } 152 153 private static List<String> getCommandList() { 154 // Set up the arguments to call stclient 155 List<String> command = new ArrayList<String>(); 156 if (System.getProperty(SVCTAG_STCLIENT_CMD) != null) { 157 // This is for jtreg testing use. This will be set to something 158 // like: 159 // $JAVA_HOME/bin/java -cp $TEST_DIR \ 160 // -Dstclient.registry.path=$TEST_DIR/registry.xml \ 161 // SvcTagClient 162 // 163 // On Windows, the JAVA_HOME and TEST_DIR path could contain 164 // space e.g. c:\Program Files\Java\jdk1.6.0_05\bin\java. 165 // The SVCTAG_STCLIENT_CMD must be set with a list of 166 // space-separated parameters. If a parameter contains spaces, 167 // it must be quoted with '"'. 168 169 String cmd = getSTclient(); 170 int len = cmd.length(); 171 int i = 0; 172 while (i < len) { 173 char separator = ' '; 174 if (cmd.charAt(i) == '"') { 175 separator = '"'; 176 i++; 177 } 178 // look for the separator or matched the closing '"' 179 int j; 180 for (j = i+1; j < len; j++) { 181 if (cmd.charAt(j) == separator) { 182 break; 183 } 184 } 185 186 if (i == j-1) { 187 // add an empty parameter 188 command.add("\"\""); 189 } else { 190 // double quotes and space are not included 191 command.add(cmd.substring(i,j)); 192 } 193 194 // skip spaces 195 for (i = j+1; i < len; i++) { 196 if (!Character.isSpaceChar(cmd.charAt(i))) { 197 break; 198 } 199 } 200 } 201 if (isVerbose()) { 202 System.out.println("Command list:"); 203 for (String s : command) { 204 System.out.println(s); 205 } 206 } 207 } else { 208 command.add(getSTclient()); 209 } 210 return command; 211 } 212 213 // Returns null if the service tag record not found; 214 // or throw UnauthorizedAccessException or IOException 215 // based on the exitValue. 216 private static ServiceTag checkReturnError(int exitValue, 217 String output, 218 ServiceTag st) throws IOException { 219 switch (exitValue) { 220 case ST_ERR_REC_NOT_FOUND: 221 return null; 222 case ST_ERR_NOT_AUTH: 223 if (st != null) { 224 throw new UnauthorizedAccessException( 225 "Not authorized to access " + st.getInstanceURN() + 226 " installer_uid=" + st.getInstallerUID()); 227 } else { 228 throw new UnauthorizedAccessException( 229 "Not authorized:" + output); 230 } 231 default: 232 throw new IOException("stclient exits with error" + 233 " (" + exitValue + ")\n" + output); 234 } 235 } 236 237 /** 238 * Adds a service tag to this registry. 239 * If the given service tag has an empty <tt>instance_urn</tt>, 240 * this helper class will generate a URN and place it in the 241 * copy of the service tag in this registry. 242 * This method will return the {@code ServiceTag} representing 243 * the service tag entry to this registry. 244 * 245 * @param st {@code ServiceTag} object 246 * @return a {@code ServiceTag} object representing the service tag 247 * entry to this registry. 248 * 249 * @throws IllegalArgumentException if a service tag of the same 250 * <tt>instance_urn</tt> already exists in this registry. 251 * 252 * @throws java.io.IOException if an I/O error occurs in this operation. 253 */ 254 public ServiceTag addServiceTag(ServiceTag st) throws IOException { 255 List<String> command = getCommandList(); 256 command.add("-a"); 257 if (st.getInstanceURN().length() > 0) { 258 ServiceTag sysSvcTag = getServiceTag(st.getInstanceURN()); 259 if (sysSvcTag != null) { 260 throw new IllegalArgumentException("Instance_urn = " + 261 st.getInstanceURN() + " already exists"); 262 } 263 command.add("-i"); 264 command.add(st.getInstanceURN()); 265 } 266 command.add("-p"); 267 command.add(st.getProductName()); 268 command.add("-e"); 269 command.add(st.getProductVersion()); 270 command.add("-t"); 271 command.add(st.getProductURN()); 272 if (st.getProductParentURN().length() > 0) { 273 command.add("-F"); 274 command.add(st.getProductParentURN()); 275 } 276 command.add("-P"); 277 command.add(st.getProductParent()); 278 if (st.getProductDefinedInstanceID().length() > 0) { 279 command.add("-I"); 280 command.add(st.getProductDefinedInstanceID()); 281 } 282 command.add("-m"); 283 command.add(st.getProductVendor()); 284 command.add("-A"); 285 command.add(st.getPlatformArch()); 286 command.add("-z"); 287 command.add(st.getContainer()); 288 command.add("-S"); 289 command.add(st.getSource()); 290 291 BufferedReader in = null; 292 try { 293 ProcessBuilder pb = new ProcessBuilder(command); 294 Process p = pb.start(); 295 String output = commandOutput(p); 296 if (isVerbose()) { 297 System.out.println("Output from stclient -a command:"); 298 System.out.println(output); 299 } 300 String urn = ""; 301 if (p.exitValue() == 0) { 302 // Obtain the instance urn from the stclient output 303 in = new BufferedReader(new StringReader(output)); 304 String line = null; 305 while ((line = in.readLine()) != null) { 306 line = line.trim(); 307 if (line.startsWith(INSTANCE_URN_DESC)) { 308 urn = line.substring(INSTANCE_URN_DESC.length()); 309 break; 310 } 311 } 312 if (urn.length() == 0) { 313 throw new IOException("Error in creating service tag:\n" + 314 output); 315 } 316 return getServiceTag(urn); 317 } else { 318 return checkReturnError(p.exitValue(), output, st); 319 } 320 } finally { 321 if (in != null) { 322 in.close(); 323 } 324 } 325 } 326 327 /** 328 * Removes a service tag of the given <tt>instance_urn</tt> from this 329 * registry. 330 * 331 * @param instanceURN the <tt>instance_urn</tt> of the service tag 332 * to be removed. 333 * 334 * @return the {@code ServiceTag} object removed from this registry; 335 * or {@code null} if the service tag does not exist in this registry. 336 * 337 * @throws UnauthorizedAccessException if the user is not authorized to 338 * remove the service tag of the given <tt>instance_urn</tt> 339 * from this registry. 340 * 341 * @throws java.io.IOException if an I/O error occurs in this operation. 342 */ 343 public ServiceTag removeServiceTag(String instanceURN) throws IOException { 344 ServiceTag st = getServiceTag(instanceURN); 345 if (st == null) { 346 return null; 347 } 348 349 List<String> command = getCommandList(); 350 command.add("-d"); 351 command.add("-i"); 352 command.add(instanceURN); 353 354 ProcessBuilder pb = new ProcessBuilder(command); 355 Process p = pb.start(); 356 String output = commandOutput(p); 357 if (isVerbose()) { 358 System.out.println("Output from stclient -d command:"); 359 System.out.println(output); 360 } 361 if (p.exitValue() == 0) { 362 return st; 363 } else { 364 return checkReturnError(p.exitValue(), output, st); 365 } 366 } 367 368 /** 369 * Updates the <tt>product_defined_instance_id</tt> in the service tag 370 * of the specified <tt>instance_urn</tt> in this registry. 371 * 372 * @param instanceURN the <tt>instance_urn</tt> of the service tag to be updated. 373 * @param productDefinedInstanceID the value of the 374 * <tt>product_defined_instance_id</tt> to be set. 375 * 376 * @return the updated {@code ServiceTag} object; 377 * or {@code null} if the service tag does not exist in this 378 * registry. 379 * 380 * @throws UnauthorizedAccessException if the user is not authorized to 381 * update the service tag from this registry. 382 * 383 * @throws IOException if an I/O error occurs in this operation. 384 */ 385 public ServiceTag updateServiceTag(String instanceURN, 386 String productDefinedInstanceID) 387 throws IOException { 388 ServiceTag svcTag = getServiceTag(instanceURN); 389 if (svcTag == null) { 390 return null; 391 } 392 393 List<String> command = getCommandList(); 394 command.add("-u"); 395 command.add("-i"); 396 command.add(instanceURN); 397 command.add("-I"); 398 if (productDefinedInstanceID.length() > 0) { 399 command.add(productDefinedInstanceID); 400 } else { 401 command.add("\"\""); 402 } 403 404 ProcessBuilder pb = new ProcessBuilder(command); 405 Process p = pb.start(); 406 String output = commandOutput(p); 407 if (isVerbose()) { 408 System.out.println("Output from stclient -u command:"); 409 System.out.println(output); 410 } 411 412 if (p.exitValue() == 0) { 413 return getServiceTag(instanceURN); 414 } else { 415 return checkReturnError(p.exitValue(), output, svcTag); 416 } 417 } 418 419 /** 420 * Returns a {@code ServiceTag} object of the given <tt>instance_urn</tt> 421 * in this registry. 422 * 423 * @param instanceURN the <tt>instance_urn</tt> of the service tag 424 * @return a {@code ServiceTag} object of the given <tt>instance_urn</tt> 425 * in this registry; or {@code null} if not found. 426 * 427 * @throws java.io.IOException if an I/O error occurs in this operation. 428 */ 429 public ServiceTag getServiceTag(String instanceURN) throws IOException { 430 if (instanceURN == null) { 431 throw new NullPointerException("instanceURN is null"); 432 } 433 434 List<String> command = getCommandList(); 435 command.add("-g"); 436 command.add("-i"); 437 command.add(instanceURN); 438 439 ProcessBuilder pb = new ProcessBuilder(command); 440 Process p = pb.start(); 441 String output = commandOutput(p); 442 if (isVerbose()) { 443 System.out.println("Output from stclient -g command:"); 444 System.out.println(output); 445 } 446 if (p.exitValue() == 0) { 447 return parseServiceTag(output); 448 } else { 449 return checkReturnError(p.exitValue(), output, null); 450 } 451 } 452 453 private ServiceTag parseServiceTag(String output) throws IOException { 454 BufferedReader in = null; 455 try { 456 Properties props = new Properties(); 457 // parse the service tag output from stclient 458 in = new BufferedReader(new StringReader(output)); 459 String line = null; 460 while ((line = in.readLine()) != null) { 461 if ((line = line.trim()).length() > 0) { 462 String[] ss = line.trim().split("=", 2); 463 if (ss.length == 2) { 464 props.setProperty(ss[0].trim(), ss[1].trim()); 465 } else { 466 props.setProperty(ss[0].trim(), ""); 467 } 468 } 469 } 470 471 String urn = props.getProperty(ST_NODE_INSTANCE_URN); 472 String productName = props.getProperty(ST_NODE_PRODUCT_NAME); 473 String productVersion = props.getProperty(ST_NODE_PRODUCT_VERSION); 474 String productURN = props.getProperty(ST_NODE_PRODUCT_URN); 475 String productParent = props.getProperty(ST_NODE_PRODUCT_PARENT); 476 String productParentURN = props.getProperty(ST_NODE_PRODUCT_PARENT_URN); 477 String productDefinedInstanceID = 478 props.getProperty(ST_NODE_PRODUCT_DEFINED_INST_ID); 479 String productVendor = props.getProperty(ST_NODE_PRODUCT_VENDOR); 480 String platformArch = props.getProperty(ST_NODE_PLATFORM_ARCH); 481 String container = props.getProperty(ST_NODE_CONTAINER); 482 String source = props.getProperty(ST_NODE_SOURCE); 483 int installerUID = 484 Util.getIntValue(props.getProperty(ST_NODE_INSTALLER_UID)); 485 Date timestamp = 486 Util.parseTimestamp(props.getProperty(ST_NODE_TIMESTAMP)); 487 488 return new ServiceTag(urn, 489 productName, 490 productVersion, 491 productURN, 492 productParent, 493 productParentURN, 494 productDefinedInstanceID, 495 productVendor, 496 platformArch, 497 container, 498 source, 499 installerUID, 500 timestamp); 501 } finally { 502 if (in != null) { 503 in.close(); 504 } 505 } 506 507 } 508 509 /** 510 * Returns the service tags of the specified 511 * <tt>product_urn</tt> in this registry. 512 * 513 * @param productURN the <tt>product_urn</tt> to look up 514 * @return a {@code Set} of {@code ServiceTag} objects 515 * of the specified <tt>product_urn</tt> in this registry. 516 * 517 * @throws java.io.IOException if an I/O error occurs in this operation. 518 */ 519 public Set<ServiceTag> findServiceTags(String productURN) throws IOException { 520 if (productURN == null) { 521 throw new NullPointerException("productURN is null"); 522 } 523 524 List<String> command = getCommandList(); 525 command.add("-f"); 526 command.add("-t"); 527 command.add(productURN); 528 529 BufferedReader in = null; 530 try { 531 ProcessBuilder pb = new ProcessBuilder(command); 532 Process p = pb.start(); 533 String output = commandOutput(p); 534 535 Set<ServiceTag> instances = new HashSet<ServiceTag>(); 536 if (p.exitValue() == 0) { 537 // parse the service tag output from stclient 538 in = new BufferedReader(new StringReader(output)); 539 String line = null; 540 while ((line = in.readLine()) != null) { 541 String s = line.trim(); 542 if (s.startsWith("urn:st:")) { 543 instances.add(getServiceTag(s)); 544 } 545 } 546 } else { 547 checkReturnError(p.exitValue(), output, null); 548 } 549 return instances; 550 } finally { 551 if (in != null) { 552 in.close(); 553 } 554 } 555 } 556 }