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