xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/routeadm/routeadm.c (revision 60a3f738d56f92ae8b80e4b62a2331c6e1f2311f)
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 2006 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <limits.h>
33 #include <ctype.h>
34 #include <stropts.h>
35 #include <errno.h>
36 #include <libintl.h>
37 #include <locale.h>
38 #include <fcntl.h>
39 #include <sys/types.h>
40 #include <sys/stat.h>
41 #include <libscf.h>
42 #include <libscf_priv.h>
43 #include <libuutil.h>
44 
45 /*
46  * This program moves routing management under SMF.  We do this by giving
47  * routeadm options that allow interaction with SMF services.  These include:
48  * - setting the routing services routeadm will enable
49  *	# routeadm -s routing-svcs="fmri [fmri...]"
50  * where each fmri is an SMF routing service.
51  * - changing properties of routing services
52  *	# routeadm -m fmri key=value [key=value...]
53  * - listing routing daemon properties
54  *	# routeadm -l fmri
55  * where all properties in the "routing" property group are listed.
56  *
57  * By providing legacy routing services (legacy-routing:ipv4 and ipv6), we
58  * can also support running of routing daemons with no SMF service under SMF.
59  * Specifying a routing daemon with no SMF counterpart results in the
60  * daemon, it`s arguments and stop command being set in the appropriate instance
61  * to be picked up by start/stop methods.
62  *
63  * Internally, routeadm keeps track of routing services by setting the
64  * "current-routing-svc" property to "true" in the services it manages.
65  * So for example, running
66  *	# routeadm -s routing-svcs="route:default ripng:default"
67  * sets this variable in each instance specified. If the user specifies a
68  * non-SMF routing daemon via
69  * 	# routeadm -s ipv4-routing-daemon=/usr/sbin/mydaemon
70  * the variable will be set for the legacy-routing:ipv4 instance.
71  *
72  * In order to ensure that the SMF versions of routing daemons are used
73  * where possible, routeadm will check the daemons specified in
74  * ipv4-routing-daemon/ipv6-routing-daemon to determine if there is an
75  * SMF counterpart.  If so, rather than running the legacy service
76  * we move configuration, specifically the associated daemon arguments
77  * to the SMF counterpart.  From there,  when the daemon is enabled,  it
78  * will pick up the daemon arguments setting,  transfer the argument string
79  * to the appropriate properties and run the service.
80  *
81  * To support the semantics of routeadm -e (enable at next boot) through SMF,
82  * we make use of temporary state changes,  which last only until reboot.
83  * For example, if a service is disabled,  and it is to be enabled via
84  * routeadm -e,  we simply change the disable to a temporary disable,
85  * and set the persistent enabled value to true.  This ensures the daemon
86  * will run at next boot,  but not now.  The reverse is true for disabling
87  * enabled instances  (and if the daemon is enabled when we issue the enable,
88  * we do nothing since it is already in the desired state).
89  *
90  * Since the code is quite involved,  we provide a guide to the more complex
91  * actions taken in response to user commands.
92  *
93  * routeadm -e[d] ipv4[6]-routing[forwarding]
94  *
95  * 	In this case,  the goal is to prepare the configured routing daemons
96  * 	(specified through routeadm -s routing-svcs="...") or forwarding
97  *	services to switch on (-e) or of (-d) at next boot.
98  *
99  *	Since this operation must be applied to multiple services in the
100  *	routing daemon case (as opposed to the single ipv4[6]-forwarding
101  *	service),  we make use of the scf_walk_fmri() function,  which
102  *	applies a callback function to all matching functions.  In the case
103  *	of the routing daemons,  we pass in a NULL signifying that all
104  *	instances should be walked  (we then weed out the relevant routing
105  *	services through presence of the routeadm/protocol property).  In
106  *	the case of enable, a routing service is enabled IFF it has the
107  *	previously-mentioned property - with an appropriate value (i.e. ipv4
108  *	for "routeadm -e ipv4-routing") - and it has routeadm/curr-routing-svc
109  *	property set to true  (this is set by other operations such as
110  *	routeadm -s routing-svcs="...").  Then,  smf_enable_instance() or
111  *	smf_disable_instance() is called,  setting the temporary state to
112  *	the current state of the service.  This then allows setting of
113  *	general/enabled value to next-boot value.  In the case of disabling
114  *	ipv4[6]-routing,  all valid ipv4[6] routing daemons are prepared
115  *	for next-boot disable, not just those specified via routing-svcs (this
116  *	means that if the user enables routing daemons with "svcadm enable",
117  *	disabling global routing does really switch off all routing daemons).
118  *
119  *	This is implemented through the ra_get_set_opt_common_cb() function,
120  *	called by the ra_set_persistent_opt_cb() function.  The same
121  *	function can be used for both routing and forwarding options,  in the
122  *	latter case we simply provide the specific FMRI of the forwarding
123  *	service in question (ipv4-forwarding or ipv6-forwarding),  and dispense
124  *	with the eligibility tests we need to weed out the routing services
125  *	from the rest.
126  *
127  *	Before we initiate the "enable" however, we must check routing daemons
128  *	specified via the legacy variables (ipv4-routing-daemon etc).
129  *	If they map to SMF routing services,  we wish to transfer their
130  *	configuration to the corresponding services and use them instead of
131  *	the legacy services.  To do this,  we need to match the daemon program
132  *	against the routeadm/daemon property of each routing daemon (we use
133  *	scf_walk_fmri() and the routeadm/protocol property again to identify
134  *	daemons).  If a match is found,  the daemon arguments are transferred
135  *	to the appropriate service`s daemon-args property, to be picked up
136  *	by it`s start method and converted into appropriate property values.
137  *	This is accomplished by ra_check_legacy_daemons(), and the callback
138  *	operation is carried out by ra_upgrade_legacy_daemons_cb().  If the
139  *	daemon was not upgraded,  we need to mark the legacy-routing:ipv4[6]
140  *	instance to be enabled (by routeadm -e),  since it now must run the
141  *	un-upgradeable legacy daemon.
142  *
143  * routeadm -l fmri
144  *
145  *	Lists all properties and values in the routing property group associated
146  *	with instance fmri.  We simply walk through the composed property
147  *	group, displaying all values.  See ra_list_props_cb().
148  *
149  * routeadm -m fmri key=value ...
150  *
151  *	Modify property values in the routing property group.  If the same
152  *	key is used more than once,  multiple property values are set for that
153  *	property.  Properties must exist in the composed property group,  but
154  *	will only ever be set at the instance level to prevent multiple
155  *	instances inheriting the property in error.  See ra_modify_props_cb().
156  *
157  * routeadm -s var=value
158  *
159  *	In all cases bar the routing-svcs variable,  this simply involves
160  *	setting the appropriate SMF property value for the variable.  The
161  *	routing-svcs case is more complex,  since we would like operations
162  *	like the following to have intuitive effects:
163  *		# routeadm -s routing-svcs=route -e ipv4-routing -u
164  *		# routeadm -s routing-svcs=rdisc -u
165  *	i.e., in the end, rdisc is the only routing service running.  To
166  *	accomplish this switchover,  we need to disable the old routing-svcs
167  *	and enable the new, marking the latter with the curr-routing-svc
168  *	property so that routeadm -e will pick them up.  This is carried
169  *	out by the ra_update_routing_svcs() function.
170  *
171  * routeadm -R alt_root ...
172  *
173  *	Used to support use of routeadm in Custom Jumpstart scripts,  this
174  *	option causes all subsequent commands to be appended to the
175  *	/var/svc/profile/upgrade file,  which is run on the subsequent boot.
176  *	This is done because the SMF repository is not available to make
177  *	the modifications to property values required in routeadm operations.
178  *
179  * routeadm -u
180  *
181  *	Update applies the "next boot" state to the current system.  Here
182  *	we simply take the persistent state (general/enabled value) and
183  *	make it the current state through smf_enable_instance() or
184  *	smf_disable_instance() as appropriate (these calls,  without the
185  *	temporary flag set,  delete the general_ovr/enabled property).
186  */
187 
188 #define	RA_OPT_IPV4_ROUTING	"ipv4-routing"
189 #define	RA_OPT_IPV6_ROUTING	"ipv6-routing"
190 #define	RA_OPT_IPV4_FORWARDING	"ipv4-forwarding"
191 #define	RA_OPT_IPV6_FORWARDING	"ipv6-forwarding"
192 
193 #define	IS_ROUTING_OPT(opt)	(strcmp(opt, RA_OPT_IPV4_ROUTING) == 0 || \
194 				strcmp(opt, RA_OPT_IPV6_ROUTING) == 0)
195 
196 #define	RA_VAR_IPV4_ROUTING_DAEMON	"ipv4-routing-daemon"
197 #define	RA_VAR_IPV4_ROUTING_DAEMON_ARGS	"ipv4-routing-daemon-args"
198 #define	RA_VAR_IPV4_ROUTING_STOP_CMD	"ipv4-routing-stop-cmd"
199 #define	RA_VAR_IPV6_ROUTING_DAEMON	"ipv6-routing-daemon"
200 #define	RA_VAR_IPV6_ROUTING_DAEMON_ARGS	"ipv6-routing-daemon-args"
201 #define	RA_VAR_IPV6_ROUTING_STOP_CMD	"ipv6-routing-stop-cmd"
202 #define	RA_VAR_ROUTING_SVCS		"routing-svcs"
203 
204 
205 #define	RA_INSTANCE_ALL			NULL
206 #define	RA_INSTANCE_ROUTING_SETUP	"svc:/network/routing-setup:default"
207 #define	RA_INSTANCE_IPV4_FORWARDING	"svc:/network/ipv4-forwarding:default"
208 #define	RA_INSTANCE_IPV6_FORWARDING	"svc:/network/ipv6-forwarding:default"
209 #define	RA_INSTANCE_LEGACY_ROUTING_IPV4 \
210 	"svc:/network/routing/legacy-routing:ipv4"
211 #define	RA_INSTANCE_LEGACY_ROUTING_IPV6 \
212 	"svc:/network/routing/legacy-routing:ipv6"
213 
214 #define	RA_PG_ROUTEADM			"routeadm"
215 #define	RA_PROP_CURR_ROUTING_SVC	"current-routing-svc"
216 #define	RA_PROP_ROUTING_SVCS		"routing-svcs"
217 #define	RA_PROP_DEFAULT_ROUTING_SVCS	"default-routing-svcs"
218 #define	RA_PROP_PROTO			"protocol"
219 #define	RA_PROP_DAEMON			"daemon"
220 #define	RA_PROP_DEFAULT_DAEMON		"default-daemon"
221 #define	RA_PROP_DAEMON_ARGS		"daemon-args"
222 #define	RA_PROP_DEFAULT_DAEMON_ARGS	"default-daemon-args"
223 #define	RA_PROP_DAEMON_STOP_CMD		"daemon-stop-cmd"
224 #define	RA_PROP_DEFAULT_STOP_CMD	"default-daemon"
225 #define	RA_PROP_LEGACY_DAEMON		"legacy-daemon"
226 #define	RA_PROP_DEFAULT_IPV4_ROUTING	"default-ipv4-routing"
227 #define	RA_PROP_DEFAULT_IPV6_ROUTING	"default-ipv6-routing"
228 #define	RA_PROP_DEFAULT_IPV4_FORWARDING	"default-ipv4-forwarding"
229 #define	RA_PROP_DEFAULT_IPV6_FORWARDING	"default-ipv6-forwarding"
230 #define	RA_PROP_IPV4_ROUTING_SET	"ipv4-routing-set"
231 #define	RA_PROP_IPV6_ROUTING_SET	"ipv6-routing-set"
232 #define	RA_PROP_ROUTING_CONF_READ	"routing-conf-read"
233 
234 #define	RA_PG_ROUTING			"routing"
235 
236 #define	RA_PROPVAL_BOOLEAN_TRUE		"true"
237 #define	RA_PROPVAL_BOOLEAN_FALSE	"false"
238 #define	RA_PROPVAL_PROTO_IPV4		"ipv4"
239 #define	RA_PROPVAL_PROTO_IPV6		"ipv6"
240 
241 #define	RA_SVC_FLAG_NONE		0x0
242 #define	RA_SVC_FLAG_IPV4_ROUTING	0x1
243 #define	RA_SVC_FLAG_IPV6_ROUTING	0x2
244 
245 #define	RA_SMF_UPGRADE_FILE		"/var/svc/profile/upgrade"
246 #define	RA_SMF_UPGRADE_MSG		" # added by routeadm(1M)"
247 #define	RA_CONF_FILE			"/etc/inet/routing.conf"
248 #define	RA_CONF_FILE_OLD		"/etc/inet/routing.conf.old"
249 #define	RA_MAX_CONF_LINE		256
250 
251 /*
252  * Option value.  Each option requires an FMRI identifying which services
253  * to run the get_current/persistent scf_walk_fmri() function with,  and
254  * associated flags (to ensure that in the case that multiple services
255  * match, we select the correct ones). In addition, we specify the FMRI
256  * and property used to set default option value.  The opt_enabled field
257  * is used to hold retrieved state from get_*_opt_() callbacks and to specify
258  * desired state for set_*_opt() operations.
259  */
260 
261 typedef struct raopt {
262 	const char	*opt_name;
263 	const char	*opt_fmri;
264 	int		opt_flags;
265 	boolean_t	opt_enabled;
266 	const char	*opt_default_fmri;
267 	const char	*opt_default_prop;
268 	boolean_t	opt_default_enabled;
269 } raopt_t;
270 
271 
272 raopt_t ra_opts[] = {
273 	{ RA_OPT_IPV4_ROUTING, RA_INSTANCE_ALL, RA_SVC_FLAG_IPV4_ROUTING,
274 	B_FALSE, RA_INSTANCE_ROUTING_SETUP, RA_PROP_DEFAULT_IPV4_ROUTING,
275 	B_FALSE },
276 	{ RA_OPT_IPV6_ROUTING, RA_INSTANCE_ALL, RA_SVC_FLAG_IPV6_ROUTING,
277 	B_FALSE, RA_INSTANCE_ROUTING_SETUP, RA_PROP_DEFAULT_IPV6_ROUTING,
278 	B_FALSE },
279 	{ RA_OPT_IPV4_FORWARDING, RA_INSTANCE_IPV4_FORWARDING, RA_SVC_FLAG_NONE,
280 	B_FALSE, RA_INSTANCE_IPV4_FORWARDING, RA_PROP_DEFAULT_IPV4_FORWARDING,
281 	B_FALSE },
282 	{ RA_OPT_IPV6_FORWARDING, RA_INSTANCE_IPV6_FORWARDING, RA_SVC_FLAG_NONE,
283 	B_FALSE, RA_INSTANCE_IPV6_FORWARDING, RA_PROP_DEFAULT_IPV6_FORWARDING,
284 	B_FALSE },
285 	{ NULL, NULL, RA_SVC_FLAG_NONE, B_FALSE, NULL, NULL, B_FALSE }
286 };
287 
288 typedef enum option_values {
289 	OPT_INVALID, OPT_ENABLED, OPT_DISABLED, OPT_DEFAULT, OPT_UNKNOWN
290 } oval_t;
291 
292 typedef struct ra_var {
293 	const char	*var_name;
294 	const char	*var_fmri;
295 	const char	*var_prop;
296 	char		*var_value;
297 	const char	*var_default_fmri;
298 	const char	*var_default_prop;
299 	char		*var_default_value;
300 } ravar_t;
301 
302 ravar_t ra_vars[] = {
303 	{ RA_VAR_IPV4_ROUTING_DAEMON, RA_INSTANCE_LEGACY_ROUTING_IPV4,
304 	RA_PROP_DAEMON, NULL, RA_INSTANCE_LEGACY_ROUTING_IPV4,
305 	RA_PROP_DEFAULT_DAEMON, NULL},
306 	{ RA_VAR_IPV4_ROUTING_DAEMON_ARGS, RA_INSTANCE_LEGACY_ROUTING_IPV4,
307 	RA_PROP_DAEMON_ARGS, NULL, RA_INSTANCE_LEGACY_ROUTING_IPV4,
308 	RA_PROP_DEFAULT_DAEMON_ARGS, NULL },
309 	{ RA_VAR_IPV4_ROUTING_STOP_CMD, RA_INSTANCE_LEGACY_ROUTING_IPV4,
310 	RA_PROP_DAEMON_STOP_CMD, NULL, RA_INSTANCE_LEGACY_ROUTING_IPV4,
311 	RA_PROP_DEFAULT_STOP_CMD, NULL },
312 	{ RA_VAR_IPV6_ROUTING_DAEMON, RA_INSTANCE_LEGACY_ROUTING_IPV6,
313 	RA_PROP_DAEMON, NULL, RA_INSTANCE_LEGACY_ROUTING_IPV6,
314 	RA_PROP_DEFAULT_DAEMON, NULL },
315 	{ RA_VAR_IPV6_ROUTING_DAEMON_ARGS, RA_INSTANCE_LEGACY_ROUTING_IPV6,
316 	RA_PROP_DAEMON_ARGS, NULL, RA_INSTANCE_LEGACY_ROUTING_IPV6,
317 	RA_PROP_DEFAULT_DAEMON_ARGS, NULL },
318 	{ RA_VAR_IPV6_ROUTING_STOP_CMD, RA_INSTANCE_LEGACY_ROUTING_IPV6,
319 	RA_PROP_DAEMON_STOP_CMD, NULL, RA_INSTANCE_LEGACY_ROUTING_IPV6,
320 	RA_PROP_DEFAULT_STOP_CMD, NULL },
321 	{ RA_VAR_ROUTING_SVCS, RA_INSTANCE_ROUTING_SETUP,
322 	RA_PROP_ROUTING_SVCS, NULL, RA_INSTANCE_ROUTING_SETUP,
323 	RA_PROP_DEFAULT_ROUTING_SVCS, NULL },
324 	{ NULL, NULL, NULL, NULL, NULL, NULL, NULL }
325 };
326 
327 char *v_opt[] = {
328 #define	IPV4_ROUTING_DAEMON			0
329 	RA_VAR_IPV4_ROUTING_DAEMON,
330 #define	IPV4_ROUTING_DAEMON_ARGS		1
331 	RA_VAR_IPV4_ROUTING_DAEMON_ARGS,
332 #define	IPV4_ROUTING_STOP_CMD			2
333 	RA_VAR_IPV4_ROUTING_STOP_CMD,
334 #define	IPV6_ROUTING_DAEMON			3
335 	RA_VAR_IPV6_ROUTING_DAEMON,
336 #define	IPV6_ROUTING_DAEMON_ARGS		4
337 	RA_VAR_IPV6_ROUTING_DAEMON_ARGS,
338 #define	IPV6_ROUTING_STOP_CMD			5
339 	RA_VAR_IPV6_ROUTING_STOP_CMD,
340 #define	ROUTING_SVCS				6
341 	RA_VAR_ROUTING_SVCS,
342 	NULL
343 };
344 
345 #define	IS_IPV4_VAR(varname)	(strncmp(varname, "ipv4", 4) == 0)
346 #define	IS_IPV6_VAR(varname)	(strncmp(varname, "ipv6", 4) == 0)
347 #define	VAR_PROTO_MATCH(varname, proto)	(strncmp(varname, proto, 4) == 0)
348 #define	IPV4_VARS_UNSET \
349 	(strtok(ra_vars[IPV4_ROUTING_DAEMON].var_value, " \t") == NULL && \
350 	strtok(ra_vars[IPV4_ROUTING_DAEMON_ARGS].var_value, " \t") == NULL && \
351 	strtok(ra_vars[IPV4_ROUTING_STOP_CMD].var_value, " \t") == NULL)
352 
353 #define	IPV6_VARS_UNSET	\
354 	(strtok(ra_vars[IPV6_ROUTING_DAEMON].var_value, " \t") == NULL && \
355 	strtok(ra_vars[IPV6_ROUTING_DAEMON_ARGS].var_value, " \t") == NULL && \
356 	strtok(ra_vars[IPV6_ROUTING_STOP_CMD].var_value, " \t") == NULL)
357 
358 /*
359  * Structure used in modify operations to tie property name and multiple values
360  * together.
361  */
362 typedef struct ra_prop {
363 	char	*prop_name;
364 	char	**prop_values;
365 	int	prop_numvalues;
366 } ra_prop_t;
367 
368 typedef int (*ra_smf_cb_t)(void *, scf_walkinfo_t *);
369 
370 /* Used to store program name */
371 static const char	*myname;
372 
373 static void usage(void);
374 
375 static int ra_check_legacy_daemons(void);
376 static int ra_upgrade_legacy_daemons(void);
377 static int ra_upgrade_cmd(char, int, char **);
378 static int ra_update(void);
379 static int ra_update_routing_svcs(char *);
380 static int ra_report(boolean_t, const char *);
381 static int ra_smf_cb(ra_smf_cb_t, const char *, void *);
382 static int ra_upgrade_from_legacy_conf(void);
383 static int ra_parseconf(void);
384 static int ra_parseopt(char *, int, raopt_t *);
385 static int ra_parsevar(char *, ravar_t *);
386 static oval_t ra_str2oval(const char *);
387 static raopt_t *ra_str2opt(const char *);
388 static void ra_resetopts(void);
389 static ravar_t *ra_str2var(const char *);
390 static void ra_resetvars(const char *);
391 static char *ra_intloptname(const char *);
392 
393 /* Callback for upgrade of legacy daemons */
394 static int ra_upgrade_legacy_daemons_cb(void *, scf_walkinfo_t *);
395 
396 /* Callbacks used to set/retieve routing options */
397 static int ra_set_current_opt_cb(void *, scf_walkinfo_t *);
398 static int ra_set_persistent_opt_cb(void *, scf_walkinfo_t *);
399 static int ra_set_default_opt_cb(void *, scf_walkinfo_t *);
400 static int ra_get_current_opt_cb(void *, scf_walkinfo_t *);
401 static int ra_get_persistent_opt_cb(void *, scf_walkinfo_t *);
402 static int ra_get_default_opt_cb(void *, scf_walkinfo_t *);
403 static int ra_get_set_opt_common_cb(raopt_t *, scf_walkinfo_t *, boolean_t,
404     boolean_t);
405 
406 /* Callbacks used to set/retrieve routing variables */
407 static int ra_set_persistent_var_cb(void *, scf_walkinfo_t *);
408 static int ra_get_persistent_var_cb(void *, scf_walkinfo_t *);
409 static int ra_get_default_var_cb(void *, scf_walkinfo_t *);
410 static int ra_mark_routing_svcs_cb(void *, scf_walkinfo_t *);
411 
412 /* Callbacks used to list/set daemon properties and list daemons and states. */
413 static int ra_list_props_cb(void *, scf_walkinfo_t *);
414 static int ra_modify_props_cb(void *, scf_walkinfo_t *);
415 static int ra_print_state_cb(void *, scf_walkinfo_t *);
416 
417 /* Utility functions for SMF operations */
418 static int ra_get_pg(scf_handle_t *, scf_instance_t *, const char *,
419     boolean_t, boolean_t, scf_propertygroup_t **);
420 static int ra_get_boolean_prop(scf_handle_t *, scf_instance_t *,
421     const char *, const char *,  boolean_t, boolean_t, boolean_t *);
422 static int ra_get_single_prop_as_string(scf_handle_t *, scf_instance_t *,
423     const char *, const char *, boolean_t, boolean_t, scf_type_t *, char **);
424 static int ra_get_prop_as_string(scf_handle_t *, scf_instance_t *,
425     const char *, const char *, boolean_t, boolean_t, scf_type_t *, int *,
426     char ***);
427 static void ra_free_prop_values(int, char **);
428 static int ra_set_boolean_prop(scf_handle_t *, scf_instance_t *,
429     const char *, const char *, boolean_t, boolean_t);
430 static int ra_set_prop_from_string(scf_handle_t *, scf_instance_t *,
431     const char *, const char *, scf_type_t, boolean_t, int,
432     const char **);
433 
434 static void
435 usage(void)
436 {
437 	(void) fprintf(stderr, gettext(
438 	    "usage: %1$s [-p] [-R <root-dir>]\n"
439 	    "       %1$s [-e <option>] [-d <option>] [-r <option>]\n"
440 	    "           [-l <FMRI>] [-m <FMRI> key=value [...]]\n"
441 	    "           [-s <var>=<val>] [-R <root-dir>]\n"
442 	    "       %1$s -u\n\n"
443 	    "       <option> is one of:\n"
444 	    "       ipv4-forwarding\n"
445 	    "       ipv4-routing\n"
446 	    "       ipv6-forwarding\n"
447 	    "       ipv6-routing\n\n"
448 	    "       <var> is one of:\n"
449 	    "       ipv4-routing-daemon\n"
450 	    "       ipv4-routing-daemon-args\n"
451 	    "       ipv4-routing-stop-cmd\n"
452 	    "       ipv6-routing-daemon\n"
453 	    "       ipv6-routing-daemon-args\n"
454 	    "       ipv6-routing-stop-cmd\n"
455 	    "       routing-svcs\n"), myname);
456 }
457 
458 int
459 main(int argc, char *argv[])
460 {
461 	int		opt, opt_index, numargs, status = 0;
462 	int		numvalues, i;
463 	ssize_t		keylen;
464 	boolean_t	modify = B_FALSE, report = B_TRUE, update = B_FALSE;
465 	boolean_t	alt_root_set = B_FALSE;
466 	boolean_t	parseable = B_FALSE;
467 	char		*key, *nk, *keyend, *val, **vals, *options, *fmri;
468 	char		*parseopt = NULL;
469 	raopt_t		*raopt;
470 	ravar_t		*ravar;
471 	ra_prop_t	raprop;
472 
473 	myname = argv[0];
474 
475 	(void) setlocale(LC_ALL, "");
476 
477 #if	!defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
478 #define	TEXT_DOMAIN	"SYS_TEST"
479 #endif
480 
481 	(void) textdomain(TEXT_DOMAIN);
482 
483 	/*
484 	 * Before processing any options, we parse /etc/inet/routing.conf
485 	 * (if present) and transfer values to SMF.
486 	 */
487 	if (ra_upgrade_from_legacy_conf() == -1)
488 		exit(EXIT_FAILURE);
489 	while ((opt = getopt(argc, argv, ":d:e:l:m:p:R:r:s:u")) != EOF) {
490 		switch (opt) {
491 		case 'd':
492 		case 'e':
493 		case 'r':
494 			if (alt_root_set) {
495 				if (ra_upgrade_cmd(opt, 1, &optarg) != 0)
496 					exit(EXIT_FAILURE);
497 				modify = B_TRUE;
498 				break;
499 			}
500 			if ((raopt = ra_str2opt(optarg)) != NULL) {
501 				/* Set current value appropriately */
502 				switch (opt) {
503 				case 'd':
504 					raopt->opt_enabled = B_FALSE;
505 					break;
506 				case 'e':
507 					/*
508 					 * Check legacy daemons, mark
509 					 * routing-svcs.
510 					 */
511 					if (IS_ROUTING_OPT(optarg) &&
512 					    ra_check_legacy_daemons() == -1)
513 						exit(EXIT_FAILURE);
514 					raopt->opt_enabled = B_TRUE;
515 					break;
516 				case 'r':
517 					/*
518 					 * This callback sets opt_enabled to
519 					 * the default value.
520 					 */
521 					ra_resetopts();
522 					if (ra_smf_cb(ra_get_default_opt_cb,
523 					    raopt->opt_default_fmri, raopt)
524 					    == -1)
525 						exit(EXIT_FAILURE);
526 					if (raopt->opt_enabled &&
527 					    IS_ROUTING_OPT(optarg) &&
528 					    ra_check_legacy_daemons() == -1)
529 						exit(EXIT_FAILURE);
530 					/* set value to default */
531 					raopt->opt_enabled =
532 					    raopt->opt_default_enabled;
533 					break;
534 				}
535 				if (ra_smf_cb(ra_set_persistent_opt_cb,
536 				    raopt->opt_fmri, raopt) == -1)
537 					exit(EXIT_FAILURE);
538 			} else if ((ravar = ra_str2var(optarg)) != NULL) {
539 				if (opt != 'r') {
540 					usage();
541 					exit(EXIT_FAILURE);
542 				}
543 				/* set current value to default */
544 				ra_resetopts();
545 				if (ra_smf_cb(ra_get_default_var_cb,
546 				    ravar->var_default_fmri, ravar) == -1)
547 					exit(EXIT_FAILURE);
548 				/* Need special case for routing-svcs var */
549 				if (strcmp(ravar->var_name, RA_VAR_ROUTING_SVCS)
550 				    == 0) {
551 					if (ra_update_routing_svcs(
552 					    ravar->var_default_value) == -1)
553 						exit(EXIT_FAILURE);
554 				} else if (ra_smf_cb(ra_set_persistent_var_cb,
555 				    ravar->var_fmri, ravar) == -1)
556 					exit(EXIT_FAILURE);
557 			} else {
558 				(void) fprintf(stderr, gettext(
559 				    "%1$s: invalid option: %2$s\n"), myname,
560 				    optarg);
561 				usage();
562 				exit(EXIT_FAILURE);
563 			}
564 			modify = B_TRUE;
565 			break;
566 		case 'l':
567 			if (ra_smf_cb(ra_list_props_cb, optarg, NULL) == -1)
568 				exit(EXIT_FAILURE);
569 			report = B_FALSE;
570 			break;
571 		case 'm':
572 			fmri = optarg;
573 			modify = B_TRUE;
574 			/*
575 			 * Argument list of key=value pairs, we need to
576 			 * collate all matching keys to set multiple values.
577 			 */
578 			numargs = 1;
579 			i = optind;
580 			for (numargs = 1; argv[i] != NULL && argv[i][0] != '-';
581 			    numargs++)
582 				i++;
583 			if (numargs == 1) {
584 				(void) fprintf(stderr, gettext(
585 				    "%s: key=value required for "
586 				    "property change\n"), myname);
587 				usage();
588 				exit(EXIT_FAILURE);
589 			}
590 			if (alt_root_set) {
591 				if (ra_upgrade_cmd(opt, numargs,
592 				    &argv[optind - 1]) == -1)
593 					exit(EXIT_FAILURE);
594 				optind += numargs - 1;
595 				break;
596 			}
597 			/*
598 			 * Collect all key=value pairs which use same key
599 			 * so we can add multiple property values.
600 			 */
601 			for (key = argv[optind]; key != NULL && key[0] != '-';
602 			    key = argv[++optind]) {
603 				if (key[0] == '\0')
604 					continue;
605 				vals = malloc(sizeof (char *));
606 				if ((vals[0] = strchr(key, '=')) == NULL) {
607 					(void) fprintf(stderr, gettext(
608 					    "%s: Malformed name=value "
609 					    "pair %s\n"), myname, key);
610 					exit(EXIT_FAILURE);
611 				}
612 				numvalues = 1;
613 				*(vals[0]) = '\0';
614 				(vals[0])++;
615 				i = optind + 1;
616 				for (nk = argv[i];
617 				    nk != NULL && nk[0] != '-';
618 				    nk = argv[++i]) {
619 					if (nk[0] == '\0')
620 						continue;
621 					if ((keyend = strchr(nk, '='))
622 					    == NULL) {
623 						(void) fprintf(stderr, gettext(
624 						    "%s: Malformed name=value "
625 						    " pair %s\n"), myname, nk);
626 						exit(EXIT_FAILURE);
627 					}
628 					if ((keylen = keyend - nk) !=
629 					    strlen(key))
630 						continue;
631 					if (strncmp(key, nk, keylen) == 0) {
632 						vals = realloc(vals, ++numvalues
633 						    * sizeof (char *));
634 						vals[numvalues - 1] = ++keyend;
635 						nk[0] = '\0';
636 						optind++;
637 					}
638 				}
639 				raprop.prop_name = key;
640 				raprop.prop_values = vals;
641 				raprop.prop_numvalues = numvalues;
642 				if (ra_smf_cb(ra_modify_props_cb, fmri,
643 				    &raprop) == -1)
644 					exit(EXIT_FAILURE);
645 			}
646 			break;
647 		case 'p':
648 			parseable = B_TRUE;
649 			parseopt = optarg;
650 			break;
651 		case 'R':
652 			if (chroot(optarg) == -1) {
653 				(void) fprintf(stderr, gettext(
654 				    "%1$s: failed to chroot to %2$s: %3$s\n"),
655 				    myname, optarg, strerror(errno));
656 				exit(EXIT_FAILURE);
657 			}
658 			alt_root_set = B_TRUE;
659 			report = B_FALSE;
660 			break;
661 		case 's':
662 			if (alt_root_set) {
663 				if (ra_upgrade_cmd(opt, 1, &optarg) == -1)
664 					exit(EXIT_FAILURE);
665 				modify = B_TRUE;
666 				break;
667 			}
668 			options = optarg;
669 			while (*options != '\0') {
670 				opt_index = getsubopt(&options, v_opt, &val);
671 				if (val == NULL) {
672 					usage();
673 					exit(EXIT_FAILURE);
674 				}
675 				if (opt_index == -1) {
676 					(void) fprintf(stderr, gettext(
677 					    "%1$s: invalid variable: %2$s\n"),
678 					    myname, optarg);
679 					usage();
680 					exit(EXIT_FAILURE);
681 				}
682 				ravar = &ra_vars[opt_index];
683 				/* Need special case for routing-svcs var */
684 				if (strcmp(ravar->var_name, RA_VAR_ROUTING_SVCS)
685 				    == 0) {
686 					if (ra_update_routing_svcs(val) == -1)
687 						return (-1);
688 				} else {
689 					ravar->var_value = strdup(val);
690 					if (ra_smf_cb(ra_set_persistent_var_cb,
691 					    ravar->var_fmri, ravar) == -1)
692 						exit(EXIT_FAILURE);
693 				}
694 			}
695 			modify = B_TRUE;
696 			break;
697 		case 'u':
698 			update = B_TRUE;
699 			break;
700 		case ':':
701 			/* if not 'p', usage failure */
702 			if (strcmp(argv[optind - 1], "-p") != 0) {
703 				(void) fprintf(stderr, gettext(
704 				    "%s: option requires an argument -%s\n"),
705 				    myname, argv[optind - 1]);
706 				usage();
707 				exit(EXIT_FAILURE);
708 			}
709 			parseable = B_TRUE;
710 			break;
711 		case '?':
712 			usage();
713 			exit(EXIT_FAILURE);
714 		}
715 	}
716 
717 	if (argc > optind) {
718 		/* There shouldn't be any extra args. */
719 		usage();
720 		exit(EXIT_FAILURE);
721 	}
722 
723 	if (parseable && (update || modify)) {
724 		(void) fprintf(stderr, gettext("%s: the -p option cannot be "
725 		    "used with any of -demrsu\n"), myname);
726 		usage();
727 		exit(EXIT_FAILURE);
728 	}
729 
730 	if (update && ! alt_root_set)
731 		status = ra_update();
732 
733 	if (report && !modify && !update)
734 		status = ra_report(parseable, parseopt);
735 
736 	return (status == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
737 }
738 
739 /*
740  * Upgrade legacy daemons,  mark to-be-enabled routing services.
741  */
742 static int
743 ra_check_legacy_daemons(void)
744 {
745 	ravar_t		*routing_svcs = ra_str2var(RA_VAR_ROUTING_SVCS);
746 	ravar_t		*v4d = ra_str2var(RA_VAR_IPV4_ROUTING_DAEMON);
747 	ravar_t		*v6d = ra_str2var(RA_VAR_IPV6_ROUTING_DAEMON);
748 	char		*fmri, *nextfmri;
749 	boolean_t	mark = B_FALSE;
750 
751 	if (ra_smf_cb(ra_get_persistent_var_cb, routing_svcs->var_fmri,
752 	    routing_svcs) == -1)
753 		return (-1);
754 
755 	/* First unmark all services */
756 	if (ra_smf_cb(ra_mark_routing_svcs_cb, NULL, &mark) == -1)
757 		return (-1);
758 
759 	mark = B_TRUE;
760 	if (routing_svcs->var_value != NULL) {
761 		/*
762 		 * For routing-svcs variable, mark each named
763 		 * service as a current-routing-svc.
764 		 */
765 		if ((fmri = strdup(routing_svcs->var_value)) == NULL) {
766 			(void) fprintf(stderr, gettext(
767 			    "%s: out of memory\n"), myname);
768 			return (-1);
769 		}
770 		/* Now, mark each service named in routing-svcs. */
771 		for (nextfmri = strtok(fmri, " \t");
772 		    nextfmri != NULL;
773 		    nextfmri = strtok(NULL, " \t")) {
774 			if (ra_smf_cb(ra_mark_routing_svcs_cb, nextfmri,
775 			    &mark) == -1) {
776 				free(fmri);
777 				return (-1);
778 			}
779 		}
780 		free(fmri);
781 	}
782 
783 	/*
784 	 * Now check if legacy variables (if specified) map to SMF routing
785 	 * daemons.  If so, transfer associated daemon arguments.
786 	 */
787 	if (ra_upgrade_legacy_daemons() == -1)
788 		return (-1);
789 
790 	ra_resetvars(NULL);
791 	/*
792 	 * At this point, if the legacy services still have ipv4/ipv6
793 	 * routing daemons specified, we know they weren`t upgraded, so
794 	 * we mark them also.
795 	 */
796 	if (ra_smf_cb(ra_get_persistent_var_cb, v4d->var_fmri, v4d) == -1 ||
797 	    ra_smf_cb(ra_get_persistent_var_cb, v6d->var_fmri, v6d) == -1)
798 		return (-1);
799 
800 	if (v4d->var_value != NULL && strtok(v4d->var_value, " \t") != NULL &&
801 	    ra_smf_cb(ra_mark_routing_svcs_cb, RA_INSTANCE_LEGACY_ROUTING_IPV4,
802 	    &mark) == -1)
803 		return (-1);
804 	if (v6d->var_value != NULL && strtok(v6d->var_value, " \t") != NULL &&
805 	    ra_smf_cb(ra_mark_routing_svcs_cb, RA_INSTANCE_LEGACY_ROUTING_IPV6,
806 	    &mark) == -1)
807 		return (-1);
808 
809 	return (0);
810 }
811 
812 /*
813  * Retrieve legacy daemon variables,  and check if any SMF routing daemons
814  * run the daemons specified.  If so, the legacy configuration (arguments
815  * to the daemon) is transferred to the routeadm/daemon-args property
816  * of the corresponding instance.  From there,  the instance picks up the
817  * value and will transfer the daemon arguments to individiual properties
818  * when enabled.
819  */
820 static int
821 ra_upgrade_legacy_daemons(void)
822 {
823 	ravar_t	*v4d = ra_str2var(RA_VAR_IPV4_ROUTING_DAEMON);
824 	ravar_t	*v6d = ra_str2var(RA_VAR_IPV6_ROUTING_DAEMON);
825 	ravar_t	*v4args = ra_str2var(RA_VAR_IPV4_ROUTING_DAEMON_ARGS);
826 	ravar_t	*v6args = ra_str2var(RA_VAR_IPV6_ROUTING_DAEMON_ARGS);
827 	ravar_t	*v4stop = ra_str2var(RA_VAR_IPV4_ROUTING_STOP_CMD);
828 	ravar_t	*v6stop = ra_str2var(RA_VAR_IPV6_ROUTING_STOP_CMD);
829 
830 	if (ra_smf_cb(ra_get_persistent_var_cb, v4d->var_fmri, v4d) == -1 ||
831 	    ra_smf_cb(ra_get_persistent_var_cb, v6d->var_fmri, v6d) == -1 ||
832 	    ra_smf_cb(ra_get_persistent_var_cb, v4args->var_fmri, v4args)
833 	    == -1 ||
834 	    ra_smf_cb(ra_get_persistent_var_cb, v6args->var_fmri, v6args)
835 	    == -1 ||
836 	    ra_smf_cb(ra_get_persistent_var_cb, v4stop->var_fmri, v4stop)
837 	    == -1 ||
838 	    ra_smf_cb(ra_get_persistent_var_cb, v6stop->var_fmri, v6stop)
839 	    == -1)
840 		return (-1);
841 
842 	return (ra_smf_cb(ra_upgrade_legacy_daemons_cb, NULL, NULL));
843 }
844 
845 /*
846  * Determine if service runs the same daemon as that which is specified
847  * in ipv4-routing-daemon or ipv6-routing-daemon.  If so, the associated
848  * daemon arguments are transferred to the service.
849  */
850 
851 /* ARGSUSED0 */
852 static int
853 ra_upgrade_legacy_daemons_cb(void *data, scf_walkinfo_t *wip)
854 {
855 	const char	*inst_fmri = wip->fmri;
856 	scf_instance_t	*inst = wip->inst;
857 	scf_handle_t	*h = scf_instance_handle(inst);
858 	char		*daemon, *l_daemon = NULL;
859 	ravar_t		*v4d = ra_str2var(RA_VAR_IPV4_ROUTING_DAEMON);
860 	ravar_t		*v6d = ra_str2var(RA_VAR_IPV6_ROUTING_DAEMON);
861 	ravar_t		*v4args = ra_str2var(RA_VAR_IPV4_ROUTING_DAEMON_ARGS);
862 	ravar_t		*v6args = ra_str2var(RA_VAR_IPV6_ROUTING_DAEMON_ARGS);
863 	ravar_t		*v4stop = ra_str2var(RA_VAR_IPV4_ROUTING_STOP_CMD);
864 	ravar_t		*v6stop = ra_str2var(RA_VAR_IPV6_ROUTING_STOP_CMD);
865 	ravar_t		*routing_svcs = ra_str2var(RA_VAR_ROUTING_SVCS);
866 	boolean_t	mark, marked;
867 	char		*new_routing_svcs;
868 
869 	/*
870 	 * Ensure instance is a routing service, and not one of the
871 	 * legacy instances - if it is, the daemon property is already
872 	 * set to the legacy daemon.
873 	 */
874 	if (ra_get_single_prop_as_string(h, inst, RA_PG_ROUTEADM,
875 	    RA_PROP_DAEMON, B_TRUE, B_FALSE, NULL, &daemon) == -1 ||
876 	    strcmp(RA_INSTANCE_LEGACY_ROUTING_IPV4, inst_fmri) == 0 ||
877 	    strcmp(RA_INSTANCE_LEGACY_ROUTING_IPV6, inst_fmri) == 0)
878 		return (0);
879 
880 	/* A legacy daemon may be defined */
881 	(void) ra_get_single_prop_as_string(h, inst, RA_PG_ROUTEADM,
882 	    RA_PROP_LEGACY_DAEMON, B_TRUE, B_FALSE, NULL, &l_daemon);
883 
884 	/*
885 	 * If we match daemon/legacy_daemon with ipv4-routing-daemon or
886 	 * ipv6-routing-daemon values, transfer daemon-args value
887 	 * to the matching service.
888 	 */
889 	if (v4d->var_value != NULL && (strcmp(v4d->var_value, daemon) == 0 ||
890 	    (l_daemon != NULL && strcmp(v4d->var_value, l_daemon) == 0))) {
891 		(void) printf(gettext("%s: migrating daemon configuration "
892 		    "for %s to %s\n"), myname, l_daemon != NULL ?
893 		    l_daemon : daemon, inst_fmri);
894 		/* Transfer daemon-args value, clear legacy v4 values */
895 		if (ra_set_prop_from_string(h, inst, RA_PG_ROUTEADM,
896 		    RA_PROP_DAEMON_ARGS, SCF_TYPE_ASTRING, B_TRUE, 1,
897 		    (const char **)&(v4args->var_value)) == -1)
898 			return (-1);
899 		ra_resetvars(RA_PROPVAL_PROTO_IPV4);
900 		if (ra_smf_cb(ra_set_persistent_var_cb,
901 		    RA_INSTANCE_LEGACY_ROUTING_IPV4, v4d) == -1 ||
902 		    ra_smf_cb(ra_set_persistent_var_cb,
903 		    RA_INSTANCE_LEGACY_ROUTING_IPV4, v4args) == -1 ||
904 		    ra_smf_cb(ra_set_persistent_var_cb,
905 		    RA_INSTANCE_LEGACY_ROUTING_IPV4, v4stop) == -1)
906 			return (-1);
907 	} else if (v6d->var_value != NULL && (strcmp(v6d->var_value, daemon)
908 	    == 0 ||
909 	    (l_daemon != NULL && strcmp(v6d->var_value, l_daemon) == 0))) {
910 		(void) printf(gettext("%s: migrating daemon configuration "
911 		    "for %s to %s\n"), myname, l_daemon != NULL ?
912 		    l_daemon : daemon, inst_fmri);
913 		/* Transfer daemon-args value, clear legacy v6 values */
914 		if (ra_set_prop_from_string(h, inst, RA_PG_ROUTEADM,
915 		    RA_PROP_DAEMON_ARGS, SCF_TYPE_ASTRING, B_TRUE, 1,
916 		    (const char **)&(v6args->var_value)) == -1)
917 			return (-1);
918 		ra_resetvars(RA_PROPVAL_PROTO_IPV6);
919 		if (ra_smf_cb(ra_set_persistent_var_cb,
920 		    RA_INSTANCE_LEGACY_ROUTING_IPV6, v6d) == -1 ||
921 		    ra_smf_cb(ra_set_persistent_var_cb,
922 		    RA_INSTANCE_LEGACY_ROUTING_IPV6, v6args) == -1 ||
923 		    ra_smf_cb(ra_set_persistent_var_cb,
924 		    RA_INSTANCE_LEGACY_ROUTING_IPV6, v6stop) == -1)
925 			return (-1);
926 	} else
927 		return (0);
928 
929 	/*
930 	 * If service is unmarked at this point, add it to routing-svcs and
931 	 * mark it.
932 	 */
933 	if (ra_get_boolean_prop(h, inst, RA_PG_ROUTEADM,
934 	    RA_PROP_CURR_ROUTING_SVC, B_FALSE, B_FALSE, &marked) == -1 ||
935 	    marked == B_FALSE) {
936 		mark = B_TRUE;
937 		if (ra_smf_cb(ra_mark_routing_svcs_cb, inst_fmri, &mark)
938 		    == -1 ||
939 		    ra_smf_cb(ra_get_persistent_var_cb, routing_svcs->var_fmri,
940 		    routing_svcs) == -1)
941 			return (-1);
942 		if ((new_routing_svcs =
943 		    malloc(strlen(routing_svcs->var_value) +
944 		    strlen(inst_fmri) + 2)) == NULL) {
945 			(void) fprintf(stderr, gettext(
946 			    "%s: out of memory"), myname);
947 			return (-1);
948 		}
949 		if (strlen(routing_svcs->var_value) == 0)
950 			(void) snprintf(new_routing_svcs,
951 			    strlen(inst_fmri) + 1, "%s", inst_fmri);
952 		else
953 			(void) snprintf(new_routing_svcs,
954 			    strlen(routing_svcs->var_value) +
955 			    strlen(inst_fmri) + 2, "%s %s",
956 			    routing_svcs->var_value, inst_fmri);
957 		free(routing_svcs->var_value);
958 		routing_svcs->var_value = new_routing_svcs;
959 		(void) smf_refresh_instance(inst_fmri);
960 		return (ra_smf_cb(ra_set_persistent_var_cb,
961 		    routing_svcs->var_fmri, routing_svcs));
962 	}
963 	(void) smf_refresh_instance(inst_fmri);
964 	return (0);
965 }
966 
967 /*
968  * If we are upgrading,  append operation to <alt_root>/var/svc/profile/upgrade.
969  */
970 static int
971 ra_upgrade_cmd(char opt, int argc, char **argv)
972 {
973 	FILE	*fp;
974 	int	i;
975 
976 	if ((fp = fopen(RA_SMF_UPGRADE_FILE, "a+")) == NULL) {
977 		(void) fprintf(stderr, gettext(
978 		    "%1$s: failed to open %2$s: %3$s\n"),
979 		    myname, RA_SMF_UPGRADE_FILE, strerror(errno));
980 		return (-1);
981 	}
982 	(void) fprintf(fp, "/sbin/routeadm -%c ", opt);
983 	if (argv != NULL) {
984 		for (i = 0; i < argc; i++)
985 			(void) fprintf(fp, "%s ", argv[i]);
986 	}
987 	(void) fprintf(fp, "%s\n", RA_SMF_UPGRADE_MSG);
988 	(void) fclose(fp);
989 	return (0);
990 }
991 
992 /*
993  * Set current state to "next boot" state, i.e. if general/enabled
994  * value is overlaid by a general_ovr/enabled value, set the current state
995  * to the value of the latter.  Doing this applies "next boot" changes to
996  * the current setup.
997  */
998 static int
999 ra_update(void)
1000 {
1001 	int	i, ret = 0;
1002 
1003 	if (ra_check_legacy_daemons() == -1)
1004 		return (-1);
1005 	for (i = 0; ra_opts[i].opt_name != NULL; i++) {
1006 		if ((ret = ra_smf_cb(ra_set_current_opt_cb, ra_opts[i].opt_fmri,
1007 		    &ra_opts[i])) == -1)
1008 			break;
1009 	}
1010 	return (ret);
1011 }
1012 
1013 /*
1014  * Here we catch the special case where ipv4/ipv6 routing was enabled,
1015  * and the user updates the routing-svcs list.  The problem is that
1016  * the enabled state is the result of services on the old routing-svcs list
1017  * being enabled, and we want to support users doing something like this:
1018  *
1019  * # routeadm -s routing-svcs=route -e ipv4-routing -u
1020  *
1021  * followed by
1022  *
1023  * # routeadm -s routing-svcs=rdisc -u
1024  *
1025  * To do this, we need to:
1026  *	- cache the old ipv4-routing/ipv6-routing values.
1027  *	- persistently disable the old routing-svcs list.
1028  *	- if ipv4-routing was enabled, mark and persistently enable all the new
1029  *	v4 routing-svcs
1030  *	- if ipv6-routing was enabled, mark and persistently enable all the new
1031  *	v6 routing-svcs.
1032  * This will result in the next "-u" switching on the new routing-svcs, and
1033  * switching off the old ones,  as the user would expect.
1034  */
1035 static int
1036 ra_update_routing_svcs(char *routing_svcs_new)
1037 {
1038 	raopt_t		*v4opt = ra_str2opt(RA_OPT_IPV4_ROUTING);
1039 	raopt_t		*v6opt = ra_str2opt(RA_OPT_IPV6_ROUTING);
1040 	ravar_t		*routing_svcs = ra_str2var(RA_VAR_ROUTING_SVCS);
1041 	char		*routing_svcs_old, *fmri;
1042 	boolean_t	v4_old, v6_old, mark = B_FALSE;
1043 
1044 	ra_resetopts();
1045 	if (ra_smf_cb(ra_get_persistent_opt_cb, v4opt->opt_fmri, v4opt) == -1 ||
1046 	    ra_smf_cb(ra_get_persistent_opt_cb, v6opt->opt_fmri, v6opt) == -1 ||
1047 	    ra_smf_cb(ra_get_persistent_var_cb, routing_svcs->var_fmri,
1048 	    routing_svcs) == -1)
1049 		return (-1);
1050 	v4_old = v4opt->opt_enabled;
1051 	v6_old = v6opt->opt_enabled;
1052 	routing_svcs_old = routing_svcs->var_value;
1053 	routing_svcs->var_value = routing_svcs_new;
1054 
1055 	if (ra_smf_cb(ra_set_persistent_var_cb, routing_svcs->var_fmri,
1056 	    routing_svcs) == -1) {
1057 		free(routing_svcs_old);
1058 		return (-1);
1059 	}
1060 
1061 	if (!v4_old && !v6_old) {
1062 		/* We don`t need to do anything, since services were disabled */
1063 		free(routing_svcs_old);
1064 		return (0);
1065 	}
1066 	v4opt->opt_enabled = B_FALSE;
1067 	v6opt->opt_enabled = B_FALSE;
1068 
1069 	/* Persistently disable each old v4/v6 "routing-svc" */
1070 	for (fmri = strtok(routing_svcs_old, " \t"); fmri != NULL;
1071 	    fmri = strtok(NULL, " \t")) {
1072 		if (ra_smf_cb(ra_mark_routing_svcs_cb, fmri, &mark) == -1) {
1073 			free(routing_svcs_old);
1074 			return (-1);
1075 		}
1076 		if (v4_old &&
1077 		    ra_smf_cb(ra_set_persistent_opt_cb, fmri, v4opt) == -1) {
1078 			free(routing_svcs_old);
1079 			return (-1);
1080 		}
1081 		if (v6_old &&
1082 		    ra_smf_cb(ra_set_persistent_opt_cb, fmri, v6opt) == -1) {
1083 			free(routing_svcs_old);
1084 			return (-1);
1085 		}
1086 	}
1087 	free(routing_svcs_old);
1088 	v4opt->opt_enabled = v4_old;
1089 	v6opt->opt_enabled = v6_old;
1090 
1091 	/* Persistently enable each new v4/v6 "routing-svc" */
1092 	mark = B_TRUE;
1093 	for (fmri = strtok(routing_svcs_new, " \t"); fmri != NULL;
1094 	    fmri = strtok(NULL, " \t")) {
1095 		if (ra_smf_cb(ra_mark_routing_svcs_cb, fmri, &mark) == -1)
1096 			return (-1);
1097 		if (v4_old &&
1098 		    ra_smf_cb(ra_set_persistent_opt_cb, fmri, v4opt) == -1)
1099 			return (-1);
1100 		if (v6_old &&
1101 		    ra_smf_cb(ra_set_persistent_opt_cb, fmri, v6opt) == -1)
1102 			return (-1);
1103 	}
1104 	return (0);
1105 }
1106 
1107 /*
1108  * Display status,  in parseable form if required.  If param is
1109  * specified,  only the named option/variable is displayed  (this option is
1110  * for parseable display only).
1111  */
1112 static int
1113 ra_report(boolean_t parseable, const char *param)
1114 {
1115 	int		i;
1116 	char		*c_state, *d_state, *p_state, *p_var, *d_var;
1117 	char		*enabled = "enabled";
1118 	char		*disabled = "disabled";
1119 	boolean_t	param_found = B_FALSE;
1120 
1121 	if (!parseable) {
1122 		(void) printf(gettext(
1123 		    "              Configuration   Current              "
1124 		    "Current\n"
1125 		    "                     Option   Configuration        "
1126 		    "System State\n"
1127 		    "---------------------------------------------------"
1128 		    "------------\n"));
1129 	}
1130 	for (i = 0; ra_opts[i].opt_name != NULL; i++) {
1131 		if (param != NULL) {
1132 			if (strcmp(ra_opts[i].opt_name, param) == 0)
1133 				param_found = B_TRUE;
1134 			else
1135 				continue;
1136 		}
1137 		if (ra_smf_cb(ra_get_current_opt_cb,
1138 		    ra_opts[i].opt_fmri, &ra_opts[i]) == -1)
1139 			return (-1);
1140 		c_state = ra_opts[i].opt_enabled ? enabled : disabled;
1141 		ra_resetopts();
1142 		if (ra_smf_cb(ra_get_persistent_opt_cb,
1143 		    ra_opts[i].opt_fmri, &ra_opts[i]) == -1)
1144 			return (-1);
1145 		p_state = ra_opts[i].opt_enabled ? enabled : disabled;
1146 		ra_resetopts();
1147 		if (ra_smf_cb(ra_get_default_opt_cb,
1148 		    ra_opts[i].opt_default_fmri, &ra_opts[i]) == -1)
1149 			return (-1);
1150 		d_state = ra_opts[i].opt_default_enabled ? enabled : disabled;
1151 		ra_resetopts();
1152 		if (parseable) {
1153 			if (param == NULL)
1154 				(void) printf("%s ", ra_opts[i].opt_name);
1155 			(void) printf("persistent=%s default=%s "
1156 			    "current=%s\n", p_state, d_state, c_state);
1157 		} else {
1158 			(void) printf(gettext("%1$27s   %2$-21s%3$s\n"),
1159 			    ra_intloptname(ra_opts[i].opt_name),
1160 			    p_state, c_state);
1161 		}
1162 	}
1163 	if (!parseable)
1164 		(void) printf("\n");
1165 
1166 	ra_resetvars(NULL);
1167 
1168 	/* Gather persistent/default variable values */
1169 	for (i = 0; ra_vars[i].var_name != NULL; i++) {
1170 		if (ra_smf_cb(ra_get_persistent_var_cb,
1171 		    ra_vars[i].var_fmri, &ra_vars[i]) == -1 ||
1172 		    ra_smf_cb(ra_get_default_var_cb,
1173 		    ra_vars[i].var_default_fmri, &ra_vars[i]) == -1)
1174 			return (-1);
1175 
1176 	}
1177 	for (i = 0; ra_vars[i].var_name != NULL; i++) {
1178 		if (param != NULL) {
1179 			if (strcmp(ra_vars[i].var_name, param) == 0)
1180 				param_found = B_TRUE;
1181 			else
1182 				continue;
1183 		}
1184 		p_var = ra_vars[i].var_value == NULL ? "":
1185 		    ra_vars[i].var_value;
1186 		d_var = ra_vars[i].var_default_value == NULL ?
1187 		    "": ra_vars[i].var_default_value;
1188 		if (parseable) {
1189 			if (param == NULL)
1190 				(void) printf("%s ", ra_vars[i].var_name);
1191 			(void) printf("persistent=\"%s\" "
1192 			    "default=\"%s\" \n", p_var, d_var);
1193 		} else {
1194 			/* If daemon variables are not set, do not display. */
1195 			if ((IS_IPV4_VAR(ra_vars[i].var_name) &&
1196 			    IPV4_VARS_UNSET) ||
1197 			    (IS_IPV6_VAR(ra_vars[i].var_name) &&
1198 			    IPV6_VARS_UNSET))
1199 				continue;
1200 			(void) printf(gettext("%1$27s   \"%2$s\"\n"),
1201 			    ra_intloptname(ra_vars[i].var_name), p_var);
1202 		}
1203 	}
1204 
1205 	if (param != NULL && !param_found) {
1206 		(void) fprintf(stderr, gettext(
1207 		    "%s: no such option/variable %s\n"), myname, param);
1208 		return (-1);
1209 	}
1210 	if (parseable)
1211 		return (0);
1212 	(void) printf(gettext("\nRouting daemons:\n"));
1213 	(void) printf("\n                      %s   %s\n", "STATE", "FMRI");
1214 	if (ra_smf_cb(ra_print_state_cb, NULL, NULL) == -1)
1215 		return (-1);
1216 	return (0);
1217 }
1218 
1219 /*
1220  * Call scf_walk_fmri() with appropriate function, fmri, and data.
1221  * A NULL fmri causes scf_walk_fmri() to run on all instances.  We make
1222  * use of this many times in applying changes to the routing services.
1223  */
1224 static int
1225 ra_smf_cb(ra_smf_cb_t cbfunc, const char *fmri, void *data)
1226 {
1227 	scf_handle_t	*h;
1228 	int		exit_status = 0;
1229 
1230 	if ((h = scf_handle_create(SCF_VERSION)) == NULL ||
1231 	    scf_handle_bind(h) == -1) {
1232 		(void) fprintf(stderr, gettext(
1233 		    "%s: cannot connect to SMF repository\n"), myname);
1234 		return (-1);
1235 	}
1236 	return (scf_walk_fmri(h, fmri == NULL ? 0 : 1,
1237 	    fmri == NULL ? NULL : (char **)&fmri, 0,
1238 	    cbfunc, data, &exit_status, uu_die));
1239 }
1240 
1241 /*
1242  * Applies persistent configuration settings to current setup.
1243  */
1244 static int
1245 ra_set_current_opt_cb(void *data, scf_walkinfo_t *wip)
1246 {
1247 	return (ra_get_set_opt_common_cb(data, wip, B_FALSE, B_FALSE));
1248 }
1249 
1250 /*
1251  * Sets persistent value for option,  to be applied on next boot
1252  * or by "routeadm -u".
1253  */
1254 static int
1255 ra_set_persistent_opt_cb(void *data, scf_walkinfo_t *wip)
1256 {
1257 	return (ra_get_set_opt_common_cb(data, wip, B_TRUE, B_FALSE));
1258 }
1259 
1260 static int
1261 ra_get_current_opt_cb(void *data, scf_walkinfo_t *wip)
1262 {
1263 	return (ra_get_set_opt_common_cb(data, wip, B_FALSE, B_TRUE));
1264 }
1265 
1266 static int
1267 ra_get_persistent_opt_cb(void *data, scf_walkinfo_t *wip)
1268 {
1269 	return (ra_get_set_opt_common_cb(data, wip, B_TRUE, B_TRUE));
1270 }
1271 
1272 /*
1273  * Shared function that either sets or determines persistent or current
1274  * state. Setting persistent state (for next boot) involves setting
1275  * the general_ovr/enabled value to the current service state, and
1276  * the general/enabled value to the desired (next-boot) state.
1277  * Setting current state involves removing the temporary state
1278  * setting so the persistent state has effect.
1279  *
1280  * Persistent state is reported as being enabled if any of the
1281  * candidate services have a general/enabled value set to true,
1282  * while current state is reported as being enabled if any of the
1283  * candidate services has a general_ovr/enabled or general/enabled
1284  * value set to true.
1285  */
1286 static int
1287 ra_get_set_opt_common_cb(raopt_t *raopt, scf_walkinfo_t *wip,
1288     boolean_t persistent, boolean_t get)
1289 {
1290 	const char		*inst_fmri = wip->fmri;
1291 	scf_instance_t		*inst = wip->inst;
1292 	scf_instance_t		*rinst = NULL;
1293 	scf_handle_t		*h = scf_instance_handle(inst);
1294 	scf_propertygroup_t	*routeadm_pg;
1295 	boolean_t		persistent_state_enabled;
1296 	boolean_t		temporary_state_enabled;
1297 	boolean_t		current_state_enabled;
1298 	boolean_t		curr_svc = B_TRUE;
1299 	boolean_t		found_proto;
1300 	char			**protolist = NULL;
1301 	int			i, ret, numvalues = 0;
1302 
1303 	/*
1304 	 * Ensure we are dealing with a routeadm-managed service.  If
1305 	 * the FMRI used for walking instances is NULL,  it is reasonable
1306 	 * that a service not have a routeadm property group as we will
1307 	 * check all services in this case.
1308 	 */
1309 	if (ra_get_pg(h, inst, RA_PG_ROUTEADM, B_TRUE, raopt->opt_fmri != NULL,
1310 	    &routeadm_pg) == -1) {
1311 			/* Not a routing service, not an error. */
1312 			if (scf_error() == SCF_ERROR_NOT_FOUND &&
1313 			    raopt->opt_fmri == NULL)
1314 				return (0);
1315 			return (-1);
1316 	}
1317 	scf_pg_destroy(routeadm_pg);
1318 
1319 	/* Services with no "protocol" property are not routing daemons */
1320 	if (raopt->opt_fmri == NULL && ra_get_prop_as_string(h, inst,
1321 	    RA_PG_ROUTEADM, RA_PROP_PROTO, B_TRUE, B_FALSE, NULL, &numvalues,
1322 	    &protolist) == -1) {
1323 		if (scf_error() == SCF_ERROR_NOT_FOUND)
1324 			return (0);
1325 		return (-1);
1326 	}
1327 
1328 	/*
1329 	 * Skip invalid services based on flag settings.  Flags are used when
1330 	 * we run callback functions on all instances to identify
1331 	 * the correct instances to operate on.
1332 	 */
1333 	if (raopt->opt_flags & RA_SVC_FLAG_IPV4_ROUTING) {
1334 		found_proto = B_FALSE;
1335 		if (protolist != NULL) {
1336 			/* Check if protolist contains "ipv4" */
1337 			for (i = 0; i < numvalues; i++) {
1338 				if (protolist[i] != NULL && strcmp(
1339 				    protolist[i], RA_PROPVAL_PROTO_IPV4) == 0)
1340 					found_proto = B_TRUE;
1341 			}
1342 		}
1343 		/* If not an ipv4 routing service, skip. */
1344 		if (protolist == NULL || !found_proto) {
1345 			ra_free_prop_values(numvalues, protolist);
1346 			return (0);
1347 		}
1348 	}
1349 	if (raopt->opt_flags & RA_SVC_FLAG_IPV6_ROUTING) {
1350 		found_proto = B_FALSE;
1351 		if (protolist != NULL) {
1352 			/* Check if protolist contains "ipv6" */
1353 			for (i = 0; i < numvalues; i++) {
1354 				if (protolist[i] != NULL && strcmp(
1355 				    protolist[i], RA_PROPVAL_PROTO_IPV6) == 0)
1356 					found_proto = B_TRUE;
1357 			}
1358 		}
1359 		/* If not an ipv6 routing service, skip. */
1360 		if (protolist == NULL || !found_proto) {
1361 			ra_free_prop_values(numvalues, protolist);
1362 			return (0);
1363 		}
1364 	}
1365 	ra_free_prop_values(numvalues, protolist);
1366 
1367 	/* If enabling routing services, select only current routing services */
1368 	if (raopt->opt_fmri == NULL && !get && raopt->opt_enabled) {
1369 		if (ra_get_boolean_prop(h, inst, RA_PG_ROUTEADM,
1370 		    RA_PROP_CURR_ROUTING_SVC, B_FALSE, B_FALSE,
1371 		    &curr_svc) == -1)
1372 			return (0);
1373 		else if (!curr_svc && persistent) {
1374 			/*
1375 			 * We apply "current" routing changes to all routing
1376 			 * daemons, whether current or not, so bail if
1377 			 * we are trying to make a persistent update to a
1378 			 * non-"routing-svc".
1379 			 */
1380 			return (0);
1381 		}
1382 	}
1383 	if (ra_get_boolean_prop(h, inst, SCF_PG_GENERAL, SCF_PROPERTY_ENABLED,
1384 	    B_FALSE, B_TRUE, &persistent_state_enabled) == -1)
1385 		return (-1);
1386 
1387 	current_state_enabled = persistent_state_enabled;
1388 
1389 	if (ra_get_boolean_prop(h, inst, SCF_PG_GENERAL_OVR,
1390 	    SCF_PROPERTY_ENABLED, B_FALSE, B_FALSE, &temporary_state_enabled)
1391 	    == 0)
1392 		current_state_enabled = temporary_state_enabled;
1393 
1394 	if (get) {
1395 		/*
1396 		 * Persistent state is enabled if any services are
1397 		 * persistently enabled, i.e. general/enabled == true).
1398 		 * current state is enabled if any services
1399 		 * services are currently enabled, i.e. if defined,
1400 		 * general_ovr/enabled == true, if not, general/enabled == true.
1401 		 */
1402 		if (persistent)
1403 			raopt->opt_enabled = raopt->opt_enabled ||
1404 			    persistent_state_enabled;
1405 		else
1406 			raopt->opt_enabled = raopt->opt_enabled ||
1407 			    current_state_enabled;
1408 	} else {
1409 		if (persistent) {
1410 			/*
1411 			 * For peristent state changes, from -e/-d,
1412 			 * we set the general_ovr/enabled value to the
1413 			 * current state (to ensure it is preserved),
1414 			 * while setting the general/enabled value to
1415 			 * the desired value.  This has the effect of
1416 			 * the desired value coming into effect on next boot.
1417 			 */
1418 			ret = current_state_enabled ?
1419 			    smf_enable_instance(inst_fmri, SMF_TEMPORARY) :
1420 			    smf_disable_instance(inst_fmri, SMF_TEMPORARY);
1421 			if (ret != 0) {
1422 				(void) fprintf(stderr, gettext(
1423 				    "%s: unexpected libscf error: %s\n"),
1424 				    myname, scf_strerror(scf_error()));
1425 				return (-1);
1426 			}
1427 			/*
1428 			 * Refresh here so general_ovr/enabled state overrides
1429 			 * general/enabled state.
1430 			 */
1431 			(void) smf_refresh_instance(inst_fmri);
1432 			/*
1433 			 * Now we can safely set the general/enabled value
1434 			 * to the value we require on next boot (or
1435 			 * "routeadm -u").
1436 			 */
1437 			ret = ra_set_boolean_prop(h, inst, SCF_PG_GENERAL,
1438 			    SCF_PROPERTY_ENABLED, B_FALSE, raopt->opt_enabled);
1439 			if (ret != 0)
1440 				return (-1);
1441 			/*
1442 			 * Refresh here so general/enabled value is set.
1443 			 */
1444 			(void) smf_refresh_instance(inst_fmri);
1445 			if (raopt->opt_fmri != NULL)
1446 				return (0);
1447 			/*
1448 			 * Notify network/routing-setup service that
1449 			 * administrator has explicitly set ipv4(6)-routing
1450 			 * value.  If no explicit setting of this value is
1451 			 * done, ipv4-routing can be enabled in the situation
1452 			 * when no default routes can be determined.
1453 			 */
1454 			if ((rinst = scf_instance_create(h)) == NULL ||
1455 			    scf_handle_decode_fmri(h, RA_INSTANCE_ROUTING_SETUP,
1456 			    NULL, NULL, rinst, NULL, NULL,
1457 			    SCF_DECODE_FMRI_EXACT) == -1) {
1458 				(void) fprintf(stderr, gettext(
1459 				    "%s: unexpected libscf error: %s\n"),
1460 				    myname, scf_strerror(scf_error()));
1461 				return (-1);
1462 			}
1463 			ret = ra_set_boolean_prop(h, rinst, RA_PG_ROUTEADM,
1464 			    raopt->opt_flags & RA_SVC_FLAG_IPV4_ROUTING ?
1465 			    RA_PROP_IPV4_ROUTING_SET :
1466 			    RA_PROP_IPV6_ROUTING_SET, B_FALSE, B_TRUE);
1467 			scf_instance_destroy(rinst);
1468 			if (ret != 0)
1469 				return (-1);
1470 			(void) smf_refresh_instance(RA_INSTANCE_ROUTING_SETUP);
1471 		} else {
1472 			/*
1473 			 * For current changes (result of -u), we
1474 			 * enable/disable depending on persistent value
1475 			 * stored in general/enabled.  Here we disable
1476 			 * old routing-svcs (identified by a current-routing-svc
1477 			 * value of false) also.
1478 			 */
1479 			ret = persistent_state_enabled && curr_svc ?
1480 			    smf_enable_instance(inst_fmri, 0) :
1481 			    smf_disable_instance(inst_fmri, 0);
1482 			if (ret != 0) {
1483 				(void) fprintf(stderr, gettext(
1484 				    "%s: unexpected libscf error: %s\n"),
1485 				    myname, scf_strerror(scf_error()));
1486 				return (-1);
1487 			}
1488 			(void) smf_refresh_instance(inst_fmri);
1489 		}
1490 	}
1491 	return (0);
1492 }
1493 
1494 static int
1495 ra_set_default_opt_cb(void *data, scf_walkinfo_t *wip)
1496 {
1497 	scf_instance_t		*inst = wip->inst;
1498 	scf_handle_t		*h = scf_instance_handle(inst);
1499 	raopt_t			*raopt = data;
1500 
1501 	return (ra_set_boolean_prop(h, inst, RA_PG_ROUTEADM,
1502 	    raopt->opt_default_prop, B_FALSE, raopt->opt_default_enabled));
1503 }
1504 
1505 static int
1506 ra_get_default_opt_cb(void *data, scf_walkinfo_t *wip)
1507 {
1508 	scf_instance_t		*inst = wip->inst;
1509 	scf_handle_t		*h = scf_instance_handle(inst);
1510 	raopt_t			*raopt = data;
1511 
1512 	return (ra_get_boolean_prop(h, inst, RA_PG_ROUTEADM,
1513 	    raopt->opt_default_prop, B_TRUE, B_TRUE,
1514 	    &(raopt->opt_default_enabled)));
1515 }
1516 
1517 /*
1518  * Callbacks to set/retrieve persistent/default routing variable values.
1519  * The set functions use the value stored in the var_value/var_default_value
1520  * field of the associated ra_var_t, while the retrieval functions store
1521  * the value retrieved in that field.
1522  */
1523 static int
1524 ra_get_persistent_var_cb(void *data, scf_walkinfo_t *wip)
1525 {
1526 	scf_instance_t		*inst = wip->inst;
1527 	scf_handle_t		*h = scf_instance_handle(inst);
1528 	ravar_t			*ravar = data;
1529 
1530 	return (ra_get_single_prop_as_string(h, inst, RA_PG_ROUTEADM,
1531 	    ravar->var_prop, B_TRUE, B_TRUE, NULL, &ravar->var_value));
1532 }
1533 
1534 static int
1535 ra_set_persistent_var_cb(void *data, scf_walkinfo_t *wip)
1536 {
1537 	scf_instance_t		*inst = wip->inst;
1538 	scf_handle_t		*h = scf_instance_handle(inst);
1539 	ravar_t			*ravar = data;
1540 
1541 	return (ra_set_prop_from_string(h, inst, RA_PG_ROUTEADM,
1542 	    ravar->var_prop, SCF_TYPE_INVALID, B_FALSE, 1,
1543 	    (const char **)&ravar->var_value));
1544 }
1545 
1546 static int
1547 ra_get_default_var_cb(void *data, scf_walkinfo_t *wip)
1548 {
1549 	scf_instance_t		*inst = wip->inst;
1550 	scf_handle_t		*h = scf_instance_handle(inst);
1551 	ravar_t			*ravar = data;
1552 
1553 	return (ra_get_single_prop_as_string(h, inst, RA_PG_ROUTEADM,
1554 	    ravar->var_default_prop, B_TRUE, B_TRUE, NULL,
1555 	    &ravar->var_default_value));
1556 }
1557 
1558 /*
1559  * Depending on the value of the boolean_t * passed in,  this callback
1560  * either marks the relevant service(s) as current-routing-svcs (or unmarking)
1561  * by setting that property to true or false.  When routing services
1562  * are to be enabled,  the a current-routing-svc value of true flags the
1563  * service as one to be enabled.
1564  */
1565 static int
1566 ra_mark_routing_svcs_cb(void *data, scf_walkinfo_t *wip)
1567 {
1568 	scf_instance_t		*inst = wip->inst;
1569 	scf_handle_t		*h = scf_instance_handle(inst);
1570 	boolean_t		*mark = data;
1571 	boolean_t		marked;
1572 	int			numvalues = 0;
1573 	char			**protolist = NULL;
1574 
1575 	/* Check we are dealing with a routing daemon service */
1576 	if (ra_get_prop_as_string(h, inst, RA_PG_ROUTEADM, RA_PROP_PROTO,
1577 	    B_TRUE, B_FALSE, NULL, &numvalues, &protolist) == -1)
1578 		return (0);
1579 	ra_free_prop_values(numvalues, protolist);
1580 	if (*mark)
1581 		return (ra_set_boolean_prop(h, inst, RA_PG_ROUTEADM,
1582 		    RA_PROP_CURR_ROUTING_SVC, B_TRUE, B_TRUE));
1583 	/* Unmark service. */
1584 	if (ra_get_boolean_prop(h, inst, RA_PG_ROUTEADM,
1585 	    RA_PROP_CURR_ROUTING_SVC, B_TRUE, B_FALSE, &marked) == 0 && marked)
1586 		return (ra_set_boolean_prop(h, inst, RA_PG_ROUTEADM,
1587 		    RA_PROP_CURR_ROUTING_SVC, B_TRUE, B_FALSE));
1588 	return (0);
1589 }
1590 
1591 /*
1592  * List property values for all properties in the "routing" property
1593  * group of the routing service instance.
1594  */
1595 
1596 /* ARGSUSED0 */
1597 static int
1598 ra_list_props_cb(void *data, scf_walkinfo_t *wip)
1599 {
1600 	const char		*inst_fmri = wip->fmri;
1601 	scf_instance_t		*inst = wip->inst;
1602 	scf_handle_t		*h = scf_instance_handle(inst);
1603 	scf_iter_t		*propiter, *valiter;
1604 	scf_propertygroup_t	*pg;
1605 	scf_property_t		*prop;
1606 	scf_value_t		*val;
1607 	char			**protolist = NULL, *pnamebuf, *valbuf;
1608 	ssize_t			pnamelen, vallen;
1609 	int			numvalues = 0;
1610 	int			propiterret, valiterret, retval = 0;
1611 
1612 	/* Services with no "protocol" property are not routing daemons */
1613 	if (ra_get_prop_as_string(h, inst, RA_PG_ROUTEADM, RA_PROP_PROTO,
1614 	    B_TRUE, B_FALSE, NULL, &numvalues, &protolist) == -1) {
1615 		if (scf_error() == SCF_ERROR_NOT_FOUND)
1616 			(void) fprintf(stderr,
1617 			    gettext("%s: %s is not a routing daemon service\n"),
1618 			    myname, inst_fmri);
1619 		else
1620 			(void) fprintf(stderr,
1621 			    gettext("%s: unexpected libscf error: %s\n"),
1622 			    myname, scf_strerror(scf_error()));
1623 		ra_free_prop_values(numvalues, protolist);
1624 		return (-1);
1625 	}
1626 	ra_free_prop_values(numvalues, protolist);
1627 
1628 	if (ra_get_pg(h, inst, RA_PG_ROUTING, B_TRUE, B_FALSE, &pg) == -1) {
1629 		if (scf_error() == SCF_ERROR_NOT_FOUND) {
1630 			(void) printf("%s: no %s property group for %s\n",
1631 			    myname, RA_PG_ROUTING, inst_fmri);
1632 			return (0);
1633 		}
1634 		(void) fprintf(stderr,
1635 		    gettext("%s: unexpected libscf error: %s\n"),
1636 		    myname, scf_strerror(scf_error()));
1637 		return (-1);
1638 	}
1639 
1640 	(void) printf("%s:\n", inst_fmri);
1641 
1642 	/* Create an iterator to walk through all properties */
1643 	if ((propiter = scf_iter_create(h)) == NULL ||
1644 	    (prop = scf_property_create(h)) == NULL ||
1645 	    scf_iter_pg_properties(propiter, pg) != 0) {
1646 		(void) fprintf(stderr, gettext
1647 		    ("%s: could not iterate through properties for %s: %s\n"),
1648 		    myname, inst_fmri, scf_strerror(scf_error()));
1649 	}
1650 	while ((propiterret = scf_iter_next_property(propiter, prop)) == 1) {
1651 		if ((pnamelen = scf_property_get_name(prop, NULL, 0) + 1)
1652 		    == 0) {
1653 			(void) fprintf(stderr, gettext("%s: could not retrieve "
1654 			    "property name for instance %s: %s\n"), myname,
1655 			    inst_fmri, scf_strerror(scf_error()));
1656 			retval = -1;
1657 			break;
1658 		}
1659 		if ((pnamebuf = malloc(pnamelen)) == NULL) {
1660 			(void) fprintf(stderr,
1661 			    gettext("%s: out of memory\n"), myname);
1662 			retval = -1;
1663 			break;
1664 		}
1665 		(void) scf_property_get_name(prop, pnamebuf,
1666 		    pnamelen);
1667 		(void) printf("\t%s = ", pnamebuf);
1668 		if ((valiter = scf_iter_create(h)) == NULL ||
1669 		    (val = scf_value_create(h)) == NULL ||
1670 		    scf_iter_property_values(valiter, prop)
1671 		    != 0) {
1672 			(void) fprintf(stderr, gettext
1673 			    ("%s: could not iterate through "
1674 			    "properties for %s: %s\n"), myname, inst_fmri,
1675 			    scf_strerror(scf_error()));
1676 			scf_value_destroy(val);
1677 			scf_iter_destroy(valiter);
1678 			free(pnamebuf);
1679 			retval = -1;
1680 			break;
1681 		}
1682 		while ((valiterret = scf_iter_next_value(valiter, val)) == 1) {
1683 			if ((vallen = scf_value_get_as_string
1684 			    (val, NULL, 0) + 1) == 0) {
1685 				(void) fprintf(stderr, gettext
1686 				    ("%s: could not retrieve "
1687 				    "property value for instance %s, "
1688 				    "property %s: %s\n"), myname, inst_fmri,
1689 				    pnamebuf, scf_strerror(scf_error()));
1690 				retval = -1;
1691 			} else if ((valbuf = malloc(vallen)) == NULL) {
1692 				(void) fprintf(stderr,
1693 				    gettext("%s: out of memory\n"), myname);
1694 				retval = -1;
1695 			}
1696 			if (retval == -1) {
1697 				scf_iter_destroy(valiter);
1698 				scf_value_destroy(val);
1699 				free(pnamebuf);
1700 				goto out;
1701 			}
1702 			(void) scf_value_get_as_string(val, valbuf, vallen);
1703 			(void) printf("%s ", valbuf);
1704 			free(valbuf);
1705 		}
1706 		(void) printf("\n");
1707 		scf_iter_destroy(valiter);
1708 		scf_value_destroy(val);
1709 		free(pnamebuf);
1710 		if (valiterret == -1) {
1711 			(void) fprintf(stderr,
1712 			    gettext("%s: could not iterate through"
1713 			    "properties for %s: %s\n"), myname, inst_fmri,
1714 			    scf_strerror(scf_error()));
1715 			retval = -1;
1716 			break;
1717 		}
1718 	}
1719 out:
1720 	scf_iter_destroy(propiter);
1721 	scf_property_destroy(prop);
1722 	scf_pg_destroy(pg);
1723 	if (propiterret == -1)
1724 		(void) fprintf(stderr, gettext
1725 		    ("%s: could not iterate through properties for %s: %s\n"),
1726 		    myname, inst_fmri, scf_strerror(scf_error()));
1727 	return (retval);
1728 }
1729 
1730 /*
1731  * Modify property with name stored in passed-in ra_prop_t to have
1732  * the assocatied values.  Only works for existing properties in
1733  * the "routing" property group for routing daemon services,  so all
1734  * routing daemons should place configurable options in that group.
1735  */
1736 static int
1737 ra_modify_props_cb(void *data, scf_walkinfo_t *wip)
1738 {
1739 	const char		*inst_fmri = wip->fmri;
1740 	scf_instance_t		*inst = wip->inst;
1741 	scf_handle_t		*h = scf_instance_handle(inst);
1742 	ra_prop_t		*raprop = data;
1743 	int			numvalues = 0;
1744 	char			**protolist = NULL;
1745 
1746 	/* Services with no "protocol" property are not routing daemons */
1747 	if (ra_get_prop_as_string(h, inst, RA_PG_ROUTEADM, RA_PROP_PROTO,
1748 	    B_TRUE, B_FALSE, NULL, &numvalues, &protolist) == -1) {
1749 		if (scf_error() == SCF_ERROR_NOT_FOUND)
1750 			(void) fprintf(stderr,
1751 			    gettext("%s: %s is not a routing daemon service\n"),
1752 			    myname, inst_fmri);
1753 		else
1754 			(void) fprintf(stderr,
1755 			    gettext("%s: unexpected libscf error: %s\n"),
1756 			    myname, scf_strerror(scf_error()));
1757 		ra_free_prop_values(numvalues, protolist);
1758 		return (-1);
1759 	}
1760 	ra_free_prop_values(numvalues, protolist);
1761 
1762 	if (ra_set_prop_from_string(h, inst, RA_PG_ROUTING, raprop->prop_name,
1763 	    SCF_TYPE_INVALID, B_FALSE, raprop->prop_numvalues,
1764 	    (const char **)raprop->prop_values) == -1)
1765 		return (-1);
1766 
1767 	(void) smf_refresh_instance(inst_fmri);
1768 	return (0);
1769 }
1770 
1771 /*
1772  * Display FMRI, state for each routing daemon service.
1773  */
1774 
1775 /* ARGSUSED0 */
1776 static int
1777 ra_print_state_cb(void *data, scf_walkinfo_t *wip)
1778 {
1779 	const char		*inst_fmri = wip->fmri;
1780 	scf_instance_t		*inst = wip->inst;
1781 	scf_handle_t		*h = scf_instance_handle(inst);
1782 	char			*inst_state, **protolist = NULL;
1783 	int			numvalues = 0;
1784 
1785 	/* Ensure service is a routing daemon */
1786 	if (ra_get_prop_as_string(h, inst, RA_PG_ROUTEADM, RA_PROP_PROTO,
1787 	    B_TRUE, B_FALSE, NULL, &numvalues, &protolist) == -1)
1788 		return (0);
1789 	ra_free_prop_values(numvalues, protolist);
1790 
1791 	if ((inst_state = smf_get_state(inst_fmri)) == NULL) {
1792 		(void) fprintf(stderr,
1793 		    gettext("%s: could not retrieve state for %s: %s\n"),
1794 		    myname, inst_fmri, scf_strerror(scf_error()));
1795 		return (-1);
1796 	}
1797 	(void) printf("%27s   %2s\n", inst_state, inst_fmri);
1798 	free(inst_state);
1799 
1800 	return (0);
1801 }
1802 
1803 static int
1804 ra_get_pg(scf_handle_t *h, scf_instance_t *inst, const char *pgname,
1805     boolean_t composed, boolean_t required, scf_propertygroup_t **pg)
1806 {
1807 	/* Retrieve (possibly composed) property group for instance */
1808 	if ((*pg = scf_pg_create(h)) == NULL || (composed &&
1809 	    scf_instance_get_pg_composed(inst, NULL, pgname, *pg) != 0) ||
1810 	    (!composed && scf_instance_get_pg(inst, pgname, *pg) != 0)) {
1811 		if (scf_error() == SCF_ERROR_NOT_FOUND) {
1812 			if (required)
1813 				(void) fprintf(stderr, gettext(
1814 				    "%s: no such property group %s\n"),
1815 				    myname, pgname);
1816 			return (-1);
1817 		}
1818 		if (required)
1819 			(void) fprintf(stderr, gettext(
1820 			    "%s: unexpected libscf error: %s\n"), myname,
1821 			    scf_strerror(scf_error()));
1822 		return (-1);
1823 	}
1824 	return (0);
1825 }
1826 
1827 static int
1828 ra_get_boolean_prop(scf_handle_t *h, scf_instance_t *inst,
1829     const char *pgname, const char *propname, boolean_t composed,
1830     boolean_t required, boolean_t *val)
1831 {
1832 	char	*valstr;
1833 
1834 	if (ra_get_single_prop_as_string(h, inst, pgname, propname,
1835 	    composed, required, NULL, &valstr) != 0)
1836 		return (-1);
1837 	*val = strcmp(valstr, RA_PROPVAL_BOOLEAN_TRUE) == 0;
1838 	free(valstr);
1839 	return (0);
1840 }
1841 
1842 static int
1843 ra_get_single_prop_as_string(scf_handle_t *h, scf_instance_t *inst,
1844     const char *pgname, const char *propname, boolean_t composed,
1845     boolean_t required, scf_type_t *type, char **value)
1846 {
1847 	char	**values;
1848 	int	numvalues = 1;
1849 
1850 	if (ra_get_prop_as_string(h, inst, pgname, propname, composed, required,
1851 	    type, &numvalues, &values) == -1)
1852 		return (-1);
1853 	*value = values[0];
1854 	free(values);
1855 	return (0);
1856 }
1857 
1858 /*
1859  * Retrieve property named in propname,  possibly using the composed
1860  * property group view (union of instance and service-level properties,
1861  * where instance-level properties override service-level values).
1862  */
1863 static int
1864 ra_get_prop_as_string(scf_handle_t *h, scf_instance_t *inst,
1865     const char *pgname, const char *propname, boolean_t composed,
1866     boolean_t required, scf_type_t *type, int *numvalues, char ***values)
1867 {
1868 	scf_propertygroup_t	*pg = NULL;
1869 	scf_property_t		*prop = NULL;
1870 	scf_iter_t		*valiter = NULL;
1871 	scf_value_t		*val = NULL;
1872 	ssize_t			vallen = 0;
1873 	int			valiterret, i, numvalues_retrieved, ret = 0;
1874 
1875 	if (ra_get_pg(h, inst, pgname, composed, required, &pg) == -1)
1876 		return (-1);
1877 
1878 	*values = NULL;
1879 	/*
1880 	 * Retrieve values. All values routeadm needs to retrieve
1881 	 * (bar those gathered by routeadm -l), are known to be single-valued.
1882 	 */
1883 	if ((prop = scf_property_create(h)) == NULL)
1884 		goto error;
1885 	if (scf_pg_get_property(pg, propname, prop) != 0) {
1886 		*numvalues = 0;
1887 		if (scf_error() == SCF_ERROR_NOT_FOUND) {
1888 			if (required)
1889 				(void) fprintf(stderr, gettext(
1890 				    "%s: property %s/%s not found\n"),
1891 				    myname, pgname, propname);
1892 			ret = -1;
1893 			goto out;
1894 		}
1895 		goto error;
1896 	}
1897 	if ((val = scf_value_create(h)) == NULL &&
1898 	    scf_property_get_value(prop, val) != 0 ||
1899 	    (valiter = scf_iter_create(h)) == NULL ||
1900 	    scf_iter_property_values(valiter, prop) != 0)
1901 		goto error;
1902 	/* retrieve each value */
1903 	for (numvalues_retrieved = 0;
1904 	    (valiterret = scf_iter_next_value(valiter, val)) == 1;
1905 	    numvalues_retrieved++) {
1906 		if ((vallen = scf_value_get_as_string
1907 		    (val, NULL, 0) + 1) == 0)
1908 			goto error;
1909 		if ((*values = realloc(*values,
1910 		    sizeof (*values) + sizeof (char *))) == NULL ||
1911 		    ((*values)[numvalues_retrieved] = malloc(vallen)) == NULL) {
1912 			(void) fprintf(stderr, gettext(
1913 			    "%s: out of memory\n"), myname);
1914 			ret = -1;
1915 			goto out;
1916 		}
1917 		(void) scf_value_get_as_string(val,
1918 		    (*values)[numvalues_retrieved], vallen);
1919 	}
1920 	if (valiterret == -1)
1921 		goto error;
1922 	/*
1923 	 * if *numvalues != 0, it holds expected number of values.  If a
1924 	 * different number are found, it is an error.
1925 	 */
1926 	if (*numvalues != 0 && *numvalues != numvalues_retrieved) {
1927 		(void) fprintf(stderr, gettext(
1928 		    "%s: got %d values for property %s/%s, expected %d\n"),
1929 		    myname, numvalues_retrieved, pgname, propname, *numvalues);
1930 		ret = -1;
1931 		goto out;
1932 	}
1933 	*numvalues = numvalues_retrieved;
1934 
1935 	/* Retrieve property type if required. */
1936 	if (type != NULL)
1937 		(void) scf_property_type(prop, type);
1938 
1939 	goto out;
1940 error:
1941 	if (scf_error() == SCF_ERROR_NOT_FOUND) {
1942 		(void) fprintf(stderr, gettext(
1943 		    "%s: property %s not found"), myname, propname);
1944 	} else {
1945 		(void) fprintf(stderr, gettext(
1946 		    "%s: unexpected libscf error: %s, "), myname);
1947 	}
1948 	for (i = 0; i < numvalues_retrieved; i++)
1949 		free((*values)[i]);
1950 	if (*values != NULL)
1951 		free(*values);
1952 
1953 	ret = -1;
1954 out:
1955 	if (val != NULL)
1956 		scf_value_destroy(val);
1957 	if (valiter != NULL)
1958 		scf_iter_destroy(valiter);
1959 	if (prop != NULL)
1960 		scf_property_destroy(prop);
1961 	if (pg != NULL)
1962 		scf_pg_destroy(pg);
1963 	return (ret);
1964 }
1965 
1966 static void
1967 ra_free_prop_values(int numvalues, char **values)
1968 {
1969 	int	i;
1970 	if (values != NULL) {
1971 		for (i = 0; i < numvalues; i++)
1972 			free(values[i]);
1973 		free(values);
1974 	}
1975 }
1976 
1977 static int
1978 ra_set_boolean_prop(scf_handle_t *h, scf_instance_t *inst, const char *pgname,
1979     const char *prop, boolean_t create, boolean_t propval)
1980 {
1981 	const char	*val = propval ? RA_PROPVAL_BOOLEAN_TRUE :
1982 	    RA_PROPVAL_BOOLEAN_FALSE;
1983 
1984 	return (ra_set_prop_from_string(h, inst, pgname, prop, SCF_TYPE_BOOLEAN,
1985 	    create, 1, &val));
1986 }
1987 
1988 /*
1989  * Set the property named in propname to the values passed in in the propvals
1990  * array.  Only create a new property if "create" is true.
1991  */
1992 static int
1993 ra_set_prop_from_string(scf_handle_t *h, scf_instance_t *inst,
1994     const char *pgname, const char *propname, scf_type_t proptype,
1995     boolean_t create, int numpropvals, const char **propvals)
1996 {
1997 	scf_propertygroup_t	*instpg = NULL, *cpg = NULL;
1998 	scf_type_t		oldproptype, newproptype = proptype;
1999 	scf_property_t		*prop = NULL;
2000 	scf_value_t		**values = NULL;
2001 	scf_transaction_t	*tx = NULL;
2002 	scf_transaction_entry_t	*ent = NULL;
2003 	boolean_t		new = B_FALSE;
2004 	int			i, retval, numvalues = 0, ret = 0;
2005 	char			*pgtype = NULL, **ovalues;
2006 	ssize_t			typelen;
2007 
2008 	/* Firstly, does property exist? If not, and create is false, bail */
2009 	if (ra_get_prop_as_string(h, inst, pgname, propname, B_TRUE,
2010 	    B_FALSE, &oldproptype, &numvalues, &ovalues) == -1) {
2011 		if (scf_error() != SCF_ERROR_NOT_FOUND)
2012 			goto error;
2013 		if (!create) {
2014 			(void) fprintf(stderr, gettext(
2015 			    "%s: no such property %s/%s\n"), myname, pgname,
2016 			    propname);
2017 			return (-1);
2018 		}
2019 	} else
2020 		ra_free_prop_values(numvalues, ovalues);
2021 
2022 	/* Use old property type */
2023 	if (proptype == SCF_TYPE_INVALID)
2024 		newproptype = oldproptype;
2025 
2026 	/*
2027 	 * Does property group exist at instance level?  If not, we need to
2028 	 * create it,  since the composed view of the property group did
2029 	 * contain the property.  We never modify properties at the service
2030 	 * level,  as it`s possible that multiple instances will inherit those
2031 	 * settings.
2032 	 */
2033 	if (ra_get_pg(h, inst, pgname, B_FALSE, B_FALSE, &instpg) == -1) {
2034 		if (scf_error() != SCF_ERROR_NOT_FOUND)
2035 			goto error;
2036 		/* Ensure pg exists at service level, get composed pg */
2037 		if (ra_get_pg(h, inst, pgname, B_TRUE, B_FALSE, &cpg) == -1)
2038 			goto error;
2039 
2040 		/* Create instance-level property group */
2041 		if ((typelen = scf_pg_get_type(cpg, NULL, 0) + 1) == 0)
2042 			goto error;
2043 		if ((pgtype = malloc(typelen)) == NULL) {
2044 			(void) fprintf(stderr, gettext(
2045 			    "%s: out of memory\n"), myname);
2046 			goto error;
2047 		}
2048 		(void) scf_pg_get_type(cpg, pgtype, typelen);
2049 		if ((instpg = scf_pg_create(h)) == NULL ||
2050 		    scf_instance_add_pg(inst, pgname, pgtype, 0, instpg)
2051 		    == -1) {
2052 			(void) fprintf(stderr, gettext(
2053 			    "%s: could not create property group %s\n"),
2054 			    myname, pgname);
2055 			goto error;
2056 		}
2057 	}
2058 	if ((prop = scf_property_create(h)) == NULL)
2059 		goto error;
2060 	if ((values = calloc(numpropvals, sizeof (scf_value_t *))) == NULL) {
2061 		(void) fprintf(stderr, gettext("%s: out of memory"), myname);
2062 		goto error;
2063 	}
2064 	if (scf_pg_get_property(instpg, propname, prop) != 0) {
2065 		/* New property? */
2066 		if (scf_error() == SCF_ERROR_NOT_FOUND)
2067 			new = B_TRUE;
2068 		else
2069 			goto error;
2070 	}
2071 	if ((tx = scf_transaction_create(h)) == NULL ||
2072 	    (ent = scf_entry_create(h)) == NULL)
2073 		goto error;
2074 retry:
2075 	if (scf_transaction_start(tx, instpg) == -1)
2076 		goto error;
2077 	if (new) {
2078 		if (scf_transaction_property_new(tx, ent, propname,
2079 		    newproptype) == -1)
2080 			goto error;
2081 	} else if (scf_transaction_property_change(tx, ent, propname,
2082 	    newproptype) == -1)
2083 		goto error;
2084 	for (i = 0; i < numpropvals; i++) {
2085 		if ((values[i] = scf_value_create(h)) == NULL ||
2086 		    scf_value_set_from_string(values[i], newproptype,
2087 		    propvals[i] == NULL ? "": propvals[i]) == -1 ||
2088 		    scf_entry_add_value(ent, values[i]) != 0)
2089 			goto error;
2090 	}
2091 	retval = scf_transaction_commit(tx);
2092 	if (retval == 0) {
2093 		scf_transaction_reset(tx);
2094 		if (scf_pg_update(instpg) == -1)
2095 			goto error;
2096 		goto retry;
2097 	}
2098 	if (retval == -1)
2099 		goto error;
2100 	goto out;
2101 error:
2102 	switch (scf_error()) {
2103 	case SCF_ERROR_INVALID_ARGUMENT:
2104 		(void) fprintf(stderr, gettext(
2105 		    "%s: invalid value for property %s/%s\n"), myname,
2106 		    pgname, propname);
2107 		break;
2108 	case SCF_ERROR_NOT_FOUND:
2109 		(void) fprintf(stderr, gettext(
2110 		    "%s: no such property %s/%s\n"), myname,
2111 		    pgname, propname);
2112 		break;
2113 	default:
2114 		(void) fprintf(stderr, gettext(
2115 		    "%s: unexpected libscf error: %s\n"), myname,
2116 		    scf_strerror(scf_error()));
2117 		break;
2118 	}
2119 	ret = -1;
2120 out:
2121 	if (tx != NULL)
2122 		scf_transaction_destroy(tx);
2123 	if (ent != NULL)
2124 		scf_entry_destroy(ent);
2125 	if (values != NULL) {
2126 		for (i = 0; i < numpropvals; i++) {
2127 			if (values[i] != NULL)
2128 				scf_value_destroy(values[i]);
2129 		}
2130 		free(values);
2131 	}
2132 	if (prop != NULL)
2133 		scf_property_destroy(prop);
2134 	if (cpg != NULL)
2135 		scf_pg_destroy(cpg);
2136 	if (instpg != NULL)
2137 		scf_pg_destroy(instpg);
2138 	if (pgtype != NULL)
2139 		free(pgtype);
2140 	return (ret);
2141 }
2142 
2143 /*
2144  * This function gathers configuration from the legacy /etc/inet/routing.conf,
2145  * if any, and sets the appropriate variable values accordingly.  Once
2146  * these are set,  the legacy daemons are checked to see if they have
2147  * SMF counterparts (ra_check_legacy_daemons()).  If they do, the
2148  * configuration is upgraded.  Finally,  the legacy option settings are
2149  * applied,  enabling/disabling the routing/forwarding services as
2150  * appropriate.
2151  */
2152 static int
2153 ra_upgrade_from_legacy_conf(void)
2154 {
2155 	scf_handle_t	*h = NULL;
2156 	scf_instance_t	*inst = NULL;
2157 	int		ret = 0, i, r;
2158 	boolean_t	old_conf_read;
2159 	ravar_t		*routing_svcs = ra_str2var(RA_VAR_ROUTING_SVCS);
2160 
2161 	/*
2162 	 * First, determine if we have already upgraded - if "routing-conf-read"
2163 	 * is true, we bail.  The use of a boolean property indicating if
2164 	 * routing.conf has been read and applied might seem a lot more
2165 	 * work than simply copying routing.conf aside,  but leaving the
2166 	 * file in place allows users to downgrade and have their old
2167 	 * routing configuration still in place.
2168 	 */
2169 	if ((h = scf_handle_create(SCF_VERSION)) == NULL ||
2170 	    scf_handle_bind(h) == -1) {
2171 		(void) fprintf(stderr, gettext(
2172 		    "%s: cannot connect to SMF repository\n"), myname);
2173 		ret = -1;
2174 		goto out;
2175 	}
2176 	if ((inst = scf_instance_create(h)) == NULL ||
2177 	    scf_handle_decode_fmri(h, RA_INSTANCE_ROUTING_SETUP,
2178 	    NULL, NULL, inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) {
2179 		(void) fprintf(stderr, gettext(
2180 		    "%s: unexpected libscf error: %s\n"), myname,
2181 		    scf_strerror(scf_error()));
2182 		ret = -1;
2183 		goto out;
2184 	}
2185 	if (ra_get_boolean_prop(h, inst, RA_PG_ROUTEADM,
2186 	    RA_PROP_ROUTING_CONF_READ, B_TRUE, B_TRUE, &old_conf_read) == -1) {
2187 		ret = -1;
2188 		goto out;
2189 	}
2190 
2191 	if (old_conf_read)
2192 		goto out;
2193 
2194 	/*
2195 	 * Now set "routing-conf-read" to true so we don`t reimport legacy
2196 	 * configuration again.
2197 	 */
2198 	if (ra_set_boolean_prop(h, inst, RA_PG_ROUTEADM,
2199 	    RA_PROP_ROUTING_CONF_READ, B_FALSE, B_TRUE) == -1)
2200 		return (-1);
2201 	(void) smf_refresh_instance(RA_INSTANCE_ROUTING_SETUP);
2202 
2203 	ra_resetvars(NULL);
2204 
2205 	/* First, gather values from routing.conf */
2206 	if ((r = ra_parseconf()) == -1) {
2207 		ret = -1;
2208 		goto out;
2209 	}
2210 	/* No routing.conf file found */
2211 	if (r == 0)
2212 		goto out;
2213 	/*
2214 	 * Now, set the options/variables gathered.  We set variables first,
2215 	 * as we cannot enable routing before we determine the daemons
2216 	 * to enable.
2217 	 */
2218 
2219 	for (i = 0; ra_vars[i].var_name != NULL; i++) {
2220 		/* Skip routing-svcs var, not featured in legacy config */
2221 		if (strcmp(ra_vars[i].var_name, RA_VAR_ROUTING_SVCS) == 0)
2222 			continue;
2223 		if (ra_smf_cb(ra_set_persistent_var_cb, ra_vars[i].var_fmri,
2224 		    &(ra_vars[i])) == -1) {
2225 			ret = -1;
2226 			goto out;
2227 		}
2228 	}
2229 	/* Clear routing-svcs value */
2230 	if (ra_smf_cb(ra_set_persistent_var_cb, routing_svcs->var_fmri,
2231 	    routing_svcs) == -1) {
2232 		ret = -1;
2233 		goto out;
2234 	}
2235 
2236 	if (ra_check_legacy_daemons() == -1) {
2237 		ret = -1;
2238 		goto out;
2239 	}
2240 
2241 	for (i = 0; ra_opts[i].opt_name != NULL; i++) {
2242 		if (ra_smf_cb(ra_set_persistent_opt_cb, ra_opts[i].opt_fmri,
2243 		    &(ra_opts[i])) == -1 ||
2244 		    ra_smf_cb(ra_set_default_opt_cb,
2245 		    ra_opts[i].opt_default_fmri, &(ra_opts[i])) == -1) {
2246 			ret = -1;
2247 			break;
2248 		}
2249 	}
2250 out:
2251 	if (inst != NULL)
2252 		scf_instance_destroy(inst);
2253 	if (h != NULL)
2254 		scf_handle_destroy(h);
2255 
2256 	return (ret);
2257 }
2258 
2259 /*
2260  * Parse the configuration file and fill the ra_opts array with opt_value
2261  * and opt_default_value values, and the ra_vars array with var_value and
2262  * var_default_value values.  Then copy aside routing.conf so it will not
2263  * be read by future invokations of routeadm.
2264  */
2265 static int
2266 ra_parseconf(void)
2267 {
2268 	FILE	*fp;
2269 	uint_t	lineno;
2270 	char	line[RA_MAX_CONF_LINE];
2271 	char	*cp, *confstr;
2272 	raopt_t	*raopt;
2273 	ravar_t *ravar;
2274 
2275 	if ((fp = fopen(RA_CONF_FILE, "r")) == NULL) {
2276 		/*
2277 		 * There's no config file, so we simply return as there
2278 		 * is no work to do.
2279 		 */
2280 		return (0);
2281 	}
2282 
2283 	for (lineno = 1; fgets(line, sizeof (line), fp) != NULL; lineno++) {
2284 		if (line[strlen(line) - 1] == '\n')
2285 			line[strlen(line) - 1] = '\0';
2286 
2287 		cp = line;
2288 
2289 		/* Skip leading whitespace */
2290 		while (isspace(*cp))
2291 			cp++;
2292 
2293 		/* Skip comment lines and empty lines */
2294 		if (*cp == '#' || *cp == '\0')
2295 			continue;
2296 
2297 		/*
2298 		 * Anything else must be of the form:
2299 		 * <option> <value> <default_value>
2300 		 */
2301 		if ((confstr = strtok(cp, " ")) == NULL) {
2302 			(void) fprintf(stderr,
2303 			    gettext("%1$s: %2$s: invalid entry on line %3$d\n"),
2304 			    myname, RA_CONF_FILE, lineno);
2305 			continue;
2306 		}
2307 
2308 		if ((raopt = ra_str2opt(confstr)) != NULL) {
2309 			if (ra_parseopt(confstr, lineno, raopt) != 0) {
2310 				(void) fclose(fp);
2311 				return (-1);
2312 			}
2313 		} else if ((ravar = ra_str2var(confstr)) != NULL) {
2314 			if (ra_parsevar(confstr, ravar) != 0) {
2315 				(void) fclose(fp);
2316 				return (-1);
2317 			}
2318 		} else {
2319 			(void) fprintf(stderr,
2320 			    gettext("%1$s: %2$s: invalid option name on "
2321 				"line %3$d\n"),
2322 			    myname, RA_CONF_FILE, lineno);
2323 			continue;
2324 		}
2325 	}
2326 
2327 	(void) fclose(fp);
2328 
2329 	return (1);
2330 }
2331 
2332 static int
2333 ra_parseopt(char *confstr, int lineno, raopt_t *raopt)
2334 {
2335 	oval_t oval, d_oval;
2336 
2337 	if ((confstr = strtok(NULL, " ")) == NULL) {
2338 		(void) fprintf(stderr,
2339 		    gettext("%1$s: %2$s: missing value on line %3$d\n"),
2340 		    myname, RA_CONF_FILE, lineno);
2341 		return (0);
2342 	}
2343 	if ((oval = ra_str2oval(confstr)) == OPT_INVALID) {
2344 		(void) fprintf(stderr,
2345 		    gettext("%1$s: %2$s: invalid option "
2346 			"value on line %3$d\n"),
2347 		    myname, RA_CONF_FILE, lineno);
2348 		return (0);
2349 	}
2350 	if (oval != OPT_DEFAULT)
2351 		raopt->opt_enabled = oval == OPT_ENABLED;
2352 
2353 	if ((confstr = strtok(NULL, " ")) == NULL) {
2354 		(void) fprintf(stderr,
2355 		    gettext("%1$s: %2$s: missing revert "
2356 			"value on line %3$d\n"),
2357 		    myname, RA_CONF_FILE, lineno);
2358 		return (0);
2359 	}
2360 	if ((d_oval = ra_str2oval(confstr)) == OPT_INVALID) {
2361 		(void) fprintf(stderr,
2362 		    gettext("%1$s: %2$s: invalid revert "
2363 			"value on line %3$d\n"),
2364 		    myname, RA_CONF_FILE, lineno, confstr);
2365 		return (0);
2366 	}
2367 	raopt->opt_default_enabled = d_oval == OPT_ENABLED;
2368 	if (oval == OPT_DEFAULT)
2369 		raopt->opt_enabled = d_oval == OPT_ENABLED;
2370 	return (0);
2371 }
2372 
2373 static int
2374 ra_parsevar(char *confstr, ravar_t *ravar)
2375 {
2376 	confstr = strtok(NULL, "=");
2377 	if (confstr == NULL) {
2378 		/*
2379 		 * This isn't an error condition, it simply means that the
2380 		 * variable has no value.
2381 		 */
2382 		ravar->var_value = NULL;
2383 		return (0);
2384 	}
2385 
2386 	if ((ravar->var_value = strdup(confstr)) == NULL) {
2387 		(void) fprintf(stderr, gettext("%s: "
2388 		    "unable to allocate memory\n"), myname);
2389 		return (-1);
2390 	}
2391 	return (0);
2392 }
2393 
2394 /* Convert a string to an option value. */
2395 static oval_t
2396 ra_str2oval(const char *valstr)
2397 {
2398 	if (strcmp(valstr, "enabled") == 0)
2399 		return (OPT_ENABLED);
2400 	else if (strcmp(valstr, "disabled") == 0)
2401 		return (OPT_DISABLED);
2402 	else if (strcmp(valstr, "default") == 0)
2403 		return (OPT_DEFAULT);
2404 	return (OPT_INVALID);
2405 }
2406 
2407 static raopt_t *
2408 ra_str2opt(const char *optnamestr)
2409 {
2410 	int	i;
2411 
2412 	for (i = 0; ra_opts[i].opt_name != NULL; i++) {
2413 		if (strcmp(optnamestr, ra_opts[i].opt_name) == 0)
2414 			break;
2415 	}
2416 	if (ra_opts[i].opt_name == NULL)
2417 		return (NULL);
2418 	return (&ra_opts[i]);
2419 }
2420 
2421 /*
2422  * Reset all option values previously gathered to B_FALSE.
2423  */
2424 static void
2425 ra_resetopts(void)
2426 {
2427 	int	i;
2428 
2429 	for (i = 0; ra_opts[i].opt_name != NULL; i++) {
2430 		ra_opts[i].opt_enabled = B_FALSE;
2431 		ra_opts[i].opt_default_enabled = B_FALSE;
2432 	}
2433 }
2434 
2435 static ravar_t *
2436 ra_str2var(const char *varnamestr)
2437 {
2438 	int	i;
2439 	for (i = 0; ra_vars[i].var_name != NULL; i++) {
2440 		if (strcmp(varnamestr, ra_vars[i].var_name) == 0)
2441 			break;
2442 	}
2443 	if (ra_vars[i].var_name == NULL)
2444 		return (NULL);
2445 	return (&ra_vars[i]);
2446 }
2447 
2448 /*
2449  * Reset variable values previously gathered to NULL.
2450  */
2451 static void
2452 ra_resetvars(const char *proto)
2453 {
2454 	int	i;
2455 	for (i = 0; ra_vars[i].var_name != NULL; i++) {
2456 		if (proto != NULL &&
2457 		    !VAR_PROTO_MATCH(ra_vars[i].var_name, proto))
2458 			continue;
2459 		if (ra_vars[i].var_value != NULL)
2460 			free(ra_vars[i].var_value);
2461 		ra_vars[i].var_value = NULL;
2462 		if (ra_vars[i].var_default_value != NULL)
2463 			free(ra_vars[i].var_default_value);
2464 		ra_vars[i].var_default_value = NULL;
2465 	}
2466 }
2467 
2468 /*
2469  * Given an option name, this function provides an internationalized, human
2470  * readable version of the option name.
2471  */
2472 static char *
2473 ra_intloptname(const char *optname)
2474 {
2475 	if (strcmp(optname, RA_OPT_IPV4_FORWARDING) == 0)
2476 		return (gettext("IPv4 forwarding"));
2477 	else if (strcmp(optname, RA_OPT_IPV4_ROUTING) == 0)
2478 		return (gettext("IPv4 routing"));
2479 	else if (strcmp(optname, RA_OPT_IPV6_FORWARDING) == 0)
2480 		return (gettext("IPv6 forwarding"));
2481 	else if (strcmp(optname, RA_OPT_IPV6_ROUTING) == 0)
2482 		return (gettext("IPv6 routing"));
2483 	else if (strcmp(optname, RA_VAR_IPV4_ROUTING_DAEMON) == 0)
2484 		return (gettext("IPv4 routing daemon"));
2485 	else if (strcmp(optname, RA_VAR_IPV4_ROUTING_DAEMON_ARGS) == 0)
2486 		return (gettext("IPv4 routing daemon args"));
2487 	else if (strcmp(optname, RA_VAR_IPV4_ROUTING_STOP_CMD) == 0)
2488 		return (gettext("IPv4 routing daemon stop"));
2489 	else if (strcmp(optname, RA_VAR_IPV6_ROUTING_DAEMON) == 0)
2490 		return (gettext("IPv6 routing daemon"));
2491 	else if (strcmp(optname, RA_VAR_IPV6_ROUTING_DAEMON_ARGS) == 0)
2492 		return (gettext("IPv6 routing daemon args"));
2493 	else if (strcmp(optname, RA_VAR_IPV6_ROUTING_STOP_CMD) == 0)
2494 		return (gettext("IPv6 routing daemon stop"));
2495 	else if (strcmp(optname, RA_VAR_ROUTING_SVCS) == 0)
2496 		return (gettext("Routing services"));
2497 	/*
2498 	 * If we get here, there's a bug and someone should trip over this
2499 	 * NULL pointer.
2500 	 */
2501 	return (NULL);
2502 }
2503