| /* -*- Mode: C; tab-width: 4 -*- |
| * |
| * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| |
| File: daemon.c |
| |
| Contains: main & associated Application layer for mDNSResponder on Linux. |
| |
| */ |
| |
| #if __APPLE__ |
| // In Mac OS X 10.5 and later trying to use the daemon function gives a “‘daemon’ is deprecated” |
| // error, which prevents compilation because we build with "-Werror". |
| // Since this is supposed to be portable cross-platform code, we don't care that daemon is |
| // deprecated on Mac OS X 10.5, so we use this preprocessor trick to eliminate the error message. |
| #define daemon yes_we_know_that_daemon_is_deprecated_in_os_x_10_5_thankyou |
| #endif |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <signal.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <pwd.h> |
| #include <sys/types.h> |
| |
| #ifdef __ANDROID__ |
| #include "cutils/sockets.h" |
| #endif |
| |
| #if __APPLE__ |
| #undef daemon |
| extern int daemon(int, int); |
| #endif |
| |
| #include "mDNSEmbeddedAPI.h" |
| #include "mDNSPosix.h" |
| #include "mDNSUNP.h" // For daemon() |
| #include "uds_daemon.h" |
| #include "PlatformCommon.h" |
| |
| #ifndef MDNS_USERNAME |
| #define MDNS_USERNAME "nobody" |
| #endif |
| |
| #define CONFIG_FILE "/etc/mdnsd.conf" |
| static domainname DynDNSZone; // Default wide-area zone for service registration |
| static domainname DynDNSHostname; |
| |
| #define RR_CACHE_SIZE 500 |
| static CacheEntity gRRCache[RR_CACHE_SIZE]; |
| static mDNS_PlatformSupport PlatformStorage; |
| |
| mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result) |
| { |
| (void)m; // Unused |
| if (result == mStatus_NoError) |
| { |
| // On successful registration of dot-local mDNS host name, daemon may want to check if |
| // any name conflict and automatic renaming took place, and if so, record the newly negotiated |
| // name in persistent storage for next time. It should also inform the user of the name change. |
| // On Mac OS X we store the current dot-local mDNS host name in the SCPreferences store, |
| // and notify the user with a CFUserNotification. |
| } |
| else if (result == mStatus_ConfigChanged) |
| { |
| udsserver_handle_configchange(m); |
| } |
| else if (result == mStatus_GrowCache) |
| { |
| // Allocate another chunk of cache storage |
| CacheEntity *storage = malloc(sizeof(CacheEntity) * RR_CACHE_SIZE); |
| if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE); |
| } |
| } |
| |
| // %%% Reconfigure() probably belongs in the platform support layer (mDNSPosix.c), not the daemon cde |
| // -- all client layers running on top of mDNSPosix.c need to handle network configuration changes, |
| // not only the Unix Domain Socket Daemon |
| |
| static void Reconfigure(mDNS *m) |
| { |
| mDNSAddr DynDNSIP; |
| const mDNSAddr dummy = { mDNSAddrType_IPv4, { { { 1, 1, 1, 1 } } } };; |
| mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL, NULL); |
| if (ParseDNSServers(m, uDNS_SERVERS_FILE) < 0) |
| LogMsg("Unable to parse DNS server list. Unicast DNS-SD unavailable"); |
| ReadDDNSSettingsFromConfFile(m, CONFIG_FILE, &DynDNSHostname, &DynDNSZone, NULL); |
| mDNSPlatformSourceAddrForDest(&DynDNSIP, &dummy); |
| if (DynDNSHostname.c[0]) mDNS_AddDynDNSHostName(m, &DynDNSHostname, NULL, NULL); |
| if (DynDNSIP.type) mDNS_SetPrimaryInterfaceInfo(m, &DynDNSIP, NULL, NULL); |
| mDNS_ConfigChanged(m); |
| } |
| |
| // Do appropriate things at startup with command line arguments. Calls exit() if unhappy. |
| mDNSlocal void ParseCmdLinArgs(int argc, char **argv) |
| { |
| if (argc > 1) |
| { |
| if (0 == strcmp(argv[1], "-debug")) mDNS_DebugMode = mDNStrue; |
| else printf("Usage: %s [-debug]\n", argv[0]); |
| } |
| #ifndef __ANDROID__ |
| if (!mDNS_DebugMode) |
| { |
| int result = daemon(0, 0); |
| if (result != 0) { LogMsg("Could not run as daemon - exiting"); exit(result); } |
| #if __APPLE__ |
| LogMsg("The POSIX mdnsd should only be used on OS X for testing - exiting"); |
| exit(-1); |
| #endif |
| } |
| #endif // !__ANDROID__ |
| } |
| |
| mDNSlocal void DumpStateLog(mDNS *const m) |
| // Dump a little log of what we've been up to. |
| { |
| LogMsg("---- BEGIN STATE LOG ----"); |
| udsserver_info(m); |
| LogMsg("---- END STATE LOG ----"); |
| } |
| |
| mDNSlocal mStatus MainLoop(mDNS *m) // Loop until we quit. |
| { |
| sigset_t signals; |
| mDNSBool gotData = mDNSfalse; |
| |
| mDNSPosixListenForSignalInEventLoop(SIGINT); |
| mDNSPosixListenForSignalInEventLoop(SIGTERM); |
| mDNSPosixListenForSignalInEventLoop(SIGUSR1); |
| mDNSPosixListenForSignalInEventLoop(SIGPIPE); |
| mDNSPosixListenForSignalInEventLoop(SIGHUP) ; |
| |
| for (; ;) |
| { |
| // Work out how long we expect to sleep before the next scheduled task |
| struct timeval timeout; |
| mDNSs32 ticks; |
| |
| // Only idle if we didn't find any data the last time around |
| if (!gotData) |
| { |
| mDNSs32 nextTimerEvent = mDNS_Execute(m); |
| nextTimerEvent = udsserver_idle(nextTimerEvent); |
| ticks = nextTimerEvent - mDNS_TimeNow(m); |
| if (ticks < 1) ticks = 1; |
| } |
| else // otherwise call EventLoop again with 0 timemout |
| ticks = 0; |
| |
| timeout.tv_sec = ticks / mDNSPlatformOneSecond; |
| timeout.tv_usec = (ticks % mDNSPlatformOneSecond) * 1000000 / mDNSPlatformOneSecond; |
| |
| (void) mDNSPosixRunEventLoopOnce(m, &timeout, &signals, &gotData); |
| |
| if (sigismember(&signals, SIGHUP )) Reconfigure(m); |
| if (sigismember(&signals, SIGUSR1)) DumpStateLog(m); |
| // SIGPIPE happens when we try to write to a dead client; death should be detected soon in request_callback() and cleaned up. |
| if (sigismember(&signals, SIGPIPE)) LogMsg("Received SIGPIPE - ignoring"); |
| if (sigismember(&signals, SIGINT) || sigismember(&signals, SIGTERM)) break; |
| } |
| return EINTR; |
| } |
| |
| int main(int argc, char **argv) |
| { |
| mStatus err; |
| |
| ParseCmdLinArgs(argc, argv); |
| |
| LogMsg("%s starting", mDNSResponderVersionString); |
| |
| err = mDNS_Init(&mDNSStorage, &PlatformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, |
| mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext); |
| |
| if (mStatus_NoError == err) |
| #ifdef __ANDROID__ |
| { |
| dnssd_sock_t s[1]; |
| char *socketname = strrchr(MDNS_UDS_SERVERPATH, '/'); |
| if (socketname) |
| { |
| socketname++; // skip '/' |
| s[0] = android_get_control_socket(socketname); |
| err = udsserver_init(s, 1); |
| } else { |
| err = udsserver_init(mDNSNULL, 0); |
| } |
| } |
| #else |
| err = udsserver_init(mDNSNULL, 0); |
| #endif // __ANDROID__ |
| |
| Reconfigure(&mDNSStorage); |
| |
| // Now that we're finished with anything privileged, switch over to running as "nobody" |
| if (mStatus_NoError == err) |
| { |
| const struct passwd *pw = getpwnam(MDNS_USERNAME); |
| if (pw != NULL) |
| setuid(pw->pw_uid); |
| else |
| LogMsg("WARNING: mdnsd continuing as root because user \"%s\" does not exist", MDNS_USERNAME); |
| } |
| |
| if (mStatus_NoError == err) |
| err = MainLoop(&mDNSStorage); |
| |
| LogMsg("%s stopping", mDNSResponderVersionString); |
| |
| mDNS_Close(&mDNSStorage); |
| |
| if (udsserver_exit() < 0) |
| LogMsg("ExitCallback: udsserver_exit failed"); |
| |
| #if MDNS_DEBUGMSGS > 0 |
| printf("mDNSResponder exiting normally with %ld\n", err); |
| #endif |
| |
| return err; |
| } |
| |
| // uds_daemon support //////////////////////////////////////////////////////////// |
| |
| mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context, void **platform_data) |
| /* Support routine for uds_daemon.c */ |
| { |
| // Depends on the fact that udsEventCallback == mDNSPosixEventCallback |
| (void) platform_data; |
| return mDNSPosixAddFDToEventLoop(fd, callback, context); |
| } |
| |
| int udsSupportReadFD(dnssd_sock_t fd, char *buf, int len, int flags, void *platform_data) |
| { |
| (void) platform_data; |
| return recv(fd, buf, len, flags); |
| } |
| |
| mStatus udsSupportRemoveFDFromEventLoop(int fd, void *platform_data) // Note: This also CLOSES the file descriptor |
| { |
| mStatus err = mDNSPosixRemoveFDFromEventLoop(fd); |
| (void) platform_data; |
| close(fd); |
| return err; |
| } |
| |
| mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay) |
| { |
| (void)m; |
| (void)delay; |
| // No-op, for now |
| } |
| |
| #if _BUILDING_XCODE_PROJECT_ |
| // If the process crashes, then this string will be magically included in the automatically-generated crash log |
| const char *__crashreporter_info__ = mDNSResponderVersionString_SCCS + 5; |
| asm(".desc ___crashreporter_info__, 0x10"); |
| #endif |
| |
| // For convenience when using the "strings" command, this is the last thing in the file |
| #if mDNSResponderVersion > 1 |
| mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder-" STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")"; |
| #elif MDNS_VERSIONSTR_NODTS |
| mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder (Engineering Build)"; |
| #else |
| mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder (Engineering Build) (" __DATE__ " " __TIME__ ")"; |
| #endif |