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 2010 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #include <ctype.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <macros.h>
33 #include <errno.h>
34 #include <locale.h>
35 #include <libdevinfo.h>
36 #include <librcm.h>
37 #define	CFGA_PLUGIN_LIB
38 #include <config_admin.h>
39 #include "ap.h"
40 
41 #ifdef	SBD_DEBUG
42 
43 static FILE *debug_fp;
44 
45 int
debugging(void)46 debugging(void)
47 {
48 	char *ep;
49 	static int inited;
50 
51 	if (inited)
52 		return (debug_fp != NULL);
53 	inited = 1;
54 
55 	if ((ep = getenv("SBD_DEBUG")) == NULL)
56 		return (0);
57 
58 	if (*ep == '\0')
59 		debug_fp = stderr;
60 	else {
61 		if ((debug_fp = fopen(ep, "a")) == NULL)
62 			return (0);
63 	}
64 	(void) fprintf(debug_fp, "\nDebug started, pid=%d\n", (int)getpid());
65 	return (1);
66 }
67 
68 /*PRINTFLIKE1*/
69 void
dbg(char * fmt,...)70 dbg(char *fmt, ...)
71 {
72 	va_list ap;
73 
74 	if (!debugging())
75 		return;
76 
77 	va_start(ap, fmt);
78 	(void) vfprintf(debug_fp, fmt, ap);
79 	va_end(ap);
80 }
81 #endif
82 
83 static char *
84 ap_err_fmts[] = {
85 	"command invalid: %s",
86 	"%s %s: %s%s%s",			/* command failed */
87 	"%s %s",				/* command nacked */
88 	"command not supported: %s %s",
89 	"command aborted: %s %s",
90 	"option invalid: %s",
91 	"option requires value: %s",
92 	"option requires no value: %s",
93 	"option value invalid: %s %s",
94 	"attachment point invalid: %s",
95 	"component invalid: %s",
96 	"sequence invalid: %s (%s %s) %s",
97 	"change signal disposition failed",
98 	"cannot get RCM handle",
99 	"RCM %s failed for %s",
100 	"\n%-30s %-10s %s",
101 	"cannot open %s%s%s",
102 	"cannot find symbol %s in %s",
103 	"cannot stat %s: %s",
104 	"not enough memory",
105 	"%s plugin: %s",
106 	"unknown error",
107 	NULL
108 };
109 
110 #define	ap_err_fmt(i)		ap_err_fmts[min((uint_t)(i), ERR_NONE)]
111 
112 static char *
113 ap_msg_fmts[] = {
114 	"%s %s\n",
115 	"%s %s skipped\n",
116 	"System may be temporarily suspended, proceed",
117 	"%s %s aborted\n",
118 	"%s %s done\n",
119 	"%s %s failed\n",
120 	"RCM library not found, feature will be disabled\n",
121 	"Unknown message\n",
122 	NULL
123 };
124 
125 #define	ap_msg_fmt(i)		ap_msg_fmts[min((uint_t)(i), MSG_NONE)]
126 
127 #define	STR_BD			"board"
128 #define	STR_SEP			": "
129 #define	STR_NULL		"NULL"
130 #define	STR_CMD_UNKNOWN		"unknown command"
131 #define	STR_ERR_UNKNOWN		"unknown error"
132 #define	STR_MSG_UNKNOWN		"unknown message\n"
133 #define	STR_TGT_UNKNOWN		"unknown target"
134 
135 #define	get_cmd(c, ap, v) \
136 { \
137 	(v) = va_arg((ap), int); \
138 	if (((c) = ap_cmd_name((v))) == NULL) \
139 		(c) = STR_CMD_UNKNOWN; \
140 }
141 #define	get_tgt(t, ap) {\
142 	(t) = va_arg((ap), char *); \
143 	if (!str_valid((t))) \
144 		(t) = STR_TGT_UNKNOWN; \
145 }
146 #define	check_tgt(tgt, t) {\
147 	if (str_valid((tgt))) \
148 		(t) = (tgt); \
149 	else \
150 		(t) = STR_TGT_UNKNOWN; \
151 }
152 #define	get_str(v, ap, d) \
153 { \
154 	(v) = va_arg((ap), char *); \
155 	if ((v) == NULL) \
156 		(v) = (d); \
157 }
158 
159 static char *
160 ap_stnames[] = {
161 	"unknown state",
162 	"empty",
163 	"disconnected",
164 	"connected",
165 	"unconfigured",
166 	"configured"
167 };
168 
169 /*
170  * ap_err() accepts a variable number of message IDs and constructs
171  * a corresponding error string.  ap_err() calls dgettext() to
172  * internationalize the proper portions of a message.  If a system
173  * error was encountered (errno set), ap_err() looks for the error
174  * string corresponding to the returned error code if one is available.
175  * If not, the standard libc error string is fetched.
176  */
177 void
ap_err(apd_t * a,...)178 ap_err(apd_t *a, ...)
179 {
180 	int v;
181 	int err;
182 	int len;
183 	char *p;
184 	char *sep;
185 	char *rsep;
186 	const char *fmt;
187 	char *cmd;
188 	char *value;
189 	char *target;
190 	char *serr;
191 	char *syserr;
192 	char *rstate;
193 	char *ostate;
194 	char *srsrc;
195 	char *sysrsrc;
196 	char *option;
197 	char *path;
198 	char *sym;
199 	char *msg;
200 	const char *error;
201 	char **errstring;
202 	char *rinfostr = NULL;
203 	va_list ap;
204 
205 	DBG("ap_err(%p)\n", (void *)a);
206 
207 	/*
208 	 * If there is no descriptor or string pointer or if
209 	 * there is an outstanding error, just return.
210 	 */
211 	if (a == NULL || (errstring = a->errstring) == NULL ||
212 	    *errstring != NULL)
213 		return;
214 
215 	va_start(ap, a);
216 
217 	err = va_arg(ap, int);
218 
219 	if ((fmt = ap_err_fmt(err)) == NULL)
220 		fmt = STR_ERR_UNKNOWN;
221 	fmt = dgettext(TEXT_DOMAIN, fmt);
222 	len = strlen(fmt);
223 
224 	sep = "";
225 	serr = NULL;
226 	srsrc = NULL;
227 	error = NULL;
228 
229 	/*
230 	 * Get the proper arguments for the error.
231 	 */
232 	switch (err) {
233 	case ERR_CMD_ABORT:
234 	case ERR_CMD_FAIL:
235 	case ERR_CMD_NACK:
236 		get_cmd(cmd, ap, v);
237 		check_tgt(a->target, target);
238 		len += strlen(cmd) + strlen(target);
239 		DBG("<%s><%s>", cmd, target);
240 		break;
241 	case ERR_CMD_NOTSUPP:
242 		get_cmd(cmd, ap, v);
243 		if (a->tgt == AP_BOARD)
244 			target = STR_BD;
245 		else
246 			check_tgt(a->cname, target);
247 		len += strlen(cmd) + strlen(target);
248 		DBG("<%s><%s>", cmd, target);
249 		break;
250 	case ERR_AP_INVAL:
251 		check_tgt((char *)a->apid, target);
252 		len += strlen(target);
253 		DBG("<%s>", target);
254 		break;
255 	case ERR_CMD_INVAL:
256 	case ERR_CM_INVAL:
257 	case ERR_OPT_INVAL:
258 	case ERR_OPT_NOVAL:
259 	case ERR_OPT_VAL:
260 	case ERR_OPT_BADVAL:
261 		get_str(option, ap, STR_NULL);
262 		len += strlen(option);
263 		DBG("<%s>", option);
264 		if (err != ERR_OPT_BADVAL)
265 			break;
266 		get_str(value, ap, STR_NULL);
267 		len += strlen(value);
268 		DBG("<%s>", value);
269 		break;
270 	case ERR_TRANS_INVAL: {
271 		cfga_stat_t rs, os;
272 
273 		get_cmd(cmd, ap, v);
274 		check_tgt(a->target, target);
275 		len += strlen(cmd) + strlen(target);
276 		ap_state(a, &rs, &os);
277 		rstate = ap_stnames[rs];
278 		ostate = ap_stnames[os];
279 		len += strlen(rstate) + strlen(ostate);
280 		DBG("<%s><%s><%s><%s>", cmd, target, rstate, ostate);
281 		break;
282 	}
283 	case ERR_RCM_CMD: {
284 
285 		get_cmd(cmd, ap, v);
286 		check_tgt(a->target, target);
287 		len += strlen(cmd) + strlen(target);
288 		DBG("<%s><%s>", cmd, target);
289 
290 		if ((ap_rcm_info(a, &rinfostr) == 0) && (rinfostr != NULL)) {
291 			len += strlen(rinfostr);
292 		}
293 
294 		break;
295 	}
296 	case ERR_LIB_OPEN:
297 		get_str(path, ap, STR_NULL);
298 		get_str(error, ap, "");
299 		if (str_valid(error))
300 			sep = STR_SEP;
301 		DBG("<%s><%s>", path, error);
302 		break;
303 	case ERR_LIB_SYM:
304 		get_str(path, ap, STR_NULL);
305 		get_str(sym, ap, STR_NULL);
306 		DBG("<%s><%s>", path, sym);
307 		break;
308 	case ERR_STAT:
309 		get_str(path, ap, STR_NULL);
310 		break;
311 	case ERR_PLUGIN:
312 		get_str(msg, ap, STR_NULL);
313 		break;
314 	default:
315 		DBG("<NOARGS>");
316 		break;
317 	}
318 
319 	va_end(ap);
320 
321 	/*
322 	 * In case of a system error, get the reason for
323 	 * the failure as well as the resource if availbale.
324 	 * If we already got some error info (e.g. from RCM)
325 	 * don't bother looking.
326 	 */
327 	if (!str_valid(error) && errno) {
328 		sep = STR_SEP;
329 		sysrsrc = NULL;
330 		if ((syserr = ap_sys_err(a, &sysrsrc)) == NULL)
331 			syserr = STR_ERR_UNKNOWN;
332 		else
333 			serr = syserr;
334 
335 		syserr = dgettext(TEXT_DOMAIN, syserr);
336 
337 		if (sysrsrc == NULL)
338 			sysrsrc = "";
339 		else
340 			srsrc = sysrsrc;
341 
342 		len += strlen(syserr) + strlen(sysrsrc);
343 
344 		if (str_valid(sysrsrc)) {
345 			rsep = STR_SEP;
346 			len += strlen(rsep);
347 		} else
348 			rsep = "";
349 
350 		DBG("<%s><%s><%s>", syserr, rsep, sysrsrc);
351 
352 	} else
353 		syserr = rsep = sysrsrc = "";
354 
355 	DBG("\n");
356 
357 	if ((p = (char *)calloc(len, 1)) != NULL)
358 		*errstring = p;
359 
360 	/*
361 	 * Print the string with appropriate arguments.
362 	 */
363 	switch (err) {
364 	case ERR_CMD_FAIL:
365 		(void) snprintf(p, len, fmt, cmd, target,
366 		    syserr, rsep, sysrsrc);
367 		break;
368 	case ERR_CMD_ABORT:
369 	case ERR_CMD_NACK:
370 	case ERR_CMD_NOTSUPP:
371 		(void) snprintf(p, len, fmt, cmd, target);
372 		break;
373 	case ERR_AP_INVAL:
374 		(void) snprintf(p, len, fmt, target);
375 		break;
376 	case ERR_CMD_INVAL:
377 	case ERR_CM_INVAL:
378 	case ERR_OPT_INVAL:
379 	case ERR_OPT_NOVAL:
380 	case ERR_OPT_VAL:
381 		(void) snprintf(p, len, fmt, option);
382 		break;
383 	case ERR_OPT_BADVAL:
384 		(void) snprintf(p, len, fmt, option, value);
385 		break;
386 	case ERR_TRANS_INVAL:
387 		(void) snprintf(p, len, fmt, cmd, rstate, ostate, target);
388 		break;
389 	case ERR_SIG_CHANGE:
390 	case ERR_RCM_HANDLE:
391 		(void) snprintf(p, len, fmt);
392 		break;
393 	case ERR_RCM_CMD:
394 		/*
395 		 * If the rinfostr has a string, then the librcm has returned
396 		 * us a text field of its reasons why the command failed.
397 		 *
398 		 * If the rinfostr is not returning data, we will use
399 		 * the standard ap_err_fmts[] for the rcm error.
400 		 */
401 		if (rinfostr != NULL)
402 			(void) snprintf(p, len, "%s", rinfostr);
403 		else
404 			(void) snprintf(p, len, fmt, cmd, target);
405 		break;
406 	case ERR_LIB_OPEN:
407 		(void) snprintf(p, len, fmt, path, sep, error);
408 		break;
409 	case ERR_LIB_SYM:
410 		(void) snprintf(p, len, fmt, sym, path);
411 		break;
412 	case ERR_STAT:
413 		(void) snprintf(p, len, fmt, path, syserr);
414 		break;
415 	case ERR_NOMEM:
416 		(void) snprintf(p, len, fmt);
417 		break;
418 	case ERR_PLUGIN:
419 		(void) snprintf(p, len, fmt, a->class, msg);
420 		break;
421 	default:
422 		break;
423 	}
424 
425 	if (serr)
426 		free(serr);
427 	if (srsrc)
428 		free(srsrc);
429 }
430 
431 /*
432  * ap_msg() accepts a variable number of message IDs and constructs
433  * a corresponding message string which is printed via the message print
434  * routine argument.  ap_msg() internationalizes the appropriate portion
435  * of the message.
436  */
437 void
ap_msg(apd_t * a,...)438 ap_msg(apd_t *a, ...)
439 {
440 	int v;
441 	int len;
442 	char *p;
443 	const char *fmt;
444 	char *cmd;
445 	char *target;
446 	struct cfga_msg *msgp;
447 	va_list ap;
448 
449 	DBG("ap_msg(%p)\n", (void *)a);
450 
451 	if (a == NULL || ap_getopt(a, OPT_VERBOSE) == 0)
452 		return;
453 
454 	msgp = a->msgp;
455 
456 	if (msgp == NULL || msgp->message_routine == NULL)
457 		return;
458 
459 	va_start(ap, a);
460 
461 	v = va_arg(ap, int);
462 
463 	if ((fmt = ap_msg_fmt(v)) == NULL)
464 		fmt = STR_MSG_UNKNOWN;
465 	fmt = dgettext(TEXT_DOMAIN, fmt);
466 	len = strlen(fmt) + 128;	/* slop */
467 
468 	DBG("<%d>", v);
469 
470 	switch (v) {
471 	case MSG_ISSUE:
472 	case MSG_SKIP:
473 	case MSG_ABORT:
474 	case MSG_FAIL:
475 	case MSG_DONE:
476 		get_cmd(cmd, ap, v);
477 		get_tgt(target, ap);
478 		DBG("<%s><%s>\n", cmd, target);
479 		len += strlen(cmd) + strlen(target);
480 		break;
481 	default:
482 		break;
483 	}
484 
485 	va_end(ap);
486 
487 	if ((p = (char *)calloc(len, 1)) == NULL)
488 		return;
489 
490 	(void) snprintf(p, len, fmt, cmd, target);
491 
492 	(*msgp->message_routine)(msgp->appdata_ptr, p);
493 	free(p);
494 }
495 
496 int
ap_confirm(apd_t * a)497 ap_confirm(apd_t *a)
498 {
499 	int rc;
500 	char *msg;
501 	struct cfga_confirm *confp;
502 
503 	if (a == NULL)
504 		return (0);
505 
506 	confp = a->confp;
507 
508 	if (confp == NULL || confp->confirm == NULL)
509 		return (0);
510 
511 	msg = dgettext(TEXT_DOMAIN, ap_msg_fmt(MSG_SUSPEND));
512 
513 	rc = (*confp->confirm)(confp->appdata_ptr, msg);
514 
515 	return (rc);
516 }
517