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