1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright (c) 2017, Joyent, Inc.
14 */
15
16 /*
17 * Private utility to get and set LED information on NICs. This should really
18 * all be integrated into FM. Until we have figured out that plumbing, this
19 * allows us to have a little something that we can use to drive work.
20 */
21
22 #include <unistd.h>
23 #include <stdio.h>
24 #include <stdarg.h>
25 #include <libgen.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <strings.h>
30
31 #include <libdladm.h>
32 #include <libdllink.h>
33 #include <sys/mac.h>
34 #include <sys/dld.h>
35 #include <sys/dld_ioc.h>
36
37 static const char *dlled_progname;
38 static dladm_handle_t dlled_hdl;
39 static char dlled_dlerrmsg[DLADM_STRSIZE];
40
41 typedef struct dlled_led_map {
42 const char *dlm_name;
43 mac_led_mode_t dlm_bits;
44 } dlled_led_map_t;
45
46 static dlled_led_map_t dlled_map[] = {
47 { "default", MAC_LED_DEFAULT },
48 { "off", MAC_LED_OFF },
49 { "on", MAC_LED_ON },
50 { "ident", MAC_LED_IDENT }
51 };
52
53 #define DLLED_MAP_NENTRIES \
54 (sizeof (dlled_map) / sizeof (dlled_led_map_t))
55
56 static void
dlled_usage(const char * fmt,...)57 dlled_usage(const char *fmt, ...)
58 {
59 if (fmt != NULL) {
60 va_list ap;
61
62 (void) fprintf(stderr, "%s: ", dlled_progname);
63 va_start(ap, fmt);
64 (void) vfprintf(stderr, fmt, ap);
65 va_end(ap);
66 }
67
68 (void) fprintf(stderr, "Usage: %s [-s mode] [link]\n"
69 "\n"
70 "\t-s mode set LED to mode\n",
71 dlled_progname);
72 }
73
74 static mac_led_mode_t
dlled_parse_mode(const char * orig)75 dlled_parse_mode(const char *orig)
76 {
77 char *mode;
78 char *part;
79 mac_led_mode_t m = 0;
80
81 mode = strdup(orig);
82 if (mode == NULL) {
83 fprintf(stderr, "failed to allocate memory to dup led "
84 "mode: %s\n", strerror(errno));
85 exit(1);
86 }
87
88 part = strtok(mode, ",");
89 while (part != NULL) {
90 int i;
91
92 for (i = 0; i < DLLED_MAP_NENTRIES; i++) {
93 if (strcmp(dlled_map[i].dlm_name, part) == 0) {
94 m |= dlled_map[i].dlm_bits;
95 break;
96 }
97 }
98
99 if (i == DLLED_MAP_NENTRIES) {
100 fprintf(stderr, "unknown LED mode: %s\n", part);
101 exit(1);
102 }
103
104 part = strtok(NULL, ",");
105 }
106
107 free(mode);
108 if (m == 0) {
109 fprintf(stderr, "failed to parse %s: no valid modes "
110 "specified\n", orig);
111 exit(1);
112 }
113
114 return (m);
115 }
116
117 static void
dlled_mode2str(mac_led_mode_t mode,char * buf,size_t len)118 dlled_mode2str(mac_led_mode_t mode, char *buf, size_t len)
119 {
120 int i;
121 boolean_t first = B_TRUE;
122 mac_led_mode_t orig = mode;
123
124 for (i = 0; i < DLLED_MAP_NENTRIES; i++) {
125 if ((mode & dlled_map[i].dlm_bits) != 0) {
126 if (first) {
127 first = B_FALSE;
128 } else {
129 (void) strlcat(buf, ",", len);
130 }
131 (void) strlcat(buf, dlled_map[i].dlm_name, len);
132 mode &= ~dlled_map[i].dlm_bits;
133 }
134 }
135
136 if (mode != 0) {
137 (void) snprintf(buf, len, "unknown mode: 0x%x\n", orig);
138 }
139 }
140
141
142 static int
dlled_set(const char * link,mac_led_mode_t mode)143 dlled_set(const char *link, mac_led_mode_t mode)
144 {
145 datalink_id_t linkid;
146 dladm_status_t status;
147 dld_ioc_led_t dil;
148
149 if ((status = dladm_name2info(dlled_hdl, link, &linkid, NULL, NULL,
150 NULL)) != DLADM_STATUS_OK) {
151 (void) fprintf(stderr, "failed to get link "
152 "id for link %s: %s\n", link,
153 dladm_status2str(status, dlled_dlerrmsg));
154 return (1);
155 }
156
157 bzero(&dil, sizeof (dil));
158 dil.dil_linkid = linkid;
159 dil.dil_active = mode;
160
161 if (ioctl(dladm_dld_fd(dlled_hdl), DLDIOC_SETLED, &dil) != 0) {
162 (void) fprintf(stderr, "failed to set LED on "
163 "device %s: %s\n", link, strerror(errno));
164 return (1);
165 }
166
167 return (0);
168 }
169
170 static int
dlled_get_led(dladm_handle_t hdl,datalink_id_t linkid,void * arg)171 dlled_get_led(dladm_handle_t hdl, datalink_id_t linkid, void *arg)
172 {
173 dladm_status_t status;
174 char name[MAXLINKNAMELEN];
175 char supported[128], active[128];
176 dld_ioc_led_t dil;
177
178 if ((status = dladm_datalink_id2info(hdl, linkid, NULL, NULL, NULL,
179 name, sizeof (name))) != DLADM_STATUS_OK) {
180 (void) fprintf(stderr, "failed to get datalink name for link "
181 "%d: %s", linkid, dladm_status2str(status,
182 dlled_dlerrmsg));
183 return (DLADM_WALK_CONTINUE);
184 }
185
186
187
188 bzero(&dil, sizeof (dil));
189 dil.dil_linkid = linkid;
190
191 if (ioctl(dladm_dld_fd(hdl), DLDIOC_GETLED, &dil) != 0) {
192 (void) fprintf(stderr, "failed to get LED information for "
193 "device %s: %s\n", name, strerror(errno));
194 return (DLADM_WALK_CONTINUE);
195 }
196
197 active[0] = '\0';
198 supported[0] = '\0';
199 dlled_mode2str(dil.dil_active, active, sizeof (active));
200 dlled_mode2str(dil.dil_supported, supported, sizeof (supported));
201
202 printf("%-20s %-12s %s\n", name, active, supported);
203
204 return (DLADM_WALK_CONTINUE);
205 }
206
207 int
main(int argc,char * argv[])208 main(int argc, char *argv[])
209 {
210 int c, ret;
211 boolean_t opt_s = B_FALSE;
212 mac_led_mode_t set_mode = 0;
213 dladm_status_t status;
214
215 dlled_progname = basename(argv[0]);
216
217 while ((c = getopt(argc, argv, ":s:")) != -1) {
218 switch (c) {
219 case 's':
220 opt_s = B_TRUE;
221 set_mode = dlled_parse_mode(optarg);
222 break;
223 case ':':
224 dlled_usage("option -%c requires an operand\n", optopt);
225 return (2);
226 case '?':
227 default:
228 dlled_usage("unknown option: -%c\n", optopt);
229 return (2);
230 }
231 }
232
233 argc -= optind;
234 argv += optind;
235
236 if (opt_s && argc > 1) {
237 dlled_usage("-s only operates on a single datalink\n");
238 return (2);
239 }
240
241 if (opt_s && argc <= 0) {
242 dlled_usage("-s requires a datalink\n");
243 return (2);
244 }
245
246 if ((status = dladm_open(&dlled_hdl)) != DLADM_STATUS_OK) {
247 (void) fprintf(stderr, "failed to open /dev/dld: %s\n",
248 dladm_status2str(status, dlled_dlerrmsg));
249 return (1);
250 }
251
252 if (opt_s) {
253 return (dlled_set(argv[0], set_mode));
254 }
255
256 (void) printf("%-20s %-12s %s\n", "LINK", "ACTIVE", "SUPPORTED");
257
258 ret = 0;
259 if (argc == 0) {
260 (void) dladm_walk_datalink_id(dlled_get_led, dlled_hdl, NULL,
261 DATALINK_CLASS_PHYS, DATALINK_ANY_MEDIATYPE,
262 DLADM_OPT_ACTIVE);
263 } else {
264 int i, dlret;
265 datalink_id_t linkid;
266
267 for (i = 0; i < argc; i++) {
268 if ((status = dladm_name2info(dlled_hdl, argv[i],
269 &linkid, NULL, NULL, NULL)) != DLADM_STATUS_OK) {
270 (void) fprintf(stderr, "failed to get link "
271 "id for link %s: %s\n", link,
272 dladm_status2str(status, dlled_dlerrmsg));
273 return (1);
274 }
275
276 dlret = dlled_get_led(dlled_hdl, linkid, NULL);
277 if (dlret != DLADM_WALK_CONTINUE) {
278 ret = 1;
279 break;
280 }
281 }
282 }
283
284 return (ret);
285 }
286