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