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