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