]> git.ipfire.org Git - thirdparty/cups.git/blob - scheduler/listen.c
Merge changes from CUPS 1.4svn-r7932.
[thirdparty/cups.git] / scheduler / listen.c
1 /*
2 * "$Id: listen.c 7673 2008-06-18 22:31:26Z mike $"
3 *
4 * Server listening routines for the Common UNIX Printing System (CUPS)
5 * scheduler.
6 *
7 * Copyright 2007-2008 by Apple Inc.
8 * Copyright 1997-2006 by Easy Software Products, all rights reserved.
9 *
10 * These coded instructions, statements, and computer programs are the
11 * property of Apple Inc. and are protected by Federal copyright
12 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
13 * which should have been included with this file. If this file is
14 * file is missing or damaged, see the license at "http://www.cups.org/".
15 *
16 * Contents:
17 *
18 * cupsdDeleteAllListeners() - Delete all listeners.
19 * cupsdPauseListening() - Clear input polling on all listening sockets...
20 * cupsdResumeListening() - Set input polling on all listening sockets...
21 * cupsdStartListening() - Create all listening sockets...
22 * cupsdStopListening() - Close all listening sockets...
23 */
24
25 /*
26 * Include necessary headers...
27 */
28
29 #include "cupsd.h"
30
31
32 /*
33 * Make sure the IPV6_V6ONLY is defined on Linux - older versions of
34 * glibc don't define it even if the kernel supports it...
35 */
36
37 #if defined(__linux) && !defined(IPV6_V6ONLY)
38 # define IPV6_V6ONLY 26
39 #endif /* __linux && !IPV6_V6ONLY */
40
41
42 /*
43 * 'cupsdDeleteAllListeners()' - Delete all listeners.
44 */
45
46 void
47 cupsdDeleteAllListeners(void)
48 {
49 cupsd_listener_t *lis; /* Current listening socket */
50
51
52 for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
53 lis;
54 lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
55 free(lis);
56
57 cupsArrayDelete(Listeners);
58 Listeners = NULL;
59 }
60
61
62 /*
63 * 'cupsdPauseListening()' - Clear input polling on all listening sockets...
64 */
65
66 void
67 cupsdPauseListening(void)
68 {
69 cupsd_listener_t *lis; /* Current listening socket */
70
71
72 if (cupsArrayCount(Listeners) < 1)
73 return;
74
75 if (cupsArrayCount(Clients) == MaxClients)
76 cupsdLogMessage(CUPSD_LOG_WARN,
77 "Max clients reached, holding new connections...");
78 else if (errno == ENFILE || errno == EMFILE)
79 cupsdLogMessage(CUPSD_LOG_WARN,
80 "Too many open files, holding new connections for "
81 "30 seconds...");
82
83 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdPauseListening: Clearing input bits...");
84
85 for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
86 lis;
87 lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
88 cupsdRemoveSelect(lis->fd);
89
90 ListeningPaused = time(NULL) + 30;
91 }
92
93
94 /*
95 * 'cupsdResumeListening()' - Set input polling on all listening sockets...
96 */
97
98 void
99 cupsdResumeListening(void)
100 {
101 cupsd_listener_t *lis; /* Current listening socket */
102
103
104 if (cupsArrayCount(Listeners) < 1)
105 return;
106
107 cupsdLogMessage(CUPSD_LOG_INFO, "Resuming new connection processing...");
108 cupsdLogMessage(CUPSD_LOG_DEBUG2,
109 "cupsdResumeListening: Setting input bits...");
110
111 for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
112 lis;
113 lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
114 cupsdAddSelect(lis->fd, (cupsd_selfunc_t)cupsdAcceptClient, NULL, lis);
115
116 ListeningPaused = 0;
117 }
118
119
120 /*
121 * 'cupsdStartListening()' - Create all listening sockets...
122 */
123
124 void
125 cupsdStartListening(void)
126 {
127 int status; /* Bind result */
128 int p, /* Port number */
129 val; /* Parameter value */
130 cupsd_listener_t *lis; /* Current listening socket */
131 char s[256]; /* String addresss */
132 const char *have_domain; /* Have a domain socket? */
133 static const char * const encryptions[] =
134 { /* Encryption values */
135 "IfRequested",
136 "Never",
137 "Required",
138 "Always"
139 };
140
141
142 cupsdLogMessage(CUPSD_LOG_DEBUG2, "cupsdStartListening: %d Listeners",
143 cupsArrayCount(Listeners));
144
145 /*
146 * Get the server's IP address...
147 */
148
149 if (ServerAddrs)
150 httpAddrFreeList(ServerAddrs);
151
152 if ((ServerAddrs = httpAddrGetList(ServerName, AF_UNSPEC, NULL)) == NULL)
153 {
154 cupsdLogMessage(CUPSD_LOG_ERROR,
155 "Unable to find IP address for server name \"%s\"!",
156 ServerName);
157
158 if (FatalErrors & CUPSD_FATAL_LISTEN)
159 cupsdEndProcess(getpid(), 0);
160 }
161
162 /*
163 * Setup socket listeners...
164 */
165
166 for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners), LocalPort = 0,
167 have_domain = NULL;
168 lis;
169 lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
170 {
171 httpAddrString(&(lis->address), s, sizeof(s));
172
173 #ifdef AF_INET6
174 if (lis->address.addr.sa_family == AF_INET6)
175 p = ntohs(lis->address.ipv6.sin6_port);
176 else
177 #endif /* AF_INET6 */
178 #ifdef AF_LOCAL
179 if (lis->address.addr.sa_family == AF_LOCAL)
180 p = 0;
181 else
182 #endif /* AF_LOCAL */
183 p = ntohs(lis->address.ipv4.sin_port);
184
185 /*
186 * If needed, create a socket for listening...
187 */
188
189 if (lis->fd == -1)
190 {
191 /*
192 * Create a socket for listening...
193 */
194
195 lis->fd = socket(lis->address.addr.sa_family, SOCK_STREAM, 0);
196
197 if (lis->fd == -1)
198 {
199 cupsdLogMessage(CUPSD_LOG_ERROR,
200 "Unable to open listen socket for address %s:%d - %s.",
201 s, p, strerror(errno));
202
203 #ifdef AF_INET6
204 /*
205 * IPv6 is often disabled while DNS returns IPv6 addresses...
206 */
207
208 if (lis->address.addr.sa_family != AF_INET6 &&
209 (FatalErrors & CUPSD_FATAL_LISTEN))
210 cupsdEndProcess(getpid(), 0);
211 #else
212 if (FatalErrors & CUPSD_FATAL_LISTEN)
213 cupsdEndProcess(getpid(), 0);
214 #endif /* AF_INET6 */
215
216 continue;
217 }
218
219 /*
220 * Set things up to reuse the local address for this port.
221 */
222
223 val = 1;
224 #ifdef __sun
225 setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val));
226 #else
227 setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
228 #endif /* __sun */
229
230 /*
231 * Bind to the port we found...
232 */
233
234 #ifdef AF_INET6
235 if (lis->address.addr.sa_family == AF_INET6)
236 {
237 # ifdef IPV6_V6ONLY
238 /*
239 * Accept only IPv6 connections on this socket, to avoid
240 * potential security issues and to make all platforms behave
241 * the same.
242 */
243
244 val = 1;
245 # ifdef __sun
246 setsockopt(lis->fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&val, sizeof(val));
247 # else
248 setsockopt(lis->fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
249 # endif /* __sun */
250 # endif /* IPV6_V6ONLY */
251
252 status = bind(lis->fd, (struct sockaddr *)&(lis->address),
253 httpAddrLength(&(lis->address)));
254 }
255 else
256 #endif /* AF_INET6 */
257 #ifdef AF_LOCAL
258 if (lis->address.addr.sa_family == AF_LOCAL)
259 {
260 mode_t mask; /* Umask setting */
261
262
263 /*
264 * Remove any existing domain socket file...
265 */
266
267 unlink(lis->address.un.sun_path);
268
269 /*
270 * Save the curent umask and set it to 0...
271 */
272
273 mask = umask(0);
274
275 /*
276 * Bind the domain socket...
277 */
278
279 status = bind(lis->fd, (struct sockaddr *)&(lis->address),
280 httpAddrLength(&(lis->address)));
281
282 /*
283 * Restore the umask...
284 */
285
286 umask(mask);
287 }
288 else
289 #endif /* AF_LOCAL */
290 status = bind(lis->fd, (struct sockaddr *)&(lis->address),
291 sizeof(lis->address.ipv4));
292
293 if (status < 0)
294 {
295 cupsdLogMessage(CUPSD_LOG_ERROR,
296 "Unable to bind socket for address %s:%d - %s.",
297 s, p, strerror(errno));
298 close(lis->fd);
299 lis->fd = -1;
300
301 if (FatalErrors & CUPSD_FATAL_LISTEN)
302 cupsdEndProcess(getpid(), 0);
303
304 continue;
305 }
306
307 /*
308 * Listen for new clients.
309 */
310
311 if (listen(lis->fd, ListenBackLog) < 0)
312 {
313 cupsdLogMessage(CUPSD_LOG_ERROR,
314 "Unable to listen for clients on address %s:%d - %s.",
315 s, p, strerror(errno));
316
317 close(lis->fd);
318 lis->fd = -1;
319
320 if (FatalErrors & CUPSD_FATAL_LISTEN)
321 cupsdEndProcess(getpid(), 0);
322
323 continue;
324 }
325 }
326
327 fcntl(lis->fd, F_SETFD, fcntl(lis->fd, F_GETFD) | FD_CLOEXEC);
328
329 if (p)
330 cupsdLogMessage(CUPSD_LOG_INFO, "Listening to %s:%d on fd %d...",
331 s, p, lis->fd);
332 else
333 {
334 cupsdLogMessage(CUPSD_LOG_INFO, "Listening to %s on fd %d...",
335 s, lis->fd);
336
337 if (chmod(s, 0140777))
338 cupsdLogMessage(CUPSD_LOG_ERROR,
339 "Unable to change permisssions on domain socket "
340 "\"%s\" - %s", s, strerror(errno));
341 }
342
343 /*
344 * Save the first port that is bound to the local loopback or
345 * "any" address...
346 */
347
348 if ((!LocalPort || LocalEncryption == HTTP_ENCRYPT_ALWAYS) && p > 0 &&
349 (httpAddrLocalhost(&(lis->address)) ||
350 httpAddrAny(&(lis->address))))
351 {
352 LocalPort = p;
353 LocalEncryption = lis->encryption;
354 }
355
356 #ifdef AF_LOCAL
357 if (lis->address.addr.sa_family == AF_LOCAL && !have_domain)
358 have_domain = lis->address.un.sun_path;
359 #endif /* AF_LOCAL */
360 }
361
362 /*
363 * Make sure that we are listening on localhost!
364 */
365
366 if (!LocalPort && !have_domain)
367 {
368 cupsdLogMessage(CUPSD_LOG_EMERG,
369 "No Listen or Port lines were found to allow access via "
370 "localhost!");
371
372 if (FatalErrors & (CUPSD_FATAL_CONFIG | CUPSD_FATAL_LISTEN))
373 cupsdEndProcess(getpid(), 0);
374 }
375
376 /*
377 * Set the CUPS_SERVER, IPP_PORT, and CUPS_ENCRYPTION variables based on
378 * the listeners...
379 */
380
381 if (have_domain)
382 {
383 /*
384 * Use domain sockets for the local connection...
385 */
386
387 cupsdSetEnv("CUPS_SERVER", have_domain);
388
389 LocalEncryption = HTTP_ENCRYPT_IF_REQUESTED;
390 }
391 else
392 {
393 /*
394 * Use the default local loopback address for the server...
395 */
396
397 cupsdSetEnv("CUPS_SERVER", "localhost");
398 }
399
400 cupsdSetEnv("CUPS_ENCRYPTION", encryptions[LocalEncryption]);
401
402 if (LocalPort)
403 cupsdSetEnvf("IPP_PORT", "%d", LocalPort);
404
405 /*
406 * Resume listening for connections...
407 */
408
409 cupsdResumeListening();
410 }
411
412
413 /*
414 * 'cupsdStopListening()' - Close all listening sockets...
415 */
416
417 void
418 cupsdStopListening(void)
419 {
420 cupsd_listener_t *lis; /* Current listening socket */
421
422
423 cupsdLogMessage(CUPSD_LOG_DEBUG2,
424 "cupsdStopListening: closing all listen sockets.");
425
426 cupsdPauseListening();
427
428 for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners);
429 lis;
430 lis = (cupsd_listener_t *)cupsArrayNext(Listeners))
431 {
432 if (lis->fd != -1)
433 {
434 #ifdef WIN32
435 closesocket(lis->fd);
436 #else
437 close(lis->fd);
438 #endif /* WIN32 */
439
440 #ifdef AF_LOCAL
441 /*
442 * Remove domain sockets...
443 */
444
445 # ifdef HAVE_LAUNCH_H
446 if (lis->address.addr.sa_family == AF_LOCAL && !Launchd)
447 # else
448 if (lis->address.addr.sa_family == AF_LOCAL)
449 # endif /* HAVE_LAUNCH_H */
450 unlink(lis->address.un.sun_path);
451 #endif /* AF_LOCAL */
452 }
453 }
454 }
455
456
457 /*
458 * End of "$Id: listen.c 7673 2008-06-18 22:31:26Z mike $".
459 */