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