1#include <stdio.h> 2#include <stdlib.h> 3#include <syslog.h> 4#include <string.h> 5 6#include <OpenDirectory/OpenDirectory.h> 7 8/* 9 * Return values for od_print_record(). 10 */ 11typedef enum { 12 OD_CB_KEEPGOING, /* continue the search */ 13 OD_CB_REJECTED, /* this record had a problem - keep going */ 14 OD_CB_ERROR /* error - quit and return an error */ 15} callback_ret_t; 16 17static void pr_msg(const char *fmt, ...); 18static int od_search(CFStringRef attr_to_match, char *value_to_match); 19 20int 21main(int argc, char **argv) 22{ 23 int ret; 24 char *pattern; 25 26 if (argc < 2 || argc > 3) { 27 fprintf(stderr, "Usage: dumammap <map name> [ <key> ]\n"); 28 return 1; 29 } 30 if (argc == 2) { 31 /* 32 * Dump the entire map. 33 */ 34 ret = od_search(kODAttributeTypeMetaAutomountMap, argv[1]); 35 } else { 36 /* 37 * Dump an entry in the map. 38 * First, construct the string value to search for. 39 */ 40 if (asprintf(&pattern, "%s,automountMapName=%s", argv[2], 41 argv[1]) == -1) { 42 pr_msg("malloc failed"); 43 return 2; 44 } 45 46 /* 47 * Now search for that entry. 48 */ 49 ret = od_search(kODAttributeTypeRecordName, pattern); 50 free(pattern); 51 } 52 return ret ? 0 : 2; 53} 54 55/* 56 * Get a C string from a CFStringRef. 57 * The string is allocated with malloc(), and must be freed when it's 58 * no longer needed. 59 */ 60static char * 61od_CFStringtoCString(CFStringRef cfstr) 62{ 63 char *string; 64 CFIndex length; 65 66 length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfstr), 67 kCFStringEncodingUTF8); 68 string = malloc(length + 1); 69 if (string == NULL) 70 return (NULL); 71 if (!CFStringGetCString(cfstr, string, length + 1, 72 kCFStringEncodingUTF8)) { 73 free(string); 74 return (NULL); 75 } 76 return (string); 77} 78 79 80static char * 81od_get_error_string(CFErrorRef err) 82{ 83 CFStringRef errstringref; 84 char *errstring; 85 86 if (err != NULL) { 87 errstringref = CFErrorCopyDescription(err); 88 errstring = od_CFStringtoCString(errstringref); 89 CFRelease(errstringref); 90 } else 91 errstring = strdup("Unknown error"); 92 return (errstring); 93} 94 95/* 96 * Looks for kODAttributeTypeRecordName and 97 * kODAttributeTypeAutomountInformation; if it finds them, it prints them. 98 */ 99static callback_ret_t 100od_print_record(ODRecordRef record) 101{ 102 CFErrorRef error; 103 char *errstring; 104 CFArrayRef keys; 105 CFStringRef key; 106 CFArrayRef values; 107 CFStringRef value; 108 char *key_cstring, *value_cstring; 109 110 /* 111 * Get kODAttributeTypeRecordName and 112 * kODAttributeTypeAutomountInformation for this record. 113 * 114 * Even though LDAP allows for multiple values per attribute, we take 115 * only the 1st value for each attribute because the automount data is 116 * organized as such (same as NIS+). 117 */ 118 error = NULL; 119 keys = ODRecordCopyValues(record, kODAttributeTypeRecordName, &error); 120 if (keys == NULL) { 121 if (error != NULL) { 122 errstring = od_get_error_string(error); 123 pr_msg("od_print_record: can't get kODAttributeTypeRecordName attribute for record: %s", 124 errstring); 125 free(errstring); 126 return (OD_CB_ERROR); 127 } else { 128 /* 129 * We just reject records missing the attributes 130 * we need. 131 */ 132 pr_msg("od_print_record: record has no kODAttributeTypeRecordName attribute"); 133 return (OD_CB_REJECTED); 134 } 135 } 136 if (CFArrayGetCount(keys) == 0) { 137 /* 138 * We just reject records missing the attributes 139 * we need. 140 */ 141 CFRelease(keys); 142 pr_msg("od_print_record: record has no kODAttributeTypeRecordName attribute"); 143 return (OD_CB_REJECTED); 144 } 145 key = CFArrayGetValueAtIndex(keys, 0); 146 error = NULL; 147 values = ODRecordCopyValues(record, 148 kODAttributeTypeAutomountInformation, &error); 149 if (values == NULL) { 150 CFRelease(keys); 151 if (error != NULL) { 152 errstring = od_get_error_string(error); 153 pr_msg("od_print_record: can't get kODAttributeTypeAutomountInformation attribute for record: %s", 154 errstring); 155 free(errstring); 156 return (OD_CB_ERROR); 157 } else { 158 /* 159 * We just reject records missing the attributes 160 * we need. 161 */ 162 pr_msg("od_print_record: record has no kODAttributeTypeAutomountInformation attribute"); 163 return (OD_CB_REJECTED); 164 } 165 } 166 if (CFArrayGetCount(values) == 0) { 167 /* 168 * We just reject records missing the attributes 169 * we need. 170 */ 171 CFRelease(values); 172 CFRelease(keys); 173 pr_msg("od_print_record: record has no kODAttributeTypeRecordName attribute"); 174 return (OD_CB_REJECTED); 175 } 176 value = CFArrayGetValueAtIndex(values, 0); 177 178 /* 179 * We have both of the attributes we need. 180 */ 181 key_cstring = od_CFStringtoCString(key); 182 value_cstring = od_CFStringtoCString(value); 183 printf("%s %s\n", key_cstring, value_cstring); 184 free(key_cstring); 185 free(value_cstring); 186 CFRelease(values); 187 CFRelease(keys); 188 return (OD_CB_KEEPGOING); 189} 190 191/* 192 * Fetch all the map records in Open Directory that have a certain attribute 193 * that matches a certain value and pass those records to od_print_record(). 194 */ 195static int 196od_search(CFStringRef attr_to_match, char *value_to_match) 197{ 198 int ret; 199 CFErrorRef error; 200 char *errstring; 201 ODNodeRef node_ref; 202 CFArrayRef attrs; 203 CFStringRef value_to_match_cfstr; 204 ODQueryRef query_ref; 205 CFArrayRef results; 206 CFIndex num_results; 207 CFIndex i; 208 ODRecordRef record; 209 callback_ret_t callback_ret; 210 211 /* 212 * Create the search node. 213 */ 214 error = NULL; 215 node_ref = ODNodeCreateWithNodeType(kCFAllocatorDefault, kODSessionDefault, 216 kODNodeTypeAuthentication, &error); 217 if (node_ref == NULL) { 218 errstring = od_get_error_string(error); 219 pr_msg("od_search: can't create search node for /Search: %s", 220 errstring); 221 free(errstring); 222 return (0); 223 } 224 225 /* 226 * Create the query. 227 */ 228 value_to_match_cfstr = CFStringCreateWithCString(kCFAllocatorDefault, 229 value_to_match, kCFStringEncodingUTF8); 230 if (value_to_match_cfstr == NULL) { 231 CFRelease(node_ref); 232 pr_msg("od_search: can't make CFString from %s", 233 value_to_match); 234 return (0); 235 } 236 attrs = CFArrayCreate(kCFAllocatorDefault, 237 (const void *[2]){kODAttributeTypeRecordName, 238 kODAttributeTypeAutomountInformation}, 2, 239 &kCFTypeArrayCallBacks); 240 if (attrs == NULL) { 241 CFRelease(value_to_match_cfstr); 242 CFRelease(node_ref); 243 pr_msg("od_search: can't make array of attribute types"); 244 return (0); 245 } 246 error = NULL; 247 query_ref = ODQueryCreateWithNode(kCFAllocatorDefault, node_ref, 248 kODRecordTypeAutomount, attr_to_match, kODMatchEqualTo, 249 value_to_match_cfstr, attrs, 0, &error); 250 CFRelease(attrs); 251 CFRelease(value_to_match_cfstr); 252 if (query_ref == NULL) { 253 CFRelease(node_ref); 254 errstring = od_get_error_string(error); 255 pr_msg("od_search: can't create query: %s", 256 errstring); 257 free(errstring); 258 return (0); 259 } 260 261 /* 262 * Wait for the query to get all the results, and then copy them. 263 */ 264 error = NULL; 265 results = ODQueryCopyResults(query_ref, false, &error); 266 if (results == NULL) { 267 CFRelease(query_ref); 268 CFRelease(node_ref); 269 errstring = od_get_error_string(error); 270 pr_msg("od_search: query failed: %s", errstring); 271 free(errstring); 272 return (0); 273 } 274 275 ret = 0; /* we haven't found any records yet */ 276 num_results = CFArrayGetCount(results); 277 for (i = 0; i < num_results; i++) { 278 /* 279 * We've found a record. 280 */ 281 record = (ODRecordRef)CFArrayGetValueAtIndex(results, i); 282 callback_ret = od_print_record(record); 283 if (callback_ret == OD_CB_KEEPGOING) { 284 /* 285 * We processed one record, but we want 286 * to keep processing records. 287 */ 288 ret = 1; 289 } else if (callback_ret == OD_CB_ERROR) { 290 /* 291 * Fatal error - give up. 292 */ 293 break; 294 } 295 296 /* 297 * Otherwise it's OD_CB_REJECTED, which is a non-fatal 298 * error. We haven't found a record, so we shouldn't 299 * return __NSW_SUCCESS yet, but if we do find a 300 * record, we shouldn't fail. 301 */ 302 } 303 CFRelease(results); 304 CFRelease(query_ref); 305 CFRelease(node_ref); 306 return (ret); 307} 308 309/* 310 * Print an error. 311 */ 312static void 313pr_msg(const char *fmt, ...) 314{ 315 va_list ap; 316 317 (void) fprintf(stderr, "dumpammap: "); 318 va_start(ap, fmt); 319 (void) vfprintf(stderr, fmt, ap); 320 putc('\n', stderr); 321 va_end(ap); 322} 323 324