17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 54045d941Ssowmini * Common Development and Distribution License (the "License"). 64045d941Ssowmini * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22*a924b63fSVasumathi Sundaram - Sun Microsystems * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate /* 277c478bd9Sstevel@tonic-gate * Copyright (c) 1990 Mentat Inc. 287c478bd9Sstevel@tonic-gate * ndd.c 2.1, last change 11/14/90 297c478bd9Sstevel@tonic-gate */ 307c478bd9Sstevel@tonic-gate 317c478bd9Sstevel@tonic-gate #include <stdio.h> 327c478bd9Sstevel@tonic-gate #include <errno.h> 337c478bd9Sstevel@tonic-gate #include <ctype.h> 347c478bd9Sstevel@tonic-gate #include <stdarg.h> 357c478bd9Sstevel@tonic-gate #include <fcntl.h> 367c478bd9Sstevel@tonic-gate #include <unistd.h> 377c478bd9Sstevel@tonic-gate #include <sys/types.h> 387c478bd9Sstevel@tonic-gate #include <stropts.h> 397c478bd9Sstevel@tonic-gate #include <inet/nd.h> 407c478bd9Sstevel@tonic-gate #include <string.h> 417c478bd9Sstevel@tonic-gate #include <stdlib.h> 424045d941Ssowmini #include <libdllink.h> 434045d941Ssowmini #include <libintl.h> 447c478bd9Sstevel@tonic-gate 457c478bd9Sstevel@tonic-gate static boolean_t do_getset(int fd, int cmd, char *buf, int buf_len); 467c478bd9Sstevel@tonic-gate static int get_value(char *msg, char *buf, int buf_len); 477c478bd9Sstevel@tonic-gate static void name_print(char *buf); 487c478bd9Sstevel@tonic-gate static void getset_interactive(int fd); 49*a924b63fSVasumathi Sundaram - Sun Microsystems static int open_device(void); 507c478bd9Sstevel@tonic-gate static char *errmsg(int err); 517c478bd9Sstevel@tonic-gate static void fatal(char *fmt, ...); 527c478bd9Sstevel@tonic-gate static void printe(boolean_t print_errno, char *fmt, ...); 537c478bd9Sstevel@tonic-gate 547c478bd9Sstevel@tonic-gate static char gbuf[65536]; /* Need 20k for 160 IREs ... */ 557c478bd9Sstevel@tonic-gate static char usage_str[] = "usage: ndd -set device_name name value\n" 567c478bd9Sstevel@tonic-gate " ndd [-get] device_name name [name ...]"; 577c478bd9Sstevel@tonic-gate 58149b7eb2SSowmini Varadhan /* 59149b7eb2SSowmini Varadhan * gldv3_warning() catches the case of /sbin/ndd abuse to administer 60149b7eb2SSowmini Varadhan * ethernet/MII props. Note that /sbin/ndd has not been abused 61149b7eb2SSowmini Varadhan * for administration of other datalink types, which makes it permissible 62149b7eb2SSowmini Varadhan * to test for support of the flowctrl property. 63149b7eb2SSowmini Varadhan */ 644045d941Ssowmini static void 65*a924b63fSVasumathi Sundaram - Sun Microsystems gldv3_warning(char *module) 664045d941Ssowmini { 674045d941Ssowmini datalink_id_t linkid; 684045d941Ssowmini dladm_status_t status; 694045d941Ssowmini char buf[DLADM_PROP_VAL_MAX], *cp; 70149b7eb2SSowmini Varadhan uint_t cnt = 1; 714045d941Ssowmini char *link; 72*a924b63fSVasumathi Sundaram - Sun Microsystems dladm_handle_t handle; 734045d941Ssowmini 744045d941Ssowmini link = strrchr(module, '/'); 754045d941Ssowmini if (link == NULL) 764045d941Ssowmini return; 77*a924b63fSVasumathi Sundaram - Sun Microsystems 78*a924b63fSVasumathi Sundaram - Sun Microsystems if (dladm_open(&handle) != DLADM_STATUS_OK) 79*a924b63fSVasumathi Sundaram - Sun Microsystems return; 80*a924b63fSVasumathi Sundaram - Sun Microsystems 814ac67f02SAnurag S. Maskey status = dladm_name2info(handle, ++link, &linkid, NULL, NULL, NULL); 82*a924b63fSVasumathi Sundaram - Sun Microsystems if (status == DLADM_STATUS_OK) { 834045d941Ssowmini cp = buf; 84*a924b63fSVasumathi Sundaram - Sun Microsystems status = dladm_get_linkprop(handle, linkid, 85*a924b63fSVasumathi Sundaram - Sun Microsystems DLADM_PROP_VAL_CURRENT, "flowctrl", &cp, &cnt); 86*a924b63fSVasumathi Sundaram - Sun Microsystems if (status == DLADM_STATUS_OK) { 874045d941Ssowmini (void) fprintf(stderr, gettext( 88*a924b63fSVasumathi Sundaram - Sun Microsystems "WARNING: The ndd commands for datalink " 89*a924b63fSVasumathi Sundaram - Sun Microsystems "administration are obsolete and may be " 90*a924b63fSVasumathi Sundaram - Sun Microsystems "removed in a future release of Solaris. " 91*a924b63fSVasumathi Sundaram - Sun Microsystems "Use dladm(1M) to manage datalink tunables.\n")); 92*a924b63fSVasumathi Sundaram - Sun Microsystems } 93*a924b63fSVasumathi Sundaram - Sun Microsystems } 94*a924b63fSVasumathi Sundaram - Sun Microsystems dladm_close(handle); 954045d941Ssowmini } 964045d941Ssowmini 977c478bd9Sstevel@tonic-gate /* ARGSUSED */ 987c478bd9Sstevel@tonic-gate int 997c478bd9Sstevel@tonic-gate main(int argc, char **argv) 1007c478bd9Sstevel@tonic-gate { 1017c478bd9Sstevel@tonic-gate char *cp, *value; 1027c478bd9Sstevel@tonic-gate int cmd; 1037c478bd9Sstevel@tonic-gate int fd; 1047c478bd9Sstevel@tonic-gate 1054045d941Ssowmini 1067c478bd9Sstevel@tonic-gate if (!(cp = *++argv)) { 107*a924b63fSVasumathi Sundaram - Sun Microsystems while ((fd = open_device()) != -1) { 1087c478bd9Sstevel@tonic-gate getset_interactive(fd); 1097c478bd9Sstevel@tonic-gate (void) close(fd); 1107c478bd9Sstevel@tonic-gate } 1117c478bd9Sstevel@tonic-gate return (EXIT_SUCCESS); 1127c478bd9Sstevel@tonic-gate } 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate cmd = ND_GET; 1157c478bd9Sstevel@tonic-gate if (cp[0] == '-') { 1167c478bd9Sstevel@tonic-gate if (strncmp(&cp[1], "set", 3) == 0) 1177c478bd9Sstevel@tonic-gate cmd = ND_SET; 1187c478bd9Sstevel@tonic-gate else if (strncmp(&cp[1], "get", 3) != 0) 1197c478bd9Sstevel@tonic-gate fatal(usage_str); 1207c478bd9Sstevel@tonic-gate if (!(cp = *++argv)) 1217c478bd9Sstevel@tonic-gate fatal(usage_str); 1227c478bd9Sstevel@tonic-gate } 123*a924b63fSVasumathi Sundaram - Sun Microsystems gldv3_warning(cp); 1244ac67f02SAnurag S. Maskey 1257c478bd9Sstevel@tonic-gate if ((fd = open(cp, O_RDWR)) == -1) 1267c478bd9Sstevel@tonic-gate fatal("open of %s failed: %s", cp, errmsg(errno)); 1277c478bd9Sstevel@tonic-gate 1287c478bd9Sstevel@tonic-gate if (!isastream(fd)) 1297c478bd9Sstevel@tonic-gate fatal("%s is not a streams device", cp); 1307c478bd9Sstevel@tonic-gate 1317c478bd9Sstevel@tonic-gate if (!(cp = *++argv)) { 1327c478bd9Sstevel@tonic-gate getset_interactive(fd); 1337c478bd9Sstevel@tonic-gate (void) close(fd); 1347c478bd9Sstevel@tonic-gate return (EXIT_SUCCESS); 1357c478bd9Sstevel@tonic-gate } 1367c478bd9Sstevel@tonic-gate 1377c478bd9Sstevel@tonic-gate if (cmd == ND_SET) { 1387c478bd9Sstevel@tonic-gate if (!(value = *++argv)) 1397c478bd9Sstevel@tonic-gate fatal(usage_str); 1407c478bd9Sstevel@tonic-gate (void) snprintf(gbuf, sizeof (gbuf), "%s%c%s%c", cp, '\0', 1417c478bd9Sstevel@tonic-gate value, '\0'); 1427c478bd9Sstevel@tonic-gate if (!do_getset(fd, cmd, gbuf, sizeof (gbuf))) 1437c478bd9Sstevel@tonic-gate return (EXIT_FAILURE); 1447c478bd9Sstevel@tonic-gate } else { 1457c478bd9Sstevel@tonic-gate do { 1467c478bd9Sstevel@tonic-gate (void) memset(gbuf, '\0', sizeof (gbuf)); 1477c478bd9Sstevel@tonic-gate (void) strlcpy(gbuf, cp, sizeof (gbuf)); 1487c478bd9Sstevel@tonic-gate if (!do_getset(fd, cmd, gbuf, sizeof (gbuf))) 1497c478bd9Sstevel@tonic-gate return (EXIT_FAILURE); 1507c478bd9Sstevel@tonic-gate if (cp = *++argv) 1517c478bd9Sstevel@tonic-gate (void) putchar('\n'); 1527c478bd9Sstevel@tonic-gate } while (cp); 1537c478bd9Sstevel@tonic-gate } 1547c478bd9Sstevel@tonic-gate 1557c478bd9Sstevel@tonic-gate (void) close(fd); 1567c478bd9Sstevel@tonic-gate return (EXIT_SUCCESS); 1577c478bd9Sstevel@tonic-gate } 1587c478bd9Sstevel@tonic-gate 1597c478bd9Sstevel@tonic-gate static void 1607c478bd9Sstevel@tonic-gate name_print(char *buf) 1617c478bd9Sstevel@tonic-gate { 1627c478bd9Sstevel@tonic-gate char *cp, *rwtag; 1637c478bd9Sstevel@tonic-gate 1647c478bd9Sstevel@tonic-gate for (cp = buf; cp[0]; ) { 1657c478bd9Sstevel@tonic-gate for (rwtag = cp; !isspace(*rwtag); rwtag++) 1667c478bd9Sstevel@tonic-gate ; 1677c478bd9Sstevel@tonic-gate *rwtag++ = '\0'; 1687c478bd9Sstevel@tonic-gate while (isspace(*rwtag)) 1697c478bd9Sstevel@tonic-gate rwtag++; 1707c478bd9Sstevel@tonic-gate (void) printf("%-30s%s\n", cp, rwtag); 1717c478bd9Sstevel@tonic-gate for (cp = rwtag; *cp++; ) 1727c478bd9Sstevel@tonic-gate ; 1737c478bd9Sstevel@tonic-gate } 1747c478bd9Sstevel@tonic-gate } 1757c478bd9Sstevel@tonic-gate 1767c478bd9Sstevel@tonic-gate /* 1777c478bd9Sstevel@tonic-gate * This function is vile, but it's better here than in the kernel. 1787c478bd9Sstevel@tonic-gate */ 1797c478bd9Sstevel@tonic-gate static boolean_t 1807c478bd9Sstevel@tonic-gate is_obsolete(const char *param) 1817c478bd9Sstevel@tonic-gate { 1827c478bd9Sstevel@tonic-gate if (strcmp(param, "ip_enable_group_ifs") == 0 || 1837c478bd9Sstevel@tonic-gate strcmp(param, "ifgrp_status") == 0) { 1847c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "The \"%s\" tunable has been superseded " 1857c478bd9Sstevel@tonic-gate "by IP Multipathing.\nPlease see the IP Network " 1867c478bd9Sstevel@tonic-gate "Multipathing Administration Guide for details.\n", param); 1877c478bd9Sstevel@tonic-gate return (B_TRUE); 1887c478bd9Sstevel@tonic-gate } 1897c478bd9Sstevel@tonic-gate return (B_FALSE); 1907c478bd9Sstevel@tonic-gate } 1917c478bd9Sstevel@tonic-gate 1927c478bd9Sstevel@tonic-gate static boolean_t 1937c478bd9Sstevel@tonic-gate do_getset(int fd, int cmd, char *buf, int buf_len) 1947c478bd9Sstevel@tonic-gate { 1957c478bd9Sstevel@tonic-gate char *cp; 1967c478bd9Sstevel@tonic-gate struct strioctl stri; 1977c478bd9Sstevel@tonic-gate boolean_t is_name_get; 1987c478bd9Sstevel@tonic-gate 1997c478bd9Sstevel@tonic-gate if (is_obsolete(buf)) 2007c478bd9Sstevel@tonic-gate return (B_TRUE); 2017c478bd9Sstevel@tonic-gate 2027c478bd9Sstevel@tonic-gate stri.ic_cmd = cmd; 2037c478bd9Sstevel@tonic-gate stri.ic_timout = 0; 2047c478bd9Sstevel@tonic-gate stri.ic_len = buf_len; 2057c478bd9Sstevel@tonic-gate stri.ic_dp = buf; 2067c478bd9Sstevel@tonic-gate is_name_get = stri.ic_cmd == ND_GET && buf[0] == '?' && buf[1] == '\0'; 2077c478bd9Sstevel@tonic-gate 2087c478bd9Sstevel@tonic-gate if (ioctl(fd, I_STR, &stri) == -1) { 2097c478bd9Sstevel@tonic-gate if (errno == ENOENT) 2107c478bd9Sstevel@tonic-gate (void) printf("name is non-existent for this module\n" 2117c478bd9Sstevel@tonic-gate "for a list of valid names, use name '?'\n"); 2127c478bd9Sstevel@tonic-gate else 2137c478bd9Sstevel@tonic-gate (void) printf("operation failed: %s\n", errmsg(errno)); 2147c478bd9Sstevel@tonic-gate return (B_FALSE); 2157c478bd9Sstevel@tonic-gate } 2167c478bd9Sstevel@tonic-gate if (is_name_get) 2177c478bd9Sstevel@tonic-gate name_print(buf); 2187c478bd9Sstevel@tonic-gate else if (stri.ic_cmd == ND_GET) { 2197c478bd9Sstevel@tonic-gate for (cp = buf; *cp != '\0'; cp += strlen(cp) + 1) 2207c478bd9Sstevel@tonic-gate (void) puts(cp); 2217c478bd9Sstevel@tonic-gate } 2227c478bd9Sstevel@tonic-gate (void) fflush(stdout); 2237c478bd9Sstevel@tonic-gate return (B_TRUE); 2247c478bd9Sstevel@tonic-gate } 2257c478bd9Sstevel@tonic-gate 2267c478bd9Sstevel@tonic-gate static int 2277c478bd9Sstevel@tonic-gate get_value(char *msg, char *buf, int buf_len) 2287c478bd9Sstevel@tonic-gate { 2297c478bd9Sstevel@tonic-gate int len; 2307c478bd9Sstevel@tonic-gate 2317c478bd9Sstevel@tonic-gate (void) printf("%s", msg); 2327c478bd9Sstevel@tonic-gate (void) fflush(stdout); 2337c478bd9Sstevel@tonic-gate 2347c478bd9Sstevel@tonic-gate buf[buf_len-1] = '\0'; 2357c478bd9Sstevel@tonic-gate if (fgets(buf, buf_len-1, stdin) == NULL) 2367c478bd9Sstevel@tonic-gate exit(EXIT_SUCCESS); 2377c478bd9Sstevel@tonic-gate len = strlen(buf); 2387c478bd9Sstevel@tonic-gate if (buf[len-1] == '\n') 2397c478bd9Sstevel@tonic-gate buf[len - 1] = '\0'; 2407c478bd9Sstevel@tonic-gate else 2417c478bd9Sstevel@tonic-gate len++; 2427c478bd9Sstevel@tonic-gate return (len); 2437c478bd9Sstevel@tonic-gate } 2447c478bd9Sstevel@tonic-gate 2457c478bd9Sstevel@tonic-gate static void 2467c478bd9Sstevel@tonic-gate getset_interactive(int fd) 2477c478bd9Sstevel@tonic-gate { 2487c478bd9Sstevel@tonic-gate int cmd; 2497c478bd9Sstevel@tonic-gate char *cp; 2507c478bd9Sstevel@tonic-gate int len, buf_len; 2517c478bd9Sstevel@tonic-gate char len_buf[10]; 2527c478bd9Sstevel@tonic-gate 2537c478bd9Sstevel@tonic-gate for (;;) { 2547c478bd9Sstevel@tonic-gate (void) memset(gbuf, '\0', sizeof (gbuf)); 2557c478bd9Sstevel@tonic-gate len = get_value("name to get/set ? ", gbuf, sizeof (gbuf)); 2567c478bd9Sstevel@tonic-gate if (len == 1 || (gbuf[0] == 'q' && gbuf[1] == '\0')) 2577c478bd9Sstevel@tonic-gate return; 2587c478bd9Sstevel@tonic-gate for (cp = gbuf; cp < &gbuf[len]; cp++) { 2597c478bd9Sstevel@tonic-gate if (isspace(*cp)) 2607c478bd9Sstevel@tonic-gate *cp = '\0'; 2617c478bd9Sstevel@tonic-gate } 2627c478bd9Sstevel@tonic-gate cmd = ND_GET; 2637c478bd9Sstevel@tonic-gate if (gbuf[0] != '?' && 2647c478bd9Sstevel@tonic-gate get_value("value ? ", &gbuf[len], sizeof (gbuf) - len) > 1) 2657c478bd9Sstevel@tonic-gate cmd = ND_SET; 2667c478bd9Sstevel@tonic-gate if (cmd == ND_GET && gbuf[0] != '?' && 2677c478bd9Sstevel@tonic-gate get_value("length ? ", len_buf, sizeof (len_buf)) > 1) { 2687c478bd9Sstevel@tonic-gate if (!isdigit(len_buf[0])) { 2697c478bd9Sstevel@tonic-gate (void) printf("invalid length\n"); 2707c478bd9Sstevel@tonic-gate continue; 2717c478bd9Sstevel@tonic-gate } 2727c478bd9Sstevel@tonic-gate buf_len = atoi(len_buf); 2737c478bd9Sstevel@tonic-gate } else 2747c478bd9Sstevel@tonic-gate buf_len = sizeof (gbuf); 2757c478bd9Sstevel@tonic-gate (void) do_getset(fd, cmd, gbuf, buf_len); 2767c478bd9Sstevel@tonic-gate } 2777c478bd9Sstevel@tonic-gate } 2787c478bd9Sstevel@tonic-gate 2797c478bd9Sstevel@tonic-gate static void 2807c478bd9Sstevel@tonic-gate printe(boolean_t print_errno, char *fmt, ...) 2817c478bd9Sstevel@tonic-gate { 2827c478bd9Sstevel@tonic-gate va_list ap; 2837c478bd9Sstevel@tonic-gate int error = errno; 2847c478bd9Sstevel@tonic-gate 2857c478bd9Sstevel@tonic-gate va_start(ap, fmt); 2867c478bd9Sstevel@tonic-gate (void) printf("*ERROR* "); 2877c478bd9Sstevel@tonic-gate (void) vprintf(fmt, ap); 2887c478bd9Sstevel@tonic-gate va_end(ap); 2897c478bd9Sstevel@tonic-gate 2907c478bd9Sstevel@tonic-gate if (print_errno) 2917c478bd9Sstevel@tonic-gate (void) printf(": %s\n", errmsg(error)); 2927c478bd9Sstevel@tonic-gate else 2937c478bd9Sstevel@tonic-gate (void) printf("\n"); 2947c478bd9Sstevel@tonic-gate } 2957c478bd9Sstevel@tonic-gate 2964045d941Ssowmini 2977c478bd9Sstevel@tonic-gate static int 298*a924b63fSVasumathi Sundaram - Sun Microsystems open_device() 2997c478bd9Sstevel@tonic-gate { 3007c478bd9Sstevel@tonic-gate char name[80]; 3017c478bd9Sstevel@tonic-gate int fd, len; 3027c478bd9Sstevel@tonic-gate 3037c478bd9Sstevel@tonic-gate for (;;) { 3047c478bd9Sstevel@tonic-gate len = get_value("module to query ? ", name, sizeof (name)); 3057c478bd9Sstevel@tonic-gate if (len <= 1 || 3067c478bd9Sstevel@tonic-gate (len == 2 && (name[0] == 'q' || name[0] == 'Q'))) 3077c478bd9Sstevel@tonic-gate return (-1); 3087c478bd9Sstevel@tonic-gate 3097c478bd9Sstevel@tonic-gate if ((fd = open(name, O_RDWR)) == -1) { 3107c478bd9Sstevel@tonic-gate printe(B_TRUE, "open of %s failed", name); 3117c478bd9Sstevel@tonic-gate continue; 3127c478bd9Sstevel@tonic-gate } 3137c478bd9Sstevel@tonic-gate 314*a924b63fSVasumathi Sundaram - Sun Microsystems gldv3_warning(name); 3154045d941Ssowmini 3167c478bd9Sstevel@tonic-gate if (isastream(fd)) 3177c478bd9Sstevel@tonic-gate return (fd); 3187c478bd9Sstevel@tonic-gate 3197c478bd9Sstevel@tonic-gate (void) close(fd); 3207c478bd9Sstevel@tonic-gate printe(B_FALSE, "%s is not a streams device", name); 3217c478bd9Sstevel@tonic-gate } 3227c478bd9Sstevel@tonic-gate } 3237c478bd9Sstevel@tonic-gate 3247c478bd9Sstevel@tonic-gate static void 3257c478bd9Sstevel@tonic-gate fatal(char *fmt, ...) 3267c478bd9Sstevel@tonic-gate { 3277c478bd9Sstevel@tonic-gate va_list ap; 3287c478bd9Sstevel@tonic-gate 3297c478bd9Sstevel@tonic-gate va_start(ap, fmt); 3307c478bd9Sstevel@tonic-gate (void) vfprintf(stderr, fmt, ap); 3317c478bd9Sstevel@tonic-gate va_end(ap); 3327c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "\n"); 3337c478bd9Sstevel@tonic-gate 3347c478bd9Sstevel@tonic-gate exit(EXIT_FAILURE); 3357c478bd9Sstevel@tonic-gate } 3367c478bd9Sstevel@tonic-gate 3377c478bd9Sstevel@tonic-gate static char * 3387c478bd9Sstevel@tonic-gate errmsg(int error) 3397c478bd9Sstevel@tonic-gate { 3407c478bd9Sstevel@tonic-gate char *msg = strerror(error); 3417c478bd9Sstevel@tonic-gate 3427c478bd9Sstevel@tonic-gate return (msg != NULL ? msg : "unknown error"); 3437c478bd9Sstevel@tonic-gate } 344