@@ -158,19 +158,149 @@ dhcpsd_mkdbdir(void)
158158 return 0 ;
159159}
160160
161+ static void
162+ dhcpsd_fork_cb (void * arg , unsigned short e )
163+ {
164+ struct ctx * ctx = arg ;
165+ int exit_code ;
166+ ssize_t nread ;
167+
168+ if (e & ELE_HANGUP ) {
169+ hangup :
170+ close (ctx -> ctx_fork_fd );
171+ ctx -> ctx_fork_fd = -1 ;
172+ eloop_exit (ctx -> ctx_eloop , EXIT_FAILURE );
173+ return ;
174+ }
175+ nread = recv (ctx -> ctx_fork_fd , & exit_code , sizeof (exit_code ), 0 );
176+ if (nread == 0 )
177+ goto hangup ;
178+ if (nread == -1 ) {
179+ logerr ("%s: recv %d" , __func__ , ctx -> ctx_fork_fd );
180+ goto hangup ;
181+ }
182+ if ((size_t )nread != sizeof (exit_code )) {
183+ logerrx ("%s: truncated %zd" , __func__ , nread );
184+ goto hangup ;
185+ }
186+ eloop_exit (ctx -> ctx_eloop , exit_code );
187+ }
188+
189+ /* Complicated, but it ensures we don't get a controlling terminal
190+ * and the ppid of any child processed match the main process. */
191+ static int
192+ dhcpsd_fork (struct ctx * ctx )
193+ {
194+ int fork_fd [2 ];
195+ pid_t pid ;
196+ #ifdef HAVE_CASPER
197+ cap_rights_t rights ;
198+ #endif
199+
200+ if (socketpair (AF_UNIX , SOCK_SEQPACKET | SOCK_CLOEXEC | SOCK_NONBLOCK ,
201+ 0 , fork_fd ) == -1 ) {
202+ logerr ("socketpair" );
203+ return -1 ;
204+ }
205+
206+ switch (pid = fork ()) {
207+ case -1 :
208+ logerr ("%s: fork" , __func__ );
209+ return -1 ;
210+ ;
211+ case 0 :
212+ ctx -> ctx_fork_fd = fork_fd [1 ];
213+ close (fork_fd [0 ]);
214+ #ifdef HAVE_CASPER
215+ cap_rights_init (& rights , CAP_WRITE );
216+ if (caph_rights_limit (ctx -> ctx_fork_fd , & rights ) == -1 ) {
217+ logerr ("%s: caph_rights_limit" , __func__ );
218+ return -1 ;
219+ }
220+ #endif
221+ if (setsid () == -1 ) {
222+ logerr ("%s: setsid" , __func__ );
223+ return -1 ;
224+ }
225+ /* Ensure we can never get a controlling terminal */
226+ switch (pid = fork ()) {
227+ case -1 :
228+ logerr ("%s: fork" , __func__ );
229+ return -1 ;
230+ case 0 :
231+ /* setsid again to ensure our child processes have the
232+ * correct ppid */
233+ if (setsid () == -1 ) {
234+ logerr ("%s: setsid" , __func__ );
235+ return -1 ;
236+ }
237+ if (eloop_forked (ctx -> ctx_eloop , ELF_KEEP_ALL ) == -1 ) {
238+ logerr ("%s: eloop_forked" , __func__ );
239+ return -1 ;
240+ }
241+ if (eloop_event_add (ctx -> ctx_eloop , ctx -> ctx_fork_fd ,
242+ ELE_READ , dhcpsd_fork_cb , ctx ) == -1 ) {
243+ logerr ("%s: eloop_event_add" , __func__ );
244+ return -1 ;
245+ }
246+ break ;
247+ default :
248+ ctx -> ctx_options &= ~DHCPSD_MAIN ;
249+ break ;
250+ }
251+ break ;
252+ default :
253+ #ifdef BSD
254+ setproctitle ("[launcher]" );
255+ #endif
256+ ctx -> ctx_options &= ~DHCPSD_MAIN ;
257+ ctx -> ctx_options |= DHCPSD_LAUNCHER ;
258+ ctx -> ctx_fork_fd = fork_fd [0 ];
259+ close (fork_fd [1 ]);
260+
261+ #ifdef HAVE_CASPER
262+ cap_rights_init (& rights , CAP_READ );
263+ if (caph_rights_limit (ctx -> ctx_fork_fd , & rights ) == -1 ) {
264+ logerr ("%s: caph_rights_limit" , __func__ );
265+ return -1 ;
266+ }
267+ #endif
268+ if (eloop_event_add (ctx -> ctx_eloop , ctx -> ctx_fork_fd , ELE_READ ,
269+ dhcpsd_fork_cb , ctx ) == -1 ) {
270+ logerr ("%s: eloop_event_add" , __func__ );
271+ return -1 ;
272+ }
273+ break ;
274+ }
275+ return 0 ;
276+ }
277+
278+ static void
279+ dhcpsd_send_launcher (struct ctx * ctx , int exit_code )
280+ {
281+ if (ctx -> ctx_fork_fd == -1 )
282+ return ;
283+ if (send (ctx -> ctx_fork_fd , & exit_code , sizeof (exit_code ), MSG_EOR ) ==
284+ -1 )
285+ logerr ("%s: sendmsg" , __func__ );
286+ close (ctx -> ctx_fork_fd );
287+ ctx -> ctx_fork_fd = -1 ;
288+ }
289+
161290int
162291main (int argc , char * * argv )
163292{
164293 struct if_head ifaces ;
165294 struct ctx ctx = {
166295 .ctx_options = DHCPSD_MAIN ,
167296 .ctx_ifaces = & ifaces ,
297+ .ctx_fork_fd = -1 ,
168298 .ctx_pf_inet_fd = -1 ,
169299#ifdef IFLR_ACTIVE
170300 .ctx_pf_link_fd = -1 ,
171301#endif
172302 };
173- int ch , exit_code = EXIT_FAILURE ;
303+ int ch , exit_code = EXIT_FAILURE , f_flag = 0 ;
174304 size_t npools ;
175305 struct interface * ifp ;
176306 unsigned int logopts = LOGERR_LOG ;
@@ -184,13 +314,15 @@ main(int argc, char **argv)
184314 TAILQ_INIT (ctx .ctx_ifaces );
185315 closefrom (STDERR_FILENO + 1 );
186316
317+ logopts |= LOGERR_ERR ; // log to stderr
187318#define OPTS "dfp:"
188319 while ((ch = getopt (argc , argv , OPTS )) != -1 ) {
189320 switch (ch ) {
190321 case 'd' :
191322 logopts |= LOGERR_DEBUG ;
192323 break ;
193324 case 'f' :
325+ f_flag = 1 ;
194326 logopts |= LOGERR_ERR ; // log to stderr
195327 logopts &= ~LOGERR_LOG ; // don't syslog
196328 break ;
@@ -200,17 +332,28 @@ main(int argc, char **argv)
200332 logsetopts (logopts );
201333 logopen (NULL );
202334
203- loginfox (PACKAGE "-" VERSION " starting" );
204- if (dhcpsd_mkdbdir () == -1 )
205- goto exit ;
206-
207335 ctx .ctx_eloop = eloop_new ();
208336 if (ctx .ctx_eloop == NULL ) {
209337 logerr ("%s: eloop_new" , __func__ );
210338 goto exit ;
211339 }
212- eloop_signal_set_cb (ctx .ctx_eloop , signals , signals_len ,
213- dhcpsd_signal_cb , & ctx );
340+ if (eloop_signal_set_cb (ctx .ctx_eloop , signals , signals_len ,
341+ dhcpsd_signal_cb , & ctx ) == -1 ) {
342+ logerr ("%s: eloop_signal_set_cb" , __func__ );
343+ goto exit ;
344+ }
345+
346+ if (!(f_flag ) & & dhcpsd_fork (& ctx ) == -1 )
347+ goto exit ;
348+ if (ctx .ctx_options & DHCPSD_LAUNCHER )
349+ goto run ;
350+ if (!(ctx .ctx_options & DHCPSD_MAIN ))
351+ goto exit ;
352+
353+ loginfox (PACKAGE "-" VERSION " starting" );
354+ if (dhcpsd_mkdbdir () == -1 )
355+ goto exit ;
356+
214357 if (eloop_signal_mask (ctx .ctx_eloop ) == -1 ) {
215358 logerr ("%s: eloop_signal_mask" , __func__ );
216359 goto exit ;
@@ -346,17 +489,6 @@ main(int argc, char **argv)
346489 if (dhcpsd_dropperms (1 ) == -1 )
347490 goto exit ;
348491
349- /* If we separate -f from no syslog we need a new variable */
350- if (logopts & LOGERR_LOG ) {
351- if (daemon (0 , 0 ) == -1 ) {
352- logerr ("%s: daemon" , __func__ );
353- goto exit ;
354- }
355- if (eloop_forked (ctx .ctx_eloop , ELF_KEEP_ALL ) == -1 ) {
356- logerr ("%s: eloop_forked" , __func__ );
357- goto exit ;
358- }
359- }
360492#ifdef BSD
361493 setproctitle ("DHCP Server Daemon" );
362494#endif
@@ -446,15 +578,19 @@ main(int argc, char **argv)
446578#endif
447579
448580 loginfox (PACKAGE " is now running" );
581+ if (ctx .ctx_fork_fd != -1 ) {
582+ dhcpsd_send_launcher (& ctx , EXIT_SUCCESS );
583+ logopts &= ~LOGERR_ERR ; // stop logging to stderr
584+ logsetopts (logopts );
585+ }
449586 dhcp_expire_leases (ctx .ctx_dhcp );
450587
451588run :
452589 exit_code = eloop_start (ctx .ctx_eloop );
453590 if (exit_code < 0 ) {
454591 logerr ("%s: eloop_start" , __func__ );
455592 exit_code = EXIT_FAILURE ;
456- } else
457- exit_code = EXIT_SUCCESS ;
593+ }
458594
459595 if (dhcpsd_store_leases (& ctx ) == -1 )
460596 exit_code = EXIT_FAILURE ;
@@ -469,12 +605,16 @@ main(int argc, char **argv)
469605 }
470606 dhcp_free (ctx .ctx_dhcp );
471607 eloop_free (ctx .ctx_eloop );
608+ ctx .ctx_eloop = NULL ;
472609 svc_free (ctx .ctx_unpriv );
473610#ifdef HAVE_CASPER
474611 if (ctx .ctx_capnet )
475612 cap_close (ctx .ctx_capnet );
476613#endif
477- if (ctx .ctx_options & DHCPSD_MAIN )
614+ if (ctx .ctx_options & DHCPSD_MAIN ) {
478615 logdebugx ("dhcpsd exited" );
616+ dhcpsd_send_launcher (& ctx , exit_code );
617+ }
618+
479619 return exit_code ;
480620}
0 commit comments