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