Mike Frysinger wrote:
> On Monday 06 April 2009 06:12:06 Mike Frysinger wrote:
> > On Saturday 28 February 2009 14:16:30 Jamie Lokier wrote:
> > > Mike Frysinger wrote:
> > > > (like daemonizing code)
> > >
> > > It is possible to daemonize on uClinux without exec'ing a new process,
> > > using clone() instead of vfork().  I have a version of the daemon()
> > > function which does that, which I posted to the Busybox bug tracker
> > > years ago; I should really submit it to uClibc.
> >
> > so we're working on converting the old mantis db to bugzilla, but it's not
> > complete (attachments are missing).  i tried searching for this but wasnt
> > able to find it.  could you give it a spin ?
> > http://bugstest.busybox.net/
> >
> > otherwise i dont suppose you could dig up your old patch ?  having daemon()
> > support in uClibc would be pretty nice to have under no-mmu ... i read
> > through the clone() man page for a while, but i couldnt seem to trick my
> > head into figuring out how to emulate proper behavior where the parent and
> > grand parent die but the child keeps running, and the child is able to
> > return to the calling function with the stack intact.
> 
> nm, what i was missing was that the function argument is evaluated
> by userspace, not the kernel.  so it's pretty easy to use clone to
> (1) have the parent immediately call _exit and (2) have the child
> return untouched, and (3) do it in parallel.  i'll add this to
> uClibc once i test with a real app other than my simple code that
> just calls daemon() after checking pgrp/sid.

It's easy but you need an asm wrapper for each architecture, as the
parent must not touch the stack before calling _exit.

You also have to block signals in the parent otherwise the parent may
clobber the stack handling a signal before _exit.

My patch is attached below.

-- Jamie

Many uClinux systems don't have an MMU.  Due to lack of MMU, they
don't have fork().  Traditionally the daemon() function depends on
fork(), however it's possible to implement it using clone() instead.

This patch adds a daemon() function for uClinux systems.  It works
properly for ARM and i386, and has a half-hearted implementation using
vfork() for others.  Proper support for other architectures can be
added by writing a little assembly code.

As a bonus, this should run a little faster with MMU, too.

I think this should be added to uClibc as it's a useful standard function.

The fork_and_exit() function is useful by itself, for programs which
need to daemonize but daemon() doesn't have quite the right behaviour.
So perhaps fork_and_exit() should be made available too.

The x86 and ARM versions are tested; ARM thumb is not.

Signed-Off-By: Jamie Lokier <ja...@shareable.org>

diff -urN busybox-1.00.orig/libbb/daemon.c busybox-1.00/libbb/daemon.c
--- busybox-1.00.orig/libbb/daemon.c    1970-01-01 01:00:00.000000000 +0100
+++ busybox-1.00/libbb/daemon.c 2006-03-22 20:05:55.000000000 +0000
@@ -0,0 +1,161 @@
+/*
+ * daemon() function for uClinux systems without MMU and fork().
+ *
+ * Copyright (C) 2005-2006 Jamie Lokier <ja...@shareable.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "busybox.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <signal.h>
+#include <errno.h>
+
+#define _PATH_DEVNULL "/dev/null"
+
+static inline int fork_and_exit( void )
+{
+#if defined(__linux__) && (defined(__i386__) || defined(__arm__))
+
+       /* Equivalent to fork-then-exit which allows the parent process
+          to exit properly even when fork() isn't available.  The trick
+          is to use clone(), and make the parent exit without
+          clobbering any memory.  Signals must be blocked to prevent
+          that clobbering.  Unfortunately each implementation of this
+          is architecture-specific. */
+
+# ifndef CLONE_VM
+#  define CLONE_VM     0x00000100
+#  define CLONE_FS     0x00000200
+#  define CLONE_FILES  0x00000400
+#  define CLONE_SIGHAND        0x00000800
+# endif
+
+       int result;
+       sigset_t new_set, old_set;
+       sigfillset(&new_set);
+       sigprocmask(SIG_BLOCK, &new_set, &old_set);
+
+       {
+               int flags = (CLONE_VM | CLONE_FS | CLONE_FILES
+                            | CLONE_SIGHAND | SIGCHLD);
+
+# ifdef __i386__
+               __asm__ __volatile__ (
+#  ifdef __PIC__
+                       "pushl  %%ebx\n\t"
+                       "movl   %1,%%ebx\n\t"
+#  endif /* __PIC__ */
+                       "movl   $120,%0\n\t"
+                       "int    $0x80\n\t"
+                       "testl  %0,%0\n\t"
+                       "jle    0f\n\t"
+                       "movl   $1,%0\n\t"
+                       "xorl   %%ebx,%%ebx\n\t"
+                       "int    $0x80\n"
+                       "0:"
+#  ifdef __PIC__
+                       "\n\tpopl       %%ebx"
+                       : "=a" (result) : "ir" (flags), "c" ((int) 0)
+#  else /* not __PIC__ */
+                       : "=a" (result) : "b" (flags), "c" ((int) 0)
+#  endif /* not __PIC__ */
+                       );
+# endif /* __i386__ */
+# ifdef __arm__
+               register int r0 __asm__("r0"), stack __asm__("r1") = 0;
+               __asm__ __volatile__ (
+#  ifdef __thumb__
+                       /* This Thumb version hasn't been tested.
+                          If you do test it, please delete this comment. */
+                       "push   {r7}\n\t"
+                       "mov    r7, #120\t@ clone\n\t"
+                       "swi    0\n\t"
+                       "cmp    r0, #0\n\t"
+                       "ble    0f\n\t"
+                       "mov    r0, #0\n\t"
+                       "mov    r7, #1\t\t@ exit\n\t"
+                       "swi    0\n"
+                       "0:\t"
+                       "pop    {r7}"
+#  else  /* Standard ARM, not Thumb. */
+                       "swi    0x900000+120\t@ clone\n\t"
+                       "cmp    r0, #0\n\t"
+                       "ble    0f\n\t"
+                       "mov    r0, #0\n\t"
+                       "swi    0x900000+1\t@ exit\n"
+                       "0:"
+#  endif /* Standard ARM, not Thumb. */
+                       : "=r" (r0)
+                       : "0" (flags), "r" (stack)
+                       : "cc", "lr");
+               result = r0;
+# endif /* __arm__ */
+
+       }
+       sigprocmask(SIG_SETMASK, &old_set, (sigset_t *)0);
+       if (result == 0)
+               return(0);
+       errno = -result;
+       return(-1);
+
+#else /* Can't do the special thing. */
+
+       /* Half-hearted version with vfork().  The parent process won't
+          be able to exit until the child does, so this keeps extra
+          processes around, and requires the program to be started in
+          the background. */
+       switch (vfork()) {
+               case -1:
+                       return(-1);
+               case 0:
+                       return(0);
+               default:
+                       _exit(0);
+       }
+
+#endif /* Can't do the special thing. */
+}
+
+int daemon( int nochdir, int noclose )
+{
+       int fd;
+
+       if (fork_and_exit() == -1)
+               return(-1);
+
+       if (setsid() == -1)
+               return(-1);
+
+       /* Make certain we are not a session leader, or else we
+        * might reacquire a controlling terminal */
+       if (fork_and_exit() == -1)
+               return(-1);
+
+       if (!nochdir)
+               chdir("/");
+
+       if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+               dup2(fd, STDIN_FILENO);
+               dup2(fd, STDOUT_FILENO);
+               dup2(fd, STDERR_FILENO);
+               if (fd > 2)
+                       close(fd);
+       }
+       return(0);
+}
_______________________________________________
uClinux-dev mailing list
uClinux-dev@uclinux.org
http://mailman.uclinux.org/mailman/listinfo/uclinux-dev
This message was resent by uclinux-dev@uclinux.org
To unsubscribe see:
http://mailman.uclinux.org/mailman/options/uclinux-dev

Reply via email to