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