xref: /illumos-gate/usr/src/cmd/cmd-inet/usr.sbin/ndd.c (revision a924b63fa966be82bd67880ed760a9532ca55939)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Copyright (c) 1990  Mentat Inc.
28  * ndd.c 2.1, last change 11/14/90
29  */
30 
31 #include <stdio.h>
32 #include <errno.h>
33 #include <ctype.h>
34 #include <stdarg.h>
35 #include <fcntl.h>
36 #include <unistd.h>
37 #include <sys/types.h>
38 #include <stropts.h>
39 #include <inet/nd.h>
40 #include <string.h>
41 #include <stdlib.h>
42 #include <libdllink.h>
43 #include <libintl.h>
44 
45 static boolean_t do_getset(int fd, int cmd, char *buf, int buf_len);
46 static int	get_value(char *msg, char *buf, int buf_len);
47 static void	name_print(char *buf);
48 static void	getset_interactive(int fd);
49 static int	open_device(void);
50 static char	*errmsg(int err);
51 static void	fatal(char *fmt, ...);
52 static void	printe(boolean_t print_errno, char *fmt, ...);
53 
54 static char	gbuf[65536];	/* Need 20k for 160 IREs ... */
55 static char	usage_str[] =	"usage: ndd -set device_name name value\n"
56 				"       ndd [-get] device_name name [name ...]";
57 
58 /*
59  * gldv3_warning() catches the case of /sbin/ndd abuse to administer
60  * ethernet/MII props. Note that /sbin/ndd has not been abused
61  * for administration of other datalink types, which makes it permissible
62  * to test for support of the flowctrl property.
63  */
64 static void
65 gldv3_warning(char *module)
66 {
67 	datalink_id_t	linkid;
68 	dladm_status_t	status;
69 	char		buf[DLADM_PROP_VAL_MAX], *cp;
70 	uint_t		cnt = 1;
71 	char		*link;
72 	dladm_handle_t	handle;
73 
74 	link = strrchr(module, '/');
75 	if (link == NULL)
76 		return;
77 
78 	if (dladm_open(&handle) != DLADM_STATUS_OK)
79 		return;
80 
81 	status = dladm_name2info(handle, ++link, &linkid, NULL, NULL, NULL);
82 	if (status == DLADM_STATUS_OK) {
83 		cp = buf;
84 		status = dladm_get_linkprop(handle, linkid,
85 		    DLADM_PROP_VAL_CURRENT, "flowctrl", &cp, &cnt);
86 		if (status == DLADM_STATUS_OK) {
87 			(void) fprintf(stderr, gettext(
88 			    "WARNING: The ndd commands for datalink "
89 			    "administration are obsolete and may be "
90 			    "removed in a future release of Solaris. "
91 			    "Use dladm(1M) to manage datalink tunables.\n"));
92 		}
93 	}
94 	dladm_close(handle);
95 }
96 
97 /* ARGSUSED */
98 int
99 main(int argc, char **argv)
100 {
101 	char	*cp, *value;
102 	int	cmd;
103 	int	fd;
104 
105 
106 	if (!(cp = *++argv)) {
107 		while ((fd = open_device()) != -1) {
108 			getset_interactive(fd);
109 			(void) close(fd);
110 		}
111 		return (EXIT_SUCCESS);
112 	}
113 
114 	cmd = ND_GET;
115 	if (cp[0] == '-') {
116 		if (strncmp(&cp[1], "set", 3) == 0)
117 			cmd = ND_SET;
118 		else if (strncmp(&cp[1], "get", 3) != 0)
119 			fatal(usage_str);
120 		if (!(cp = *++argv))
121 			fatal(usage_str);
122 	}
123 	gldv3_warning(cp);
124 
125 	if ((fd = open(cp, O_RDWR)) == -1)
126 		fatal("open of %s failed: %s", cp, errmsg(errno));
127 
128 	if (!isastream(fd))
129 		fatal("%s is not a streams device", cp);
130 
131 	if (!(cp = *++argv)) {
132 		getset_interactive(fd);
133 		(void) close(fd);
134 		return (EXIT_SUCCESS);
135 	}
136 
137 	if (cmd == ND_SET) {
138 		if (!(value = *++argv))
139 			fatal(usage_str);
140 		(void) snprintf(gbuf, sizeof (gbuf), "%s%c%s%c", cp, '\0',
141 		    value, '\0');
142 		if (!do_getset(fd, cmd, gbuf, sizeof (gbuf)))
143 			return (EXIT_FAILURE);
144 	} else {
145 		do {
146 			(void) memset(gbuf, '\0', sizeof (gbuf));
147 			(void) strlcpy(gbuf, cp, sizeof (gbuf));
148 			if (!do_getset(fd, cmd, gbuf, sizeof (gbuf)))
149 				return (EXIT_FAILURE);
150 			if (cp = *++argv)
151 				(void) putchar('\n');
152 		} while (cp);
153 	}
154 
155 	(void) close(fd);
156 	return (EXIT_SUCCESS);
157 }
158 
159 static void
160 name_print(char *buf)
161 {
162 	char *cp, *rwtag;
163 
164 	for (cp = buf; cp[0]; ) {
165 		for (rwtag = cp; !isspace(*rwtag); rwtag++)
166 			;
167 		*rwtag++ = '\0';
168 		while (isspace(*rwtag))
169 			rwtag++;
170 		(void) printf("%-30s%s\n", cp, rwtag);
171 		for (cp = rwtag; *cp++; )
172 			;
173 	}
174 }
175 
176 /*
177  * This function is vile, but it's better here than in the kernel.
178  */
179 static boolean_t
180 is_obsolete(const char *param)
181 {
182 	if (strcmp(param, "ip_enable_group_ifs") == 0 ||
183 	    strcmp(param, "ifgrp_status") == 0) {
184 		(void) fprintf(stderr, "The \"%s\" tunable has been superseded "
185 		    "by IP Multipathing.\nPlease see the IP Network "
186 		    "Multipathing Administration Guide for details.\n", param);
187 		return (B_TRUE);
188 	}
189 	return (B_FALSE);
190 }
191 
192 static boolean_t
193 do_getset(int fd, int cmd, char *buf, int buf_len)
194 {
195 	char	*cp;
196 	struct strioctl	stri;
197 	boolean_t	is_name_get;
198 
199 	if (is_obsolete(buf))
200 		return (B_TRUE);
201 
202 	stri.ic_cmd = cmd;
203 	stri.ic_timout = 0;
204 	stri.ic_len = buf_len;
205 	stri.ic_dp = buf;
206 	is_name_get = stri.ic_cmd == ND_GET && buf[0] == '?' && buf[1] == '\0';
207 
208 	if (ioctl(fd, I_STR, &stri) == -1) {
209 		if (errno == ENOENT)
210 			(void) printf("name is non-existent for this module\n"
211 			    "for a list of valid names, use name '?'\n");
212 		else
213 			(void) printf("operation failed: %s\n", errmsg(errno));
214 		return (B_FALSE);
215 	}
216 	if (is_name_get)
217 		name_print(buf);
218 	else if (stri.ic_cmd == ND_GET) {
219 		for (cp = buf; *cp != '\0'; cp += strlen(cp) + 1)
220 			(void) puts(cp);
221 	}
222 	(void) fflush(stdout);
223 	return (B_TRUE);
224 }
225 
226 static int
227 get_value(char *msg, char *buf, int buf_len)
228 {
229 	int	len;
230 
231 	(void) printf("%s", msg);
232 	(void) fflush(stdout);
233 
234 	buf[buf_len-1] = '\0';
235 	if (fgets(buf, buf_len-1, stdin) == NULL)
236 		exit(EXIT_SUCCESS);
237 	len = strlen(buf);
238 	if (buf[len-1] == '\n')
239 		buf[len - 1] = '\0';
240 	else
241 		len++;
242 	return (len);
243 }
244 
245 static void
246 getset_interactive(int fd)
247 {
248 	int	cmd;
249 	char	*cp;
250 	int	len, buf_len;
251 	char	len_buf[10];
252 
253 	for (;;) {
254 		(void) memset(gbuf, '\0', sizeof (gbuf));
255 		len = get_value("name to get/set ? ", gbuf, sizeof (gbuf));
256 		if (len == 1 || (gbuf[0] == 'q' && gbuf[1] == '\0'))
257 			return;
258 		for (cp = gbuf; cp < &gbuf[len]; cp++) {
259 			if (isspace(*cp))
260 				*cp = '\0';
261 		}
262 		cmd = ND_GET;
263 		if (gbuf[0] != '?' &&
264 		    get_value("value ? ", &gbuf[len], sizeof (gbuf) - len) > 1)
265 			cmd = ND_SET;
266 		if (cmd == ND_GET && gbuf[0] != '?' &&
267 		    get_value("length ? ", len_buf, sizeof (len_buf)) > 1) {
268 			if (!isdigit(len_buf[0])) {
269 				(void) printf("invalid length\n");
270 				continue;
271 			}
272 			buf_len = atoi(len_buf);
273 		} else
274 			buf_len = sizeof (gbuf);
275 		(void) do_getset(fd, cmd, gbuf, buf_len);
276 	}
277 }
278 
279 static void
280 printe(boolean_t print_errno, char *fmt, ...)
281 {
282 	va_list	ap;
283 	int error = errno;
284 
285 	va_start(ap, fmt);
286 	(void) printf("*ERROR* ");
287 	(void) vprintf(fmt, ap);
288 	va_end(ap);
289 
290 	if (print_errno)
291 		(void) printf(": %s\n", errmsg(error));
292 	else
293 		(void) printf("\n");
294 }
295 
296 
297 static int
298 open_device()
299 {
300 	char	name[80];
301 	int	fd, len;
302 
303 	for (;;) {
304 		len = get_value("module to query ? ", name, sizeof (name));
305 		if (len <= 1 ||
306 		    (len == 2 && (name[0] == 'q' || name[0] == 'Q')))
307 			return (-1);
308 
309 		if ((fd = open(name, O_RDWR)) == -1) {
310 			printe(B_TRUE, "open of %s failed", name);
311 			continue;
312 		}
313 
314 		gldv3_warning(name);
315 
316 		if (isastream(fd))
317 			return (fd);
318 
319 		(void) close(fd);
320 		printe(B_FALSE, "%s is not a streams device", name);
321 	}
322 }
323 
324 static void
325 fatal(char *fmt, ...)
326 {
327 	va_list	ap;
328 
329 	va_start(ap, fmt);
330 	(void) vfprintf(stderr, fmt, ap);
331 	va_end(ap);
332 	(void) fprintf(stderr, "\n");
333 
334 	exit(EXIT_FAILURE);
335 }
336 
337 static char *
338 errmsg(int error)
339 {
340 	char *msg = strerror(error);
341 
342 	return (msg != NULL ? msg : "unknown error");
343 }
344