/*
 * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
#import <Foundation/Foundation.h>
#import <stdio.h>
#import <err.h>
#import <sys/types.h>
#import <sys/mount.h>
#import <sys/sysctl.h>
#import <afs/sysctl.h>

enum Type {
    TypeNode = 0,
    TypeNum,
    TypeStr
};

typedef struct _setting {
    NSString *key;
    int selector;
    enum Type type;
    struct _setting *children;
} Setting;

Setting s_darwin_all[] = {
    {@"RealModes", AFS_SC_DARWIN_ALL_REALMODES, TypeNum, NULL},
    {NULL, 0, 0, NULL}
};
Setting s_darwin[] = {
    {@"All", AFS_SC_DARWIN_ALL, TypeNode, s_darwin_all},
    {@"Darwin12", AFS_SC_DARWIN_12, TypeNode, NULL},
    {@"Darwin13", AFS_SC_DARWIN_13, TypeNode, NULL},
    {@"Darwin14", AFS_SC_DARWIN_14, TypeNode, NULL},
    {@"Darwin60", AFS_SC_DARWIN_60, TypeNode, NULL},
    {@"Darwin70", AFS_SC_DARWIN_70, TypeNode, NULL},
    {NULL, 0, 0, NULL}
};
Setting s_first[] = {
    {@"All", AFS_SC_ALL, TypeNode, NULL},
    {@"Darwin", AFS_SC_DARWIN, TypeNode, s_darwin},
    {NULL, 0, 0, NULL}
};
Setting s_top = {NULL, -1, TypeNode, s_first};

int oid[CTL_MAXNAME] = {CTL_VFS};
NSString *path = @"/var/db/openafs/etc/config/settings.plist";

char *oidString(int *oid, int len);
void init(void);
void walk(id obj, Setting *s, int level);

void
init(void)
{
    int oidmax[] = {CTL_VFS, VFS_GENERIC, VFS_MAXTYPENUM};
    int oidvfs[] = {CTL_VFS, VFS_GENERIC, VFS_CONF, 0};
    int max;
    struct vfsconf conf;
    size_t len;
    int i;

    len = sizeof(max);
    if(sysctl(oidmax, 3, &max, &len, NULL, NULL) < 0)
	err(1, "sysctl VFS_MAXTYPENUM");
    for(i = max; --i >= 0; ) {
	oidvfs[3] = i;
	len = sizeof(conf);
	if(sysctl(oidvfs, 4, &conf, &len, NULL, NULL) < 0)
	    continue;
	if(strcmp("afs", conf.vfc_name) == 0) {
	    s_top.selector = conf.vfc_typenum;
	    break;
	}
    }
    if(s_top.selector < 0)
	errx(1, "AFS is not loaded");
}

char *
oidString(int *oid, int len)
{
    static char buf[256];
    char *cp = buf;

    for(;;) {
	sprintf(cp, "%d", *oid++);
	if(--len <= 0)
	    break;
	cp += strlen(cp);
	*cp++ = '.';
    }
    return buf;
}

void
walk(id obj, Setting *s, int level)
{
    Setting *child;
    id newobj;
    int intval;
    const char *cp;
    int level1 = level + 1;

    oid[level] = s->selector;
    switch(s->type) {
      case TypeNode:
	for(child = s->children; child->key; child++) {
	    if(child->type == TypeNode && !child->children)
		continue;
	    newobj = [obj objectForKey: child->key];
	    if(newobj)
		walk(newobj, child, level1);
	}
	break;
      case TypeNum:
	intval = [obj intValue];
	if(sysctl(oid, level1, NULL, NULL, &intval, sizeof(intval)) < 0)
	    err(1, "sysctl %s => %d", oidString(oid, level1), intval);
	break;
      case TypeStr:
	cp = [obj UTF8String];
	if(sysctl(oid, level1, NULL, NULL, (void *)cp, strlen(cp)) < 0)
	    err(1, "sysctl %s => %s", oidString(oid, level1), cp);
	break;
    }
}

main()
{
    NSData *plistData;
    id plist;
    NSString *error;
    NSPropertyListFormat format;
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    init();
    plistData = [NSData dataWithContentsOfFile: path];
    if(plistData) {
	plist = [NSPropertyListSerialization propertyListFromData: plistData
	  mutabilityOption: NSPropertyListImmutable
	  format: &format
	  errorDescription: &error
	];
	if(plist)
	    walk(plist, &s_top, 1);
	else
	    errx(1, "%s: %s", [path UTF8String], [error UTF8String]);
    }

    [pool release];
    return 0;
}
