1/*
2 * Copyright (c) 2013 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#include <sys/errno.h>
30#include <sys/types.h>
31#include <sys/malloc.h>
32#include <sys/buf.h>
33#include <sys/time.h>
34#include <sys/kauth.h>
35#include <sys/mount.h>
36#include <sys/vnode.h>
37#include <sys/syslog.h>
38#include <sys/vnode_internal.h>
39#include <sys/fslog.h>
40#include <sys/mount_internal.h>
41#include <sys/kasl.h>
42
43#include <dev/random/randomdev.h>
44
45#include <uuid/uuid.h>
46
47#include <stdarg.h>
48
49/* String to append as format modifier for each key-value pair */
50#define	KASL_KEYVAL_FMT	"[%s %s] "
51#define	KASL_KEYVAL_FMT_LEN	(sizeof(KASL_KEYVAL_FMT) - 1)
52
53#define	KASL_NEWLINE_CHAR	"\n"
54#define	KASL_NEWLINE_CHAR_LEN	(sizeof(KASL_NEWLINE_CHAR) - 1)
55
56/* Length of entire ASL message in 10 characters.  Kernel defaults to zero */
57#define	KASL_ASL_MSG_LEN	"         0"
58
59/* Length of default format string to be used by printf */
60#define	MAX_FMT_LEN		256
61
62
63/* Function to print input values as key-value pairs in format
64 * identifiable by Apple system log (ASL) facility. All key-value pairs
65 * are assumed to be pointer to strings and are provided using two ways -
66 * (a) va_list argument which is a list of varying number of arguments
67 *     created by the caller of this function.
68 * (b) variable number of arguments passed to this function.
69 *
70 * Parameters -
71 * 	level 	  - Priority level for this ASL message
72 *	facility  - Facility for this ASL message.
73 *	num_pairs - Number of key-value pairs provided by vargs argument.
74 *	vargs 	  - List of key-value pairs.
75 *	... 	  - Additional key-value pairs (apart from vargs) as variable
76 *	      	    argument list.  A NULL value indicates the end of the
77 *	      	    variable argument list.
78 *
79 * Returns -
80 *	zero	- On success, when it prints all key-values pairs provided.
81 *	E2BIG	- When it cannot print all key-value pairs provided and had
82 *		  to truncate the output.
83 */
84int
85kern_asl_msg_va(int level, const char *facility, int num_pairs, va_list vargs, ...)
86{
87	int err = 0;
88	char fmt[MAX_FMT_LEN];	/* Format string to use with vaddlog */
89	int calc_pairs = 0;
90	size_t len;
91	int i;
92	va_list ap;
93	char *ptr;
94
95	/* Mask extra bits, if any, from priority level */
96	level = LOG_PRI(level);
97
98	/* Create the first part of format string consisting of ASL
99	 * message length, level, and facility.
100	 */
101	if (facility) {
102		snprintf(fmt, MAX_FMT_LEN, "%s [%s %d] [%s %s] ",
103			KASL_ASL_MSG_LEN,
104			KASL_KEY_LEVEL, level,
105			KASL_KEY_FACILITY, facility);
106	} else {
107		snprintf(fmt, MAX_FMT_LEN, "%s [%s %d] ",
108			KASL_ASL_MSG_LEN,
109			KASL_KEY_LEVEL, level);
110	}
111
112	/* Determine the number of key-value format string [%s %s] that
113	 * should be added in format string for every key-value pair provided
114	 * in va_list.  Calculate maximum number of format string that can be
115	 * accommodated in the remaining format buffer (after saving space
116	 * for newline character).  If the caller provided pairs in va_list
117	 * is more than calculated pairs, truncate extra pairs.
118	 */
119	len = MAX_FMT_LEN - strlen(fmt) - KASL_NEWLINE_CHAR_LEN - 1;
120	calc_pairs = len / KASL_KEYVAL_FMT_LEN;
121	if (num_pairs <= calc_pairs) {
122		calc_pairs = num_pairs;
123	} else {
124		err = E2BIG;
125	}
126
127	/* Append format strings [%s %s] for the key-value pairs in vargs */
128	len = MAX_FMT_LEN - KASL_NEWLINE_CHAR_LEN;
129	for (i = 0; i < calc_pairs; i++) {
130		(void) strlcat(fmt, KASL_KEYVAL_FMT, len);
131	}
132
133	/* Count number of variable arguments provided to this function
134	 * and determine total number of key-value pairs.
135	 */
136	calc_pairs = 0;
137	va_start(ap, vargs);
138	ptr = va_arg(ap, char *);
139	while (ptr) {
140		calc_pairs++;
141		ptr = va_arg(ap, char *);
142	}
143	calc_pairs /= 2;
144	va_end(ap);
145
146	/* If user provided variable number of arguments, append them as
147	 * as real key-value "[k v]" into the format string.  If the format
148	 * string is too small, ignore the key-value pair completely.
149	 */
150	if (calc_pairs) {
151		char *key, *val;
152		size_t pairlen;
153		int offset;
154
155		/* Calculate bytes available for key-value pairs after reserving
156		 * bytes for newline character and NULL terminator
157		 */
158		len = MAX_FMT_LEN - strlen(fmt) - KASL_NEWLINE_CHAR_LEN - 1;
159		offset = strlen(fmt);
160
161		va_start(ap, vargs);
162		for (i = 0; i < calc_pairs; i++) {
163			key = va_arg(ap, char *);
164			val = va_arg(ap, char *);
165
166			/* Calculate bytes required to store next key-value pair
167			 * as "[key val] " including space for '[', ']', and
168			 * two spaces.
169			 */
170			pairlen = strlen(key) + strlen(val) + 4;
171			if (pairlen > len) {
172				err = E2BIG;
173				break;
174			}
175
176			/* len + 1 because one byte has been set aside for NULL
177			 * terminator in calculation of 'len' above
178			 */
179			snprintf((fmt + offset), len + 1, KASL_KEYVAL_FMT,
180			    key, val);
181			offset += pairlen;
182			len -= pairlen;
183		}
184		va_end(ap);
185	}
186
187	/* Append newline */
188	(void) strlcat(fmt, KASL_NEWLINE_CHAR, MAX_FMT_LEN);
189
190	/* Print the key-value pairs in ASL format */
191	vaddlog(fmt, vargs);
192
193	return (err);
194}
195
196int
197kern_asl_msg(int level, const char *facility, int num_pairs, ...)
198{
199	int err;
200	va_list ap;
201
202	va_start(ap, num_pairs);
203	err = kern_asl_msg_va(level, facility,
204	    num_pairs, ap, NULL);
205	va_end(ap);
206
207	return err;
208}
209
210/* Search if given string contains '[' and ']'.  If any, escape it by
211 * prefixing with a '\'.  If the length of the string is not big enough,
212 * no changes are done and error is returned.
213 *
214 * Parameters -
215 * 	str - string that can contain '[' or ']', should be NULL terminated
216 *	len - length, in bytes, of valid data, including NULL character.
217 *	buflen - size of buffer that contains the string
218 */
219int
220escape_str(char *str, int len, int buflen)
221{
222	int count;
223	char *src, *dst;
224
225	/* Count number of characters to escape */
226	src = str;
227	count = 0;
228	do {
229		if ((*src == '[') || (*src == ']')) {
230			count++;
231		}
232	} while (*src++);
233
234	if (count) {
235		/*
236		 * Check if the buffer has enough space to escape all
237		 * characters
238		 */
239		if ((buflen - len) < count) {
240			return (ENOSPC);
241		}
242
243		src = str + len;
244		dst = src + count;
245		while (count) {
246			*dst-- = *src;
247			if ((*src == '[') || (*src == ']')) {
248				/* Last char copied needs to be escaped */
249				*dst-- = '\\';
250				count--;
251			}
252			src--;
253		}
254	}
255
256	return (0);
257}
258