1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate /* 30*0Sstevel@tonic-gate * bootlog() - error notification and progress reporting for 31*0Sstevel@tonic-gate * WAN boot components 32*0Sstevel@tonic-gate */ 33*0Sstevel@tonic-gate 34*0Sstevel@tonic-gate #include <sys/varargs.h> 35*0Sstevel@tonic-gate #include <sys/types.h> 36*0Sstevel@tonic-gate #include <sys/strlog.h> 37*0Sstevel@tonic-gate #include <sys/wanboot_impl.h> 38*0Sstevel@tonic-gate #include <errno.h> 39*0Sstevel@tonic-gate #include <time.h> 40*0Sstevel@tonic-gate #include <boot_http.h> 41*0Sstevel@tonic-gate #include <stdio.h> 42*0Sstevel@tonic-gate #include <parseURL.h> 43*0Sstevel@tonic-gate #include <bootlog.h> 44*0Sstevel@tonic-gate #include <strings.h> 45*0Sstevel@tonic-gate #include <stdlib.h> 46*0Sstevel@tonic-gate #include <unistd.h> 47*0Sstevel@tonic-gate #include <netdb.h> 48*0Sstevel@tonic-gate #include <libintl.h> 49*0Sstevel@tonic-gate #include <netboot_paths.h> 50*0Sstevel@tonic-gate #include <wanboot_conf.h> 51*0Sstevel@tonic-gate #include <bootinfo.h> 52*0Sstevel@tonic-gate #ifdef _BOOT 53*0Sstevel@tonic-gate #include <sys/bootdebug.h> 54*0Sstevel@tonic-gate #endif 55*0Sstevel@tonic-gate 56*0Sstevel@tonic-gate static struct code pri_names[] = { 57*0Sstevel@tonic-gate "panic", BOOTLOG_EMERG, 58*0Sstevel@tonic-gate "alert", BOOTLOG_ALERT, 59*0Sstevel@tonic-gate "crit", BOOTLOG_CRIT, 60*0Sstevel@tonic-gate "warn", BOOTLOG_WARNING, 61*0Sstevel@tonic-gate "info", BOOTLOG_INFO, 62*0Sstevel@tonic-gate "debug", BOOTLOG_DEBUG, 63*0Sstevel@tonic-gate "verbose", BOOTLOG_VERBOSE, 64*0Sstevel@tonic-gate "progress", BOOTLOG_PROGRESS, 65*0Sstevel@tonic-gate "none", NOPRI, 66*0Sstevel@tonic-gate NULL, -1 67*0Sstevel@tonic-gate }; 68*0Sstevel@tonic-gate 69*0Sstevel@tonic-gate typedef enum { 70*0Sstevel@tonic-gate BL_NO_TRANSPORT, 71*0Sstevel@tonic-gate BL_LOCAL_FILE, 72*0Sstevel@tonic-gate BL_CONSOLE, 73*0Sstevel@tonic-gate BL_HTTP, 74*0Sstevel@tonic-gate BL_HTTPS 75*0Sstevel@tonic-gate } bl_transport_t; 76*0Sstevel@tonic-gate 77*0Sstevel@tonic-gate typedef struct list_entry { 78*0Sstevel@tonic-gate char message[BOOTLOG_QS_MAX]; 79*0Sstevel@tonic-gate struct list_entry *flink; 80*0Sstevel@tonic-gate } list; 81*0Sstevel@tonic-gate 82*0Sstevel@tonic-gate #define BOOTLOG_RING_NELEM 512 83*0Sstevel@tonic-gate 84*0Sstevel@tonic-gate static struct ringbuffer_t { 85*0Sstevel@tonic-gate int w_ptr; 86*0Sstevel@tonic-gate int r_ptr; 87*0Sstevel@tonic-gate list entries[BOOTLOG_RING_NELEM]; 88*0Sstevel@tonic-gate } ringbuffer; 89*0Sstevel@tonic-gate 90*0Sstevel@tonic-gate static FILE *bl_filehandle = NULL; 91*0Sstevel@tonic-gate static http_handle_t bl_httphandle = NULL; 92*0Sstevel@tonic-gate static url_t bl_url; 93*0Sstevel@tonic-gate static bl_transport_t bl_transport = BL_NO_TRANSPORT; 94*0Sstevel@tonic-gate 95*0Sstevel@tonic-gate static bl_transport_t openbootlog(void); 96*0Sstevel@tonic-gate static boolean_t setup_con(http_handle_t, boolean_t, boolean_t); 97*0Sstevel@tonic-gate static char *url_encode(const char *); 98*0Sstevel@tonic-gate static boolean_t sendmessage(bl_transport_t, char *, const char *, 99*0Sstevel@tonic-gate bootlog_severity_t, int); 100*0Sstevel@tonic-gate static int ptr_incr(int ptr); 101*0Sstevel@tonic-gate static int ptr_decr(int ptr); 102*0Sstevel@tonic-gate static void rb_init(struct ringbuffer_t *); 103*0Sstevel@tonic-gate static void rb_write(struct ringbuffer_t *, const char *); 104*0Sstevel@tonic-gate static int rb_read(struct ringbuffer_t *, char *); 105*0Sstevel@tonic-gate 106*0Sstevel@tonic-gate /* 107*0Sstevel@tonic-gate * Return a string representing the current time; not thread-safe. 108*0Sstevel@tonic-gate */ 109*0Sstevel@tonic-gate static const char * 110*0Sstevel@tonic-gate gettime(void) 111*0Sstevel@tonic-gate { 112*0Sstevel@tonic-gate static char timebuf[sizeof ("Tue Jan 19 03:14:07 2038\n")]; 113*0Sstevel@tonic-gate time_t curtime; 114*0Sstevel@tonic-gate 115*0Sstevel@tonic-gate if (time(&curtime) == 0) 116*0Sstevel@tonic-gate return ("<time unavailable>"); 117*0Sstevel@tonic-gate 118*0Sstevel@tonic-gate (void) strlcpy(timebuf, ctime(&curtime), sizeof (timebuf)); 119*0Sstevel@tonic-gate timebuf[19] = '\0'; /* truncate before "2038" above */ 120*0Sstevel@tonic-gate return (timebuf); 121*0Sstevel@tonic-gate } 122*0Sstevel@tonic-gate 123*0Sstevel@tonic-gate /* 124*0Sstevel@tonic-gate * bootlog_common() - Common routine used by bootlog() and 125*0Sstevel@tonic-gate * bootlog_internal() to write a message comprising a message 126*0Sstevel@tonic-gate * header and a message body to the appropriate transport. 127*0Sstevel@tonic-gate * The message header comprises an ident string and a message 128*0Sstevel@tonic-gate * severity. 129*0Sstevel@tonic-gate */ 130*0Sstevel@tonic-gate static void 131*0Sstevel@tonic-gate bootlog_common(const char *ident, bootlog_severity_t severity, char *message) 132*0Sstevel@tonic-gate { 133*0Sstevel@tonic-gate bl_transport_t entry_transport; 134*0Sstevel@tonic-gate static int blrecurs; 135*0Sstevel@tonic-gate static int blretry; 136*0Sstevel@tonic-gate 137*0Sstevel@tonic-gate /* 138*0Sstevel@tonic-gate * This function may be called recursively because the HTTP code 139*0Sstevel@tonic-gate * is a bootlog consumer. The blrecurs variable is used to determine 140*0Sstevel@tonic-gate * whether or not the invocation is recursive. 141*0Sstevel@tonic-gate */ 142*0Sstevel@tonic-gate blrecurs++; 143*0Sstevel@tonic-gate entry_transport = bl_transport; 144*0Sstevel@tonic-gate 145*0Sstevel@tonic-gate /* 146*0Sstevel@tonic-gate * If this is the first bootlog call then setup the transport. 147*0Sstevel@tonic-gate * We only do this in a non-recursive invocation as openbootlog() 148*0Sstevel@tonic-gate * results in a recursive call for a HTTP or HTTPS transport. 149*0Sstevel@tonic-gate */ 150*0Sstevel@tonic-gate if (bl_transport == BL_NO_TRANSPORT && blrecurs == 1) { 151*0Sstevel@tonic-gate rb_init(&ringbuffer); 152*0Sstevel@tonic-gate bl_transport = openbootlog(); 153*0Sstevel@tonic-gate } 154*0Sstevel@tonic-gate 155*0Sstevel@tonic-gate /* 156*0Sstevel@tonic-gate * If we're not there already, try to move up a level. 157*0Sstevel@tonic-gate * This is necessary because our consumer may have begun 158*0Sstevel@tonic-gate * logging before it had enough information to initialize 159*0Sstevel@tonic-gate * its HTTP or HTTPS transport. We've arbitrarily decided 160*0Sstevel@tonic-gate * that we'll only check to see if we should move up, on 161*0Sstevel@tonic-gate * every third (blretry) non-recursive invocation. 162*0Sstevel@tonic-gate */ 163*0Sstevel@tonic-gate if (blrecurs == 1 && 164*0Sstevel@tonic-gate !(bl_transport == BL_HTTPS || bl_transport == BL_HTTP)) { 165*0Sstevel@tonic-gate if (blretry > 3) { 166*0Sstevel@tonic-gate bl_transport = openbootlog(); 167*0Sstevel@tonic-gate blretry = 0; 168*0Sstevel@tonic-gate } else 169*0Sstevel@tonic-gate blretry++; 170*0Sstevel@tonic-gate } 171*0Sstevel@tonic-gate 172*0Sstevel@tonic-gate if (entry_transport != bl_transport) { 173*0Sstevel@tonic-gate switch (bl_transport) { 174*0Sstevel@tonic-gate 175*0Sstevel@tonic-gate case BL_CONSOLE: 176*0Sstevel@tonic-gate (void) printf( 177*0Sstevel@tonic-gate "%s wanboot info: WAN boot messages->console\n", 178*0Sstevel@tonic-gate gettime()); 179*0Sstevel@tonic-gate break; 180*0Sstevel@tonic-gate 181*0Sstevel@tonic-gate case BL_HTTP: 182*0Sstevel@tonic-gate case BL_HTTPS: 183*0Sstevel@tonic-gate (void) printf( 184*0Sstevel@tonic-gate "%s wanboot info: WAN boot messages->%s:%u\n", 185*0Sstevel@tonic-gate gettime(), bl_url.hport.hostname, 186*0Sstevel@tonic-gate bl_url.hport.port); 187*0Sstevel@tonic-gate break; 188*0Sstevel@tonic-gate 189*0Sstevel@tonic-gate default: 190*0Sstevel@tonic-gate break; 191*0Sstevel@tonic-gate } 192*0Sstevel@tonic-gate } 193*0Sstevel@tonic-gate 194*0Sstevel@tonic-gate /* 195*0Sstevel@tonic-gate * Failed attempts and recursively generated log messages are 196*0Sstevel@tonic-gate * sent to the fallback transport. 197*0Sstevel@tonic-gate */ 198*0Sstevel@tonic-gate if (blrecurs > 1 || !sendmessage(bl_transport, message, ident, 199*0Sstevel@tonic-gate severity, 0)) { 200*0Sstevel@tonic-gate /* 201*0Sstevel@tonic-gate * Fallback to a log file if one exists, or the console 202*0Sstevel@tonic-gate * as a last resort. Note that bl_filehandle will always 203*0Sstevel@tonic-gate * be NULL in standalone. 204*0Sstevel@tonic-gate */ 205*0Sstevel@tonic-gate (void) sendmessage(bl_filehandle != NULL ? BL_LOCAL_FILE : 206*0Sstevel@tonic-gate BL_CONSOLE, message, ident, severity, 1); 207*0Sstevel@tonic-gate } 208*0Sstevel@tonic-gate blrecurs--; 209*0Sstevel@tonic-gate } 210*0Sstevel@tonic-gate 211*0Sstevel@tonic-gate /* 212*0Sstevel@tonic-gate * bootlog() - the exposed interface for logging boot messages. 213*0Sstevel@tonic-gate */ 214*0Sstevel@tonic-gate /* PRINTFLIKE3 */ 215*0Sstevel@tonic-gate void 216*0Sstevel@tonic-gate bootlog(const char *ident, bootlog_severity_t severity, char *fmt, ...) 217*0Sstevel@tonic-gate { 218*0Sstevel@tonic-gate char message[BOOTLOG_MSG_MAX_LEN]; 219*0Sstevel@tonic-gate va_list adx; 220*0Sstevel@tonic-gate 221*0Sstevel@tonic-gate va_start(adx, fmt); 222*0Sstevel@tonic-gate (void) vsnprintf(message, BOOTLOG_MSG_MAX_LEN, fmt, adx); 223*0Sstevel@tonic-gate va_end(adx); 224*0Sstevel@tonic-gate 225*0Sstevel@tonic-gate bootlog_common(ident, severity, message); 226*0Sstevel@tonic-gate } 227*0Sstevel@tonic-gate 228*0Sstevel@tonic-gate /* 229*0Sstevel@tonic-gate * libbootlog() - an internal interface for logging boot 230*0Sstevel@tonic-gate * messages. 231*0Sstevel@tonic-gate */ 232*0Sstevel@tonic-gate /* PRINTFLIKE2 */ 233*0Sstevel@tonic-gate void 234*0Sstevel@tonic-gate libbootlog(bootlog_severity_t severity, char *fmt, ...) 235*0Sstevel@tonic-gate { 236*0Sstevel@tonic-gate char message[BOOTLOG_MSG_MAX_LEN]; 237*0Sstevel@tonic-gate va_list adx; 238*0Sstevel@tonic-gate 239*0Sstevel@tonic-gate va_start(adx, fmt); 240*0Sstevel@tonic-gate (void) vsnprintf(message, BOOTLOG_MSG_MAX_LEN, 241*0Sstevel@tonic-gate dgettext(TEXT_DOMAIN, fmt), adx); 242*0Sstevel@tonic-gate va_end(adx); 243*0Sstevel@tonic-gate 244*0Sstevel@tonic-gate bootlog_common("libwanboot", severity, message); 245*0Sstevel@tonic-gate } 246*0Sstevel@tonic-gate 247*0Sstevel@tonic-gate static boolean_t 248*0Sstevel@tonic-gate send_http(void) 249*0Sstevel@tonic-gate { 250*0Sstevel@tonic-gate http_respinfo_t *resp = NULL; 251*0Sstevel@tonic-gate char buffer[BOOTLOG_MAX_URL + (BOOTLOG_QS_MAX * 3)]; 252*0Sstevel@tonic-gate char ringmessage[BOOTLOG_QS_MAX]; 253*0Sstevel@tonic-gate char *lenstr; 254*0Sstevel@tonic-gate size_t length; 255*0Sstevel@tonic-gate int retries; 256*0Sstevel@tonic-gate 257*0Sstevel@tonic-gate while ((rb_read(&ringbuffer, ringmessage) != -1)) { 258*0Sstevel@tonic-gate (void) snprintf(buffer, sizeof (buffer), "%s?%s", 259*0Sstevel@tonic-gate bl_url.abspath, url_encode(ringmessage)); 260*0Sstevel@tonic-gate 261*0Sstevel@tonic-gate for (retries = 0; retries < BOOTLOG_CONN_RETRIES; retries++) { 262*0Sstevel@tonic-gate if (retries > 0) { 263*0Sstevel@tonic-gate (void) http_srv_disconnect(bl_httphandle); 264*0Sstevel@tonic-gate if (http_srv_connect(bl_httphandle) != 0) 265*0Sstevel@tonic-gate continue; 266*0Sstevel@tonic-gate } 267*0Sstevel@tonic-gate 268*0Sstevel@tonic-gate if (http_get_request(bl_httphandle, buffer) != 0 || 269*0Sstevel@tonic-gate http_process_headers(bl_httphandle, &resp) != 0) 270*0Sstevel@tonic-gate continue; 271*0Sstevel@tonic-gate 272*0Sstevel@tonic-gate if (resp->code != 200) { 273*0Sstevel@tonic-gate http_free_respinfo(resp); 274*0Sstevel@tonic-gate continue; 275*0Sstevel@tonic-gate } 276*0Sstevel@tonic-gate 277*0Sstevel@tonic-gate http_free_respinfo(resp); 278*0Sstevel@tonic-gate lenstr = http_get_header_value(bl_httphandle, 279*0Sstevel@tonic-gate "Content-Length"); 280*0Sstevel@tonic-gate length = strtol(lenstr, NULL, 10); 281*0Sstevel@tonic-gate if (http_read_body(bl_httphandle, buffer, length) > 0) 282*0Sstevel@tonic-gate break; 283*0Sstevel@tonic-gate } 284*0Sstevel@tonic-gate 285*0Sstevel@tonic-gate /* 286*0Sstevel@tonic-gate * The attempt to log the message failed. Back the 287*0Sstevel@tonic-gate * read pointer up so that we'll try to log it again 288*0Sstevel@tonic-gate * later. 289*0Sstevel@tonic-gate */ 290*0Sstevel@tonic-gate if (retries == BOOTLOG_CONN_RETRIES) { 291*0Sstevel@tonic-gate ringbuffer.r_ptr = ptr_decr(ringbuffer.r_ptr); 292*0Sstevel@tonic-gate return (B_FALSE); 293*0Sstevel@tonic-gate } 294*0Sstevel@tonic-gate } 295*0Sstevel@tonic-gate 296*0Sstevel@tonic-gate return (B_TRUE); 297*0Sstevel@tonic-gate } 298*0Sstevel@tonic-gate 299*0Sstevel@tonic-gate static boolean_t 300*0Sstevel@tonic-gate sendmessage(bl_transport_t transport, char *message, const char *ident, 301*0Sstevel@tonic-gate bootlog_severity_t severity, int failure) 302*0Sstevel@tonic-gate { 303*0Sstevel@tonic-gate static char *progtype = NULL; 304*0Sstevel@tonic-gate char ringmessage[BOOTLOG_QS_MAX]; 305*0Sstevel@tonic-gate char hostname[MAXHOSTNAMELEN]; 306*0Sstevel@tonic-gate uint32_t msgid; 307*0Sstevel@tonic-gate boolean_t ret; 308*0Sstevel@tonic-gate int i; 309*0Sstevel@tonic-gate 310*0Sstevel@tonic-gate /* 311*0Sstevel@tonic-gate * In standalone, only log VERBOSE and DEBUG messages if the 312*0Sstevel@tonic-gate * corresponding flag (-V or -d) has been passed to boot. 313*0Sstevel@tonic-gate * 314*0Sstevel@tonic-gate * Note that some bootlog() consumers impose additional constraints on 315*0Sstevel@tonic-gate * printing these messages -- for instance, http_set_verbose() must be 316*0Sstevel@tonic-gate * used before the HTTP code will call bootlog() with BOOTLOG_VERBOSE 317*0Sstevel@tonic-gate * messages. 318*0Sstevel@tonic-gate */ 319*0Sstevel@tonic-gate #ifdef _BOOT 320*0Sstevel@tonic-gate if (severity == BOOTLOG_DEBUG && !(boothowto & RB_DEBUG)) 321*0Sstevel@tonic-gate return (B_TRUE); 322*0Sstevel@tonic-gate if (severity == BOOTLOG_VERBOSE && !verbosemode) 323*0Sstevel@tonic-gate return (B_TRUE); 324*0Sstevel@tonic-gate #endif 325*0Sstevel@tonic-gate 326*0Sstevel@tonic-gate for (i = 0; pri_names[i].c_val != NOPRI; i++) { 327*0Sstevel@tonic-gate if (severity == pri_names[i].c_val) 328*0Sstevel@tonic-gate break; 329*0Sstevel@tonic-gate } 330*0Sstevel@tonic-gate 331*0Sstevel@tonic-gate /* 332*0Sstevel@tonic-gate * VERBOSE and DEBUG messages always go to the console 333*0Sstevel@tonic-gate */ 334*0Sstevel@tonic-gate if (transport != BL_CONSOLE && 335*0Sstevel@tonic-gate (severity == BOOTLOG_DEBUG || severity == BOOTLOG_VERBOSE)) { 336*0Sstevel@tonic-gate (void) printf("%s %s %s: %s\n", gettime(), ident, 337*0Sstevel@tonic-gate pri_names[i].c_name, message); 338*0Sstevel@tonic-gate } 339*0Sstevel@tonic-gate 340*0Sstevel@tonic-gate STRLOG_MAKE_MSGID(message, msgid); 341*0Sstevel@tonic-gate (void) gethostname(hostname, sizeof (hostname)); 342*0Sstevel@tonic-gate 343*0Sstevel@tonic-gate /* 344*0Sstevel@tonic-gate * Note that in this case, "<time>" is a placeholder that will be used 345*0Sstevel@tonic-gate * to fill in the actual time on the remote end. 346*0Sstevel@tonic-gate */ 347*0Sstevel@tonic-gate (void) snprintf(ringmessage, sizeof (ringmessage), 348*0Sstevel@tonic-gate "<time> %s %s: [ID %u user.%s] %s", hostname, ident, msgid, 349*0Sstevel@tonic-gate pri_names[i].c_name, message); 350*0Sstevel@tonic-gate 351*0Sstevel@tonic-gate /* 352*0Sstevel@tonic-gate * Prevent duplicate messages from being inserted into 353*0Sstevel@tonic-gate * the ring buffer. 354*0Sstevel@tonic-gate */ 355*0Sstevel@tonic-gate if (failure == 0) { 356*0Sstevel@tonic-gate rb_write(&ringbuffer, ringmessage); 357*0Sstevel@tonic-gate } 358*0Sstevel@tonic-gate 359*0Sstevel@tonic-gate switch (transport) { 360*0Sstevel@tonic-gate case BL_CONSOLE: 361*0Sstevel@tonic-gate /* 362*0Sstevel@tonic-gate * PROGRESS messages update in-place on the console, as long 363*0Sstevel@tonic-gate * as they are of the same 'progress type' (see below) -- 364*0Sstevel@tonic-gate * if not, reset the progress information. 365*0Sstevel@tonic-gate */ 366*0Sstevel@tonic-gate if (progtype != NULL && (severity != BOOTLOG_PROGRESS || 367*0Sstevel@tonic-gate strncmp(progtype, message, strlen(progtype)) != 0)) { 368*0Sstevel@tonic-gate (void) printf("\n"); 369*0Sstevel@tonic-gate free(progtype); 370*0Sstevel@tonic-gate progtype = NULL; 371*0Sstevel@tonic-gate } 372*0Sstevel@tonic-gate 373*0Sstevel@tonic-gate (void) printf("%s %s %s: %s\r", gettime(), ident, 374*0Sstevel@tonic-gate pri_names[i].c_name, message); 375*0Sstevel@tonic-gate 376*0Sstevel@tonic-gate if (severity != BOOTLOG_PROGRESS) { 377*0Sstevel@tonic-gate (void) printf("\n"); 378*0Sstevel@tonic-gate } else if (progtype == NULL) { 379*0Sstevel@tonic-gate /* 380*0Sstevel@tonic-gate * New progress message; save its "type" (the part 381*0Sstevel@tonic-gate * of the message up to and including the first 382*0Sstevel@tonic-gate * colon). This should be made less clumsy in the 383*0Sstevel@tonic-gate * future. 384*0Sstevel@tonic-gate */ 385*0Sstevel@tonic-gate progtype = strdup(message); 386*0Sstevel@tonic-gate if (progtype != NULL) { 387*0Sstevel@tonic-gate for (i = 0; progtype[i] != '\0'; i++) { 388*0Sstevel@tonic-gate if (progtype[i] == ':') { 389*0Sstevel@tonic-gate progtype[++i] = '\0'; 390*0Sstevel@tonic-gate break; 391*0Sstevel@tonic-gate } 392*0Sstevel@tonic-gate } 393*0Sstevel@tonic-gate } 394*0Sstevel@tonic-gate } 395*0Sstevel@tonic-gate ret = B_TRUE; 396*0Sstevel@tonic-gate break; 397*0Sstevel@tonic-gate 398*0Sstevel@tonic-gate case BL_LOCAL_FILE: 399*0Sstevel@tonic-gate if (bl_filehandle == NULL) 400*0Sstevel@tonic-gate return (B_FALSE); 401*0Sstevel@tonic-gate 402*0Sstevel@tonic-gate (void) fprintf(bl_filehandle, "%s %s %s: [ID %u user.%s] %s\n", 403*0Sstevel@tonic-gate gettime(), hostname, ident, msgid, pri_names[i].c_name, 404*0Sstevel@tonic-gate message); 405*0Sstevel@tonic-gate ret = B_TRUE; 406*0Sstevel@tonic-gate break; 407*0Sstevel@tonic-gate 408*0Sstevel@tonic-gate case BL_HTTP: 409*0Sstevel@tonic-gate case BL_HTTPS: 410*0Sstevel@tonic-gate if (bl_httphandle == NULL) 411*0Sstevel@tonic-gate return (B_FALSE); 412*0Sstevel@tonic-gate ret = send_http(); 413*0Sstevel@tonic-gate break; 414*0Sstevel@tonic-gate 415*0Sstevel@tonic-gate case BL_NO_TRANSPORT: 416*0Sstevel@tonic-gate default: 417*0Sstevel@tonic-gate ret = B_FALSE; 418*0Sstevel@tonic-gate } 419*0Sstevel@tonic-gate 420*0Sstevel@tonic-gate return (ret); 421*0Sstevel@tonic-gate } 422*0Sstevel@tonic-gate 423*0Sstevel@tonic-gate static bl_transport_t 424*0Sstevel@tonic-gate openbootlog(void) 425*0Sstevel@tonic-gate { 426*0Sstevel@tonic-gate static boolean_t got_boot_logger = B_FALSE; 427*0Sstevel@tonic-gate static boolean_t bl_url_valid = B_FALSE; 428*0Sstevel@tonic-gate static boolean_t clientauth = B_FALSE; 429*0Sstevel@tonic-gate static bc_handle_t bootconf_handle; 430*0Sstevel@tonic-gate bl_transport_t transport; 431*0Sstevel@tonic-gate 432*0Sstevel@tonic-gate /* 433*0Sstevel@tonic-gate * We try to use a logfile in userland since our consumer (install) 434*0Sstevel@tonic-gate * needs complete control over the terminal. 435*0Sstevel@tonic-gate */ 436*0Sstevel@tonic-gate #ifndef _BOOT 437*0Sstevel@tonic-gate if (bl_filehandle == NULL) 438*0Sstevel@tonic-gate bl_filehandle = fopen("/var/log/bootlog", "a"); 439*0Sstevel@tonic-gate #endif 440*0Sstevel@tonic-gate transport = (bl_filehandle != NULL) ? BL_LOCAL_FILE : BL_CONSOLE; 441*0Sstevel@tonic-gate 442*0Sstevel@tonic-gate /* 443*0Sstevel@tonic-gate * If we haven't already been able to access wanboot.conf for a 444*0Sstevel@tonic-gate * boot_logger URL, see if we can now. 445*0Sstevel@tonic-gate */ 446*0Sstevel@tonic-gate if (!got_boot_logger && 447*0Sstevel@tonic-gate bootconf_init(&bootconf_handle, NULL) == BC_SUCCESS) { 448*0Sstevel@tonic-gate char *urlstr; 449*0Sstevel@tonic-gate char *cas; 450*0Sstevel@tonic-gate 451*0Sstevel@tonic-gate /* 452*0Sstevel@tonic-gate * If there is a boot_logger, ensure that it's is a legal URL. 453*0Sstevel@tonic-gate */ 454*0Sstevel@tonic-gate if ((urlstr = bootconf_get(&bootconf_handle, 455*0Sstevel@tonic-gate BC_BOOT_LOGGER)) != NULL && 456*0Sstevel@tonic-gate url_parse(urlstr, &bl_url) == URL_PARSE_SUCCESS) { 457*0Sstevel@tonic-gate bl_url_valid = B_TRUE; 458*0Sstevel@tonic-gate } 459*0Sstevel@tonic-gate 460*0Sstevel@tonic-gate /* 461*0Sstevel@tonic-gate * If the boot_logger URL uses an HTTPS scheme, see if 462*0Sstevel@tonic-gate * client authentication is specified. 463*0Sstevel@tonic-gate */ 464*0Sstevel@tonic-gate if (bl_url.https) { 465*0Sstevel@tonic-gate cas = bootconf_get(&bootconf_handle, 466*0Sstevel@tonic-gate BC_CLIENT_AUTHENTICATION); 467*0Sstevel@tonic-gate if (cas != NULL) { 468*0Sstevel@tonic-gate clientauth = (strcmp(cas, BC_YES) == 0); 469*0Sstevel@tonic-gate } 470*0Sstevel@tonic-gate } 471*0Sstevel@tonic-gate 472*0Sstevel@tonic-gate bootconf_end(&bootconf_handle); 473*0Sstevel@tonic-gate 474*0Sstevel@tonic-gate /* 475*0Sstevel@tonic-gate * Having now accessed wanboot.conf, remember not to come 476*0Sstevel@tonic-gate * this way again; the value of boot_logger cannot change. 477*0Sstevel@tonic-gate */ 478*0Sstevel@tonic-gate got_boot_logger = B_TRUE; 479*0Sstevel@tonic-gate } 480*0Sstevel@tonic-gate 481*0Sstevel@tonic-gate /* 482*0Sstevel@tonic-gate * If there is no legal boot_logger URL available, then we're done. 483*0Sstevel@tonic-gate */ 484*0Sstevel@tonic-gate if (!bl_url_valid) { 485*0Sstevel@tonic-gate return (transport); 486*0Sstevel@tonic-gate } 487*0Sstevel@tonic-gate 488*0Sstevel@tonic-gate /* 489*0Sstevel@tonic-gate * If we don't already have a bl_httphandle, try to get one. 490*0Sstevel@tonic-gate * If we fail, then we're done. 491*0Sstevel@tonic-gate */ 492*0Sstevel@tonic-gate if (bl_httphandle == NULL) { 493*0Sstevel@tonic-gate bl_httphandle = http_srv_init(&bl_url); 494*0Sstevel@tonic-gate if (bl_httphandle == NULL) { 495*0Sstevel@tonic-gate return (transport); 496*0Sstevel@tonic-gate } 497*0Sstevel@tonic-gate } 498*0Sstevel@tonic-gate 499*0Sstevel@tonic-gate /* 500*0Sstevel@tonic-gate * If we succeed in setting up the connection, 501*0Sstevel@tonic-gate * then we use the connection as our transport. 502*0Sstevel@tonic-gate * Otherwise, we use the transport we've already 503*0Sstevel@tonic-gate * determined above. 504*0Sstevel@tonic-gate */ 505*0Sstevel@tonic-gate if (setup_con(bl_httphandle, bl_url.https, clientauth)) { 506*0Sstevel@tonic-gate transport = bl_url.https ? BL_HTTPS : BL_HTTP; 507*0Sstevel@tonic-gate } 508*0Sstevel@tonic-gate 509*0Sstevel@tonic-gate return (transport); 510*0Sstevel@tonic-gate } 511*0Sstevel@tonic-gate 512*0Sstevel@tonic-gate static boolean_t 513*0Sstevel@tonic-gate setup_con(http_handle_t handle, boolean_t https, boolean_t client_auth) 514*0Sstevel@tonic-gate { 515*0Sstevel@tonic-gate static boolean_t got_proxy = B_FALSE; 516*0Sstevel@tonic-gate static boolean_t proxy_valid = B_FALSE; 517*0Sstevel@tonic-gate static url_hport_t proxy; 518*0Sstevel@tonic-gate int i; 519*0Sstevel@tonic-gate 520*0Sstevel@tonic-gate /* 521*0Sstevel@tonic-gate * If an HTTPS scheme is specified, then check that time 522*0Sstevel@tonic-gate * has been initialized. 523*0Sstevel@tonic-gate * If time() returns a non-zero value, then we know 524*0Sstevel@tonic-gate * that the boot file system has been mounted and that 525*0Sstevel@tonic-gate * we have a trusted time. 526*0Sstevel@tonic-gate */ 527*0Sstevel@tonic-gate if (https && time(0) == 0) 528*0Sstevel@tonic-gate return (B_FALSE); 529*0Sstevel@tonic-gate 530*0Sstevel@tonic-gate if (!got_proxy && bootinfo_init()) { 531*0Sstevel@tonic-gate char hpstr[URL_MAX_STRLEN]; 532*0Sstevel@tonic-gate size_t vallen = sizeof (hpstr); 533*0Sstevel@tonic-gate 534*0Sstevel@tonic-gate /* 535*0Sstevel@tonic-gate * If there is a http-proxy, ensure that it's a legal host:port. 536*0Sstevel@tonic-gate */ 537*0Sstevel@tonic-gate if (bootinfo_get(BI_HTTP_PROXY, hpstr, &vallen, NULL) == 538*0Sstevel@tonic-gate BI_E_SUCCESS && vallen > 0) { 539*0Sstevel@tonic-gate hpstr[vallen] = '\0'; 540*0Sstevel@tonic-gate if (url_parse_hostport(hpstr, &proxy, 541*0Sstevel@tonic-gate URL_DFLT_PROXY_PORT) == URL_PARSE_SUCCESS) { 542*0Sstevel@tonic-gate proxy_valid = B_TRUE; 543*0Sstevel@tonic-gate } 544*0Sstevel@tonic-gate } 545*0Sstevel@tonic-gate 546*0Sstevel@tonic-gate got_proxy = B_TRUE; 547*0Sstevel@tonic-gate } 548*0Sstevel@tonic-gate if (proxy_valid && http_set_proxy(handle, &proxy) != 0) 549*0Sstevel@tonic-gate return (B_FALSE); 550*0Sstevel@tonic-gate 551*0Sstevel@tonic-gate (void) http_set_keepalive(handle, 1); 552*0Sstevel@tonic-gate (void) http_set_socket_read_timeout(handle, BOOTLOG_HTTP_TIMEOUT); 553*0Sstevel@tonic-gate 554*0Sstevel@tonic-gate /* 555*0Sstevel@tonic-gate * If an HTTPS scheme is specified, then setup the necessary 556*0Sstevel@tonic-gate * SSL context for the connection 557*0Sstevel@tonic-gate */ 558*0Sstevel@tonic-gate if (https) { 559*0Sstevel@tonic-gate if (http_set_random_file(handle, "/dev/urandom") == -1) 560*0Sstevel@tonic-gate return (B_FALSE); 561*0Sstevel@tonic-gate 562*0Sstevel@tonic-gate if (http_set_certificate_authority_file(NB_CA_CERT_PATH) < 0) 563*0Sstevel@tonic-gate return (B_FALSE); 564*0Sstevel@tonic-gate 565*0Sstevel@tonic-gate /* 566*0Sstevel@tonic-gate * The client certificate and key will not exist unless 567*0Sstevel@tonic-gate * client authentication has been configured. If it is 568*0Sstevel@tonic-gate * configured then the webserver will have added these 569*0Sstevel@tonic-gate * files to the wanboot file system and the HTTP library 570*0Sstevel@tonic-gate * needs to be made aware of their existence. 571*0Sstevel@tonic-gate */ 572*0Sstevel@tonic-gate if (client_auth) { 573*0Sstevel@tonic-gate if (http_set_client_certificate_file(handle, 574*0Sstevel@tonic-gate NB_CLIENT_CERT_PATH) < 0) { 575*0Sstevel@tonic-gate return (B_FALSE); 576*0Sstevel@tonic-gate } 577*0Sstevel@tonic-gate 578*0Sstevel@tonic-gate if (http_set_private_key_file(handle, 579*0Sstevel@tonic-gate NB_CLIENT_KEY_PATH) < 0) { 580*0Sstevel@tonic-gate return (B_FALSE); 581*0Sstevel@tonic-gate } 582*0Sstevel@tonic-gate } 583*0Sstevel@tonic-gate 584*0Sstevel@tonic-gate if (http_set_password(handle, WANBOOT_PASSPHRASE) < 0) 585*0Sstevel@tonic-gate return (B_FALSE); 586*0Sstevel@tonic-gate } 587*0Sstevel@tonic-gate 588*0Sstevel@tonic-gate for (i = 0; i < BOOTLOG_CONN_RETRIES; i++) { 589*0Sstevel@tonic-gate if (http_srv_connect(handle) == 0) 590*0Sstevel@tonic-gate return (B_TRUE); 591*0Sstevel@tonic-gate 592*0Sstevel@tonic-gate (void) http_srv_disconnect(handle); 593*0Sstevel@tonic-gate } 594*0Sstevel@tonic-gate 595*0Sstevel@tonic-gate return (B_FALSE); 596*0Sstevel@tonic-gate } 597*0Sstevel@tonic-gate 598*0Sstevel@tonic-gate static char * 599*0Sstevel@tonic-gate url_encode(const char *ibufp) 600*0Sstevel@tonic-gate { 601*0Sstevel@tonic-gate int i; 602*0Sstevel@tonic-gate char c; 603*0Sstevel@tonic-gate unsigned char nibble; 604*0Sstevel@tonic-gate static char obuff[BOOTLOG_QS_MAX * 3]; 605*0Sstevel@tonic-gate char *obufp = obuff; 606*0Sstevel@tonic-gate 607*0Sstevel@tonic-gate /* 608*0Sstevel@tonic-gate * Encode special characters as outlined in RFC2396. 609*0Sstevel@tonic-gate * 610*0Sstevel@tonic-gate * Special characters are encoded as a triplets beginning 611*0Sstevel@tonic-gate * with '%' followed by the two hexidecimal digits representing 612*0Sstevel@tonic-gate * the octet code. The space character is special. It can be encoded 613*0Sstevel@tonic-gate * simply as a '+'. 614*0Sstevel@tonic-gate */ 615*0Sstevel@tonic-gate while ((c = *ibufp++) != '\0') { 616*0Sstevel@tonic-gate /* 617*0Sstevel@tonic-gate * Is the character one of the special characters 618*0Sstevel@tonic-gate * that require encoding? If so append '%' to the output 619*0Sstevel@tonic-gate * buffer follow that by the hexascii value. 620*0Sstevel@tonic-gate */ 621*0Sstevel@tonic-gate if (strchr("/?{}|^~[]`<>#%=\"\t", c) != NULL) { 622*0Sstevel@tonic-gate *obufp++ = '%'; 623*0Sstevel@tonic-gate /* 624*0Sstevel@tonic-gate * Compute the character's hex value and 625*0Sstevel@tonic-gate * convert it to ASCII. That is two nibbles 626*0Sstevel@tonic-gate * per character. 627*0Sstevel@tonic-gate */ 628*0Sstevel@tonic-gate for (i = 1; i >= 0; i--) { 629*0Sstevel@tonic-gate nibble = ((uchar_t)c >> (4 * i)) & 0x0f; 630*0Sstevel@tonic-gate /* 631*0Sstevel@tonic-gate * If the hex digit is 0xa - 0xf, then 632*0Sstevel@tonic-gate * compute its ASCII value by adding 0x37 633*0Sstevel@tonic-gate * else 0x0 - 0x9 just add 0x30. 634*0Sstevel@tonic-gate */ 635*0Sstevel@tonic-gate if (nibble > 0x9) 636*0Sstevel@tonic-gate nibble += 0x37; 637*0Sstevel@tonic-gate else 638*0Sstevel@tonic-gate nibble += 0x30; 639*0Sstevel@tonic-gate *obufp++ = nibble; 640*0Sstevel@tonic-gate } 641*0Sstevel@tonic-gate /* 642*0Sstevel@tonic-gate * The space character gets a special mapping. 643*0Sstevel@tonic-gate */ 644*0Sstevel@tonic-gate } else if (c == ' ') { 645*0Sstevel@tonic-gate *obufp++ = '+'; 646*0Sstevel@tonic-gate 647*0Sstevel@tonic-gate /* 648*0Sstevel@tonic-gate * Append the rest (sans any CR character) 649*0Sstevel@tonic-gate */ 650*0Sstevel@tonic-gate } else if (c != '\n') { 651*0Sstevel@tonic-gate *obufp++ = c; 652*0Sstevel@tonic-gate } 653*0Sstevel@tonic-gate } 654*0Sstevel@tonic-gate *obufp = '\0'; 655*0Sstevel@tonic-gate return (obuff); 656*0Sstevel@tonic-gate } 657*0Sstevel@tonic-gate 658*0Sstevel@tonic-gate static void 659*0Sstevel@tonic-gate rb_init(struct ringbuffer_t *buffer) 660*0Sstevel@tonic-gate { 661*0Sstevel@tonic-gate int i; 662*0Sstevel@tonic-gate 663*0Sstevel@tonic-gate buffer->w_ptr = 0; 664*0Sstevel@tonic-gate buffer->r_ptr = 0; 665*0Sstevel@tonic-gate 666*0Sstevel@tonic-gate for (i = 0; i < BOOTLOG_RING_NELEM; i++) 667*0Sstevel@tonic-gate buffer->entries[i].message[0] = '\0'; 668*0Sstevel@tonic-gate } 669*0Sstevel@tonic-gate 670*0Sstevel@tonic-gate static int 671*0Sstevel@tonic-gate ptr_incr(int ptr) 672*0Sstevel@tonic-gate { 673*0Sstevel@tonic-gate if (++ptr < BOOTLOG_RING_NELEM) 674*0Sstevel@tonic-gate return (ptr); 675*0Sstevel@tonic-gate else 676*0Sstevel@tonic-gate return (0); 677*0Sstevel@tonic-gate } 678*0Sstevel@tonic-gate 679*0Sstevel@tonic-gate static int 680*0Sstevel@tonic-gate ptr_decr(int ptr) 681*0Sstevel@tonic-gate { 682*0Sstevel@tonic-gate if (ptr == 0) 683*0Sstevel@tonic-gate return (BOOTLOG_RING_NELEM - 1); 684*0Sstevel@tonic-gate else 685*0Sstevel@tonic-gate return (--ptr); 686*0Sstevel@tonic-gate } 687*0Sstevel@tonic-gate 688*0Sstevel@tonic-gate static void 689*0Sstevel@tonic-gate rb_write(struct ringbuffer_t *buffer, const char *buff) 690*0Sstevel@tonic-gate { 691*0Sstevel@tonic-gate (void) strlcpy(buffer->entries[buffer->w_ptr].message, buff, 692*0Sstevel@tonic-gate BOOTLOG_QS_MAX); 693*0Sstevel@tonic-gate buffer->w_ptr = ptr_incr(buffer->w_ptr); 694*0Sstevel@tonic-gate if (buffer->r_ptr == buffer->w_ptr) 695*0Sstevel@tonic-gate buffer->r_ptr = ptr_incr(buffer->r_ptr); 696*0Sstevel@tonic-gate } 697*0Sstevel@tonic-gate 698*0Sstevel@tonic-gate static int 699*0Sstevel@tonic-gate rb_read(struct ringbuffer_t *buffer, char *buff) 700*0Sstevel@tonic-gate { 701*0Sstevel@tonic-gate if (buffer->r_ptr != buffer->w_ptr) { 702*0Sstevel@tonic-gate (void) strlcpy(buff, buffer->entries[buffer->r_ptr].message, 703*0Sstevel@tonic-gate BOOTLOG_QS_MAX); 704*0Sstevel@tonic-gate buffer->r_ptr = ptr_incr(buffer->r_ptr); 705*0Sstevel@tonic-gate return (0); 706*0Sstevel@tonic-gate } 707*0Sstevel@tonic-gate return (-1); 708*0Sstevel@tonic-gate } 709