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