| 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 | */ |
| 84 | int |
| 85 | kern_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 | /* |
| 194 | * Note: can't use os_log_with_args() here because 'fmt' is |
| 195 | * constructed on the stack i.e. doesn't come from a text |
| 196 | * section. More importantly, the newer logging system |
| 197 | * doesn't grok ASL either. |
| 198 | */ |
| 199 | |
| 200 | return (err); |
| 201 | } |
| 202 | |
| 203 | int |
| 204 | kern_asl_msg(int level, const char *facility, int num_pairs, ...) |
| 205 | { |
| 206 | int err; |
| 207 | va_list ap; |
| 208 | |
| 209 | va_start(ap, num_pairs); |
| 210 | err = kern_asl_msg_va(level, facility, |
| 211 | num_pairs, ap, NULL); |
| 212 | va_end(ap); |
| 213 | |
| 214 | return err; |
| 215 | } |
| 216 | |
| 217 | /* Search if given string contains '[' and ']'. If any, escape it by |
| 218 | * prefixing with a '\'. If the length of the string is not big enough, |
| 219 | * no changes are done and error is returned. |
| 220 | * |
| 221 | * Parameters - |
| 222 | * str - string that can contain '[' or ']', should be NULL terminated |
| 223 | * len - length, in bytes, of valid data, including NULL character. |
| 224 | * buflen - size of buffer that contains the string |
| 225 | */ |
| 226 | int |
| 227 | escape_str(char *str, int len, int buflen) |
| 228 | { |
| 229 | int count; |
| 230 | char *src, *dst; |
| 231 | |
| 232 | /* Count number of characters to escape */ |
| 233 | src = str; |
| 234 | count = 0; |
| 235 | do { |
| 236 | if ((*src == '[') || (*src == ']')) { |
| 237 | count++; |
| 238 | } |
| 239 | } while (*src++); |
| 240 | |
| 241 | if (count) { |
| 242 | /* |
| 243 | * Check if the buffer has enough space to escape all |
| 244 | * characters |
| 245 | */ |
| 246 | if ((buflen - len) < count) { |
| 247 | return (ENOSPC); |
| 248 | } |
| 249 | |
| 250 | src = str + len; |
| 251 | dst = src + count; |
| 252 | while (count) { |
| 253 | *dst-- = *src; |
| 254 | if ((*src == '[') || (*src == ']')) { |
| 255 | /* Last char copied needs to be escaped */ |
| 256 | *dst-- = '\\'; |
| 257 | count--; |
| 258 | } |
| 259 | src--; |
| 260 | } |
| 261 | } |
| 262 | |
| 263 | return (0); |
| 264 | } |
| 265 | |