/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * */ // sldp.java : The service location daemon. // Author: Erik Guttman // package com.sun.slp; import java.io.*; import java.util.*; import java.net.*; import java.lang.reflect.*; import java.awt.*; /** * The slpd class is the main class for the slpd directory agent/ * service agent server of SLP. Slpd can run in two possible modes, * depending on the configuration of the classes with which it was shipped * and information read from the optional configuration file argument: * *
    *
  1. Service Agent server * In this mode, slpd functions as a service agent server only. * Directory agent functionality is disabled. Service agent * clients on the local machine register and deregister services * with slpd, and slpd responds to multicast requests for * services. It passively and actively listens for directory agent * advertisements, caches them, and forwards them to both * user agent and service agent clients. A file of serialized * proxy registrations can be read at startup. * This mode is normally default. * *
  2. Directory Agent/Service Agent server * In this mode, slpd functions as a directory agent * for port 427 on the local machine. The directory agent * caches service advertisements, makes directory agent * advertisements of its services, and responds to requests * for TCP connections with service agents and user agents. * In addition, slpd functions in the first mode, as a * service agent server, for SAs and UAs on the local host. * *
* * The slpd is invoked as follows:
*
* * java com.sun.slpd [monitor] [stop] [-f ] * *
* * The optional monitor argument specifies that a GUI monitor should be * brought up. The optional stop argument indicates that slpd should * signal a running slpd to stop. The optional argument * specifies that the named file should be used as the configuration file. *

* See slpd.conf for more information on * configuration file syntax and slpd.reg * for more information on proxy registration file syntax. * * @author Erik Guttman, James Kempf */ // Note that the inheritance is *only* so slpd can initialize the // internals of SLP config. public class slpd extends SLPConfig { private static final String SERVER_BUNDLE_NAME = "com/sun/slp/Server"; // Server bundle. Set the parent. static class ServerBundle extends ResourceBundle { private ResourceBundle bundle = null; static ResourceBundle getBundle(ResourceBundle parent, Locale locale) throws MissingResourceException { return new ServerBundle(parent, locale); } private ServerBundle(ResourceBundle parent, Locale locale) throws MissingResourceException { if (parent != null) { this.parent = parent; } try { URL[] urls = null; urls = new URL[] {new URL("file:/usr/share/lib/locale/")}; URLClassLoader ld = new URLClassLoader(urls); bundle = ResourceBundle.getBundle(SERVER_BUNDLE_NAME, locale, ld); } catch (MalformedURLException e) { // fallthru to default location } // No locales in slpd.jar, so propagate the // MissingResourceException bundle = bundle != null ? bundle : ResourceBundle.getBundle(SERVER_BUNDLE_NAME, locale); } protected Object handleGetObject(String key) throws MissingResourceException { Object ret = null; try { ret = bundle.getObject(key); } catch (MissingResourceException ex) { ret = parent.getObject(key); } return ret; } public Enumeration getKeys() { return bundle.getKeys(); } } /** * Log object for SLP to write to GUI. This class follows the * semantics of the other SLP logging classes: * * This class does not actually write anything until the flush() method * in invoked; this will write the concatenation of all messages * passed to the write() method since the last invocation of flush(). * * The actual logging class used can be controlled via the * sun.net.slp.loggerClass property. * * See also the StderrLog and Syslog classes. */ static class SLPLog extends Writer { private TextArea taLog = null; private StringBuffer buf; SLPLog(TextArea nta) { taLog = nta; buf = new StringBuffer(); } // Write to the StringBuffer public void write(char[] cbuf, int off, int len) throws IOException { buf.append(cbuf, off, len); } // Write to the Frame. public void flush() throws IOException { String date = SLPConfig.getDateString(); taLog.append( "********" + date + "\n" + buf.toString() + "\n" + "********\n"); buf = new StringBuffer(); } // These is a no-op public void close() throws IOException {} } // // slpd definition. // private static String configFile; // name of configuration file private static SLPDgui slpdgui; // GUI monitor, if desired private static SLPConfig config; // handles system properties private static ServerDATable daTable; // handle recording of DAs // Called by the slpd and subclasses only. protected slpd() { super(); } private static void usage() { ResourceBundle bundle = getMessageBundleInternal(Locale.getDefault(), null); System.err.println(formatMessageInternal("slpd_usage", new Object[0], bundle)); System.exit(1); } /** * Usage: slpd [monitor] [stop] [-f config-file name]
*
* String arguments are: *
* monitor Puts up a rudimentary GUI for the slpd.
* stop Bring down a running slpd and exit. * config-file name Reads the specified configuration file.
* * The default running mode is to have no GUI and to use SLP * defined configuration. */ public static void main(String args[]) { boolean bMon = false; boolean bStop = false; configFile = null; Thread.currentThread().setName("slpd"); // Process args. if (args.length > 3) { usage(); } int i, n = args.length; for (i = 0; i < n; i++) { // Argument is a config file. if (args[i].equals("-f")) { if (configFile != null) { usage(); } // Make sure we can open it. try { File f = new File(args[++i]); configFile = args[i]; } catch (Exception ex) { usage(); } } else if (args[i].equals("monitor")) { bMon = true; } else if (args[i].equals("stop")) { bStop = true; } else { usage(); } } // Read message bundle file, load config file into properties. ResourceBundle bundle = getMessageBundleInternal(Locale.getDefault(), null); try { if (configFile != null) { Properties props = System.getProperties(); props.setProperty("sun.net.slp.configURL", "file:" + configFile); } // Create a new SLP Config object from the config file. config = initializeSLPConfig(); // Create a GUI if the user asked for one. if (bMon) { try { slpdgui = new SLPDgui(configFile); SLPLog log = new SLPLog(slpdgui.getTALog()); synchronized (config) { config.log = log; } slpdgui.setVisible(true); } catch (Exception ex) { System.err.println(formatMessageInternal("slpd_no_gui", new Object[0], bundle)); } } // Either start or stop the server, depending on what was // requested. if (!bStop) { start(); } else { stop(); } } catch (ServiceLocationException ex) { errorExit(bundle, ex); } } /** * Start the slpd. * * @param bMon True if initializing with GUI monitor. * @exception ServiceLocationException Internal error or network * initialization error or * internal networking error. * */ static void start() throws ServiceLocationException { // Initialize the service table. ServiceTable table = ServiceTable.getServiceTable(); // Initialize the class name for the DA table to the Sun-specific // DA table. Properties props = System.getProperties(); props.put(DATable.DA_TABLE_CLASS_PROP, "com.sun.slp.SunServerDATable"); // If there is a request on stdin, process it now try { if (System.in.available() > 0) { RequestHandler rh = new RequestHandler(System.in, System.out, config); rh.start(); } } catch (IOException e) {} // Start a StreamListener on loopback to start accepting locals regs StreamListener.initializeStreamListenerOnInterface( config.getLoopback()); // Create a ServerDATable from the class. This will initialize // active discovery. Note that we need to record our own presence // in the DA table because we are not yet listening for requests. // We do this after deserialization so that if we discover any // DAs, we can perform registrations of the serialized advertisements. daTable = ServerDATable.getServerDATable(); // Deserialize any serialized advertisements and do them now. // Waiting until here allows any error messages to appear in // the GUI log, if any, and at this point the DA table is ready. table.deserializeTable(); // Need to create datagram and stream listeners, and a // DAAdvertiser on all network interfaces. Vector interfaces = config.getInterfaces(); int i, n = interfaces.size(); for (i = 0; i < n; i++) { InetAddress interfac = (InetAddress)interfaces.elementAt(i); // Initialize the complex of listener/sender objects on the // interface. This includes a datagram listener, a DAAdvertiser // (which shares the same socket as the datagram listener), and // a stream listener. Listener.initializeInterfaceManagers(interfac); } // If we've been configured as a DA, then create a DA advertiser to // periodically advertise our presence on this interface. This // is only done on the default interface. if (config.isDA()) { DAAdvertiser.initializeDAAdvertiserOnInterface( config.getLocalHost()); } // Report scopes and whether DA or SA. Vector discoveredScopes = daTable.findScopes(); Vector serverScopes = config.getSAConfiguredScopes(); Vector daAttributes = config.getDAAttributes(); Vector saAttributes = config.getSAAttributes(); // Report that we are running if tracing is on if (config.regTest() || config.traceMsg() || config.traceDrop() || config.traceDATraffic()) { config.writeLog((config.isDA() ? "hello_da":"hello"), new Object[] {interfaces, serverScopes, discoveredScopes, (config.isDA() ? daAttributes:saAttributes)}); } // If V1 is supported, crank up V1 support as well. if (config.isV1Supported()) { SLPV1Manager.start(config, daTable, table); } } // Stop a running server by sending a DAAdvert or SAAdvert. static void stop() throws ServiceLocationException { if (daemonIsDA()) { stopDA(); } else { stopSA(); } } // Determine whether the daemon running on this machine is a DA // or not. static boolean daemonIsDA() throws ServiceLocationException { // Get a DA table with available DAs. DATable table = DATable.getDATable(); // Get DAs. Hashtable das = table.findDAScopes(config.getSAConfiguredScopes()); Vector daRecs = (Vector)das.get(DATable.UNICAST_KEY); Vector interfaces = config.getInterfaces(); // If no DAs, then simply return. if (daRecs == null) { return false; } // Find our address in the list, if it exists. int i, n = daRecs.size(); for (i = 0; i < n; i++) { DATable.DARecord rec = (DATable.DARecord)daRecs.elementAt(i); Vector daAddresses = rec.daAddresses; int j, m = interfaces.size(); for (j = 0; j < m; j++) { if (daAddresses.contains(interfaces.elementAt(i))) { return true; } } } return false; } // Stop a DA by multicasting the DAAdvert with boot timestamp 0. private static void stopDA() throws ServiceLocationException { // Make the DA URL and the DAAdvert. Note that we only need signal // on the default local host interface because that is the only // one on which the server is listening. ServiceURL url = new ServiceURL(Defaults.DA_SERVICE_TYPE + "://" + config.getLocalHost().getHostAddress(), ServiceURL.LIFETIME_DEFAULT); SDAAdvert advert = new SDAAdvert(new SLPServerHeaderV2(), (short)0x0, // sez we're unsolicited... 0L, // sez we're going down... url, config.getSAConfiguredScopes(), new Vector()); // no attributes needed to go down... // Make the DAAdvertiser. DAAdvertiser daadv = new DAAdvertiser(config.getLocalHost(), advert.getHeader()); // Send out unsolicted "going down" message. daadv.sendAdvert(); // That's it! No need for any messages here. System.exit(0); } // Stop an SA server by unicasting an SA advert with xid 0. private static void stopSA() throws ServiceLocationException { // We signal for stop on the local host, which is guaranteed // to have an SA listener. ServiceURL url = new ServiceURL(Defaults.SA_SERVICE_TYPE + "://" + config.getLocalHost().getHostAddress(), ServiceURL.LIFETIME_DEFAULT); SSAAdvert advert = new SSAAdvert(Defaults.version, (short)0x0, // sez we're going down... config.getLocale(), url, config.getSAConfiguredScopes(), new Vector()); // don't care about attrs.., // Send it TCP. We ignore NETWORK_ERROR because it only means // that the daemon didn't send us a reply, which is expected. try { SrvLocMsg msg = Transact.transactTCPMsg(config.getLoopback(), advert, false); if (msg.getErrorCode() != ServiceLocationException.OK) { config.writeLog("slpd_sa_stop_failure", new Object[] { new Integer(msg.getErrorCode())}); } } catch (ServiceLocationException ex) { if (ex.getErrorCode() != ServiceLocationException.NETWORK_ERROR) { config.writeLog("slpd_sa_stop_failure", new Object[] {new Integer(ex.getErrorCode())}); } } // That's it! System.exit(0); } // Print error message, exit. static void errorExit(ResourceBundle bundle, ServiceLocationException ex) { switch (ex.getErrorCode()) { case ServiceLocationException.INTERNAL_SYSTEM_ERROR: System.err.println(formatMessageInternal("slpd_int_err", new Object[] { ex.getMessage()}, bundle)); break; case ServiceLocationException.NETWORK_INIT_FAILED: System.err.println(formatMessageInternal("slpd_intnet_err", new Object[] { ex.getMessage()}, bundle)); break; case ServiceLocationException.NETWORK_ERROR: System.err.println(formatMessageInternal("slpd_net_err", new Object[] { ex.getMessage()}, bundle)); break; default: System.err.println(formatMessageInternal("slpd_err", new Object[] { new Integer(ex.getErrorCode()), ex.getMessage()}, bundle)); } ex.printStackTrace(); System.err.println(formatMessageInternal("exiting_msg", new Object[0], bundle)); System.exit(1); } // Make a new SLPConfig object of the right class type. private static SLPConfig initializeSLPConfig() { // The server *always* runs as an SA. It may also run as a DA. config.isSA = true; // set default logging class for slpd to syslog if (System.getProperty("sun.net.slp.loggerClass") == null) { Properties props = System.getProperties(); props.setProperty("sun.net.slp.loggerClass", "com.sun.slp.Syslog"); System.setProperties(props); } // slpd is the server side config. theSLPConfig = new slpd(); return theSLPConfig; } // // Extensions to sldp for server side only. // boolean isDA() { return Boolean.getBoolean("net.slp.isDA"); } // Determine whether V1 is supported. Default is no. boolean isV1Supported() { if (!isDA() || super.getSLPv1NotSupported()) { return false; } boolean v1Supported = false; try { Class.forName("com.sun.slp.SLPV1Manager"); v1Supported = true; } catch (ClassNotFoundException ex) { // Not there. } return v1Supported; } // Load server message bundle. private static final String serverMsgBundle = "Server"; ResourceBundle getMessageBundle(Locale locale) { // Get the parent bundle first. ResourceBundle parentBundle = super.getMessageBundle(locale); return getMessageBundleInternal(locale, parentBundle); } // We need this in case we get an error before the config object is // created. static private ResourceBundle getMessageBundleInternal( Locale locale, ResourceBundle parentBundle) { // Now create a server subclass. ResourceBundle msgBundle = null; try { msgBundle = ServerBundle.getBundle(parentBundle, locale); } catch (MissingResourceException ex) { // can't localize this one! // We can't print out to the log, because we may be in the // process of trying to. System.out.println("Missing resource bundle ``"+ SERVER_BUNDLE_NAME+ "'' for locale ``"+ locale+ "''"); // Hosed if the default locale is missing. if (locale.equals(Defaults.locale)) { System.out.println("Exiting..."); System.exit(1); } // Otherwise, return the default locale. System.out.println("Using SLP default locale ``" + Defaults.locale+"''"); msgBundle = getMessageBundleInternal(Defaults.locale, parentBundle); } return msgBundle; } }