/* * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * "Portions Copyright (c) 1999 Apple Computer, Inc. All Rights * Reserved. 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 1.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.apple.com/publicsource 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 OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License." * * @APPLE_LICENSE_HEADER_END@ */ /* * Controller.m * * Controller for lookupd * * Copyright (c) 1995, NeXT Computer Inc. * All rights reserved. * Written by Marc Majka */ #import #import #import #import "Controller.h" #import "Thread.h" #import "LUPrivate.h" #import "LUCachedDictionary.h" #import "Config.h" #import "LUGlobal.h" #import "MachRPC.h" #import "LUServer.h" #import "CacheAgent.h" #import "DNSAgent.h" #import "NILAgent.h" #import #import "sys.h" #import #import #import #import #import #import #import #import #import #import #import #define forever for(;;) extern sys_port_type server_port; extern sys_port_type _lookupd_port(sys_port_type); extern int _lookup_link(); @implementation Controller /* * Server runs in this loop to answer requests */ - (void)serverLoop { kern_return_t status; lookup_request_msg *request; Thread *t, *x; t = [Thread currentThread]; forever { /* Receive and service a request */ [t setState:ThreadStateIdle]; request = (lookup_request_msg *)calloc(1, sizeof(lookup_request_msg)); status = sys_receive_message(&request->head, sizeof(lookup_request_msg), server_port, 0); [t setState:ThreadStateActive]; if (shutting_down) break; if (status != KERN_SUCCESS) { system_log(LOG_NOTICE, "Server status = %s (%d)", sys_strerror(status), status); continue; } syslock_lock(threadCountLock); idleThreads--; if ((idleThreads == 0) && (threadCount < maxThreads)) { x = [[Thread alloc] init]; [x setName:"IPC Server"]; [x shouldTerminate:YES]; [x run:@selector(serverLoop) context:self]; threadCount++; idleThreads++; } syslock_unlock(threadCountLock); /* request is owned by the thread, which needs to free it when done */ [t setData:(void *)request]; [machRPC process]; /* check idle threads */ syslock_lock(threadCountLock); idleThreads++; if ((idleThreads > maxIdleThreads) && ([t shouldTerminate])) { idleThreads--; threadCount--; syslock_unlock(threadCountLock); system_log(LOG_DEBUG, "serverloop terminated thread (%d idle, max %d)", idleThreads, maxIdleThreads); [t terminateSelf]; } syslock_unlock(threadCountLock); if (shutting_down) break; } } - (void)newAgent:(id)agent name:(char *)name { if (agentCount == 0) agents = (id *)malloc(sizeof(id)); else agents = (id *)realloc(agents, (agentCount + 1) * sizeof(id)); agents[agentCount] = agent; agentNames = appendString(name, agentNames); agentCount++; } - (id)agentClassNamed:(char *)name { int status; unsigned int i; char *cname; i = listIndex(name, agentNames); if (i != IndexNull) return agents[i]; status = asprintf(&cname, "%sAgent", name); if (status < 0) return nil; i = listIndex(cname, agentNames); free(cname); if (i != IndexNull) return agents[i]; return nil; } - (void)initConfig { LUArray *configurationArray; LUDictionary *global; char *logFileName; char *logFacilityName; FILE *fp; int pri, status; time_t now; char *str; configurationArray = [configManager config]; global = [configManager configGlobal:configurationArray]; debug_enabled = [configManager boolForKey:"Debug" dict:global default:debug_enabled]; trace_enabled = [configManager boolForKey:"Trace" dict:global default:trace_enabled]; aaaa_cutoff_enabled = [configManager boolForKey:"AAAACutoff" dict:global default:aaaa_cutoff_enabled]; str = [configManager stringForKey:"GAISearch" dict:global default:NULL]; if (str != NULL) { if (!strcasecmp(str, "Parallel")) gai_pref = GAI_P; else if (!strcasecmp(str, "Default")) gai_pref = GAI_P; else if (!strcasecmp(str, "P")) gai_pref = GAI_P; else if (!strcasecmp(str, "IPv4")) gai_pref = GAI_4; else if (!strcasecmp(str, "4")) gai_pref = GAI_4; else if (!strcasecmp(str, "IPv6")) gai_pref = GAI_6; else if (!strcasecmp(str, "6")) gai_pref = GAI_6; else if (!strcasecmp(str, "Serial")) gai_pref = GAI_S46; else if (!strcasecmp(str, "Serial46")) gai_pref = GAI_S46; else if (!strcasecmp(str, "46")) gai_pref = GAI_S46; else if (!strcasecmp(str, "Serial64")) gai_pref = GAI_S64; else if (!strcasecmp(str, "64")) gai_pref = GAI_S64; free(str); } gai_wait = [configManager intForKey:"GAIWait" dict:global default:gai_wait]; if (debug_enabled) { statistics_enabled = YES; coredump_enabled = YES; system_log_set_max_priority(LOG_DEBUG); } else { statistics_enabled = [configManager boolForKey:"StatisticsEnabled" dict:global default:statistics_enabled]; coredump_enabled = [configManager boolForKey:"CoreDumpEnabled" dict:global default:coredump_enabled]; } logFileName = [configManager stringForKey:"LogFile" dict:global default:NULL]; fp = fopen(logFileName, "a"); system_log_set_logfile(fp); freeString(logFileName); logFacilityName = [configManager stringForKey:"LogFacility" dict:global default:"LOG_NETINFO"]; freeString(logFacilityName); now = time(0); status = asprintf(&str, "lookupd (version %s) starting - %s", _PROJECT_VERSION_, ctime(&now)); if (status >= 0) { /* remove ctime trailing newline */ str[strlen(str) - 1] = '\0'; system_log(LOG_NOTICE, str); free(str); } maxThreads = [configManager intForKey:"MaxThreads" dict:global default:64]; maxIdleThreads = [configManager intForKey:"MaxIdleThreads" dict:global default:2]; maxIdleServers = [configManager intForKey:"MaxIdleServers" dict:global default:16]; pri = [configManager intForKey:"LogPriority" dict:global default:system_log_max_priority()]; system_log_set_max_priority(pri); [configurationArray release]; } - (void)initNetAddrList { struct ifaddrs *ifa, *ifap; sa_family_t family; struct sockaddr_in6 *sin6; struct sockaddr_in *sin4; char buf[128]; BOOL isLinkLocal, isSiteLocal, isLoopback; if (getifaddrs(&ifa) != 0) return; for (ifap = ifa; ifap != NULL; ifap = ifap->ifa_next) { family = ifap->ifa_addr->sa_family; if (family != AF_INET && family != AF_INET6) continue; if (family == AF_INET6) { sin6 = (struct sockaddr_in6 *)(ifap->ifa_addr); isLinkLocal = (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) != 0); isSiteLocal = (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr) != 0); isLoopback = (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr) != 0); if (isLinkLocal || isSiteLocal) { sin6->sin6_scope_id = ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]); sin6->sin6_addr.s6_addr[2] = 0; sin6->sin6_addr.s6_addr[3] = 0; } /* * Anything other than link-local or loopback is considered routable. * If we have routable IPv6 addresees, we want to do AAAA queries in DNS. */ if ((!isLinkLocal) && (!isLoopback)) aaaa_cutoff_enabled = NO; } if (family == AF_INET) { sin4 = (struct sockaddr_in *)(ifap->ifa_addr); if (inet_ntop(AF_INET, (void *)&(sin4->sin_addr), buf, sizeof(buf)) == NULL) continue; } else { sin6 = (struct sockaddr_in6 *)(ifap->ifa_addr); if (inet_ntop(AF_INET6, (void *)&(sin6->sin6_addr), buf, sizeof(buf)) == NULL) continue; } netAddrList = appendString(buf, netAddrList); } freeifaddrs(ifa); } - (id)initWithName:(char *)name { DNSAgent *dns; [super init]; dnsSearchList = NULL; netAddrList = NULL; controller = self; if (name == NULL) portName = NULL; else portName = copyString((char *)name); [self setBanner:"Controller"]; if (![self registerPort:portName]) return nil; serverLock = syslock_new(1); threadCountLock = syslock_new(0); threadCount = 1; idleThreads = 1; serverList = [[LUArray alloc] init]; [serverList setBanner:"Controller server list"]; statistics = [[LUDictionary alloc] init]; [statistics setBanner:"lookupd statistics"]; [statistics setValue:_PROJECT_BUILD_INFO_ forKey:"# Build"]; [statistics setValue:_PROJECT_VERSION_ forKey:"# Version"]; [self newAgent:[CacheAgent class] name:"CacheAgent"]; [self newAgent:[DNSAgent class] name:"DNSAgent"]; [self newAgent:[NILAgent class] name:"NILAgent"]; /* * Build a list of local network addresses */ [self initNetAddrList]; [self initConfig]; loginUser = nil; machRPC = [[MachRPC alloc] init:self]; dns = [[DNSAgent alloc] init]; if (dns != nil) { dnsSearchList = [dns searchList]; [dns release]; } return self; } - (char *)portName { return portName; } - (char **)dnsSearchList { return dnsSearchList; } - (char **)netAddrList { return netAddrList; } - (void)dealloc { if (dnsSearchList != NULL) freeList(dnsSearchList); dnsSearchList = NULL; [machRPC release]; machRPC = nil; if (serverList != nil) [serverList release]; if (portName != NULL) { sys_destroy_service(portName); freeString(portName); } portName = NULL; sys_port_free(server_port); if (loginUser != nil) [loginUser release]; syslock_free(serverLock); syslock_free(threadCountLock); if (statistics != nil) [statistics release]; freeList(agentNames); agentNames = NULL; free(agents); freeList(netAddrList); netAddrList = NULL; [super dealloc]; } - (BOOL)registerPort:(char *)name { kern_return_t status; int restart; if (portName == NULL) return YES; restart = 0; system_log(LOG_DEBUG, "Registering service \"%s\"", portName); if (streq(name, DefaultName)) restart = 1; status = sys_create_service(name, &server_port, restart); if (status == KERN_SUCCESS) return YES; system_log(LOG_ERR, "Can't create service! (error %d)", status); return NO; } - (void)startServerThread { Thread *t; syslock_lock(serverLock); /* * Create the thread */ t = [[Thread alloc] init]; [t setName:"IPC Server"]; [t shouldTerminate:YES]; [t run:@selector(serverLoop) context:self]; idleThreads = 1; syslock_unlock(serverLock); system_log(LOG_DEBUG, "Started IPC Server"); } /* * Get an idle server from the server list */ - (LUServer *)checkOutServer { LUServer *server; int i; syslock_lock(serverLock); server = nil; for (i = [serverList count] - 1; i >= 0; i--) { if ([[serverList objectAtIndex:i] isIdle]) { server = [serverList objectAtIndex:i]; [server setIsIdle:NO]; break; } } if (server == nil) { /* * No servers available - create a new server */ server = [[LUServer alloc] init]; [server setIsIdle:NO]; [serverList addObject:server]; [server release]; } syslock_unlock(serverLock); return server; } - (void)checkInServer:(LUServer *)server { int i, len, idleServerCount; syslock_lock(serverLock); [server setIsIdle:YES]; idleServerCount = 0; len = [serverList count]; for (i = 0; i < len; i++) { if ([[serverList objectAtIndex:i] isIdle]) idleServerCount++; } if (idleServerCount > maxIdleServers) { [serverList removeObject:server]; } syslock_unlock(serverLock); } - (void)setLoginUser:(int)uid { LUServer *s; char *scratch, *name; int status; if (loginUser != nil) { [cacheAgent removeObject:loginUser]; [loginUser release]; loginUser = nil; } status = asprintf(&scratch, "%d", uid); if (status < 0) return; s = [self checkOutServer]; loginUser = [s itemWithKey:"uid" value:scratch category:LUCategoryUser]; [self checkInServer:s]; free(scratch); if (loginUser != nil) { [cacheAgent addObject:loginUser key:"name" category:LUCategoryUser]; [cacheAgent addObject:loginUser key:"uid" category:LUCategoryUser]; [loginUser setTimeToLive:(time_t)-1]; name = [loginUser banner]; if (name != NULL) { status = asprintf(&scratch, "%s (console user)", [loginUser banner]); if (status < 0) return; [loginUser setBanner:scratch]; free(scratch); } } } - (void)flushCache { [cacheAgent flushCache]; } - (void)suspend { /* XXX suspend */ } /* * This is just here to send a message to the server port. * This wakes the thread blocked on message receive in serverLoop. */ - (void)lookupdMessage { sys_port_type s; kern_return_t status; int proc; s = lookupd_port(portName); status = _lookup_link(s, "_getstatistics", &proc); [[Thread currentThread] terminateSelf]; } - (unsigned int)memorySize { unsigned int size, i; size = [super memorySize]; size += 60; if (serverLock != NULL) size += sizeof(syslock); size += (NCATEGORIES * 4); if (agentNames != NULL) { for (i = 0; agentNames[i] != NULL; i++) { size += 4; size += (strlen(agentNames[i]) + 1); } } if (dnsSearchList != NULL) { for (i = 0; dnsSearchList[i] != NULL; i++) { size += 4; size += (strlen(dnsSearchList[i]) + 1); } } if (portName != NULL) size += (strlen(portName) + 1); size += (agentCount * 4); return size; } @end