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