]>
Commit | Line | Data |
---|---|---|
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 | ||
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 | ||
ef416fc2 | 62 | /* |
63 | * 'cupsdPauseListening()' - Clear input polling on all listening sockets... | |
64 | */ | |
65 | ||
66 | void | |
67 | cupsdPauseListening(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 | ||
98 | void | |
99 | cupsdResumeListening(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 | ||
124 | void | |
125 | cupsdStartListening(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 | /* |
253 | * Save the curent umask and set it to 0... | |
254 | */ | |
bd7854cb | 255 | |
a4d04587 | 256 | mask = umask(0); |
bd7854cb | 257 | |
a4d04587 | 258 | /* |
259 | * Bind the domain socket... | |
260 | */ | |
bd7854cb | 261 | |
a4d04587 | 262 | status = bind(lis->fd, (struct sockaddr *)&(lis->address), |
263 | httpAddrLength(&(lis->address))); | |
bd7854cb | 264 | |
a4d04587 | 265 | /* |
266 | * Restore the umask... | |
267 | */ | |
bd7854cb | 268 | |
a4d04587 | 269 | umask(mask); |
270 | } | |
271 | else | |
e00b005a | 272 | #endif /* AF_LOCAL */ |
ef416fc2 | 273 | status = bind(lis->fd, (struct sockaddr *)&(lis->address), |
a4d04587 | 274 | sizeof(lis->address.ipv4)); |
bd7854cb | 275 | |
a4d04587 | 276 | if (status < 0) |
277 | { | |
278 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
bd7854cb | 279 | "Unable to bind socket for address %s:%d - %s.", |
a4d04587 | 280 | s, p, strerror(errno)); |
281 | close(lis->fd); | |
282 | lis->fd = -1; | |
49d87452 MS |
283 | |
284 | if (FatalErrors & CUPSD_FATAL_LISTEN) | |
285 | cupsdEndProcess(getpid(), 0); | |
286 | ||
a4d04587 | 287 | continue; |
288 | } | |
bd7854cb | 289 | |
ef416fc2 | 290 | /* |
a4d04587 | 291 | * Listen for new clients. |
ef416fc2 | 292 | */ |
bd7854cb | 293 | |
a4d04587 | 294 | if (listen(lis->fd, ListenBackLog) < 0) |
295 | { | |
296 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
bd7854cb | 297 | "Unable to listen for clients on address %s:%d - %s.", |
a4d04587 | 298 | s, p, strerror(errno)); |
49d87452 MS |
299 | |
300 | close(lis->fd); | |
301 | lis->fd = -1; | |
302 | ||
303 | if (FatalErrors & CUPSD_FATAL_LISTEN) | |
304 | cupsdEndProcess(getpid(), 0); | |
305 | ||
306 | continue; | |
a4d04587 | 307 | } |
ef416fc2 | 308 | } |
ef416fc2 | 309 | |
a4d04587 | 310 | fcntl(lis->fd, F_SETFD, fcntl(lis->fd, F_GETFD) | FD_CLOEXEC); |
ef416fc2 | 311 | |
312 | if (p) | |
bd7854cb | 313 | cupsdLogMessage(CUPSD_LOG_INFO, "Listening to %s:%d on fd %d...", |
ef416fc2 | 314 | s, p, lis->fd); |
315 | else | |
4400e98d | 316 | { |
bd7854cb | 317 | cupsdLogMessage(CUPSD_LOG_INFO, "Listening to %s on fd %d...", |
ef416fc2 | 318 | s, lis->fd); |
319 | ||
4400e98d | 320 | if (chmod(s, 0140777)) |
321 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
bd7854cb | 322 | "Unable to change permisssions on domain socket " |
323 | "\"%s\" - %s", s, strerror(errno)); | |
4400e98d | 324 | } |
325 | ||
ef416fc2 | 326 | /* |
327 | * Save the first port that is bound to the local loopback or | |
328 | * "any" address... | |
329 | */ | |
330 | ||
8ca02f3c | 331 | if ((!LocalPort || LocalEncryption == HTTP_ENCRYPT_ALWAYS) && p > 0 && |
ef416fc2 | 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, | |
07725fee | 352 | "No Listen or Port lines were found to allow access via " |
353 | "localhost!"); | |
ef416fc2 | 354 | |
49d87452 MS |
355 | if (FatalErrors & (CUPSD_FATAL_CONFIG | CUPSD_FATAL_LISTEN)) |
356 | cupsdEndProcess(getpid(), 0); | |
ef416fc2 | 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); | |
8ca02f3c | 371 | |
372 | LocalEncryption = HTTP_ENCRYPT_IF_REQUESTED; | |
ef416fc2 | 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]); | |
07725fee | 384 | |
385 | if (LocalPort) | |
386 | cupsdSetEnvf("IPP_PORT", "%d", LocalPort); | |
ef416fc2 | 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 | { | |
ef416fc2 | 403 | cupsd_listener_t *lis; /* Current listening socket */ |
404 | ||
405 | ||
bd7854cb | 406 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
ef416fc2 | 407 | "cupsdStopListening: closing all listen sockets."); |
408 | ||
409 | cupsdPauseListening(); | |
410 | ||
bd7854cb | 411 | for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); |
412 | lis; | |
413 | lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) | |
ef416fc2 | 414 | { |
a4d04587 | 415 | if (lis->fd != -1) |
416 | { | |
ef416fc2 | 417 | #ifdef WIN32 |
a4d04587 | 418 | closesocket(lis->fd); |
ef416fc2 | 419 | #else |
a4d04587 | 420 | close(lis->fd); |
ef416fc2 | 421 | #endif /* WIN32 */ |
422 | ||
423 | #ifdef AF_LOCAL | |
4400e98d | 424 | /* |
425 | * Remove domain sockets... | |
426 | */ | |
ef416fc2 | 427 | |
4400e98d | 428 | # ifdef HAVE_LAUNCH_H |
429 | if (lis->address.addr.sa_family == AF_LOCAL && !Launchd) | |
430 | # else | |
a4d04587 | 431 | if (lis->address.addr.sa_family == AF_LOCAL) |
4400e98d | 432 | # endif /* HAVE_LAUNCH_H */ |
a4d04587 | 433 | unlink(lis->address.un.sun_path); |
ef416fc2 | 434 | #endif /* AF_LOCAL */ |
a4d04587 | 435 | } |
ef416fc2 | 436 | } |
437 | } | |
438 | ||
439 | ||
440 | /* | |
b19ccc9e | 441 | * End of "$Id: listen.c 7918 2008-09-08 22:03:01Z mike $". |
ef416fc2 | 442 | */ |