/*
From ds5000.irb.hr!tmpuhf.saar.de!wg.saar.de!wg.saar.de!bof Thu Oct 13 10:38:01 1994
Return-Path: <ds5000.irb.hr!tmpuhf.saar.de!wg.saar.de!wg.saar.de!bof>
Received: from ds5000.irb.hr by emard.uucp with uucp
	(Smail3.1.28.1 #5) id m0qvMbl-0002BDC; Thu, 13 Oct 94 10:37 MET
Received: from TMPuhf.Saar.DE (tmpuhf.saar.de [192.109.53.3]) by shug-internet.saar.de (8.6.8.1/8.5) with SMTP id DAA24646; Thu, 13 Oct 1994 03:14:55 +0100
Received: from wg.saar.de by TMPuhf.Saar.DE with uucp
	(Smail3.1.28.1 #1) id m0qvGYX-00021NC; Thu, 13 Oct 94 03:10 WET
Received: by bellona.wg.saar.de id m0qvF2S-0002enC; Thu, 13 Oct 94 01:33 
Received: by midget id m0qvF9I-0008yQC; Thu, 13 Oct 94 01:40 
Message-Id: <m0qvF9I-0008yQC@midget>
Date: Thu, 13 Oct 94 01:40 
From: ds5000.irb.hr!wg.saar.de!bof (Patrick Schaaf)
To: ds5000.irb.hr!davj
Subject: Re: growing stack for clone
Newsgroups: comp.os.linux.development
References: <37h27p$9pf@nippur.irb.hr>
Status: RO

In comp.os.linux.development you write:

>Some time is needed to adapt clone examples based on postings
>from Rob Janssen and Michael David McCartney. If there are
>newer versions, please lemme know.

Appended below; the change fixes some problems with GCC.

>Growing stack for clone syscall would be nice. I am 
>interested did someone consider how to make it? Or 
>already has some patches that provide it? I'd appreciate
>any info.

I see two alternatives, both ugly in my opinion:
- setup the clone's stack at another place in the same page directory.
  problem: where do you place the new stack? how big do you make it?
  what about a clone() clone()ing again? Some kind of coordination
  is needed there.
- fork the page directory, and overlay the stacks. This solves the
  placement problem.
  problem: can't pass addresses on the stack between clones.

I feel the best is to leave it to the application, and provide
a /dev/stack pseudo device for mmap()ing with the needed semantics.
I didn't think more about that, though...

bye
  Patrick
*/

#include <errno.h>
#include <stdio.h>
#include <signal.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/unistd.h>

/* NOTE: the clone() starts up on a new stack. Thus, we cannot return
 * from this function in the clone (we would have to copy stack frames
 * for main and the call). Instead, we call the given function, and
 * call _exit() with whatever it returns.
 */

#define STR(x) #x
#define DEREF_STR(x) STR(x)

int do_clone(unsigned long esp, unsigned long flags, int (*func)(void))
{ int ret;

  __asm__ (
	   /* register setting (see input/output constraints):
	    * %ebx is esp
	    * %ecx is flags
	    * %edx is func
	    */
	   "movl $" DEREF_STR(__NR_clone) ", %%eax\n\t"
	   "int $0x80\n\t"
	   /* error? */
	   "jnc 1f\n\t"
	   /* error. */
	   "movl %%eax, _errno\n\t"
	   "movl $-1, %0\n\t"
	   "jmp 3f\n\t"
	   "1:\n\t"
	   "testl %0, %0\n\t"
	   "jne 3f\n\t"
	   /* the clone */
	   "call *%3\n\t"
	   "pushl $0\n\t"
	   "call _exit\n\t"
	   /* not reached */
           "1:\n\t"
	   "jmp 1b\n\t"
	   /* the parent */
	   "3:\n\t"
	   : "=a" (ret)
	   : "b" (esp),
	     "c" (flags),
	     "d" (func)
  );
  return ret;
}

int clone_pid = -1;
int parent_pid = -1;
int do_terminate = 0;

/* we use sigusr1() for child termination signalling */
void sigusr1(int sig)
{ unsigned long status;
  int pid;

  printf("parent: got SIGUSR1, waiting for children... clone_pid=%d\n",
         clone_pid);
  pid = wait4(clone_pid, &status, __WCLONE, (struct rusage *)0);
  if (pid < 0) {
    perror("wait4");
    return;
  }
  printf("parent: wait4 returned %d\n", pid);
  if (clone_pid == pid)
    do_terminate = 1;
  signal(SIGUSR1, sigusr1);
  return;
}

char clone_stack[4*4096];

int clone_function(void)
{
  clone_pid = getpid();
  fprintf(stderr, "clone running, pid = %d\n", clone_pid);
  sleep(5);
  fprintf(stderr, "clone terminating\n");
  return 0;
}

int main(int argc, char **argv)
{ int pid;

  parent_pid = getpid();
  printf("parent pid = %d\n", parent_pid);
  signal(SIGUSR1, sigusr1);
  pid = do_clone((unsigned long)(clone_stack+sizeof(clone_stack)-1),
                 SIGUSR1 | COPYPID,
		 clone_function);
  if (pid < 0)
    perror("clone");
  else if (pid == 0)
    fprintf(stderr, "funny, clone() returned pid=0 in parent\n");
  else {
    printf("parent: clone running, pid = %d. waiting for termination.\n", pid);
    while (!do_terminate) sleep(1);
    printf("parent: looks like our kid is gone. BTW, clone_pid = %d\n", clone_pid);
  }
  return 0;
}


