bootlog.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* bootlog() - error notification and progress reporting for
* WAN boot components
*/
#include <sys/wanboot_impl.h>
#include <errno.h>
#include <time.h>
#include <boot_http.h>
#include <stdio.h>
#include <parseURL.h>
#include <bootlog.h>
#include <strings.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <libintl.h>
#include <netboot_paths.h>
#include <wanboot_conf.h>
#include <bootinfo.h>
#ifdef _BOOT
#include <sys/bootdebug.h>
#endif
"panic", BOOTLOG_EMERG,
"alert", BOOTLOG_ALERT,
"crit", BOOTLOG_CRIT,
"warn", BOOTLOG_WARNING,
"info", BOOTLOG_INFO,
"debug", BOOTLOG_DEBUG,
"verbose", BOOTLOG_VERBOSE,
"progress", BOOTLOG_PROGRESS,
"none", NOPRI,
NULL, -1
};
typedef enum {
typedef struct list_entry {
char message[BOOTLOG_QS_MAX];
struct list_entry *flink;
} list;
#define BOOTLOG_RING_NELEM 512
static struct ringbuffer_t {
int w_ptr;
int r_ptr;
} ringbuffer;
static bl_transport_t openbootlog(void);
static char *url_encode(const char *);
bootlog_severity_t, int);
static void rb_init(struct ringbuffer_t *);
static void rb_write(struct ringbuffer_t *, const char *);
static int rb_read(struct ringbuffer_t *, char *);
/*
* Return a string representing the current time; not thread-safe.
*/
static const char *
gettime(void)
{
static char timebuf[sizeof ("Tue Jan 19 03:14:07 2038\n")];
return ("<time unavailable>");
return (timebuf);
}
/*
* bootlog_common() - Common routine used by bootlog() and
* bootlog_internal() to write a message comprising a message
* header and a message body to the appropriate transport.
* The message header comprises an ident string and a message
* severity.
*/
static void
{
static int blrecurs;
static int blretry;
/*
* This function may be called recursively because the HTTP code
* is a bootlog consumer. The blrecurs variable is used to determine
* whether or not the invocation is recursive.
*/
blrecurs++;
/*
* If this is the first bootlog call then setup the transport.
* We only do this in a non-recursive invocation as openbootlog()
* results in a recursive call for a HTTP or HTTPS transport.
*/
bl_transport = openbootlog();
}
/*
* If we're not there already, try to move up a level.
* This is necessary because our consumer may have begun
* logging before it had enough information to initialize
* its HTTP or HTTPS transport. We've arbitrarily decided
* that we'll only check to see if we should move up, on
* every third (blretry) non-recursive invocation.
*/
if (blrecurs == 1 &&
if (blretry > 3) {
bl_transport = openbootlog();
blretry = 0;
} else
blretry++;
}
if (entry_transport != bl_transport) {
switch (bl_transport) {
case BL_CONSOLE:
(void) printf(
"%s wanboot info: WAN boot messages->console\n",
gettime());
break;
case BL_HTTP:
case BL_HTTPS:
(void) printf(
"%s wanboot info: WAN boot messages->%s:%u\n",
break;
default:
break;
}
}
/*
* Failed attempts and recursively generated log messages are
* sent to the fallback transport.
*/
severity, 0)) {
/*
* Fallback to a log file if one exists, or the console
* as a last resort. Note that bl_filehandle will always
* be NULL in standalone.
*/
}
blrecurs--;
}
/*
* bootlog() - the exposed interface for logging boot messages.
*/
/* PRINTFLIKE3 */
void
{
char message[BOOTLOG_MSG_MAX_LEN];
}
/*
* libbootlog() - an internal interface for logging boot
* messages.
*/
/* PRINTFLIKE2 */
void
{
char message[BOOTLOG_MSG_MAX_LEN];
}
static boolean_t
send_http(void)
{
char ringmessage[BOOTLOG_QS_MAX];
char *lenstr;
int retries;
if (retries > 0) {
(void) http_srv_disconnect(bl_httphandle);
if (http_srv_connect(bl_httphandle) != 0)
continue;
}
continue;
continue;
}
"Content-Length");
break;
}
/*
* The attempt to log the message failed. Back the
* read pointer up so that we'll try to log it again
* later.
*/
if (retries == BOOTLOG_CONN_RETRIES) {
return (B_FALSE);
}
}
return (B_TRUE);
}
static boolean_t
{
char ringmessage[BOOTLOG_QS_MAX];
char hostname[MAXHOSTNAMELEN];
int i;
/*
* In standalone, only log VERBOSE and DEBUG messages if the
* corresponding flag (-V or -d) has been passed to boot.
*
* Note that some bootlog() consumers impose additional constraints on
* printing these messages -- for instance, http_set_verbose() must be
* used before the HTTP code will call bootlog() with BOOTLOG_VERBOSE
* messages.
*/
#ifdef _BOOT
return (B_TRUE);
return (B_TRUE);
#endif
break;
}
/*
* VERBOSE and DEBUG messages always go to the console
*/
if (transport != BL_CONSOLE &&
}
/*
* Note that in this case, "<time>" is a placeholder that will be used
* to fill in the actual time on the remote end.
*/
/*
* Prevent duplicate messages from being inserted into
* the ring buffer.
*/
if (failure == 0) {
}
switch (transport) {
case BL_CONSOLE:
/*
* PROGRESS messages update in-place on the console, as long
* as they are of the same 'progress type' (see below) --
* if not, reset the progress information.
*/
(void) printf("\n");
}
if (severity != BOOTLOG_PROGRESS) {
(void) printf("\n");
/*
* New progress message; save its "type" (the part
* of the message up to and including the first
* colon). This should be made less clumsy in the
* future.
*/
for (i = 0; progtype[i] != '\0'; i++) {
if (progtype[i] == ':') {
progtype[++i] = '\0';
break;
}
}
}
}
break;
case BL_LOCAL_FILE:
if (bl_filehandle == NULL)
return (B_FALSE);
message);
break;
case BL_HTTP:
case BL_HTTPS:
if (bl_httphandle == NULL)
return (B_FALSE);
break;
case BL_NO_TRANSPORT:
default:
}
return (ret);
}
static bl_transport_t
openbootlog(void)
{
static bc_handle_t bootconf_handle;
/*
* We try to use a logfile in userland since our consumer (install)
* needs complete control over the terminal.
*/
#ifndef _BOOT
if (bl_filehandle == NULL)
#endif
/*
* If we haven't already been able to access wanboot.conf for a
* boot_logger URL, see if we can now.
*/
if (!got_boot_logger &&
char *urlstr;
char *cas;
/*
* If there is a boot_logger, ensure that it's is a legal URL.
*/
BC_BOOT_LOGGER)) != NULL &&
}
/*
* If the boot_logger URL uses an HTTPS scheme, see if
* client authentication is specified.
*/
}
}
/*
* Having now accessed wanboot.conf, remember not to come
* this way again; the value of boot_logger cannot change.
*/
}
/*
* If there is no legal boot_logger URL available, then we're done.
*/
if (!bl_url_valid) {
return (transport);
}
/*
* If we don't already have a bl_httphandle, try to get one.
* If we fail, then we're done.
*/
if (bl_httphandle == NULL) {
if (bl_httphandle == NULL) {
return (transport);
}
}
/*
* If we succeed in setting up the connection,
* then we use the connection as our transport.
* Otherwise, we use the transport we've already
* determined above.
*/
}
return (transport);
}
static boolean_t
{
static url_hport_t proxy;
int i;
/*
* If an HTTPS scheme is specified, then check that time
* has been initialized.
* If time() returns a non-zero value, then we know
* that the boot file system has been mounted and that
* we have a trusted time.
*/
return (B_FALSE);
if (!got_proxy && bootinfo_init()) {
char hpstr[URL_MAX_STRLEN];
/*
* If there is a http-proxy, ensure that it's a legal host:port.
*/
BI_E_SUCCESS && vallen > 0) {
}
}
}
return (B_FALSE);
/*
* If an HTTPS scheme is specified, then setup the necessary
* SSL context for the connection
*/
if (https) {
return (B_FALSE);
return (B_FALSE);
/*
* The client certificate and key will not exist unless
* client authentication has been configured. If it is
* configured then the webserver will have added these
* files to the wanboot file system and the HTTP library
* needs to be made aware of their existence.
*/
if (client_auth) {
NB_CLIENT_CERT_PATH) < 0) {
return (B_FALSE);
}
NB_CLIENT_KEY_PATH) < 0) {
return (B_FALSE);
}
}
return (B_FALSE);
}
for (i = 0; i < BOOTLOG_CONN_RETRIES; i++) {
if (http_srv_connect(handle) == 0)
return (B_TRUE);
(void) http_srv_disconnect(handle);
}
return (B_FALSE);
}
static char *
url_encode(const char *ibufp)
{
int i;
char c;
unsigned char nibble;
/*
* Encode special characters as outlined in RFC2396.
*
* Special characters are encoded as a triplets beginning
* with '%' followed by the two hexidecimal digits representing
* the octet code. The space character is special. It can be encoded
* simply as a '+'.
*/
while ((c = *ibufp++) != '\0') {
/*
* Is the character one of the special characters
* that require encoding? If so append '%' to the output
* buffer follow that by the hexascii value.
*/
*obufp++ = '%';
/*
* Compute the character's hex value and
* convert it to ASCII. That is two nibbles
* per character.
*/
for (i = 1; i >= 0; i--) {
/*
* If the hex digit is 0xa - 0xf, then
* compute its ASCII value by adding 0x37
* else 0x0 - 0x9 just add 0x30.
*/
if (nibble > 0x9)
nibble += 0x37;
else
nibble += 0x30;
}
/*
* The space character gets a special mapping.
*/
} else if (c == ' ') {
*obufp++ = '+';
/*
* Append the rest (sans any CR character)
*/
} else if (c != '\n') {
*obufp++ = c;
}
}
*obufp = '\0';
return (obuff);
}
static void
{
int i;
for (i = 0; i < BOOTLOG_RING_NELEM; i++)
}
static int
{
if (++ptr < BOOTLOG_RING_NELEM)
return (ptr);
else
return (0);
}
static int
{
if (ptr == 0)
return (BOOTLOG_RING_NELEM - 1);
else
return (--ptr);
}
static void
{
}
static int
{
return (0);
}
return (-1);
}