xref: /illumos-gate/usr/src/contrib/mDNSResponder/mDNSPosix/PosixDaemon.c (revision b12258b69ac245658b0ca5ae070b3ff004186148)
1 /* -*- Mode: C; tab-width: 4 -*-
2  *
3  * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16 
17     File:		daemon.c
18 
19     Contains:	main & associated Application layer for mDNSResponder on Linux.
20 
21  */
22 
23 #if __APPLE__
24 // In Mac OS X 10.5 and later trying to use the daemon function gives a “‘daemon’ is deprecated”
25 // error, which prevents compilation because we build with "-Werror".
26 // Since this is supposed to be portable cross-platform code, we don't care that daemon is
27 // deprecated on Mac OS X 10.5, so we use this preprocessor trick to eliminate the error message.
28 #define daemon yes_we_know_that_daemon_is_deprecated_in_os_x_10_5_thankyou
29 #endif
30 
31 #include <stdio.h>
32 #include <string.h>
33 #include <unistd.h>
34 #include <stdlib.h>
35 #include <signal.h>
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <pwd.h>
39 #include <sys/types.h>
40 
41 #if __APPLE__
42 #undef daemon
43 extern int daemon(int, int);
44 #endif
45 
46 #include "mDNSEmbeddedAPI.h"
47 #include "mDNSPosix.h"
48 #include "mDNSUNP.h"        // For daemon()
49 #include "uds_daemon.h"
50 #include "PlatformCommon.h"
51 
52 #ifndef MDNSD_USER
53 #define	MDNSD_USER "nobody"
54 #endif
55 
56 #define CONFIG_FILE "/etc/mdnsd.conf"
57 static domainname DynDNSZone;                // Default wide-area zone for service registration
58 static domainname DynDNSHostname;
59 
60 #define RR_CACHE_SIZE 500
61 static CacheEntity gRRCache[RR_CACHE_SIZE];
62 static mDNS_PlatformSupport PlatformStorage;
63 
64 mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result)
65 {
66     (void)m; // Unused
67     if (result == mStatus_NoError)
68     {
69         // On successful registration of dot-local mDNS host name, daemon may want to check if
70         // any name conflict and automatic renaming took place, and if so, record the newly negotiated
71         // name in persistent storage for next time. It should also inform the user of the name change.
72         // On Mac OS X we store the current dot-local mDNS host name in the SCPreferences store,
73         // and notify the user with a CFUserNotification.
74     }
75     else if (result == mStatus_ConfigChanged)
76     {
77         udsserver_handle_configchange(m);
78     }
79     else if (result == mStatus_GrowCache)
80     {
81         // Allocate another chunk of cache storage
82         CacheEntity *storage = malloc(sizeof(CacheEntity) * RR_CACHE_SIZE);
83         if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE);
84     }
85 }
86 
87 // %%% Reconfigure() probably belongs in the platform support layer (mDNSPosix.c), not the daemon cde
88 // -- all client layers running on top of mDNSPosix.c need to handle network configuration changes,
89 // not only the Unix Domain Socket Daemon
90 
91 static void Reconfigure(mDNS *m)
92 {
93     mDNSAddr DynDNSIP;
94     const mDNSAddr dummy = { mDNSAddrType_IPv4, { { { 1, 1, 1, 1 } } } };;
95     mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL, NULL);
96     if (ParseDNSServers(m, uDNS_SERVERS_FILE) < 0)
97         LogMsg("Unable to parse DNS server list. Unicast DNS-SD unavailable");
98     ReadDDNSSettingsFromConfFile(m, CONFIG_FILE, &DynDNSHostname, &DynDNSZone, NULL);
99     mDNSPlatformSourceAddrForDest(&DynDNSIP, &dummy);
100     if (DynDNSHostname.c[0]) mDNS_AddDynDNSHostName(m, &DynDNSHostname, NULL, NULL);
101     if (DynDNSIP.type) mDNS_SetPrimaryInterfaceInfo(m, &DynDNSIP, NULL, NULL);
102     mDNS_ConfigChanged(m);
103 }
104 
105 // Do appropriate things at startup with command line arguments. Calls exit() if unhappy.
106 mDNSlocal void ParseCmdLinArgs(int argc, char **argv)
107 {
108     if (argc > 1)
109     {
110         if (0 == strcmp(argv[1], "-debug")) mDNS_DebugMode = mDNStrue;
111         else printf("Usage: %s [-debug]\n", argv[0]);
112     }
113 
114     if (!mDNS_DebugMode)
115     {
116         int result = daemon(0, 0);
117         if (result != 0) { LogMsg("Could not run as daemon - exiting"); exit(result); }
118 #if __APPLE__
119         LogMsg("The POSIX mdnsd should only be used on OS X for testing - exiting");
120         exit(-1);
121 #endif
122     }
123 }
124 
125 mDNSlocal void ToggleLog(void)
126 {
127     mDNS_LoggingEnabled = !mDNS_LoggingEnabled;
128 }
129 
130 mDNSlocal void ToggleLogPacket(void)
131 {
132     mDNS_PacketLoggingEnabled = !mDNS_PacketLoggingEnabled;
133 }
134 
135 mDNSlocal void DumpStateLog()
136 // Dump a little log of what we've been up to.
137 {
138 	LogMsg("---- BEGIN STATE LOG ----");
139     udsserver_info();
140     LogMsg("----  END STATE LOG  ----");
141 }
142 
143 mDNSlocal mStatus MainLoop(mDNS *m) // Loop until we quit.
144 {
145     sigset_t signals;
146     mDNSBool gotData = mDNSfalse;
147 
148     mDNSPosixListenForSignalInEventLoop(SIGINT);
149     mDNSPosixListenForSignalInEventLoop(SIGTERM);
150     mDNSPosixListenForSignalInEventLoop(SIGUSR1);
151     mDNSPosixListenForSignalInEventLoop(SIGUSR2);
152     mDNSPosixListenForSignalInEventLoop(SIGINFO);
153     mDNSPosixListenForSignalInEventLoop(SIGPIPE);
154     mDNSPosixListenForSignalInEventLoop(SIGHUP) ;
155 
156     for (; ;)
157     {
158         // Work out how long we expect to sleep before the next scheduled task
159         struct timeval timeout;
160         mDNSs32 ticks;
161 
162         // Only idle if we didn't find any data the last time around
163         if (!gotData)
164         {
165             mDNSs32 nextTimerEvent = mDNS_Execute(m);
166             nextTimerEvent = udsserver_idle(nextTimerEvent);
167             ticks = nextTimerEvent - mDNS_TimeNow(m);
168             if (ticks < 1) ticks = 1;
169         }
170         else    // otherwise call EventLoop again with 0 timemout
171             ticks = 0;
172 
173         timeout.tv_sec = ticks / mDNSPlatformOneSecond;
174         timeout.tv_usec = (ticks % mDNSPlatformOneSecond) * 1000000 / mDNSPlatformOneSecond;
175 
176         (void) mDNSPosixRunEventLoopOnce(m, &timeout, &signals, &gotData);
177 
178         if (sigismember(&signals, SIGHUP )) Reconfigure(m);
179         if (sigismember(&signals, SIGINFO)) DumpStateLog();
180         if (sigismember(&signals, SIGUSR1)) ToggleLog();
181         if (sigismember(&signals, SIGUSR2)) ToggleLogPacket();
182         // SIGPIPE happens when we try to write to a dead client; death should be detected soon in request_callback() and cleaned up.
183         if (sigismember(&signals, SIGPIPE)) LogMsg("Received SIGPIPE - ignoring");
184         if (sigismember(&signals, SIGINT) || sigismember(&signals, SIGTERM)) break;
185     }
186     return EINTR;
187 }
188 
189 int main(int argc, char **argv)
190 {
191     mStatus err;
192 
193     ParseCmdLinArgs(argc, argv);
194 
195     LogInfo("%s starting", mDNSResponderVersionString);
196 
197     err = mDNS_Init(&mDNSStorage, &PlatformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses,
198                     mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext);
199 
200     if (mStatus_NoError == err)
201         err = udsserver_init(mDNSNULL, 0);
202 
203     Reconfigure(&mDNSStorage);
204 
205     // Now that we're finished with anything privileged, switch over to running as "nobody"
206     if (mStatus_NoError == err)
207     {
208         const struct passwd *pw = getpwnam(MDNSD_USER);
209         if (pw != NULL)
210             setuid(pw->pw_uid);
211         else
212 #ifdef MDNSD_NOROOT
213 	{
214 	    LogMsg("WARNING: mdnsd exiting because user \""MDNSD_USER"\" does not exist");
215 	    err = mStatus_Invalid;
216 	}
217 #else
218             LogMsg("WARNING: mdnsd continuing as root because user \""MDNSD_USER"\" does not exist");
219 #endif
220     }
221 
222     if (mStatus_NoError == err)
223         err = MainLoop(&mDNSStorage);
224 
225     LogInfo("%s stopping", mDNSResponderVersionString);
226 
227     mDNS_Close(&mDNSStorage);
228 
229     if (udsserver_exit() < 0)
230         LogMsg("ExitCallback: udsserver_exit failed");
231 
232  #if MDNS_DEBUGMSGS > 0
233     printf("mDNSResponder exiting normally with %ld\n", err);
234  #endif
235 
236     return err;
237 }
238 
239 //		uds_daemon support		////////////////////////////////////////////////////////////
240 
241 mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context, void **platform_data)
242 /* Support routine for uds_daemon.c */
243 {
244     // Depends on the fact that udsEventCallback == mDNSPosixEventCallback
245     (void) platform_data;
246     return mDNSPosixAddFDToEventLoop(fd, callback, context);
247 }
248 
249 int udsSupportReadFD(dnssd_sock_t fd, char *buf, int len, int flags, void *platform_data)
250 {
251     (void) platform_data;
252     return recv(fd, buf, len, flags);
253 }
254 
255 mStatus udsSupportRemoveFDFromEventLoop(int fd, void *platform_data)        // Note: This also CLOSES the file descriptor
256 {
257     mStatus err = mDNSPosixRemoveFDFromEventLoop(fd);
258     (void) platform_data;
259     close(fd);
260     return err;
261 }
262 
263 mDNSexport void RecordUpdatedNiceLabel(mDNSs32 delay)
264 {
265     (void)delay;
266     // No-op, for now
267 }
268 
269 #if _BUILDING_XCODE_PROJECT_
270 // If the process crashes, then this string will be magically included in the automatically-generated crash log
271 const char *__crashreporter_info__ = mDNSResponderVersionString_SCCS + 5;
272 asm (".desc ___crashreporter_info__, 0x10");
273 #endif
274 
275 // For convenience when using the "strings" command, this is the last thing in the file
276 #if defined(mDNSResponderVersion)
277 // Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
278 // e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
279 // To expand "version" to its value before making the string, use STRINGIFY(version) instead
280 #define	STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) # s
281 #define	STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
282 
283 mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder-" STRINGIFY(mDNSResponderVersion);
284 #elif MDNS_VERSIONSTR_NODTS
285 mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder (Engineering Build)";
286 #else
287 mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder (Engineering Build) (" __DATE__ " " __TIME__ ")";
288 #endif
289