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