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