1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 * 25 */ 26 27 // sldp.java : The service location daemon. 28 // Author: Erik Guttman 29 // 30 31 package com.sun.slp; 32 33 import java.io.*; 34 import java.util.*; 35 import java.net.*; 36 import java.lang.reflect.*; 37 import java.awt.*; 38 39 /** 40 * The slpd class is the main class for the slpd directory agent/ 41 * service agent server of SLP. Slpd can run in two possible modes, 42 * depending on the configuration of the classes with which it was shipped 43 * and information read from the optional configuration file argument: 44 * 45 * <ol> 46 * <li> <b> Service Agent server </b> 47 * In this mode, slpd functions as a service agent server only. 48 * Directory agent functionality is disabled. Service agent 49 * clients on the local machine register and deregister services 50 * with slpd, and slpd responds to multicast requests for 51 * services. It passively and actively listens for directory agent 52 * advertisements, caches them, and forwards them to both 53 * user agent and service agent clients. A file of serialized 54 * proxy registrations can be read at startup. 55 * This mode is normally default. 56 * 57 * <li> <b> Directory Agent/Service Agent server </b> 58 * In this mode, slpd functions as a directory agent 59 * for port 427 on the local machine. The directory agent 60 * caches service advertisements, makes directory agent 61 * advertisements of its services, and responds to requests 62 * for TCP connections with service agents and user agents. 63 * In addition, slpd functions in the first mode, as a 64 * service agent server, for SAs and UAs on the local host. 65 * 66 * </ol> 67 * 68 * The slpd is invoked as follows:<br> 69 *<blockquote> 70 * 71 * java com.sun.slpd [monitor] [stop] [-f <config file name>] 72 * 73 *</blockquote> 74 * 75 * The optional monitor argument specifies that a GUI monitor should be 76 * brought up. The optional stop argument indicates that slpd should 77 * signal a running slpd to stop. The optional <config file name> argument 78 * specifies that the named file should be used as the configuration file. 79 * <p> 80 * See <a href="slpd.conf.html">slpd.conf</a> for more information on 81 * configuration file syntax and <a href="slpd.reg.html">slpd.reg</a> 82 * for more information on proxy registration file syntax. 83 * 84 * @author Erik Guttman, James Kempf 85 */ 86 87 // Note that the inheritance is *only* so slpd can initialize the 88 // internals of SLP config. 89 90 public class slpd extends SLPConfig { 91 92 private static final String SERVER_BUNDLE_NAME = "com/sun/slp/Server"; 93 94 // Server bundle. Set the parent. 95 96 static class ServerBundle extends ResourceBundle { 97 98 private ResourceBundle bundle = null; 99 100 static ResourceBundle 101 getBundle(ResourceBundle parent, Locale locale) 102 throws MissingResourceException { 103 104 return new ServerBundle(parent, locale); 105 106 } 107 108 private ServerBundle(ResourceBundle parent, Locale locale) 109 throws MissingResourceException { 110 111 if (parent != null) { 112 this.parent = parent; 113 114 } 115 116 try { 117 URL[] urls = null; 118 urls = new URL[] {new URL("file:/usr/share/lib/locale/")}; 119 URLClassLoader ld = new URLClassLoader(urls); 120 121 bundle = 122 ResourceBundle.getBundle(SERVER_BUNDLE_NAME, locale, ld); 123 124 } catch (MalformedURLException e) { 125 // fallthru to default location 126 } // No locales in slpd.jar, so propagate the 127 // MissingResourceException 128 129 bundle = bundle != null ? 130 bundle : 131 ResourceBundle.getBundle(SERVER_BUNDLE_NAME, locale); 132 133 } 134 135 protected Object handleGetObject(String key) 136 throws MissingResourceException { 137 Object ret = null; 138 139 try { 140 141 ret = bundle.getObject(key); 142 143 } catch (MissingResourceException ex) { 144 ret = parent.getObject(key); 145 146 } 147 148 return ret; 149 150 } 151 152 public Enumeration getKeys() { 153 return bundle.getKeys(); 154 155 } 156 157 } 158 159 /** 160 * Log object for SLP to write to GUI. This class follows the 161 * semantics of the other SLP logging classes: 162 * 163 * This class does not actually write anything until the flush() method 164 * in invoked; this will write the concatenation of all messages 165 * passed to the write() method since the last invocation of flush(). 166 * 167 * The actual logging class used can be controlled via the 168 * sun.net.slp.loggerClass property. 169 * 170 * See also the StderrLog and Syslog classes. 171 */ 172 173 static class SLPLog extends Writer { 174 175 private TextArea taLog = null; 176 private StringBuffer buf; 177 178 SLPLog(TextArea nta) { 179 taLog = nta; 180 buf = new StringBuffer(); 181 182 } 183 184 // Write to the StringBuffer 185 186 public void write(char[] cbuf, int off, int len) 187 throws IOException { 188 buf.append(cbuf, off, len); 189 190 } 191 192 // Write to the Frame. 193 194 public void flush() throws IOException { 195 String date = SLPConfig.getDateString(); 196 197 taLog.append( 198 "********" + 199 date + "\n" + 200 buf.toString() + "\n" + 201 "********\n"); 202 buf = new StringBuffer(); 203 204 } 205 206 // These is a no-op 207 208 public void close() throws IOException {} 209 210 } 211 212 // 213 // slpd definition. 214 // 215 216 private static String configFile; // name of configuration file 217 private static SLPDgui slpdgui; // GUI monitor, if desired 218 private static SLPConfig config; // handles system properties 219 private static ServerDATable daTable; // handle recording of DAs 220 221 // Called by the slpd and subclasses only. 222 223 protected slpd() { 224 super(); 225 } 226 227 private static void usage() { 228 ResourceBundle bundle = 229 getMessageBundleInternal(Locale.getDefault(), null); 230 System.err.println(formatMessageInternal("slpd_usage", 231 new Object[0], 232 bundle)); 233 System.exit(1); 234 } 235 236 /** 237 * Usage: slpd [monitor] [stop] [-f config-file name]<br> 238 * <br> 239 * String arguments are: 240 * <br> 241 * <b> monitor </b> Puts up a rudimentary GUI for the slpd.<br> 242 * <b>stop <b> Bring down a running slpd and exit. 243 * <b> config-file name </b> Reads the specified configuration file.<br> 244 * 245 * The default running mode is to have no GUI and to use SLP 246 * defined configuration. 247 */ 248 249 public static void main(String args[]) { 250 boolean bMon = false; 251 boolean bStop = false; 252 configFile = null; 253 254 Thread.currentThread().setName("slpd"); 255 256 // Process args. 257 258 if (args.length > 3) { 259 usage(); 260 261 } 262 263 int i, n = args.length; 264 265 for (i = 0; i < n; i++) { 266 267 // Argument is a config file. 268 269 if (args[i].equals("-f")) { 270 271 if (configFile != null) { 272 usage(); 273 274 } 275 276 // Make sure we can open it. 277 278 try { 279 File f = new File(args[++i]); 280 configFile = args[i]; 281 282 } catch (Exception ex) { 283 usage(); 284 285 } 286 } else if (args[i].equals("monitor")) { 287 bMon = true; 288 289 } else if (args[i].equals("stop")) { 290 bStop = true; 291 292 } else { 293 usage(); 294 295 } 296 } 297 298 // Read message bundle file, load config file into properties. 299 300 ResourceBundle bundle = 301 getMessageBundleInternal(Locale.getDefault(), null); 302 303 try { 304 if (configFile != null) { 305 Properties props = System.getProperties(); 306 props.setProperty("sun.net.slp.configURL", 307 "file:" + configFile); 308 309 } 310 311 // Create a new SLP Config object from the config file. 312 config = initializeSLPConfig(); 313 314 // Create a GUI if the user asked for one. 315 316 if (bMon) { 317 318 try { 319 slpdgui = new SLPDgui(configFile); 320 SLPLog log = new SLPLog(slpdgui.getTALog()); 321 322 synchronized (config) { 323 config.log = log; 324 } 325 326 slpdgui.setVisible(true); 327 328 } catch (Exception ex) { 329 System.err.println(formatMessageInternal("slpd_no_gui", 330 new Object[0], 331 bundle)); 332 } 333 } 334 335 // Either start or stop the server, depending on what was 336 // requested. 337 338 if (!bStop) { 339 start(); 340 341 } else { 342 stop(); 343 344 } 345 } catch (ServiceLocationException ex) { 346 347 errorExit(bundle, ex); 348 349 } 350 351 } 352 353 /** 354 * Start the slpd. 355 * 356 * @param bMon True if initializing with GUI monitor. 357 * @exception ServiceLocationException Internal error or network 358 * initialization error or 359 * internal networking error. 360 * 361 */ 362 363 static void start() throws ServiceLocationException { 364 365 // Initialize the service table. 366 367 ServiceTable table = ServiceTable.getServiceTable(); 368 369 // Initialize the class name for the DA table to the Sun-specific 370 // DA table. 371 372 Properties props = System.getProperties(); 373 props.put(DATable.DA_TABLE_CLASS_PROP, "com.sun.slp.SunServerDATable"); 374 375 // If there is a request on stdin, process it now 376 try { 377 if (System.in.available() > 0) { 378 RequestHandler rh = 379 new RequestHandler(System.in, System.out, config); 380 rh.start(); 381 } 382 } catch (IOException e) {} 383 384 // Start a StreamListener on loopback to start accepting locals regs 385 386 StreamListener.initializeStreamListenerOnInterface( 387 config.getLoopback()); 388 389 // Create a ServerDATable from the class. This will initialize 390 // active discovery. Note that we need to record our own presence 391 // in the DA table because we are not yet listening for requests. 392 // We do this after deserialization so that if we discover any 393 // DAs, we can perform registrations of the serialized advertisements. 394 395 daTable = ServerDATable.getServerDATable(); 396 397 // Deserialize any serialized advertisements and do them now. 398 // Waiting until here allows any error messages to appear in 399 // the GUI log, if any, and at this point the DA table is ready. 400 401 table.deserializeTable(); 402 403 // Need to create datagram and stream listeners, and a 404 // DAAdvertiser on all network interfaces. 405 406 Vector interfaces = config.getInterfaces(); 407 int i, n = interfaces.size(); 408 409 for (i = 0; i < n; i++) { 410 InetAddress interfac = (InetAddress)interfaces.elementAt(i); 411 412 // Initialize the complex of listener/sender objects on the 413 // interface. This includes a datagram listener, a DAAdvertiser 414 // (which shares the same socket as the datagram listener), and 415 // a stream listener. 416 417 Listener.initializeInterfaceManagers(interfac); 418 419 } 420 421 // If we've been configured as a DA, then create a DA advertiser to 422 // periodically advertise our presence on this interface. This 423 // is only done on the default interface. 424 425 if (config.isDA()) { 426 DAAdvertiser.initializeDAAdvertiserOnInterface( 427 config.getLocalHost()); 428 429 } 430 431 // Report scopes and whether DA or SA. 432 433 Vector discoveredScopes = daTable.findScopes(); 434 Vector serverScopes = config.getSAConfiguredScopes(); 435 Vector daAttributes = config.getDAAttributes(); 436 Vector saAttributes = config.getSAAttributes(); 437 438 // Report that we are running if tracing is on 439 440 if (config.regTest() || 441 config.traceMsg() || 442 config.traceDrop() || 443 config.traceDATraffic()) { 444 445 config.writeLog((config.isDA() ? "hello_da":"hello"), 446 new Object[] {interfaces, 447 serverScopes, 448 discoveredScopes, 449 (config.isDA() ? 450 daAttributes:saAttributes)}); 451 } 452 453 // If V1 is supported, crank up V1 support as well. 454 455 if (config.isV1Supported()) { 456 SLPV1Manager.start(config, daTable, table); 457 458 } 459 } 460 461 // Stop a running server by sending a DAAdvert or SAAdvert. 462 463 static void stop() throws ServiceLocationException { 464 465 if (daemonIsDA()) { 466 stopDA(); 467 468 } else { 469 stopSA(); 470 471 } 472 } 473 474 // Determine whether the daemon running on this machine is a DA 475 // or not. 476 477 static boolean daemonIsDA() throws ServiceLocationException { 478 479 // Get a DA table with available DAs. 480 481 DATable table = 482 DATable.getDATable(); 483 484 // Get DAs. 485 486 Hashtable das = 487 table.findDAScopes(config.getSAConfiguredScopes()); 488 Vector daRecs = (Vector)das.get(DATable.UNICAST_KEY); 489 Vector interfaces = config.getInterfaces(); 490 491 // If no DAs, then simply return. 492 493 if (daRecs == null) { 494 return false; 495 496 } 497 498 // Find our address in the list, if it exists. 499 500 int i, n = daRecs.size(); 501 502 for (i = 0; i < n; i++) { 503 DATable.DARecord rec = 504 (DATable.DARecord)daRecs.elementAt(i); 505 Vector daAddresses = rec.daAddresses; 506 507 int j, m = interfaces.size(); 508 509 for (j = 0; j < m; j++) { 510 if (daAddresses.contains(interfaces.elementAt(i))) { 511 return true; 512 513 } 514 } 515 } 516 517 return false; 518 } 519 520 // Stop a DA by multicasting the DAAdvert with boot timestamp 0. 521 522 private static void stopDA() throws ServiceLocationException { 523 524 // Make the DA URL and the DAAdvert. Note that we only need signal 525 // on the default local host interface because that is the only 526 // one on which the server is listening. 527 528 ServiceURL url = 529 new ServiceURL(Defaults.DA_SERVICE_TYPE + 530 "://" + 531 config.getLocalHost().getHostAddress(), 532 ServiceURL.LIFETIME_DEFAULT); 533 534 SDAAdvert advert = 535 new SDAAdvert(new SLPServerHeaderV2(), 536 (short)0x0, // sez we're unsolicited... 537 0L, // sez we're going down... 538 url, 539 config.getSAConfiguredScopes(), 540 new Vector()); // no attributes needed to go down... 541 542 // Make the DAAdvertiser. 543 544 DAAdvertiser daadv = new DAAdvertiser(config.getLocalHost(), 545 advert.getHeader()); 546 547 // Send out unsolicted "going down" message. 548 549 daadv.sendAdvert(); 550 551 // That's it! No need for any messages here. 552 553 System.exit(0); 554 555 } 556 557 // Stop an SA server by unicasting an SA advert with xid 0. 558 559 private static void stopSA() throws ServiceLocationException { 560 561 // We signal for stop on the local host, which is guaranteed 562 // to have an SA listener. 563 564 ServiceURL url = 565 new ServiceURL(Defaults.SA_SERVICE_TYPE + "://" + 566 config.getLocalHost().getHostAddress(), 567 ServiceURL.LIFETIME_DEFAULT); 568 569 SSAAdvert advert = new SSAAdvert(Defaults.version, 570 (short)0x0, // sez we're going down... 571 config.getLocale(), 572 url, 573 config.getSAConfiguredScopes(), 574 new Vector()); 575 // don't care about attrs.., 576 577 // Send it TCP. We ignore NETWORK_ERROR because it only means 578 // that the daemon didn't send us a reply, which is expected. 579 580 try { 581 582 SrvLocMsg msg = 583 Transact.transactTCPMsg(config.getLoopback(), advert, false); 584 585 if (msg.getErrorCode() != ServiceLocationException.OK) { 586 config.writeLog("slpd_sa_stop_failure", 587 new Object[] { 588 new Integer(msg.getErrorCode())}); 589 590 } 591 592 } catch (ServiceLocationException ex) { 593 594 if (ex.getErrorCode() != ServiceLocationException.NETWORK_ERROR) { 595 config.writeLog("slpd_sa_stop_failure", 596 new Object[] {new Integer(ex.getErrorCode())}); 597 598 } 599 600 } 601 602 // That's it! 603 604 System.exit(0); 605 } 606 607 // Print error message, exit. 608 609 static void errorExit(ResourceBundle bundle, ServiceLocationException ex) { 610 611 switch (ex.getErrorCode()) { 612 613 case ServiceLocationException.INTERNAL_SYSTEM_ERROR: 614 System.err.println(formatMessageInternal("slpd_int_err", 615 new Object[] { 616 ex.getMessage()}, 617 bundle)); 618 break; 619 620 case ServiceLocationException.NETWORK_INIT_FAILED: 621 System.err.println(formatMessageInternal("slpd_intnet_err", 622 new Object[] { 623 ex.getMessage()}, 624 bundle)); 625 break; 626 627 case ServiceLocationException.NETWORK_ERROR: 628 System.err.println(formatMessageInternal("slpd_net_err", 629 new Object[] { 630 ex.getMessage()}, 631 bundle)); 632 break; 633 634 default: 635 System.err.println(formatMessageInternal("slpd_err", 636 new Object[] { 637 new Integer(ex.getErrorCode()), 638 ex.getMessage()}, 639 bundle)); 640 641 } 642 643 ex.printStackTrace(); 644 System.err.println(formatMessageInternal("exiting_msg", 645 new Object[0], 646 bundle)); 647 648 System.exit(1); 649 650 } 651 652 // Make a new SLPConfig object of the right class type. 653 654 private static SLPConfig initializeSLPConfig() { 655 656 // The server *always* runs as an SA. It may also run as a DA. 657 658 config.isSA = true; 659 660 // set default logging class for slpd to syslog 661 662 if (System.getProperty("sun.net.slp.loggerClass") == null) { 663 Properties props = System.getProperties(); 664 props.setProperty("sun.net.slp.loggerClass", "com.sun.slp.Syslog"); 665 System.setProperties(props); 666 667 } 668 669 // slpd is the server side config. 670 671 theSLPConfig = new slpd(); 672 673 return theSLPConfig; 674 675 } 676 677 // 678 // Extensions to sldp for server side only. 679 // 680 681 boolean isDA() { 682 return Boolean.getBoolean("net.slp.isDA"); 683 } 684 685 // Determine whether V1 is supported. Default is no. 686 687 boolean isV1Supported() { 688 689 if (!isDA() || super.getSLPv1NotSupported()) { 690 return false; 691 692 } 693 694 boolean v1Supported = false; 695 696 try { 697 698 Class.forName("com.sun.slp.SLPV1Manager"); 699 v1Supported = true; 700 701 } catch (ClassNotFoundException ex) { 702 703 // Not there. 704 705 } 706 707 return v1Supported; 708 709 } 710 711 // Load server message bundle. 712 713 private static final String serverMsgBundle = "Server"; 714 715 ResourceBundle getMessageBundle(Locale locale) { 716 717 // Get the parent bundle first. 718 719 ResourceBundle parentBundle = super.getMessageBundle(locale); 720 721 return getMessageBundleInternal(locale, parentBundle); 722 723 } 724 725 // We need this in case we get an error before the config object is 726 // created. 727 728 static private ResourceBundle getMessageBundleInternal( 729 Locale locale, 730 ResourceBundle parentBundle) { 731 732 // Now create a server subclass. 733 734 ResourceBundle msgBundle = null; 735 736 try { 737 msgBundle = ServerBundle.getBundle(parentBundle, locale); 738 739 } catch (MissingResourceException ex) { // can't localize this one! 740 741 // We can't print out to the log, because we may be in the 742 // process of trying to. 743 744 System.out.println("Missing resource bundle ``"+ 745 SERVER_BUNDLE_NAME+ 746 "'' for locale ``"+ 747 locale+ 748 "''"); 749 // Hosed if the default locale is missing. 750 751 if (locale.equals(Defaults.locale)) { 752 753 System.out.println("Exiting..."); 754 System.exit(1); 755 } 756 757 // Otherwise, return the default locale. 758 759 System.out.println("Using SLP default locale ``" + 760 Defaults.locale+"''"); 761 762 msgBundle = 763 getMessageBundleInternal(Defaults.locale, parentBundle); 764 765 } 766 767 return msgBundle; 768 } 769 770 } 771