Load cups into easysw/current.
[thirdparty/cups.git] / scheduler / listen.c
1 /*
2  * "$Id: listen.c 4780 2005-10-13 00:38:28Z 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     * Create a socket for listening...
173     */
174
175     lis->fd = socket(lis->address.addr.sa_family, SOCK_STREAM, 0);
176
177     if (lis->fd == -1)
178     {
179       cupsdLogMessage(CUPSD_LOG_ERROR,
180                       "cupsdStartListening: Unable to open listen socket for address %s:%d - %s.",
181                       s, p, strerror(errno));
182       continue;
183     }
184
185     fcntl(lis->fd, F_SETFD, fcntl(lis->fd, F_GETFD) | FD_CLOEXEC);
186
187    /*
188     * Set things up to reuse the local address for this port.
189     */
190
191     val = 1;
192 #ifdef __sun
193     setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, (char *)&val, sizeof(val));
194 #else
195     setsockopt(lis->fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
196 #endif /* __sun */
197
198    /*
199     * Bind to the port we found...
200     */
201
202 #ifdef AF_INET6
203     if (lis->address.addr.sa_family == AF_INET6)
204     {
205 #  ifdef IPV6_V6ONLY
206      /*
207       * Accept only IPv6 connections on this socket, to avoid
208       * potential security issues and to make all platforms behave
209       * the same.
210       */
211
212       val = 1;
213 #    ifdef __sun
214       setsockopt(lis->fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&val, sizeof(val));
215 #    else
216       setsockopt(lis->fd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof(val));
217 #    endif /* __sun */
218 #  endif /* IPV6_V6ONLY */
219
220       status = bind(lis->fd, (struct sockaddr *)&(lis->address),
221                     httpAddrLength(&(lis->address)));
222     }
223     else
224 #endif /* AF_INET6 */
225 #ifdef AF_LOCAL
226     if (lis->address.addr.sa_family == AF_LOCAL)
227     {
228       mode_t    mask;                   /* Umask setting */
229
230
231      /*
232       * Remove any existing domain socket file...
233       */
234
235       unlink(lis->address.un.sun_path);
236
237      /*
238       * Save the curent umask and set it to 0...
239       */
240
241       mask = umask(0);
242
243      /*
244       * Bind the domain socket...
245       */
246
247       status = bind(lis->fd, (struct sockaddr *)&(lis->address),
248                     httpAddrLength(&(lis->address)));
249
250      /*
251       * Restore the umask...
252       */
253
254       umask(mask);
255     }
256     else
257 #endif /* AF_LOCAL */
258     status = bind(lis->fd, (struct sockaddr *)&(lis->address),
259                   sizeof(lis->address.ipv4));
260
261     if (status < 0)
262     {
263       cupsdLogMessage(CUPSD_LOG_ERROR,
264                       "cupsdStartListening: Unable to bind socket for address %s:%d - %s.",
265                       s, p, strerror(errno));
266       close(lis->fd);
267       lis->fd = -1;
268       continue;
269     }
270
271    /*
272     * Listen for new clients.
273     */
274
275     if (listen(lis->fd, ListenBackLog) < 0)
276     {
277       cupsdLogMessage(CUPSD_LOG_ERROR,
278                       "cupsdStartListening: Unable to listen for clients on address %s:%d - %s.",
279                       s, p, strerror(errno));
280       exit(errno);
281     }
282
283     if (p)
284       cupsdLogMessage(CUPSD_LOG_INFO,
285                       "cupsdStartListening: Listening to %s:%d on fd %d...",
286                       s, p, lis->fd);
287     else
288       cupsdLogMessage(CUPSD_LOG_INFO,
289                       "cupsdStartListening: Listening to %s on fd %d...",
290                       s, lis->fd);
291
292    /*
293     * Save the first port that is bound to the local loopback or
294     * "any" address...
295     */
296
297     if (!LocalPort && p > 0 &&
298         (httpAddrLocalhost(&(lis->address)) ||
299          httpAddrAny(&(lis->address))))
300     {
301       LocalPort       = p;
302       LocalEncryption = lis->encryption;
303     }
304
305 #ifdef AF_LOCAL
306     if (lis->address.addr.sa_family == AF_LOCAL && !have_domain)
307       have_domain = lis->address.un.sun_path;
308 #endif /* AF_LOCAL */
309   }
310
311  /*
312   * Make sure that we are listening on localhost!
313   */
314
315   if (!LocalPort && !have_domain)
316   {
317     cupsdLogMessage(CUPSD_LOG_EMERG,
318                     "No Listen or Port lines were found to allow access via localhost!");
319
320    /*
321     * Commit suicide...
322     */
323
324     cupsdEndProcess(getpid(), 0);
325   }
326
327  /*
328   * Set the CUPS_SERVER, IPP_PORT, and CUPS_ENCRYPTION variables based on
329   * the listeners...
330   */
331
332   if (have_domain)
333   {
334    /*
335     * Use domain sockets for the local connection...
336     */
337
338     cupsdSetEnv("CUPS_SERVER", have_domain);
339   }
340   else
341   {
342    /*
343     * Use the default local loopback address for the server...
344     */
345
346     cupsdSetEnv("CUPS_SERVER", "localhost");
347   }
348
349   cupsdSetEnv("CUPS_ENCRYPTION", encryptions[LocalEncryption]);
350   cupsdSetEnvf("IPP_PORT", "%d", LocalPort);
351
352  /*
353   * Resume listening for connections...
354   */
355
356   cupsdResumeListening();
357 }
358
359
360 /*
361  * 'cupsdStopListening()' - Close all listening sockets...
362  */
363
364 void
365 cupsdStopListening(void)
366 {
367   int           i;              /* Looping var */
368   cupsd_listener_t      *lis;           /* Current listening socket */
369
370
371   cupsdLogMessage(CUPSD_LOG_DEBUG,
372                   "cupsdStopListening: closing all listen sockets.");
373
374   cupsdPauseListening();
375
376   for (i = NumListeners, lis = Listeners; i > 0; i --, lis ++)
377   {
378 #ifdef WIN32
379     closesocket(lis->fd);
380 #else
381     close(lis->fd);
382 #endif /* WIN32 */
383
384 #ifdef AF_LOCAL
385    /*
386     * Remove domain sockets...
387     */
388
389     if (lis->address.addr.sa_family == AF_LOCAL)
390       unlink(lis->address.un.sun_path);
391 #endif /* AF_LOCAL */
392   }
393 }
394
395
396 /*
397  * End of "$Id: listen.c 4780 2005-10-13 00:38:28Z mike $".
398  */