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