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