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 
28 
29 #include "cfga_fp.h"
30 
31 /*
32  * This file contains the entry points to the plug-in as defined in the
33  * config_admin(3X) man page.
34  */
35 
36 /*
37  * Set the version number
38  */
39 int cfga_version = CFGA_HSL_V2;
40 
41 /*ARGSUSED*/
42 cfga_err_t
43 cfga_change_state(
44 	cfga_cmd_t state_change_cmd,
45 	const char *ap_id,
46 	const char *options,
47 	struct cfga_confirm *confp,
48 	struct cfga_msg *msgp,
49 	char **errstring,
50 	cfga_flags_t flags)
51 {
52 	apid_t		apidt = {NULL};
53 	fpcfga_ret_t	ret;
54 	la_wwn_t	pwwn;
55 	char *value, *hw_option, *hw_option_p;
56 	char *fp_cs_hw_opts[] = {"disable_rcm", "force_update",
57 		"no_update", "unusable_SCSI_LUN", "unusable_FCP_dev", NULL};
58 	HBA_HANDLE	handle;
59 	HBA_PORTATTRIBUTES	portAttrs;
60 	int			portIndex;
61 
62 	if (errstring != NULL) {
63 		*errstring = NULL;
64 	}
65 
66 	/* Check for super user priveleges */
67 	if (geteuid() != 0) {
68 		return (CFGA_PRIV);
69 	}
70 
71 	/* Only configure and unconfigure operations are supported */
72 	if (state_change_cmd != CFGA_CMD_CONFIGURE &&
73 				state_change_cmd != CFGA_CMD_UNCONFIGURE) {
74 		return (CFGA_OPNOTSUPP);
75 	}
76 
77 	if ((ret = apidt_create(ap_id, &apidt, errstring)) != CFGA_OK) {
78 		return (err_cvt(ret));
79 	}
80 
81 	if (options != NULL) {
82 		hw_option = calloc(1, strlen(options) + 1);
83 		(void) snprintf(hw_option, strlen(options) + 1, "%s", options);
84 		hw_option_p = hw_option;
85 		/* Use getsubopt() if more options get added */
86 		while (*hw_option_p != '\0') {
87 		    switch (getsubopt(&hw_option_p, fp_cs_hw_opts, &value)) {
88 			case OPT_DISABLE_RCM :
89 				apidt.flags |= FLAG_DISABLE_RCM;
90 				break;
91 			case OPT_FORCE_UPDATE_REP :
92 				apidt.flags |= FLAG_FORCE_UPDATE_REP;
93 				break;
94 			case OPT_NO_UPDATE_REP :
95 				apidt.flags |= FLAG_NO_UPDATE_REP;
96 				break;
97 			case OPT_REMOVE_UNUSABLE_FCP_DEV :
98 			case OPT_REMOVE_UNUSABLE_SCSI_LUN:
99 				if (state_change_cmd != CFGA_CMD_UNCONFIGURE) {
100 					cfga_err(errstring, 0, ERRARG_OPT_INVAL,
101 						options, 0);
102 					S_FREE(hw_option);
103 					apidt_free(&apidt);
104 					return (CFGA_ERROR);
105 				}
106 				apidt.flags |= FLAG_REMOVE_UNUSABLE_FCP_DEV;
107 				break;
108 			default :
109 				/* process unknonw option. */
110 				cfga_err(errstring, 0, ERRARG_OPT_INVAL,
111 					options, 0);
112 				S_FREE(hw_option);
113 				apidt_free(&apidt);
114 				return (CFGA_ERROR);
115 		    }
116 		}
117 		S_FREE(hw_option);
118 	}
119 
120 	if (options != NULL && apidt.flags == 0) {
121 		/* invalid option specified. */
122 		cfga_err(errstring, 0, ERRARG_OPT_INVAL, options, 0);
123 		apidt_free(&apidt);
124 		return (CFGA_ERROR);
125 	}
126 
127 	if (apidt.dyncomp != NULL) {	/* Was there a port WWN passed ? */
128 		/*
129 		 * Yes - so change state of the particular device
130 		 *
131 		 * First Get the WWN in la_wwn_t form
132 		 */
133 		if (cvt_dyncomp_to_lawwn(apidt.dyncomp, &pwwn)) {
134 			cfga_err(errstring, 0, ERR_APID_INVAL, 0);
135 			return (err_cvt(FPCFGA_LIB_ERR));
136 		}
137 
138 		if ((ret = findMatchingAdapterPort(apidt.xport_phys,
139 		    &handle, &portIndex, &portAttrs, errstring)) ==
140 		    FPCFGA_OK) {
141 		    ret = dev_change_state(state_change_cmd, &apidt, &pwwn,
142 				flags, errstring, handle, portAttrs);
143 		    HBA_CloseAdapter(handle);
144 		    HBA_FreeLibrary();
145 		}
146 	} else {
147 		/* Change state of all devices on FCA and the FCA itself */
148 		ret = fca_change_state(state_change_cmd, &apidt,
149 							flags, errstring);
150 	}
151 
152 	apidt_free(&apidt);
153 	return (err_cvt(ret));
154 }
155 
156 
157 /*ARGSUSED*/
158 cfga_err_t
159 cfga_private_func(
160 	const char *func,
161 	const char *ap_id,
162 	const char *options,
163 	struct cfga_confirm *confp,
164 	struct cfga_msg *msgp,
165 	char **errstring,
166 	cfga_flags_t flags)
167 {
168 	if (errstring != NULL) {
169 		*errstring = NULL;
170 	}
171 
172 	if (geteuid() != 0) {
173 		return (CFGA_PRIV);
174 	}
175 
176 	return (CFGA_OPNOTSUPP);
177 }
178 
179 
180 /*ARGSUSED*/
181 cfga_err_t
182 cfga_test(
183 	const char *ap_id,
184 	const char *options,
185 	struct cfga_msg *msgp,
186 	char **errstring,
187 	cfga_flags_t flags)
188 {
189 	if (errstring != NULL) {
190 		*errstring = NULL;
191 	}
192 
193 	if (geteuid() != 0) {
194 		return (CFGA_PRIV);
195 	}
196 
197 	return (CFGA_OPNOTSUPP);
198 }
199 
200 
201 /*ARGSUSED*/
202 cfga_err_t
203 cfga_list_ext(
204 	const char *ap_id,
205 	cfga_list_data_t **ap_id_list,
206 	int *nlistp,
207 	const char *options,
208 	const char *listopts,
209 	char **errstring,
210 	cfga_flags_t flags)
211 {
212 	int fca, expand, nelem;
213 	ldata_list_t *ldatalistp = NULL;
214 	apid_t apidt = {NULL};
215 	fpcfga_cmd_t cmd;
216 	fpcfga_ret_t ret;
217 	char *value, *hw_option, *hw_option_p;
218 	uint_t fp_flags = 0;
219 	char *fp_list_hw_opts[] = {"devinfo_force", "show_SCSI_LUN",
220 		"show_FCP_dev", NULL};
221 
222 	if (errstring != NULL) {
223 		*errstring = NULL;
224 	}
225 
226 	/* Check for super user privileges */
227 	if (geteuid() != 0) {
228 		return (CFGA_PRIV);
229 	}
230 
231 	if (ap_id_list == NULL || nlistp == NULL) {
232 		return (CFGA_ERROR);
233 	}
234 
235 	*ap_id_list = NULL;
236 	*nlistp = 0;
237 
238 	if (options != NULL) {
239 		hw_option = calloc(1, strlen(options) + 1);
240 		(void) snprintf(hw_option, strlen(options) + 1, "%s", options);
241 		hw_option_p = hw_option;
242 		/* Use getsubopt() if more options get added */
243 		while (*hw_option_p != '\0') {
244 		    switch (getsubopt(&hw_option_p, fp_list_hw_opts, &value)) {
245 			case OPT_DEVINFO_FORCE :
246 				fp_flags |= FLAG_DEVINFO_FORCE;
247 				break;
248 			case OPT_FCP_DEV :
249 			case OPT_SHOW_SCSI_LUN:
250 				fp_flags |= FLAG_FCP_DEV;
251 				break;
252 			default :
253 				/* process unknonw option. */
254 				cfga_err(errstring, 0, ERRARG_OPT_INVAL,
255 					options, 0);
256 				S_FREE(hw_option);
257 			return (CFGA_ERROR);
258 		    }
259 		}
260 		S_FREE(hw_option);
261 	}
262 
263 	/* if force_devinfo is specified check uid = 0 or not. */
264 	if (((fp_flags & FLAG_DEVINFO_FORCE) == FLAG_DEVINFO_FORCE) &&
265 			(geteuid() != 0)) {
266 		return (CFGA_PRIV);
267 	}
268 
269 	fca = 0;
270 	if (GET_DYN(ap_id) == NULL) {
271 		fca = 1;
272 	}
273 
274 	expand = 0;
275 	if ((flags & CFGA_FLAG_LIST_ALL) == CFGA_FLAG_LIST_ALL) {
276 		expand = 1;
277 	}
278 
279 	/*
280 	 * We expand published attachment points but not
281 	 * dynamic attachment points
282 	 */
283 
284 	if (!fca) { /* Stat a single device - no expansion for devices */
285 		cmd = FPCFGA_STAT_FC_DEV;
286 	} else if (!expand) { /* Stat only the HBA */
287 		cmd = FPCFGA_STAT_FCA_PORT;
288 	} else { /* Expand HBA attachment point */
289 		cmd = FPCFGA_STAT_ALL;
290 	}
291 
292 	ldatalistp = NULL;
293 	nelem = 0;
294 
295 	if ((fp_flags & FLAG_FCP_DEV) == FLAG_FCP_DEV) {
296 		ret = do_list_FCP_dev(ap_id, fp_flags, cmd, &ldatalistp, &nelem,
297 			errstring);
298 		if (ret != FPCFGA_OK) {
299 			list_free(&ldatalistp);
300 			return (err_cvt(ret));
301 		}
302 	} else {
303 		if ((ret = apidt_create(ap_id, &apidt, errstring))
304 				!= FPCFGA_OK) {
305 			return (err_cvt(ret));
306 		}
307 
308 		if (options != NULL) {
309 			apidt.flags |= fp_flags;
310 		}
311 
312 		ret = do_list(&apidt, cmd, &ldatalistp, &nelem, errstring);
313 		if (ret != FPCFGA_OK) {
314 			list_free(&ldatalistp);
315 			apidt_free(&apidt);
316 			return (err_cvt(ret));
317 		}
318 		apidt_free(&apidt);
319 	}
320 
321 	assert(ldatalistp != NULL);
322 
323 	if (list_ext_postprocess(&ldatalistp, nelem, ap_id_list, nlistp,
324 	    errstring) != FPCFGA_OK) {
325 		assert(*ap_id_list == NULL && *nlistp == 0);
326 		ret = FPCFGA_LIB_ERR;
327 	} else {
328 		assert(*ap_id_list != NULL && *nlistp == nelem);
329 		ret = FPCFGA_OK;
330 	}
331 
332 	list_free(&ldatalistp);
333 	return (err_cvt(ret));
334 }
335 
336 
337 /*ARGSUSED*/
338 cfga_err_t
339 cfga_help(struct cfga_msg *msgp, const char *options, cfga_flags_t flags)
340 {
341 	cfga_msg(msgp, MSG_HELP_HDR, MSG_HELP_USAGE, 0);
342 
343 	return (CFGA_OK);
344 
345 }
346 
347 /*ARGSUSED*/
348 int
349 cfga_ap_id_cmp(const char *ap_id1, const char *ap_id2)
350 {
351 	int i = 0;
352 	long long ret;
353 
354 	if (ap_id1 == ap_id2) {
355 		return (0);
356 	}
357 
358 	if (ap_id1 == NULL || ap_id2 == NULL) {
359 		if (ap_id1 == NULL) {
360 			/* Return a negative value */
361 			return (0 - (uchar_t)ap_id2[0]);
362 		} else {
363 			return ((uchar_t)ap_id1[0]);
364 		}
365 	}
366 
367 	/*
368 	 * Search for first different char
369 	 */
370 	while (ap_id1[i] == ap_id2[i] && ap_id1[i] != '\0')
371 		i++;
372 
373 	if ((ap_id1[i] == '\0') &&
374 		!(strncmp(&ap_id2[i], LUN_COMP_SEP, strlen(LUN_COMP_SEP)))) {
375 		return (0);
376 	} else if ((ap_id2[i] == '\0') &&
377 		!(strncmp(&ap_id1[i], LUN_COMP_SEP, strlen(LUN_COMP_SEP)))) {
378 		return (0);
379 	}
380 
381 	/*
382 	 * If one of the char is a digit, back up to where the
383 	 * number started, compare the number.
384 	 */
385 	if (isxdigit(ap_id1[i]) || isxdigit(ap_id2[i])) {
386 		while ((i > 0) && isxdigit(ap_id1[i - 1]))
387 			i--;
388 
389 		if (isxdigit(ap_id1[i]) && isxdigit(ap_id2[i])) {
390 			ret = (strtoll((ap_id1 + i), NULL, 16)) -
391 				(strtoll((ap_id2 + i), NULL, 16));
392 			if (ret > 0) {
393 				return (1);
394 			} else if (ret < 0) {
395 				return (-1);
396 			} else {
397 				return (0);
398 			}
399 		}
400 	}
401 
402 	/* One of them isn't a number, compare the char */
403 	return (ap_id1[i] - ap_id2[i]);
404 }
405