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 *
gettime(void)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
bootlog_common(const char * ident,bootlog_severity_t severity,char * message)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
bootlog(const char * ident,bootlog_severity_t severity,char * fmt,...)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
libbootlog(bootlog_severity_t severity,char * fmt,...)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
send_http(void)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
sendmessage(bl_transport_t transport,char * message,const char * ident,bootlog_severity_t severity,int failure)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
openbootlog(void)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
setup_con(http_handle_t handle,boolean_t https,boolean_t client_auth)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 *
url_encode(const char * ibufp)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
rb_init(struct ringbuffer_t * buffer)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
ptr_incr(int ptr)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
ptr_decr(int ptr)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
rb_write(struct ringbuffer_t * buffer,const char * buff)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
rb_read(struct ringbuffer_t * buffer,char * buff)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