]> git.saurik.com Git - apple/system_cmds.git/blob - nvram.tproj/nvram.c
system_cmds-597.1.1.tar.gz
[apple/system_cmds.git] / nvram.tproj / nvram.c
1 /*
2 * Copyright (c) 2000-2012 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
11 *
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22 /*
23 cc -o nvram nvram.c -framework CoreFoundation -framework IOKit -Wall
24 */
25
26 #include <stdio.h>
27 #include <IOKit/IOKitLib.h>
28 #include <IOKit/IOKitKeys.h>
29 #include <CoreFoundation/CoreFoundation.h>
30 #include <err.h>
31 #include <mach/mach_error.h>
32
33 // Prototypes
34 static void UsageMessage(char *message);
35 static void ParseFile(char *fileName);
36 static void ParseXMLFile(char *fileName);
37 static void SetOrGetOFVariable(char *str);
38 static kern_return_t GetOFVariable(char *name, CFStringRef *nameRef,
39 CFTypeRef *valueRef);
40 static kern_return_t SetOFVariable(char *name, char *value);
41 static void DeleteOFVariable(char *name);
42 static void PrintOFVariables(void);
43 static void PrintOFVariable(const void *key,const void *value,void *context);
44 static void SetOFVariableFromFile(const void *key, const void *value, void *context);
45 static void ClearOFVariables(void);
46 static void ClearOFVariable(const void *key,const void *value,void *context);
47 static CFTypeRef ConvertValueToCFTypeRef(CFTypeID typeID, char *value);
48
49 static void NVRamSyncNow(char *name);
50
51 // Global Variables
52 static char *gToolName;
53 static io_registry_entry_t gOptionsRef;
54 static bool gUseXML;
55
56
57 int main(int argc, char **argv)
58 {
59 long cnt;
60 char *str, errorMessage[256];
61 kern_return_t result;
62 mach_port_t masterPort;
63
64 // Get the name of the command.
65 gToolName = strrchr(argv[0], '/');
66 if (gToolName != 0) gToolName++;
67 else gToolName = argv[0];
68
69 result = IOMasterPort(bootstrap_port, &masterPort);
70 if (result != KERN_SUCCESS) {
71 errx(1, "Error getting the IOMaster port: %s",
72 mach_error_string(result));
73 }
74
75 gOptionsRef = IORegistryEntryFromPath(masterPort, "IODeviceTree:/options");
76 if (gOptionsRef == 0) {
77 errx(1, "nvram is not supported on this system");
78 }
79
80 for (cnt = 1; cnt < argc; cnt++) {
81 str = argv[cnt];
82 if (str[0] == '-' && str[1] != 0) {
83 // Parse the options.
84 for (str += 1 ; *str; str++) {
85 switch (*str) {
86 case 'p' :
87 PrintOFVariables();
88 break;
89
90 case 'x' :
91 gUseXML = true;
92 break;
93
94 case 'f':
95 cnt++;
96 if (cnt < argc && *argv[cnt] != '-') {
97 ParseFile(argv[cnt]);
98 } else {
99 UsageMessage("missing filename");
100 }
101 break;
102
103 case 'd':
104 cnt++;
105 if (cnt < argc && *argv[cnt] != '-') {
106 DeleteOFVariable(argv[cnt]);
107 } else {
108 UsageMessage("missing name");
109 }
110 break;
111
112 case 'c':
113 ClearOFVariables();
114 break;
115
116 default:
117 strcpy(errorMessage, "no such option as --");
118 errorMessage[strlen(errorMessage)-1] = *str;
119 UsageMessage(errorMessage);
120 }
121 }
122 } else {
123 // Other arguments will be firmware variable requests.
124 SetOrGetOFVariable(str);
125 }
126 }
127
128 IOObjectRelease(gOptionsRef);
129
130 return 0;
131 }
132
133 // UsageMessage(message)
134 //
135 // Print the usage information and exit.
136 //
137 static void UsageMessage(char *message)
138 {
139 warnx("(usage: %s)", message);
140
141 printf("%s [-x] [-p] [-f filename] [-d name] [-c] name[=value] ...\n", gToolName);
142 printf("\t-x use XML format for printing or reading variables\n");
143 printf("\t (must appear before -p or -f)\n");
144 printf("\t-p print all firmware variables\n");
145 printf("\t-f set firmware variables from a text file\n");
146 printf("\t-d delete the named variable\n");
147 printf("\t-c delete all variables\n");
148 printf("\tname=value set named variable\n");
149 printf("\tname print variable\n");
150 printf("Note that arguments and options are executed in order.\n");
151
152 exit(1);
153 }
154
155
156 // States for ParseFile.
157 enum {
158 kFirstColumn = 0,
159 kScanComment,
160 kFindName,
161 kCollectName,
162 kFindValue,
163 kCollectValue,
164 kContinueValue,
165 kSetenv,
166
167 kMaxStringSize = 0x800,
168 kMaxNameSize = 0x100
169 };
170
171
172 // ParseFile(fileName)
173 //
174 // Open and parse the specified file.
175 //
176 static void ParseFile(char *fileName)
177 {
178 long state, tc, ni = 0, vi = 0;
179 char name[kMaxNameSize];
180 char value[kMaxStringSize];
181 FILE *patches;
182 kern_return_t kret;
183
184 if (gUseXML) {
185 ParseXMLFile(fileName);
186 return;
187 }
188
189 patches = fopen(fileName, "r");
190 if (patches == 0) {
191 err(1, "Couldn't open patch file - '%s'", fileName);
192 }
193
194 state = kFirstColumn;
195 while ((tc = getc(patches)) != EOF) {
196 if(ni==(kMaxNameSize-1))
197 errx(1, "Name exceeded max length of %d", kMaxNameSize);
198 if(vi==(kMaxStringSize-1))
199 errx(1, "Value exceeded max length of %d", kMaxStringSize);
200 switch (state) {
201 case kFirstColumn :
202 ni = 0;
203 vi = 0;
204 if (tc == '#') {
205 state = kScanComment;
206 } else if (tc == '\n') {
207 // state stays kFirstColumn.
208 } else if (isspace(tc)) {
209 state = kFindName;
210 } else {
211 state = kCollectName;
212 name[ni++] = tc;
213 }
214 break;
215
216 case kScanComment :
217 if (tc == '\n') {
218 state = kFirstColumn;
219 } else {
220 // state stays kScanComment.
221 }
222 break;
223
224 case kFindName :
225 if (tc == '\n') {
226 state = kFirstColumn;
227 } else if (isspace(tc)) {
228 // state stays kFindName.
229 } else {
230 state = kCollectName;
231 name[ni++] = tc;
232 }
233 break;
234
235 case kCollectName :
236 if (tc == '\n') {
237 name[ni] = 0;
238 warnx("Name must be followed by white space - '%s'", name);
239 state = kFirstColumn;
240 } else if (isspace(tc)) {
241 state = kFindValue;
242 } else {
243 name[ni++] = tc;
244 // state staus kCollectName.
245 }
246 break;
247
248 case kFindValue :
249 case kContinueValue :
250 if (tc == '\n') {
251 state = kSetenv;
252 } else if (isspace(tc)) {
253 // state stays kFindValue or kContinueValue.
254 } else {
255 state = kCollectValue;
256 value[vi++] = tc;
257 }
258 break;
259
260 case kCollectValue :
261 if (tc == '\n') {
262 if (value[vi-1] == '\\') {
263 value[vi-1] = '\r';
264 state = kContinueValue;
265 } else {
266 state = kSetenv;
267 }
268 } else {
269 // state stays kCollectValue.
270 value[vi++] = tc;
271 }
272 break;
273 }
274
275 if (state == kSetenv) {
276 name[ni] = 0;
277 value[vi] = 0;
278 if ((kret = SetOFVariable(name, value)) != KERN_SUCCESS) {
279 errx(1, "Error setting variable - '%s': %s", name,
280 mach_error_string(kret));
281 }
282 state = kFirstColumn;
283 }
284 }
285
286 if (state != kFirstColumn) {
287 errx(1, "Last line ended abruptly");
288 }
289 }
290
291
292 // ParseXMLFile(fileName)
293 //
294 // Open and parse the specified file in XML format,
295 // and set variables appropriately.
296 //
297 static void ParseXMLFile(char *fileName)
298 {
299 CFPropertyListRef plist;
300 CFURLRef fileURL = NULL;
301 CFStringRef filePath = NULL;
302 CFStringRef errorString = NULL;
303 CFDataRef data = NULL;
304 SInt32 errorCode = 0;
305
306 filePath = CFStringCreateWithCString(kCFAllocatorDefault, fileName, kCFStringEncodingUTF8);
307 if (filePath == NULL) {
308 errx(1, "Could not create file path string");
309 }
310
311 // Create a URL that specifies the file we will create to
312 // hold the XML data.
313 fileURL = CFURLCreateWithFileSystemPath( kCFAllocatorDefault,
314 filePath,
315 kCFURLPOSIXPathStyle,
316 false /* not a directory */ );
317 if (fileURL == NULL) {
318 errx(1, "Could not create file path URL");
319 }
320
321 CFRelease(filePath);
322
323 if (! CFURLCreateDataAndPropertiesFromResource(
324 kCFAllocatorDefault,
325 fileURL,
326 &data,
327 NULL,
328 NULL,
329 &errorCode) || data == NULL ) {
330 errx(1, "Error reading XML file (%d)", (int)errorCode);
331 }
332
333 CFRelease(fileURL);
334
335 plist = CFPropertyListCreateFromXMLData(kCFAllocatorDefault,
336 data,
337 kCFPropertyListImmutable,
338 &errorString);
339
340 CFRelease(data);
341
342 if (plist == NULL) {
343 errx(1, "Error parsing XML file");
344 }
345
346 if (errorString != NULL) {
347 errx(1, "Error parsing XML file: %s", CFStringGetCStringPtr(errorString, kCFStringEncodingUTF8));
348 }
349
350 CFDictionaryApplyFunction(plist, &SetOFVariableFromFile, 0);
351
352 CFRelease(plist);
353 }
354
355 // SetOrGetOFVariable(str)
356 //
357 // Parse the input string, then set or get the specified
358 // firmware variable.
359 //
360 static void SetOrGetOFVariable(char *str)
361 {
362 long set = 0;
363 char *name;
364 char *value;
365 CFStringRef nameRef;
366 CFTypeRef valueRef;
367 kern_return_t result;
368
369 // OF variable name is first.
370 name = str;
371
372 // Find the equal sign for set
373 while (*str) {
374 if (*str == '=') {
375 set = 1;
376 *str++ = '\0';
377 break;
378 }
379 str++;
380 }
381
382 if (set == 1) {
383 // On sets, the OF variable's value follows the equal sign.
384 value = str;
385
386 result = SetOFVariable(name, value);
387 NVRamSyncNow(name); /* Try syncing the new data to device, best effort! */
388 if (result != KERN_SUCCESS) {
389 errx(1, "Error setting variable - '%s': %s", name,
390 mach_error_string(result));
391 }
392 } else {
393 result = GetOFVariable(name, &nameRef, &valueRef);
394 if (result != KERN_SUCCESS) {
395 errx(1, "Error getting variable - '%s': %s", name,
396 mach_error_string(result));
397 }
398
399 PrintOFVariable(nameRef, valueRef, 0);
400 CFRelease(nameRef);
401 CFRelease(valueRef);
402 }
403 }
404
405
406 // GetOFVariable(name, nameRef, valueRef)
407 //
408 // Get the named firmware variable.
409 // Return it and it's symbol in valueRef and nameRef.
410 //
411 static kern_return_t GetOFVariable(char *name, CFStringRef *nameRef,
412 CFTypeRef *valueRef)
413 {
414 *nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name,
415 kCFStringEncodingUTF8);
416 if (*nameRef == 0) {
417 errx(1, "Error creating CFString for key %s", name);
418 }
419
420 *valueRef = IORegistryEntryCreateCFProperty(gOptionsRef, *nameRef, 0, 0);
421 if (*valueRef == 0) return kIOReturnNotFound;
422
423 return KERN_SUCCESS;
424 }
425
426
427 // SetOFVariable(name, value)
428 //
429 // Set or create an firmware variable with name and value.
430 //
431 static kern_return_t SetOFVariable(char *name, char *value)
432 {
433 CFStringRef nameRef;
434 CFTypeRef valueRef;
435 CFTypeID typeID;
436 kern_return_t result = KERN_SUCCESS;
437
438 nameRef = CFStringCreateWithCString(kCFAllocatorDefault, name,
439 kCFStringEncodingUTF8);
440 if (nameRef == 0) {
441 errx(1, "Error creating CFString for key %s", name);
442 }
443
444 valueRef = IORegistryEntryCreateCFProperty(gOptionsRef, nameRef, 0, 0);
445 if (valueRef) {
446 typeID = CFGetTypeID(valueRef);
447 CFRelease(valueRef);
448
449 valueRef = ConvertValueToCFTypeRef(typeID, value);
450 if (valueRef == 0) {
451 errx(1, "Error creating CFTypeRef for value %s", value);
452 } result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
453 } else {
454 while (1) {
455 // In the default case, try data, string, number, then boolean.
456
457 valueRef = ConvertValueToCFTypeRef(CFDataGetTypeID(), value);
458 if (valueRef != 0) {
459 result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
460 if (result == KERN_SUCCESS) break;
461 }
462
463 valueRef = ConvertValueToCFTypeRef(CFStringGetTypeID(), value);
464 if (valueRef != 0) {
465 result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
466 if (result == KERN_SUCCESS) break;
467 }
468
469 valueRef = ConvertValueToCFTypeRef(CFNumberGetTypeID(), value);
470 if (valueRef != 0) {
471 result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
472 if (result == KERN_SUCCESS) break;
473 }
474
475 valueRef = ConvertValueToCFTypeRef(CFBooleanGetTypeID(), value);
476 if (valueRef != 0) {
477 result = IORegistryEntrySetCFProperty(gOptionsRef, nameRef, valueRef);
478 if (result == KERN_SUCCESS) break;
479 }
480
481 break;
482 }
483 }
484
485 CFRelease(nameRef);
486
487 return result;
488 }
489
490
491 // DeleteOFVariable(name)
492 //
493 // Delete the named firmware variable.
494 //
495 //
496 static void DeleteOFVariable(char *name)
497 {
498 SetOFVariable(kIONVRAMDeletePropertyKey, name);
499 }
500
501 static void NVRamSyncNow(char *name)
502 {
503 SetOFVariable(kIONVRAMSyncNowPropertyKey, name);
504 }
505
506 // PrintOFVariables()
507 //
508 // Print all of the firmware variables.
509 //
510 static void PrintOFVariables()
511 {
512 kern_return_t result;
513 CFMutableDictionaryRef dict;
514
515 result = IORegistryEntryCreateCFProperties(gOptionsRef, &dict, 0, 0);
516 if (result != KERN_SUCCESS) {
517 errx(1, "Error getting the firmware variables: %s", mach_error_string(result));
518 }
519
520 if (gUseXML) {
521 CFDataRef data;
522
523 data = CFPropertyListCreateXMLData( kCFAllocatorDefault, dict );
524 if (data == NULL) {
525 errx(1, "Error converting variables to xml");
526 }
527
528 fwrite(CFDataGetBytePtr(data), sizeof(UInt8), CFDataGetLength(data), stdout);
529
530 CFRelease(data);
531
532 } else {
533
534 CFDictionaryApplyFunction(dict, &PrintOFVariable, 0);
535
536 }
537
538 CFRelease(dict);
539 }
540
541 // PrintOFVariable(key, value, context)
542 //
543 // Print the given firmware variable.
544 //
545 static void PrintOFVariable(const void *key, const void *value, void *context)
546 {
547 long cnt, cnt2;
548 CFIndex nameLen;
549 char *nameBuffer = 0;
550 const char *nameString;
551 char numberBuffer[10];
552 const uint8_t *dataPtr;
553 uint8_t dataChar;
554 char *dataBuffer = 0;
555 CFIndex valueLen;
556 char *valueBuffer = 0;
557 const char *valueString = 0;
558 uint32_t number, length;
559 CFTypeID typeID;
560
561 // Get the OF variable's name.
562 nameLen = CFStringGetLength(key) + 1;
563 nameBuffer = malloc(nameLen);
564 if( nameBuffer && CFStringGetCString(key, nameBuffer, nameLen, kCFStringEncodingUTF8) )
565 nameString = nameBuffer;
566 else {
567 warnx("Unable to convert property name to C string");
568 nameString = "<UNPRINTABLE>";
569 }
570
571 // Get the OF variable's type.
572 typeID = CFGetTypeID(value);
573
574 if (typeID == CFBooleanGetTypeID()) {
575 if (CFBooleanGetValue(value)) valueString = "true";
576 else valueString = "false";
577 } else if (typeID == CFNumberGetTypeID()) {
578 CFNumberGetValue(value, kCFNumberSInt32Type, &number);
579 if (number == 0xFFFFFFFF) sprintf(numberBuffer, "-1");
580 else if (number < 1000) sprintf(numberBuffer, "%d", number);
581 else sprintf(numberBuffer, "0x%x", number);
582 valueString = numberBuffer;
583 } else if (typeID == CFStringGetTypeID()) {
584 valueLen = CFStringGetLength(value) + 1;
585 valueBuffer = malloc(valueLen + 1);
586 if ( valueBuffer && CFStringGetCString(value, valueBuffer, valueLen, kCFStringEncodingUTF8) )
587 valueString = valueBuffer;
588 else {
589 warnx("Unable to convert value to C string");
590 valueString = "<UNPRINTABLE>";
591 }
592 } else if (typeID == CFDataGetTypeID()) {
593 length = CFDataGetLength(value);
594 if (length == 0) valueString = "";
595 else {
596 dataBuffer = malloc(length * 3 + 1);
597 if (dataBuffer != 0) {
598 dataPtr = CFDataGetBytePtr(value);
599 for (cnt = cnt2 = 0; cnt < length; cnt++) {
600 dataChar = dataPtr[cnt];
601 if (isprint(dataChar)) dataBuffer[cnt2++] = dataChar;
602 else {
603 sprintf(dataBuffer + cnt2, "%%%02x", dataChar);
604 cnt2 += 3;
605 }
606 }
607 dataBuffer[cnt2] = '\0';
608 valueString = dataBuffer;
609 }
610 }
611 } else {
612 valueString="<INVALID>";
613 }
614
615 if ((nameString != 0) && (valueString != 0))
616 printf("%s\t%s\n", nameString, valueString);
617
618 if (dataBuffer != 0) free(dataBuffer);
619 if (nameBuffer != 0) free(nameBuffer);
620 if (valueBuffer != 0) free(valueBuffer);
621 }
622
623 // ClearOFVariables()
624 //
625 // Deletes all OF variables
626 //
627 static void ClearOFVariables(void)
628 {
629 kern_return_t result;
630 CFMutableDictionaryRef dict;
631
632 result = IORegistryEntryCreateCFProperties(gOptionsRef, &dict, 0, 0);
633 if (result != KERN_SUCCESS) {
634 errx(1, "Error getting the firmware variables: %s", mach_error_string(result));
635 }
636 CFDictionaryApplyFunction(dict, &ClearOFVariable, 0);
637
638 CFRelease(dict);
639 }
640
641 static void ClearOFVariable(const void *key, const void *value, void *context)
642 {
643 kern_return_t result;
644 result = IORegistryEntrySetCFProperty(gOptionsRef,
645 CFSTR(kIONVRAMDeletePropertyKey), key);
646 if (result != KERN_SUCCESS) {
647 errx(1, "Error clearing firmware variables: %s", mach_error_string(result));
648 }
649 }
650
651 // ConvertValueToCFTypeRef(typeID, value)
652 //
653 // Convert the value into a CFType given the typeID.
654 //
655 static CFTypeRef ConvertValueToCFTypeRef(CFTypeID typeID, char *value)
656 {
657 CFTypeRef valueRef = 0;
658 long cnt, cnt2, length;
659 unsigned long number, tmp;
660
661 if (typeID == CFBooleanGetTypeID()) {
662 if (!strcmp("true", value)) valueRef = kCFBooleanTrue;
663 else if (!strcmp("false", value)) valueRef = kCFBooleanFalse;
664 } else if (typeID == CFNumberGetTypeID()) {
665 number = strtol(value, 0, 0);
666 valueRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type,
667 &number);
668 } else if (typeID == CFStringGetTypeID()) {
669 valueRef = CFStringCreateWithCString(kCFAllocatorDefault, value,
670 kCFStringEncodingUTF8);
671 } else if (typeID == CFDataGetTypeID()) {
672 length = strlen(value);
673 for (cnt = cnt2 = 0; cnt < length; cnt++, cnt2++) {
674 if (value[cnt] == '%') {
675 if (!ishexnumber(value[cnt + 1]) ||
676 !ishexnumber(value[cnt + 2])) return 0;
677 number = toupper(value[++cnt]) - '0';
678 if (number > 9) number -= 7;
679 tmp = toupper(value[++cnt]) - '0';
680 if (tmp > 9) tmp -= 7;
681 number = (number << 4) + tmp;
682 value[cnt2] = number;
683 } else value[cnt2] = value[cnt];
684 }
685 valueRef = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, (const UInt8 *)value,
686 cnt2, kCFAllocatorDefault);
687 } else return 0;
688
689 return valueRef;
690 }
691
692 static void SetOFVariableFromFile(const void *key, const void *value, void *context)
693 {
694 kern_return_t result;
695
696 result = IORegistryEntrySetCFProperty(gOptionsRef, key, value);
697 if ( result != KERN_SUCCESS ) {
698 int nameLen;
699 char *nameBuffer;
700 char *nameString;
701
702 // Get the variable's name.
703 nameLen = CFStringGetLength(key) + 1;
704 nameBuffer = malloc(nameLen);
705 if( nameBuffer && CFStringGetCString(key, nameBuffer, nameLen, kCFStringEncodingUTF8) )
706 nameString = nameBuffer;
707 else {
708 warnx("Unable to convert property name to C string");
709 nameString = "<UNPRINTABLE>";
710 }
711 errx(1, "Error setting variable - '%s': %s", nameString,
712 mach_error_string(result));
713 }
714 }