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 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 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 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 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 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