]>
Commit | Line | Data |
---|---|---|
ef416fc2 | 1 | /* |
75bd9771 | 2 | * "$Id: listen.c 7673 2008-06-18 22:31:26Z mike $" |
ef416fc2 | 3 | * |
4 | * Server listening routines for the Common UNIX Printing System (CUPS) | |
5 | * scheduler. | |
6 | * | |
bc44d920 | 7 | * Copyright 2007 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) | |
153 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
75bd9771 | 154 | "Unable to find IP address for server name \"%s\"!", |
bd7854cb | 155 | ServerName); |
ef416fc2 | 156 | |
157 | /* | |
158 | * Setup socket listeners... | |
159 | */ | |
160 | ||
bd7854cb | 161 | for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners), LocalPort = 0, |
162 | have_domain = NULL; | |
163 | lis; | |
164 | lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) | |
ef416fc2 | 165 | { |
166 | httpAddrString(&(lis->address), s, sizeof(s)); | |
167 | ||
168 | #ifdef AF_INET6 | |
169 | if (lis->address.addr.sa_family == AF_INET6) | |
170 | p = ntohs(lis->address.ipv6.sin6_port); | |
171 | else | |
172 | #endif /* AF_INET6 */ | |
173 | #ifdef AF_LOCAL | |
174 | if (lis->address.addr.sa_family == AF_LOCAL) | |
175 | p = 0; | |
176 | else | |
177 | #endif /* AF_LOCAL */ | |
178 | p = ntohs(lis->address.ipv4.sin_port); | |
179 | ||
180 | /* | |
a4d04587 | 181 | * If needed, create a socket for listening... |
ef416fc2 | 182 | */ |
183 | ||
ef416fc2 | 184 | if (lis->fd == -1) |
185 | { | |
ef416fc2 | 186 | /* |
a4d04587 | 187 | * Create a socket for listening... |
ef416fc2 | 188 | */ |
bd7854cb | 189 | |
a4d04587 | 190 | lis->fd = socket(lis->address.addr.sa_family, SOCK_STREAM, 0); |
bd7854cb | 191 | |
a4d04587 | 192 | if (lis->fd == -1) |
193 | { | |
194 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
bd7854cb | 195 | "Unable to open listen socket for address %s:%d - %s.", |
a4d04587 | 196 | s, p, strerror(errno)); |
197 | continue; | |
198 | } | |
bd7854cb | 199 | |
ef416fc2 | 200 | /* |
a4d04587 | 201 | * Set things up to reuse the local address for this port. |
ef416fc2 | 202 | */ |
bd7854cb | 203 | |
a4d04587 | 204 | val = 1; |
e00b005a | 205 | #ifdef __sun |
a4d04587 | 206 | setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val)); |
e00b005a | 207 | #else |
a4d04587 | 208 | setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); |
e00b005a | 209 | #endif /* __sun */ |
bd7854cb | 210 | |
ef416fc2 | 211 | /* |
a4d04587 | 212 | * Bind to the port we found... |
ef416fc2 | 213 | */ |
bd7854cb | 214 | |
e00b005a | 215 | #ifdef AF_INET6 |
a4d04587 | 216 | if (lis->address.addr.sa_family == AF_INET6) |
217 | { | |
e00b005a | 218 | # ifdef IPV6_V6ONLY |
a4d04587 | 219 | /* |
220 | * Accept only IPv6 connections on this socket, to avoid | |
221 | * potential security issues and to make all platforms behave | |
222 | * the same. | |
223 | */ | |
bd7854cb | 224 | |
a4d04587 | 225 | val = 1; |
e00b005a | 226 | # ifdef __sun |
a4d04587 | 227 | setsockopt(lis->fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&val, sizeof(val)); |
e00b005a | 228 | # else |
a4d04587 | 229 | setsockopt(lis->fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val)); |
e00b005a | 230 | # endif /* __sun */ |
231 | # endif /* IPV6_V6ONLY */ | |
bd7854cb | 232 | |
a4d04587 | 233 | status = bind(lis->fd, (struct sockaddr *)&(lis->address), |
234 | httpAddrLength(&(lis->address))); | |
235 | } | |
236 | else | |
e00b005a | 237 | #endif /* AF_INET6 */ |
238 | #ifdef AF_LOCAL | |
a4d04587 | 239 | if (lis->address.addr.sa_family == AF_LOCAL) |
240 | { | |
241 | mode_t mask; /* Umask setting */ | |
bd7854cb | 242 | |
243 | ||
a4d04587 | 244 | /* |
245 | * Remove any existing domain socket file... | |
246 | */ | |
bd7854cb | 247 | |
a4d04587 | 248 | unlink(lis->address.un.sun_path); |
bd7854cb | 249 | |
a4d04587 | 250 | /* |
251 | * Save the curent umask and set it to 0... | |
252 | */ | |
bd7854cb | 253 | |
a4d04587 | 254 | mask = umask(0); |
bd7854cb | 255 | |
a4d04587 | 256 | /* |
257 | * Bind the domain socket... | |
258 | */ | |
bd7854cb | 259 | |
a4d04587 | 260 | status = bind(lis->fd, (struct sockaddr *)&(lis->address), |
261 | httpAddrLength(&(lis->address))); | |
bd7854cb | 262 | |
a4d04587 | 263 | /* |
264 | * Restore the umask... | |
265 | */ | |
bd7854cb | 266 | |
a4d04587 | 267 | umask(mask); |
268 | } | |
269 | else | |
e00b005a | 270 | #endif /* AF_LOCAL */ |
ef416fc2 | 271 | status = bind(lis->fd, (struct sockaddr *)&(lis->address), |
a4d04587 | 272 | sizeof(lis->address.ipv4)); |
bd7854cb | 273 | |
a4d04587 | 274 | if (status < 0) |
275 | { | |
276 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
bd7854cb | 277 | "Unable to bind socket for address %s:%d - %s.", |
a4d04587 | 278 | s, p, strerror(errno)); |
279 | close(lis->fd); | |
280 | lis->fd = -1; | |
281 | continue; | |
282 | } | |
bd7854cb | 283 | |
ef416fc2 | 284 | /* |
a4d04587 | 285 | * Listen for new clients. |
ef416fc2 | 286 | */ |
bd7854cb | 287 | |
a4d04587 | 288 | if (listen(lis->fd, ListenBackLog) < 0) |
289 | { | |
290 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
bd7854cb | 291 | "Unable to listen for clients on address %s:%d - %s.", |
a4d04587 | 292 | s, p, strerror(errno)); |
293 | exit(errno); | |
294 | } | |
ef416fc2 | 295 | } |
ef416fc2 | 296 | |
a4d04587 | 297 | fcntl(lis->fd, F_SETFD, fcntl(lis->fd, F_GETFD) | FD_CLOEXEC); |
ef416fc2 | 298 | |
299 | if (p) | |
bd7854cb | 300 | cupsdLogMessage(CUPSD_LOG_INFO, "Listening to %s:%d on fd %d...", |
ef416fc2 | 301 | s, p, lis->fd); |
302 | else | |
4400e98d | 303 | { |
bd7854cb | 304 | cupsdLogMessage(CUPSD_LOG_INFO, "Listening to %s on fd %d...", |
ef416fc2 | 305 | s, lis->fd); |
306 | ||
4400e98d | 307 | if (chmod(s, 0140777)) |
308 | cupsdLogMessage(CUPSD_LOG_ERROR, | |
bd7854cb | 309 | "Unable to change permisssions on domain socket " |
310 | "\"%s\" - %s", s, strerror(errno)); | |
4400e98d | 311 | } |
312 | ||
ef416fc2 | 313 | /* |
314 | * Save the first port that is bound to the local loopback or | |
315 | * "any" address... | |
316 | */ | |
317 | ||
8ca02f3c | 318 | if ((!LocalPort || LocalEncryption == HTTP_ENCRYPT_ALWAYS) && p > 0 && |
ef416fc2 | 319 | (httpAddrLocalhost(&(lis->address)) || |
320 | httpAddrAny(&(lis->address)))) | |
321 | { | |
322 | LocalPort = p; | |
323 | LocalEncryption = lis->encryption; | |
324 | } | |
325 | ||
326 | #ifdef AF_LOCAL | |
327 | if (lis->address.addr.sa_family == AF_LOCAL && !have_domain) | |
328 | have_domain = lis->address.un.sun_path; | |
329 | #endif /* AF_LOCAL */ | |
330 | } | |
331 | ||
332 | /* | |
333 | * Make sure that we are listening on localhost! | |
334 | */ | |
335 | ||
336 | if (!LocalPort && !have_domain) | |
337 | { | |
338 | cupsdLogMessage(CUPSD_LOG_EMERG, | |
07725fee | 339 | "No Listen or Port lines were found to allow access via " |
340 | "localhost!"); | |
ef416fc2 | 341 | |
342 | /* | |
343 | * Commit suicide... | |
344 | */ | |
345 | ||
346 | cupsdEndProcess(getpid(), 0); | |
347 | } | |
348 | ||
349 | /* | |
350 | * Set the CUPS_SERVER, IPP_PORT, and CUPS_ENCRYPTION variables based on | |
351 | * the listeners... | |
352 | */ | |
353 | ||
354 | if (have_domain) | |
355 | { | |
356 | /* | |
357 | * Use domain sockets for the local connection... | |
358 | */ | |
359 | ||
360 | cupsdSetEnv("CUPS_SERVER", have_domain); | |
8ca02f3c | 361 | |
362 | LocalEncryption = HTTP_ENCRYPT_IF_REQUESTED; | |
ef416fc2 | 363 | } |
364 | else | |
365 | { | |
366 | /* | |
367 | * Use the default local loopback address for the server... | |
368 | */ | |
369 | ||
370 | cupsdSetEnv("CUPS_SERVER", "localhost"); | |
371 | } | |
372 | ||
373 | cupsdSetEnv("CUPS_ENCRYPTION", encryptions[LocalEncryption]); | |
07725fee | 374 | |
375 | if (LocalPort) | |
376 | cupsdSetEnvf("IPP_PORT", "%d", LocalPort); | |
ef416fc2 | 377 | |
378 | /* | |
379 | * Resume listening for connections... | |
380 | */ | |
381 | ||
382 | cupsdResumeListening(); | |
383 | } | |
384 | ||
385 | ||
386 | /* | |
387 | * 'cupsdStopListening()' - Close all listening sockets... | |
388 | */ | |
389 | ||
390 | void | |
391 | cupsdStopListening(void) | |
392 | { | |
ef416fc2 | 393 | cupsd_listener_t *lis; /* Current listening socket */ |
394 | ||
395 | ||
bd7854cb | 396 | cupsdLogMessage(CUPSD_LOG_DEBUG2, |
ef416fc2 | 397 | "cupsdStopListening: closing all listen sockets."); |
398 | ||
399 | cupsdPauseListening(); | |
400 | ||
bd7854cb | 401 | for (lis = (cupsd_listener_t *)cupsArrayFirst(Listeners); |
402 | lis; | |
403 | lis = (cupsd_listener_t *)cupsArrayNext(Listeners)) | |
ef416fc2 | 404 | { |
a4d04587 | 405 | if (lis->fd != -1) |
406 | { | |
ef416fc2 | 407 | #ifdef WIN32 |
a4d04587 | 408 | closesocket(lis->fd); |
ef416fc2 | 409 | #else |
a4d04587 | 410 | close(lis->fd); |
ef416fc2 | 411 | #endif /* WIN32 */ |
412 | ||
413 | #ifdef AF_LOCAL | |
4400e98d | 414 | /* |
415 | * Remove domain sockets... | |
416 | */ | |
ef416fc2 | 417 | |
4400e98d | 418 | # ifdef HAVE_LAUNCH_H |
419 | if (lis->address.addr.sa_family == AF_LOCAL && !Launchd) | |
420 | # else | |
a4d04587 | 421 | if (lis->address.addr.sa_family == AF_LOCAL) |
4400e98d | 422 | # endif /* HAVE_LAUNCH_H */ |
a4d04587 | 423 | unlink(lis->address.un.sun_path); |
ef416fc2 | 424 | #endif /* AF_LOCAL */ |
a4d04587 | 425 | } |
ef416fc2 | 426 | } |
427 | } | |
428 | ||
429 | ||
430 | /* | |
75bd9771 | 431 | * End of "$Id: listen.c 7673 2008-06-18 22:31:26Z mike $". |
ef416fc2 | 432 | */ |