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