1 /*
   2  * Copyright (c) 2000, 2012, 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 sun.print;
  27 
  28 import java.io.BufferedReader;
  29 import java.io.FileInputStream;
  30 import java.io.InputStream;
  31 import java.io.InputStreamReader;
  32 import java.io.IOException;
  33 import java.util.ArrayList;
  34 import java.util.Vector;
  35 import java.security.AccessController;
  36 import java.security.PrivilegedActionException;
  37 import java.security.PrivilegedExceptionAction;
  38 import javax.print.DocFlavor;
  39 import javax.print.MultiDocPrintService;
  40 import javax.print.PrintService;
  41 import javax.print.PrintServiceLookup;
  42 import javax.print.attribute.Attribute;
  43 import javax.print.attribute.AttributeSet;
  44 import javax.print.attribute.HashPrintRequestAttributeSet;
  45 import javax.print.attribute.HashPrintServiceAttributeSet;
  46 import javax.print.attribute.PrintRequestAttribute;
  47 import javax.print.attribute.PrintRequestAttributeSet;
  48 import javax.print.attribute.PrintServiceAttribute;
  49 import javax.print.attribute.PrintServiceAttributeSet;
  50 import javax.print.attribute.standard.PrinterName;
  51 import java.io.File;
  52 import java.io.FileReader;
  53 import java.net.URL;
  54 import java.nio.file.Files;
  55 
  56 /*
  57  * Remind: This class uses solaris commands. We also need a linux
  58  * version
  59  */
  60 public class UnixPrintServiceLookup extends PrintServiceLookup
  61     implements BackgroundServiceLookup, Runnable {
  62 
  63     /* Remind: the current implementation is static, as its assumed
  64      * its preferable to minimise creation of PrintService instances.
  65      * Later we should add logic to add/remove services on the fly which
  66      * will take a hit of needing to regather the list of services.
  67      */
  68     private String defaultPrinter;
  69     private PrintService defaultPrintService;
  70     private PrintService[] printServices; /* includes the default printer */
  71     private Vector lookupListeners = null;
  72     private static String debugPrefix = "UnixPrintServiceLookup>> ";
  73     private static boolean pollServices = true;
  74     private static final int DEFAULT_MINREFRESH = 120;  // 2 minutes
  75     private static int minRefreshTime = DEFAULT_MINREFRESH;
  76 
  77 
  78     static String osname;
  79 
  80     static {
  81         /* The system property "sun.java2d.print.polling"
  82          * can be used to force the printing code to poll or not poll
  83          * for PrintServices.
  84          */
  85         String pollStr = java.security.AccessController.doPrivileged(
  86             new sun.security.action.GetPropertyAction("sun.java2d.print.polling"));
  87 
  88         if (pollStr != null) {
  89             if (pollStr.equalsIgnoreCase("true")) {
  90                 pollServices = true;
  91             } else if (pollStr.equalsIgnoreCase("false")) {
  92                 pollServices = false;
  93             }
  94         }
  95 
  96         /* The system property "sun.java2d.print.minRefreshTime"
  97          * can be used to specify minimum refresh time (in seconds)
  98          * for polling PrintServices.  The default is 120.
  99          */
 100         String refreshTimeStr = java.security.AccessController.doPrivileged(
 101             new sun.security.action.GetPropertyAction(
 102                 "sun.java2d.print.minRefreshTime"));
 103 
 104         if (refreshTimeStr != null) {
 105             try {
 106                 minRefreshTime = (new Integer(refreshTimeStr)).intValue();
 107             } catch (NumberFormatException e) {
 108             }
 109             if (minRefreshTime < DEFAULT_MINREFRESH) {
 110                 minRefreshTime = DEFAULT_MINREFRESH;
 111             }
 112         }
 113 
 114         osname = java.security.AccessController.doPrivileged(
 115             new sun.security.action.GetPropertyAction("os.name"));
 116     }
 117 
 118     static boolean isMac() {
 119         return osname.startsWith("Mac");
 120     }
 121 
 122     static boolean isSysV() {
 123         return osname.equals("SunOS");
 124     }
 125 
 126     static boolean isBSD() {
 127         return (osname.equals("Linux") ||
 128                 osname.contains("OS X"));
 129     }
 130 
 131     static final int UNINITIALIZED = -1;
 132     static final int BSD_LPD = 0;
 133     static final int BSD_LPD_NG = 1;
 134 
 135     static int cmdIndex = UNINITIALIZED;
 136 
 137     String[] lpcFirstCom = {
 138         "/usr/sbin/lpc status | grep : | sed -ne '1,1 s/://p'",
 139         "/usr/sbin/lpc status | grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}'"
 140     };
 141 
 142     String[] lpcAllCom = {
 143         "/usr/sbin/lpc status all | grep : | sed -e 's/://'",
 144         "/usr/sbin/lpc status all | grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}' | sort"
 145     };
 146 
 147     String[] lpcNameCom = {
 148         "| grep : | sed -ne 's/://p'",
 149         "| grep -E '^[ 0-9a-zA-Z_-]*@' | awk -F'@' '{print $1}'"
 150     };
 151 
 152 
 153     static int getBSDCommandIndex() {
 154         String command  = "/usr/sbin/lpc status all";
 155         String[] names = execCmd(command);
 156 
 157         if ((names == null) || (names.length == 0)) {
 158             return BSD_LPD_NG;
 159         }
 160 
 161         for (int i=0; i<names.length; i++) {
 162             if (names[i].indexOf('@') != -1) {
 163                 return BSD_LPD_NG;
 164             }
 165         }
 166 
 167         return BSD_LPD;
 168     }
 169 
 170 
 171     public UnixPrintServiceLookup() {
 172         // start the printer listener thread
 173         if (pollServices) {
 174             PrinterChangeListener thr = new PrinterChangeListener();
 175             thr.setDaemon(true);
 176             thr.start();
 177             IPPPrintService.debug_println(debugPrefix+"polling turned on");
 178         }
 179     }
 180 
 181     /* Want the PrintService which is default print service to have
 182      * equality of reference with the equivalent in list of print services
 183      * This isn't required by the API and there's a risk doing this will
 184      * lead people to assume its guaranteed.
 185      */
 186     public synchronized PrintService[] getPrintServices() {
 187         SecurityManager security = System.getSecurityManager();
 188         if (security != null) {
 189             security.checkPrintJobAccess();
 190         }
 191 
 192         if (printServices == null || !pollServices) {
 193             refreshServices();
 194         }
 195         if (printServices == null) {
 196             return new PrintService[0];
 197         } else {
 198             return printServices.clone();
 199         }
 200     }
 201 
 202 
 203     // refreshes "printServices"
 204     public synchronized void refreshServices() {
 205         /* excludes the default printer */
 206         String[] printers = null; // array of printer names
 207         String[] printerURIs = null; //array of printer URIs
 208 
 209         getDefaultPrintService();
 210         if (CUPSPrinter.isCupsRunning()) {
 211             printerURIs = CUPSPrinter.getAllPrinters();
 212             if ((printerURIs != null) && (printerURIs.length > 0)) {
 213                 printers = new String[printerURIs.length];
 214                 for (int i=0; i<printerURIs.length; i++) {
 215                     int lastIndex = printerURIs[i].lastIndexOf("/");
 216                     printers[i] = printerURIs[i].substring(lastIndex+1);
 217                 }
 218             }
 219         } else {
 220             if (isMac() || isSysV()) {
 221                 printers = getAllPrinterNamesSysV();
 222             } else { //BSD
 223                 printers = getAllPrinterNamesBSD();
 224             }
 225         }
 226 
 227         if (printers == null) {
 228             if (defaultPrintService != null) {
 229                 printServices = new PrintService[1];
 230                 printServices[0] = defaultPrintService;
 231             } else {
 232                 printServices = null;
 233             }
 234             return;
 235         }
 236 
 237         ArrayList printerList = new ArrayList();
 238         int defaultIndex = -1;
 239         for (int p=0; p<printers.length; p++) {
 240             if (printers[p] == null) {
 241                 continue;
 242             }
 243             if ((defaultPrintService != null)
 244                 && printers[p].equals(defaultPrintService.getName())) {
 245                 printerList.add(defaultPrintService);
 246                 defaultIndex = printerList.size() - 1;
 247             } else {
 248                 if (printServices == null) {
 249                     IPPPrintService.debug_println(debugPrefix+
 250                                                   "total# of printers = "+printers.length);
 251 
 252                     if (CUPSPrinter.isCupsRunning()) {
 253                         try {
 254                             printerList.add(new IPPPrintService(printers[p],
 255                                                                 printerURIs[p],
 256                                                                 true));
 257                         } catch (Exception e) {
 258                             IPPPrintService.debug_println(debugPrefix+
 259                                                           " getAllPrinters Exception "+
 260                                                           e);
 261 
 262                         }
 263                     } else {
 264                         printerList.add(new UnixPrintService(printers[p]));
 265                     }
 266                 } else {
 267                     int j;
 268                     for (j=0; j<printServices.length; j++) {
 269                         if ((printServices[j] != null) &&
 270                             (printers[p].equals(printServices[j].getName()))) {
 271                             printerList.add(printServices[j]);
 272                             printServices[j] = null;
 273                             break;
 274                         }
 275                     }
 276 
 277                     if (j == printServices.length) {      // not found?
 278                         if (CUPSPrinter.isCupsRunning()) {
 279                             try {
 280                                 printerList.add(new IPPPrintService(
 281                                                                printers[p],
 282                                                                printerURIs[p],
 283                                                                true));
 284                             } catch (Exception e) {
 285                                 IPPPrintService.debug_println(debugPrefix+
 286                                                               " getAllPrinters Exception "+
 287                                                               e);
 288 
 289                             }
 290                         } else {
 291                             printerList.add(new UnixPrintService(printers[p]));
 292                         }
 293                     }
 294                 }
 295             }
 296         }
 297 
 298         // Look for deleted services and invalidate these
 299         if (printServices != null) {
 300             for (int j=0; j < printServices.length; j++) {
 301                 if ((printServices[j] instanceof UnixPrintService) &&
 302                     (!printServices[j].equals(defaultPrintService))) {
 303                     ((UnixPrintService)printServices[j]).invalidateService();
 304                 }
 305             }
 306         }
 307 
 308         //if defaultService is not found in printerList
 309         if (defaultIndex == -1 && defaultPrintService != null) {
 310             //add default to the list
 311             printerList.add(defaultPrintService);
 312             defaultIndex = printerList.size() - 1;
 313         }
 314 
 315         printServices = (PrintService[])printerList.toArray(
 316                                       new PrintService[] {});
 317 
 318         // swap default with the first in the list
 319         if (defaultIndex > 0) {
 320             PrintService saveService = printServices[0];
 321             printServices[0] = printServices[defaultIndex];
 322             printServices[defaultIndex] = saveService;
 323         }
 324     }
 325 
 326     private boolean matchesAttributes(PrintService service,
 327                                       PrintServiceAttributeSet attributes) {
 328 
 329         Attribute [] attrs =  attributes.toArray();
 330         Attribute serviceAttr;
 331         for (int i=0; i<attrs.length; i++) {
 332             serviceAttr
 333                 = service.getAttribute((Class<PrintServiceAttribute>)attrs[i].getCategory());
 334             if (serviceAttr == null || !serviceAttr.equals(attrs[i])) {
 335                 return false;
 336             }
 337         }
 338         return true;
 339     }
 340 
 341       /* This checks for validity of the printer name before passing as
 342        * parameter to a shell command.
 343        */
 344       private boolean checkPrinterName(String s) {
 345         char c;
 346 
 347         for (int i=0; i < s.length(); i++) {
 348           c = s.charAt(i);
 349           if (Character.isLetterOrDigit(c) ||
 350               c == '-' || c == '_' || c == '.' || c == '/') {
 351             continue;
 352           } else {
 353             return false;
 354           }
 355         }
 356         return true;
 357       }
 358 
 359     /* On a network with many (hundreds) of network printers, it
 360      * can save several seconds if you know all you want is a particular
 361      * printer, to ask for that printer rather than retrieving all printers.
 362      */
 363     private PrintService getServiceByName(PrinterName nameAttr) {
 364         String name = nameAttr.getValue();
 365         PrintService printer = null;
 366         if (name == null || name.equals("") || !checkPrinterName(name)) {
 367             return null;
 368         }
 369         if (isMac() || isSysV()) {
 370             printer = getNamedPrinterNameSysV(name);
 371         } else {
 372             printer = getNamedPrinterNameBSD(name);
 373         }
 374         return printer;
 375     }
 376 
 377     private PrintService[]
 378         getPrintServices(PrintServiceAttributeSet serviceSet) {
 379 
 380         if (serviceSet == null || serviceSet.isEmpty()) {
 381             return getPrintServices();
 382         }
 383 
 384         /* Typically expect that if a service attribute is specified that
 385          * its a printer name and there ought to be only one match.
 386          * Directly retrieve that service and confirm
 387          * that it meets the other requirements.
 388          * If printer name isn't mentioned then go a slow path checking
 389          * all printers if they meet the reqiremements.
 390          */
 391         PrintService[] services;
 392         PrinterName name = (PrinterName)serviceSet.get(PrinterName.class);
 393         PrintService defService;
 394         if (name != null && (defService = getDefaultPrintService()) != null) {
 395             /* To avoid execing a unix command  see if the client is asking
 396              * for the default printer by name, since we already have that
 397              * initialised.
 398              */
 399 
 400             PrinterName defName = defService.getAttribute(PrinterName.class);
 401 
 402             if (defName != null && name.equals(defName)) {
 403                 if (matchesAttributes(defService, serviceSet)) {
 404                     services = new PrintService[1];
 405                     services[0] = defService;
 406                     return services;
 407                 } else {
 408                     return new PrintService[0];
 409                 }
 410             } else {
 411                 /* Its not the default service */
 412                 PrintService service = getServiceByName(name);
 413                 if (service != null &&
 414                     matchesAttributes(service, serviceSet)) {
 415                     services = new PrintService[1];
 416                     services[0] = service;
 417                     return services;
 418                 } else {
 419                     return new PrintService[0];
 420                 }
 421             }
 422         } else {
 423             /* specified service attributes don't include a name.*/
 424             Vector matchedServices = new Vector();
 425             services = getPrintServices();
 426             for (int i = 0; i< services.length; i++) {
 427                 if (matchesAttributes(services[i], serviceSet)) {
 428                     matchedServices.add(services[i]);
 429                 }
 430             }
 431             services = new PrintService[matchedServices.size()];
 432             for (int i = 0; i< services.length; i++) {
 433                 services[i] = (PrintService)matchedServices.elementAt(i);
 434             }
 435             return services;
 436         }
 437     }
 438 
 439     /*
 440      * If service attributes are specified then there must be additional
 441      * filtering.
 442      */
 443     public PrintService[] getPrintServices(DocFlavor flavor,
 444                                            AttributeSet attributes) {
 445         SecurityManager security = System.getSecurityManager();
 446         if (security != null) {
 447           security.checkPrintJobAccess();
 448         }
 449         PrintRequestAttributeSet requestSet = null;
 450         PrintServiceAttributeSet serviceSet = null;
 451 
 452         if (attributes != null && !attributes.isEmpty()) {
 453 
 454             requestSet = new HashPrintRequestAttributeSet();
 455             serviceSet = new HashPrintServiceAttributeSet();
 456 
 457             Attribute[] attrs = attributes.toArray();
 458             for (int i=0; i<attrs.length; i++) {
 459                 if (attrs[i] instanceof PrintRequestAttribute) {
 460                     requestSet.add(attrs[i]);
 461                 } else if (attrs[i] instanceof PrintServiceAttribute) {
 462                     serviceSet.add(attrs[i]);
 463                 }
 464             }
 465         }
 466 
 467         PrintService[] services = getPrintServices(serviceSet);
 468         if (services.length == 0) {
 469             return services;
 470         }
 471 
 472         if (CUPSPrinter.isCupsRunning()) {
 473             ArrayList matchingServices = new ArrayList();
 474             for (int i=0; i<services.length; i++) {
 475                 try {
 476                     if (services[i].
 477                         getUnsupportedAttributes(flavor, requestSet) == null) {
 478                         matchingServices.add(services[i]);
 479                     }
 480                 } catch (IllegalArgumentException e) {
 481                 }
 482             }
 483             services = new PrintService[matchingServices.size()];
 484             return (PrintService[])matchingServices.toArray(services);
 485 
 486         } else {
 487             // We only need to compare 1 PrintService because all
 488             // UnixPrintServices are the same anyway.  We will not use
 489             // default PrintService because it might be null.
 490             PrintService service = services[0];
 491             if ((flavor == null ||
 492                  service.isDocFlavorSupported(flavor)) &&
 493                  service.getUnsupportedAttributes(flavor, requestSet) == null)
 494             {
 495                 return services;
 496             } else {
 497                 return new PrintService[0];
 498             }
 499         }
 500     }
 501 
 502     /*
 503      * return empty array as don't support multi docs
 504      */
 505     public MultiDocPrintService[]
 506         getMultiDocPrintServices(DocFlavor[] flavors,
 507                                  AttributeSet attributes) {
 508         SecurityManager security = System.getSecurityManager();
 509         if (security != null) {
 510           security.checkPrintJobAccess();
 511         }
 512         return new MultiDocPrintService[0];
 513     }
 514 
 515 
 516     public synchronized PrintService getDefaultPrintService() {
 517         SecurityManager security = System.getSecurityManager();
 518         if (security != null) {
 519           security.checkPrintJobAccess();
 520         }
 521 
 522         // clear defaultPrintService
 523         defaultPrintService = null;
 524 
 525         IPPPrintService.debug_println("isRunning ? "+
 526                                       (CUPSPrinter.isCupsRunning()));
 527         if (CUPSPrinter.isCupsRunning()) {
 528             defaultPrinter = CUPSPrinter.getDefaultPrinter();
 529         } else {
 530             if (isMac() || isSysV()) {
 531                 defaultPrinter = getDefaultPrinterNameSysV();
 532             } else {
 533                 defaultPrinter = getDefaultPrinterNameBSD();
 534             }
 535         }
 536         if (defaultPrinter == null) {
 537             return null;
 538         }
 539         defaultPrintService = null;
 540         if (printServices != null) {
 541             for (int j=0; j<printServices.length; j++) {
 542                 if (defaultPrinter.equals(printServices[j].getName())) {
 543                     defaultPrintService = printServices[j];
 544                     break;
 545                 }
 546             }
 547         }
 548         if (defaultPrintService == null) {
 549             if (CUPSPrinter.isCupsRunning()) {
 550                 try {
 551                     PrintService defaultPS =
 552                         new IPPPrintService(defaultPrinter,
 553                                             new URL("http://"+
 554                                                     CUPSPrinter.getServer()+":"+
 555                                                     CUPSPrinter.getPort()+"/"+
 556                                                     defaultPrinter));
 557                     defaultPrintService = defaultPS;
 558                 } catch (Exception e) {
 559                 }
 560             } else {
 561                 defaultPrintService = new UnixPrintService(defaultPrinter);
 562             }
 563         }
 564 
 565         return defaultPrintService;
 566     }
 567 
 568     public synchronized void
 569         getServicesInbackground(BackgroundLookupListener listener) {
 570         if (printServices != null) {
 571             listener.notifyServices(printServices);
 572         } else {
 573             if (lookupListeners == null) {
 574                 lookupListeners = new Vector();
 575                 lookupListeners.add(listener);
 576                 Thread lookupThread = new Thread(this);
 577                 lookupThread.start();
 578             } else {
 579                 lookupListeners.add(listener);
 580             }
 581         }
 582     }
 583 
 584     /* This method isn't used in most cases because we rely on code in
 585      * javax.print.PrintServiceLookup. This is needed just for the cases
 586      * where those interfaces are by-passed.
 587      */
 588     private PrintService[] copyOf(PrintService[] inArr) {
 589         if (inArr == null || inArr.length == 0) {
 590             return inArr;
 591         } else {
 592             PrintService []outArr = new PrintService[inArr.length];
 593             System.arraycopy(inArr, 0, outArr, 0, inArr.length);
 594             return outArr;
 595         }
 596     }
 597 
 598     public void run() {
 599         PrintService[] services = getPrintServices();
 600         synchronized (this) {
 601             BackgroundLookupListener listener;
 602             for (int i=0; i<lookupListeners.size(); i++) {
 603                 listener =
 604                     (BackgroundLookupListener)lookupListeners.elementAt(i);
 605                 listener.notifyServices(copyOf(services));
 606             }
 607             lookupListeners = null;
 608         }
 609     }
 610 
 611     private String getDefaultPrinterNameBSD() {
 612         if (cmdIndex == UNINITIALIZED) {
 613             cmdIndex = getBSDCommandIndex();
 614         }
 615         String[] names = execCmd(lpcFirstCom[cmdIndex]);
 616         if (names == null || names.length == 0) {
 617             return null;
 618         }
 619 
 620         if ((cmdIndex==BSD_LPD_NG) &&
 621             (names[0].startsWith("missingprinter"))) {
 622             return null;
 623         }
 624         return names[0];
 625     }
 626 
 627     private PrintService getNamedPrinterNameBSD(String name) {
 628       if (cmdIndex == UNINITIALIZED) {
 629         cmdIndex = getBSDCommandIndex();
 630       }
 631       String command = "/usr/sbin/lpc status " + name + lpcNameCom[cmdIndex];
 632       String[] result = execCmd(command);
 633 
 634       if (result == null || !(result[0].equals(name))) {
 635           return null;
 636       }
 637       return new UnixPrintService(name);
 638     }
 639 
 640     private String[] getAllPrinterNamesBSD() {
 641         if (cmdIndex == UNINITIALIZED) {
 642             cmdIndex = getBSDCommandIndex();
 643         }
 644         String[] names = execCmd(lpcAllCom[cmdIndex]);
 645         if (names == null || names.length == 0) {
 646           return null;
 647         }
 648         return names;
 649     }
 650 
 651     static String getDefaultPrinterNameSysV() {
 652         String defaultPrinter = "lp";
 653         String command = "/usr/bin/lpstat -d";
 654 
 655         String [] names = execCmd(command);
 656         if (names == null || names.length == 0) {
 657             return defaultPrinter;
 658         } else {
 659             int index = names[0].indexOf(":");
 660             if (index == -1  || (names[0].length() <= index+1)) {
 661                 return null;
 662             } else {
 663                 String name = names[0].substring(index+1).trim();
 664                 if (name.length() == 0) {
 665                     return null;
 666                 } else {
 667                     return name;
 668                 }
 669             }
 670         }
 671     }
 672 
 673     private PrintService getNamedPrinterNameSysV(String name) {
 674 
 675         String command = "/usr/bin/lpstat -v " + name;
 676         String []result = execCmd(command);
 677 
 678         if (result == null || result[0].indexOf("unknown printer") > 0) {
 679             return null;
 680         } else {
 681             return new UnixPrintService(name);
 682         }
 683     }
 684 
 685     private String[] getAllPrinterNamesSysV() {
 686         String defaultPrinter = "lp";
 687         String command = "/usr/bin/lpstat -v|/usr/bin/expand|/usr/bin/cut -f3 -d' ' |/usr/bin/cut -f1 -d':' | /usr/bin/sort";
 688 
 689         String [] names = execCmd(command);
 690         ArrayList printerNames = new ArrayList();
 691         for (int i=0; i < names.length; i++) {
 692             if (!names[i].equals("_default") &&
 693                 !names[i].equals(defaultPrinter) &&
 694                 !names[i].equals("")) {
 695                 printerNames.add(names[i]);
 696             }
 697         }
 698         return (String[])printerNames.toArray(new String[printerNames.size()]);
 699     }
 700 
 701     static String[] execCmd(final String command) {
 702         ArrayList results = null;
 703         try {
 704             final String[] cmd = new String[3];
 705             if (isSysV()) {
 706                 cmd[0] = "/usr/bin/sh";
 707                 cmd[1] = "-c";
 708                 cmd[2] = "env LC_ALL=C " + command;
 709             } else {
 710                 cmd[0] = "/bin/sh";
 711                 cmd[1] = "-c";
 712                 cmd[2] = "LC_ALL=C " + command;
 713             }
 714 
 715             results = (ArrayList)AccessController.doPrivileged(
 716                 new PrivilegedExceptionAction() {
 717                     public Object run() throws IOException {
 718 
 719                         Process proc;
 720                         BufferedReader bufferedReader = null;
 721                         File f = Files.createTempFile("prn","xc").toFile();
 722                         cmd[2] = cmd[2]+">"+f.getAbsolutePath();
 723 
 724                         proc = Runtime.getRuntime().exec(cmd);
 725                         try {
 726                             boolean done = false; // in case of interrupt.
 727                             while (!done) {
 728                                 try {
 729                                     proc.waitFor();
 730                                     done = true;
 731                                 } catch (InterruptedException e) {
 732                                 }
 733                             }
 734 
 735                             if (proc.exitValue() == 0) {
 736                                 FileReader reader = new FileReader(f);
 737                                 bufferedReader = new BufferedReader(reader);
 738                                 String line;
 739                                 ArrayList results = new ArrayList();
 740                                 while ((line = bufferedReader.readLine())
 741                                        != null) {
 742                                     results.add(line);
 743                                 }
 744                                 return results;
 745                             }
 746                         } finally {
 747                             f.delete();
 748                             // promptly close all streams.
 749                             if (bufferedReader != null) {
 750                                 bufferedReader.close();
 751                             }
 752                             proc.getInputStream().close();
 753                             proc.getErrorStream().close();
 754                             proc.getOutputStream().close();
 755                         }
 756                         return null;
 757                     }
 758                 });
 759         } catch (PrivilegedActionException e) {
 760         }
 761         if (results == null) {
 762             return new String[0];
 763         } else {
 764             return (String[])results.toArray(new String[results.size()]);
 765         }
 766     }
 767 
 768     private class PrinterChangeListener extends Thread {
 769 
 770         public void run() {
 771             int refreshSecs;
 772             while (true) {
 773                 try {
 774                     refreshServices();
 775                 } catch (Exception se) {
 776                     IPPPrintService.debug_println(debugPrefix+"Exception in refresh thread.");
 777                     break;
 778                 }
 779 
 780                 if ((printServices != null) &&
 781                     (printServices.length > minRefreshTime)) {
 782                     // compute new refresh time 1 printer = 1 sec
 783                     refreshSecs = printServices.length;
 784                 } else {
 785                     refreshSecs = minRefreshTime;
 786                 }
 787                 try {
 788                     sleep(refreshSecs * 1000);
 789                 } catch (InterruptedException e) {
 790                     break;
 791                 }
 792             }
 793         }
 794     }
 795 }