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