fixes init script for non ipv6 enabled systems #472755
[packages/xinetd.git] / xinetd / init.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 #include "config.h"
9 #include <sys/types.h>
10 #include <sys/time.h>
11 #include <sys/stat.h>
12 #ifdef HAVE_SYS_RESOURCE_H
13 #include <sys/resource.h>
14 #endif
15 #include <syslog.h>
16 #include <fcntl.h>
17 #include <unistd.h>
18 #include <stdio.h>
19 #include <errno.h>
20
21 #include "sio.h"
22 #include "init.h"
23 #include "defs.h"
24 #include "msg.h"
25 #include "signals.h"
26 #include "env.h"
27 #include "confparse.h"
28 #include "options.h"
29 #include "main.h"
30 #include "xconfig.h"
31 #include "special.h"
32 #include "retry.h"
33 #include "internals.h"
34 #include "libportable.h"
35
36 struct module
37 {
38    const char *name ;
39    status_e (*initializer)() ;
40 } ;
41
42
43
44 static const struct module program_modules[] = 
45    {
46       { "signal",                       signal_init       },
47       { "environment",                  initenv           },
48       { CHAR_NULL,                      NULL              }
49    } ;
50
51
52 static bool_int have_stderr ;
53
54 #define STDERR_FD                  2
55
56 static void set_fd_limit(void);
57
58 /*
59  * This function is invoked when a system call fails during initialization.
60  * A message is printed to stderr, and the program is terminated
61  */
62 #ifdef __GNUC__
63 __attribute__ ((noreturn))
64 #endif
65 static void syscall_failed( const char *call )
66 {
67    char *err ;
68
69    if ( have_stderr )
70    {
71       err = strerror(errno);
72       Sprint( STDERR_FD, "%s: %s failed: %s\n", program_name, call, err ) ;
73    }
74    exit( 1 ) ;
75 }
76
77
78
79 /*
80  * Close all descriptors except STDERR_FD. We need this to report
81  * errors and the process pid of the daemon.
82  * Open all descriptors in the range 0..MAX_PASS_FD (except STDERR_FD)
83  * to /dev/null.
84  * STDERR_FD should not be 0.
85  *
86  * msg() cannot be used from this function, as it has not been initialized yet.
87  */
88 static void setup_file_descriptors(void)
89 {
90    int   fd ;
91    int   new_fd ;
92    int   null_fd ;
93
94    if ( Smorefds(3) == SIO_ERR )
95    {
96       syscall_failed("Smorefds");
97       exit( 1 ) ;
98    }
99
100    set_fd_limit() ;
101
102    /*
103     * Close all unneeded descriptors
104     */
105    for ( fd = STDERR_FD + 1 ; (unsigned)fd < ps.ros.max_descriptors ; fd++ )
106       if ( Sclose( fd ) && errno != EBADF )
107       {
108          syscall_failed("Sclose");
109          exit( 1 ) ;
110       }
111    
112    /*
113     * Check if the STDERR_FD descriptor is open.
114     */
115    new_fd = dup( STDERR_FD ) ;
116    if ( new_fd != -1 )
117    {
118       have_stderr = TRUE ;
119       (void) Sclose( new_fd ) ;
120    }
121
122    if ( ( null_fd = open( "/dev/null", O_RDONLY ) ) == -1 )
123       syscall_failed( "open of '/dev/null'" ) ;
124
125    for ( fd = 0 ; fd <= MAX_PASS_FD ; fd++ )
126    {
127       if ( have_stderr && fd == STDERR_FD )
128          continue ;
129       if ( fd != null_fd && dup2( null_fd, fd ) == -1 )
130          syscall_failed( "dup2" ) ;
131    }
132
133    if ( null_fd > MAX_PASS_FD )
134       (void) Sclose( null_fd ) ;
135 }
136
137
138 /* msg() cannot be used in this function, as it has not been initialized yet. */
139 static void set_fd_limit(void)
140 {
141 #ifdef RLIMIT_NOFILE
142    struct rlimit rl ;
143    rlim_t maxfd ;
144     
145    /*
146     * Set the soft file descriptor limit to the hard limit.
147     */
148    if ( getrlimit( RLIMIT_NOFILE, &rl ) == -1 )
149    {
150       syscall_failed("getrlimit(RLIMIT_NOFILE)");
151       exit( 1 ) ;
152    }
153
154    maxfd = rl.rlim_max;
155    if ( rl.rlim_max == RLIM_INFINITY ) 
156       rl.rlim_max = FD_SETSIZE;
157
158    /* XXX: a dumb way to prevent fd_set overflow possibilities; the rest
159     * of xinetd should be changed to use an OpenBSD inetd-like fd_grow(). */
160    if ( rl.rlim_max > FD_SETSIZE )
161       rl.rlim_max = FD_SETSIZE;
162      
163    rl.rlim_cur = rl.rlim_max ;
164    if ( setrlimit( RLIMIT_NOFILE, &rl ) == -1 )
165    {
166       syscall_failed("setrlimit(RLIMIT_NOFILE)");
167       ps.ros.max_descriptors = FD_SETSIZE;
168       ps.ros.orig_max_descriptors = FD_SETSIZE;
169       return ;
170    }
171
172    ps.ros.orig_max_descriptors = maxfd ;
173    ps.ros.max_descriptors = rl.rlim_max ;
174 #else      /* ! RLIMIT_NOFILE */
175    ps.ros.max_descriptors = getdtablesize() ;
176 #endif   /* RLIMIT_NOFILE */
177 }
178
179
180 static void init_common( int argc, char *argv[] )
181 {
182    const struct module *mp = NULL;
183    const char *func = "init_common" ;
184
185    /*
186     * Initialize the program state
187     */
188
189    ps.ros.Argv = argv ;
190    ps.ros.Argc = argc ;
191    ps.ros.is_superuser = ( geteuid() == 0 ) ;
192
193    /*
194     * Initialize the program modules
195     */
196    for ( mp = program_modules ; mp->name ; mp++ )
197       if ( (*mp->initializer)() == FAILED )
198       {
199          msg( LOG_CRIT, func,
200             "Initialization of %s facility failed. Exiting...", mp->name ) ;
201          exit( 1 ) ;
202       }
203    (void) umask( umask( 077 ) | 022 ) ;
204 }
205
206 /* Create the pidfile. 
207  * This is called after msg_init(), and potentially after 
208  * we've become_daemon() (depending on if we're in debug or not-forking)
209  */
210 static void create_pidfile(void)
211 {
212    int    pidfd;
213    FILE  *pidfile;
214
215    if ( ps.ros.pid_file ) {
216       unlink(ps.ros.pid_file);
217       pidfd = open(ps.ros.pid_file, O_EXCL|O_CREAT|O_WRONLY,
218          S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
219       if (pidfd >= 0) { /* successfully created file */
220          pidfile = fdopen(pidfd, "w");
221          if (pidfile) {
222             fchmod(pidfd, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
223             fprintf(pidfile, "%d\n", getpid());
224             fclose(pidfile);
225          } else {
226             msg(LOG_DEBUG, "create_pidfile", "fdopen failed: %m");
227             Sclose(pidfd);
228          }
229       } else
230          msg(LOG_DEBUG, "create_pidfile", "open failed: %m");
231    }
232 }
233
234 /*
235  * Become a daemon by forking a new process. The parent process exits.
236  */
237 static void become_daemon(void)
238 {
239    int   tries ;
240    int   pid ;
241    const char  *func = "become_daemon" ;
242
243    /*
244     * First fork so that the parent will think we have exited
245     */
246    for ( tries = 0 ;; tries++ )
247    {
248       if ( tries == 5 )
249       {
250          msg( LOG_CRIT, func, "fork: %m. Exiting..." ) ;
251          exit( 0 ) ;
252       }
253
254       pid = fork() ;
255
256       if ( pid == -1 )
257       {
258          sleep( 1 ) ;   /* wait for a second */
259          continue ;      /* and then retry      */
260       }
261       else if ( pid == 0 )
262          break ;
263       else
264          exit( 0 ) ;
265    }
266
267    (void) dup2( 0, STDERR_FD ) ;
268    no_control_tty() ;
269
270 #ifdef DEBUG_DAEMON
271    sleep( 20 ) ;       /* XXX: timers will probably not work after this */
272 #endif
273 }
274
275
276 static pset_h new_table( unsigned size )
277 {
278    const char *func = "new_table" ;
279    pset_h tab = pset_create( size, 0 ) ;
280
281    if ( tab == NULL )
282    {
283       msg( LOG_CRIT, func, "Failed to create table" ) ;
284       exit( 1 ) ;
285    }
286    return( tab ) ;
287 }
288
289
290 /*
291  * Create tables
292  */
293 static void init_rw_state( void )
294 {
295    SERVERS( ps ) = new_table( 0 ) ;
296    RETRIES( ps ) = new_table( 0 ) ;
297    SERVICES( ps ) = new_table( 0 ) ;
298
299    ps.rws.descriptors_free = ps.ros.max_descriptors - DESCRIPTORS_RESERVED ;
300
301    FD_ZERO( &ps.rws.socket_mask ) ;
302    ps.rws.mask_max = 0 ;
303
304 }
305
306
307 /*
308  * Perform all necessary initializations
309  */
310 void init_daemon( int argc, char *argv[] )
311 {
312    const char *fail = NULL;
313
314    debug.on = 0;
315    memset(&ps, 0, sizeof(ps));
316
317    setup_file_descriptors() ;
318    ps.ros.config_file = DEFAULT_CONFIG_FILE ;
319    (void) opt_recognize( argc, argv ) ;
320
321    /*
322     * XXX: we only use xlog_parms on XLOG_SYSLOG-type logs but in general
323     *        we should do it for all types of xlog's we may use. We can get
324     *        away with this now, because xlog_parms for XLOG_FILELOG is a noop.
325     */
326    (void) xlog_parms( XLOG_SYSLOG,
327                program_name, LOG_PID + LOG_NOWAIT, LOG_DAEMON ) ;
328
329    /*
330     * Initialize the message facility; after this everything can use the
331     * msg() interface
332     */
333    if ( (fail = msg_init()) )
334    {
335       if ( have_stderr )
336          Sprint( STDERR_FD, "%s: msg_init failed: %s\n", program_name, fail ) ;
337       exit( 1 ) ;
338    }
339
340    init_common( argc, argv ) ;
341
342    if ( ! debug.on && !dont_fork )
343       become_daemon() ;
344    create_pidfile();
345    
346    init_rw_state() ;
347 }
348
349
350 /*
351  * Initialize all services
352  *
353  * This function is either successful in starting some services 
354  * or it terminates the program.
355  */
356 void init_services( void )
357 {
358    struct configuration conf ;
359    const char *func = "init_services" ;
360
361    if ( cnf_get( &conf ) == FAILED )
362    {
363       msg( LOG_CRIT, func, "couldn't get configuration. Exiting..." ) ;
364       exit( 1 ) ;
365    }
366
367    DEFAULTS( ps ) = CNF_DEFAULTS( &conf ) ;
368    (void) cnf_start_services( &conf ) ;
369    CNF_DEFAULTS( &conf ) = NULL ;      /* to avoid the free by cnf_free */
370    cnf_free( &conf ) ;
371
372    /*
373     * The number of available/active services is kept by the service functions
374     */
375    if ( stayalive_option == 0 ) {
376       if ( ps.rws.available_services == 0 )
377       {
378          msg( LOG_CRIT, func, "no services. Exiting..." ) ;
379          if ( ps.ros.pid_file ) {
380             unlink(ps.ros.pid_file);
381          }
382          exit( 1 ) ;
383       }
384    }
385
386    spec_include() ;      /* include special services */
387 }
388