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