]>
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 | * | |
49d87452 | 7 | * Copyright 2007-2008 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 | |
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) | |
49d87452 | 153 | { |
ef416fc2 | 154 | cupsdLogMessage(CUPSD_LOG_ERROR, |
75bd9771 | 155 | "Unable to find IP address for server name \"%s\"!", |
bd7854cb | 156 | ServerName); |
ef416fc2 | 157 | |
49d87452 MS |
158 | if (FatalErrors & CUPSD_FATAL_LISTEN) |
159 | cupsdEndProcess(getpid(), 0); | |
160 | } | |
161 | ||
ef416fc2 | 162 | /* |
163 | * Setup socket listeners... | |
164 | */ | |
165 | ||
bd7854cb | 166 | for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners), LocalPort = 0, |
167 | have_domain = NULL; | |
168 | lis; | |
169 | lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) | |
ef416fc2 | 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 | /* | |
a4d04587 | 186 | * If needed, create a socket for listening... |
ef416fc2 | 187 | */ |
188 | ||
ef416fc2 | 189 | if (lis->fd == -1) |
190 | { | |
ef416fc2 | 191 | /* |
a4d04587 | 192 | * Create a socket for listening... |
ef416fc2 | 193 | */ |
bd7854cb | 194 | |
a4d04587 | 195 | lis->fd = socket(lis->address.addr.sa_family, SOCK_STREAM, 0); |
bd7854cb | 196 | |
a4d04587 | 197 | if (lis->fd == -1) |
198 | { | |
199 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
bd7854cb | 200 | "Unable to open listen socket for address %s:%d - %s.", |
a4d04587 | 201 | s, p, strerror(errno)); |
49d87452 MS |
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 | ||
a4d04587 | 216 | continue; |
217 | } | |
bd7854cb | 218 | |
ef416fc2 | 219 | /* |
a4d04587 | 220 | * Set things up to reuse the local address for this port. |
ef416fc2 | 221 | */ |
bd7854cb | 222 | |
a4d04587 | 223 | val = 1; |
e00b005a | 224 | #ifdef __sun |
a4d04587 | 225 | setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)); |
e00b005a | 226 | #else |
a4d04587 | 227 | setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); |
e00b005a | 228 | #endif /* __sun */ |
bd7854cb | 229 | |
ef416fc2 | 230 | /* |
a4d04587 | 231 | * Bind to the port we found... |
ef416fc2 | 232 | */ |
bd7854cb | 233 | |
e00b005a | 234 | #ifdef AF_INET6 |
a4d04587 | 235 | if (lis->address.addr.sa_family == AF_INET6) |
236 | { | |
e00b005a | 237 | # ifdef IPV6_V6ONLY |
a4d04587 | 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 | */ | |
bd7854cb | 243 | |
a4d04587 | 244 | val = 1; |
e00b005a | 245 | # ifdef __sun |
a4d04587 | 246 | setsockopt(lis->fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&val, sizeof(val)); |
e00b005a | 247 | # else |
a4d04587 | 248 | setsockopt(lis->fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); |
e00b005a | 249 | # endif /* __sun */ |
250 | # endif /* IPV6_V6ONLY */ | |
bd7854cb | 251 | |
a4d04587 | 252 | status = bind(lis->fd, (struct sockaddr *)&(lis->address), |
253 | httpAddrLength(&(lis->address))); | |
254 | } | |
255 | else | |
e00b005a | 256 | #endif /* AF_INET6 */ |
257 | #ifdef AF_LOCAL | |
a4d04587 | 258 | if (lis->address.addr.sa_family == AF_LOCAL) |
259 | { | |
260 | mode_t mask; /* Umask setting */ | |
bd7854cb | 261 | |
262 | ||
a4d04587 | 263 | /* |
264 | * Remove any existing domain socket file... | |
265 | */ | |
bd7854cb | 266 | |
a4d04587 | 267 | unlink(lis->address.un.sun_path); |
bd7854cb | 268 | |
a4d04587 | 269 | /* |
270 | * Save the curent umask and set it to 0... | |
271 | */ | |
bd7854cb | 272 | |
a4d04587 | 273 | mask = umask(0); |
bd7854cb | 274 | |
a4d04587 | 275 | /* |
276 | * Bind the domain socket... | |
277 | */ | |
bd7854cb | 278 | |
a4d04587 | 279 | status = bind(lis->fd, (struct sockaddr *)&(lis->address), |
280 | httpAddrLength(&(lis->address))); | |
bd7854cb | 281 | |
a4d04587 | 282 | /* |
283 | * Restore the umask... | |
284 | */ | |
bd7854cb | 285 | |
a4d04587 | 286 | umask(mask); |
287 | } | |
288 | else | |
e00b005a | 289 | #endif /* AF_LOCAL */ |
ef416fc2 | 290 | status = bind(lis->fd, (struct sockaddr *)&(lis->address), |
a4d04587 | 291 | sizeof(lis->address.ipv4)); |
bd7854cb | 292 | |
a4d04587 | 293 | if (status < 0) |
294 | { | |
295 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
bd7854cb | 296 | "Unable to bind socket for address %s:%d - %s.", |
a4d04587 | 297 | s, p, strerror(errno)); |
298 | close(lis->fd); | |
299 | lis->fd = -1; | |
49d87452 MS |
300 | |
301 | if (FatalErrors & CUPSD_FATAL_LISTEN) | |
302 | cupsdEndProcess(getpid(), 0); | |
303 | ||
a4d04587 | 304 | continue; |
305 | } | |
bd7854cb | 306 | |
ef416fc2 | 307 | /* |
a4d04587 | 308 | * Listen for new clients. |
ef416fc2 | 309 | */ |
bd7854cb | 310 | |
a4d04587 | 311 | if (listen(lis->fd, ListenBackLog) < 0) |
312 | { | |
313 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
bd7854cb | 314 | "Unable to listen for clients on address %s:%d - %s.", |
a4d04587 | 315 | s, p, strerror(errno)); |
49d87452 MS |
316 | |
317 | close(lis->fd); | |
318 | lis->fd = -1; | |
319 | ||
320 | if (FatalErrors & CUPSD_FATAL_LISTEN) | |
321 | cupsdEndProcess(getpid(), 0); | |
322 | ||
323 | continue; | |
a4d04587 | 324 | } |
ef416fc2 | 325 | } |
ef416fc2 | 326 | |
a4d04587 | 327 | fcntl(lis->fd, F_SETFD, fcntl(lis->fd, F_GETFD) | FD_CLOEXEC); |
ef416fc2 | 328 | |
329 | if (p) | |
bd7854cb | 330 | cupsdLogMessage(CUPSD_LOG_INFO, "Listening to %s:%d on fd %d...", |
ef416fc2 | 331 | s, p, lis->fd); |
332 | else | |
4400e98d | 333 | { |
bd7854cb | 334 | cupsdLogMessage(CUPSD_LOG_INFO, "Listening to %s on fd %d...", |
ef416fc2 | 335 | s, lis->fd); |
336 | ||
4400e98d | 337 | if (chmod(s, 0140777)) |
338 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
bd7854cb | 339 | "Unable to change permisssions on domain socket " |
340 | "\"%s\" - %s", s, strerror(errno)); | |
4400e98d | 341 | } |
342 | ||
ef416fc2 | 343 | /* |
344 | * Save the first port that is bound to the local loopback or | |
345 | * "any" address... | |
346 | */ | |
347 | ||
8ca02f3c | 348 | if ((!LocalPort || LocalEncryption == HTTP_ENCRYPT_ALWAYS) && p > 0 && |
ef416fc2 | 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, | |
07725fee | 369 | "No Listen or Port lines were found to allow access via " |
370 | "localhost!"); | |
ef416fc2 | 371 | |
49d87452 MS |
372 | if (FatalErrors & (CUPSD_FATAL_CONFIG | CUPSD_FATAL_LISTEN)) |
373 | cupsdEndProcess(getpid(), 0); | |
ef416fc2 | 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); | |
8ca02f3c | 388 | |
389 | LocalEncryption = HTTP_ENCRYPT_IF_REQUESTED; | |
ef416fc2 | 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]); | |
07725fee | 401 | |
402 | if (LocalPort) | |
403 | cupsdSetEnvf("IPP_PORT", "%d", LocalPort); | |
ef416fc2 | 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 | { | |
ef416fc2 | 420 | cupsd_listener_t *lis; /* Current listening socket */ |
421 | ||
422 | ||
bd7854cb | 423 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
ef416fc2 | 424 | "cupsdStopListening: closing all listen sockets."); |
425 | ||
426 | cupsdPauseListening(); | |
427 | ||
bd7854cb | 428 | for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); |
429 | lis; | |
430 | lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) | |
ef416fc2 | 431 | { |
a4d04587 | 432 | if (lis->fd != -1) |
433 | { | |
ef416fc2 | 434 | #ifdef WIN32 |
a4d04587 | 435 | closesocket(lis->fd); |
ef416fc2 | 436 | #else |
a4d04587 | 437 | close(lis->fd); |
ef416fc2 | 438 | #endif /* WIN32 */ |
439 | ||
440 | #ifdef AF_LOCAL | |
4400e98d | 441 | /* |
442 | * Remove domain sockets... | |
443 | */ | |
ef416fc2 | 444 | |
4400e98d | 445 | # ifdef HAVE_LAUNCH_H |
446 | if (lis->address.addr.sa_family == AF_LOCAL && !Launchd) | |
447 | # else | |
a4d04587 | 448 | if (lis->address.addr.sa_family == AF_LOCAL) |
4400e98d | 449 | # endif /* HAVE_LAUNCH_H */ |
a4d04587 | 450 | unlink(lis->address.un.sun_path); |
ef416fc2 | 451 | #endif /* AF_LOCAL */ |
a4d04587 | 452 | } |
ef416fc2 | 453 | } |
454 | } | |
455 | ||
456 | ||
457 | /* | |
b19ccc9e | 458 | * End of "$Id: listen.c 7918 2008-09-08 22:03:01Z mike $". |
ef416fc2 | 459 | */ |