fixes init script for non ipv6 enabled systems #472755
[packages/xinetd.git] / xinetd / int.c
1 /*
2  * (c) Copyright 1992 by Panagiotis Tsirigotis
3  * (c) Sections Copyright 1998-2001 by Rob Braun
4  * All rights reserved.  The file named COPYRIGHT specifies the terms 
5  * and conditions for redistribution.
6  */
7
8
9 #include "config.h"
10 #include <sys/types.h>
11 #include <sys/socket.h>
12 #include <netinet/in.h>
13 #include <arpa/inet.h>
14 #include <syslog.h>
15 #include <errno.h>
16 #include <signal.h>
17 #include <stdlib.h>
18 #include <unistd.h>
19
20 #include "int.h"
21 #include "msg.h"
22 #include "log.h"
23 #include "tcpint.h"
24 #include "udpint.h"
25 #include "sconf.h"
26 #include "intcommon.h"
27 #include "child.h"
28 #include "state.h"
29 #include "main.h"
30 #include "signals.h"
31 #include "xconfig.h"
32 #include <netdb.h>
33
34 static void start_server( struct intercept_s *ip );
35 static void terminate_server( struct intercept_s *ip );
36
37 typedef struct intercept_s *(*initfunc)() ;
38
39 struct lookup_table
40 {
41    initfunc   initializer ;
42    int        socket_type ;
43 } ;
44
45
46 static struct lookup_table intercept_lookup_table[] =
47    {
48       { di_init,         SOCK_DGRAM  },
49       { si_init,         SOCK_STREAM },
50       { NULL,            0           }
51    } ;
52
53          
54 /*
55  * This variable has file scope for the benefit of the signal handler
56  */
57 static struct intercept_s *intp = NULL;
58
59
60
61 static initfunc find_initializer( int type )
62 {
63    struct lookup_table *ltp ;
64
65    for ( ltp = intercept_lookup_table ; ltp->initializer ; ltp++ )
66       if ( ltp->socket_type == type )
67          return( ltp->initializer ) ;
68    msg( LOG_ERR, "find_initializer", "No initializer for type %d", type ) ;
69    _exit( 0 ) ;
70    /* NOTREACHED */
71    return (initfunc)0;
72 }
73
74
75 /*
76  * This function is the interface of the intercept code with the rest of 
77  * the program. 
78  */
79 void intercept( struct server *serp )
80 {
81    struct service *sp = SERVER_SERVICE( serp ) ;
82    initfunc initializer ;
83
84 #ifdef DEBUG_INTERCEPTOR
85    if ( debug.on )
86    {
87       msg( LOG_DEBUG, "intercept", "%d is sleeping", getpid() ) ;
88       sleep( 10 ) ;
89    }
90 #endif
91
92    initializer = find_initializer( SVC_SOCKET_TYPE( sp ) ) ;
93    intp = (*initializer)( serp ) ;
94    start_server( intp ) ;
95    (*intp->int_ops->mux)() ;
96    terminate_server( intp ) ;
97    /*
98     * the terminate_server function should not return but even if it
99     * does, child_process will do the _exit.
100     */ 
101 }
102
103
104 /*
105  * Create a socket and bind it to (INADDR_LOOPBACK,0)
106  */
107 static int get_server_socket( struct intercept_s *ip )
108 {
109    struct service *sp = SERVER_SERVICE( INT_SERVER( ip ) ) ;
110    union xsockaddr *sinp = INT_LOCALADDR( ip ) ;
111    int sd ;
112    socklen_t size ;
113    const char *func = "get_server_socket" ;
114
115    if( SC_IPV6(SVC_CONF(sp)) ) {
116       struct addrinfo hint, *res = NULL;
117       memset(&hint, 0, sizeof(struct addrinfo));
118       hint.ai_family = AF_INET6;
119       hint.ai_flags = AI_NUMERICHOST;
120       sinp->sa_in6.sin6_family = AF_INET6;
121       sinp->sa_in6.sin6_port = 0;
122       if( getaddrinfo("::1", NULL, &hint, &res) != 0  )
123          int_fail( ip, "can't find ::1" );
124       if( res == NULL )
125          int_fail( ip, "no results for ::1" );
126       if( res->ai_family != AF_INET6 )
127          int_fail( ip, "non IPv6 result for ::1" );
128       memcpy(sinp, res->ai_addr, sizeof( struct sockaddr_in6 ));
129       freeaddrinfo(res);
130       size = sizeof(struct sockaddr_in6);
131    } else if( SC_IPV4(SVC_CONF(sp)) ) {
132       sinp->sa_in.sin_family = AF_INET;
133       sinp->sa_in.sin_port = 0;
134       sinp->sa_in.sin_addr.s_addr = inet_addr( "127.0.0.1" );
135       size = sizeof(struct sockaddr_in);
136    } else
137       int_fail( ip, "unknown socket family" );
138
139    if ( ( sd = socket( sinp->sa.sa_family, SVC_SOCKET_TYPE( sp ), SC_PROTOVAL(SVC_CONF(sp)) ) ) == -1 )
140       int_fail( ip, "socket creation" ) ;
141
142    if ( bind( sd, SA( sinp ), size ) == -1 )
143       int_fail( ip, "bind" ) ;
144    
145    size = sizeof( *sinp ) ;
146    if ( getsockname( sd, SA( sinp ), &size ) == -1 )
147       int_fail( ip, "getsockname" ) ;
148    
149    if ( debug.on )
150       msg( LOG_DEBUG, func, "address = %s, port = %d",
151          xaddrname( sinp ), ntohs( xaddrport( sinp ) ) ) ;
152       
153    if ( ip->int_socket_type == SOCK_STREAM )
154       (void) listen( sd, LISTEN_BACKLOG ) ;
155    
156    return( sd ) ;
157 }
158
159
160 static void start_server( struct intercept_s *ip )
161 {
162    struct server      *serp = INT_SERVER( ip ) ;
163    struct service     *sp = SERVER_SERVICE( serp ) ;
164    int                server_socket ;
165    pid_t              pid ;
166
167    server_socket = get_server_socket( ip ) ;
168    
169    pid = fork() ;
170
171    switch ( pid )
172    {
173       case -1:
174          int_fail( ip, "fork" ) ;
175          /* NOTREACHED */
176       
177       case 0:
178          CONN_SET_DESCRIPTOR( SERVER_CONNECTION( serp ), server_socket ) ;
179          SVC_MAKE_EXTERNAL( sp ) ;            /* avoid looping */
180          child_process( serp ) ;
181          /* NOTREACHED */
182       
183       default:
184          SERVER_SET_PID( serp, pid ) ;
185          (void) Sclose( server_socket ) ;
186    }
187 }
188
189
190
191 /*
192  * Return value:
193  *         OK          if the server died
194  *         FAILED       otherwise
195  */
196 static status_e wait_child( struct intercept_s *ip )
197 {
198    const char *func = "wait_child" ;
199    int status ;
200    status_e ret = FAILED;
201    pid_t pid ;
202
203    while( (pid = waitpid( -1, &status, WNOHANG )) != 0 )
204    {
205
206       if ( pid == -1 )
207       {
208          if ( errno != EINTR )
209          {
210             msg( LOG_ERR, func, "wait: %m" ) ;
211             return( ret ) ;
212          }
213       }
214       else if ( pid == SERVER_PID( INT_SERVER( ip ) ) )
215       {
216          if ( PROC_STOPPED( status ) )
217             ret = FAILED;
218          SERVER_SET_EXIT_STATUS( INT_SERVER( ip ), status ) ;
219          ret = OK;
220       }
221       else
222       {
223          unsigned u;
224
225          /* Ideally, this will never be executed */
226          msg( LOG_ERR, func,
227             "wait returned pid of unknown process: %d", pid ) ;
228
229          /* Since we don't have the intercept pointer to this service,
230           * do our best to shut it down safely...
231           */
232          for( u = 0; u < pset_count( SERVERS(ps) ); u++ ) {
233             struct server *p = SERP( pset_pointer( SERVERS(ps), u) );
234
235             if( (p != NULL) && (SERVER_PID(p) == pid) ) {
236                struct service *sp = SERVER_SERVICE(p);
237                struct service_config *scp = SVC_CONF(sp);
238
239                if( SC_PROTOVAL(scp) == IPPROTO_TCP ) {
240                   SERVER_SET_EXIT_STATUS( p, status );
241                   si_exit();
242                } else if( SC_PROTOVAL(scp) == IPPROTO_UDP ) {
243                   SERVER_SET_EXIT_STATUS( p, status );
244                   di_exit();
245                } else {
246                   msg( LOG_ERR, func, "Don't know how to exit %d", pid);
247                }
248                break;
249             }
250          }
251       }
252    }
253
254    return ret;
255 }
256
257
258 static void terminate_server( struct intercept_s *ip )
259 {
260    pid_t pid = SERVER_PID( INT_SERVER( intp ) ) ;
261
262    if ( pid > 0 )
263       (void) kill( pid, SIGKILL ) ;
264
265    /*
266     * Normally, wait_child should never return since a SIGCHLD will 
267     * invoke the signal handler which will then call the exit function.
268     */
269    if ( wait_child( ip ) == OK )
270       (*intp->int_ops->exit)() ;
271 }
272
273
274 void int_sighandler( int sig )
275 {
276    const char *func = "int_sighandler" ;
277
278    if ( debug.on )
279       msg( LOG_DEBUG, func, "Received signal %s", sig_name( sig ) ) ;
280
281    if ( sig == SERVER_EXIT_SIG )
282    {
283       if ( wait_child( intp ) == OK )
284          (*intp->int_ops->exit)() ;
285    }
286    else if ( sig == INTERCEPT_SIG )
287       INTERCEPT( intp ) = FALSE ;
288    else if ( sig == SIGTERM )
289       terminate_server( intp ) ;
290 }