syscall-advent

System Call Advent Calendar
git clone git://code.dwrz.net/syscall-advent
Log | Files | Refs

clone.c (4046B)


      1 #define _GNU_SOURCE
      2 #include <sched.h>
      3 #include <stdarg.h>
      4 #include <stdlib.h>
      5 #include <sys/wait.h>
      6 #include <stdio.h>
      7 #include <errno.h>
      8 #include <stdint.h>
      9 #include <unistd.h>
     10 #include <sys/types.h>
     11 #include <string.h>
     12 #include <syscall.h>
     13 #include <sys/syscall.h>
     14 #include <sys/types.h>
     15 #include <sys/stat.h>
     16 #include <fcntl.h>
     17 #include <stdio.h>
     18 #include <errno.h>
     19 #include <unistd.h>
     20 
     21 /* For our clone experiments, we working on a very low level and
     22  * fiddle around with threading. However, this leads to a problem with
     23  * the libc, which must perform some user-space operations to setup a
     24  * new thread. For example, in a clone()ed thread, we cannot simply
     25  * use printf(). Therefore, we provide you with a simple function to
     26  * output a string and a number. The writing to stdout (fd=1) is done
     27  * with plain system calls.
     28  *
     29  * Example: syscall_write("foobar = ", 23);
     30  */
     31 int syscall_write(char *msg, int number) {
     32 	write(1, msg, strlen(msg));
     33 	if (number != 0) {
     34 		char buffer[sizeof(number) * 3];
     35 		char *p = &buffer[sizeof(number) * 3];
     36 		int len = 1;
     37 		*(--p) = '\n';
     38 		if (number < 0) {
     39 			write(1, "-", 1);
     40 			number *= -1;
     41 		}
     42 		while (number > 0) {
     43 			*(--p) =  (number % 10) + '0';
     44 			number /= 10;
     45 			len ++;
     46 		}
     47 		write(1, p, len);
     48 	} else {
     49 		write(1, "0\n", 2);
     50 	}
     51 
     52 
     53 	return 0;
     54 }
     55 
     56 // For the new task, we always require an stack area. To make our life
     57 // easier, we just statically allocate an global variable of PAGE_SIZE.
     58 char stack[4096];
     59 
     60 // To demonstrate whether child and parent are within the same
     61 // namespace, we count up a global variable. If they are within the
     62 // same address space, we will see modification to this counter in
     63 // both namespaces
     64 volatile int counter = 0;
     65 
     66 int child_entry(void* arg) {
     67 	// We just give a little bit of information to the user.
     68 	syscall_write(": Hello from child_entry", 0);
     69 	syscall_write(": getppid() = ", getppid()); // What is our parent PID
     70 	syscall_write(": getpid()  = ", getpid());  // What is our thread group/process id
     71 	syscall_write(": gettid()  = ", gettid());  // The ID of this thread!
     72 	syscall_write(": getuid()  = ", getuid());  // What is the user id of this thread.
     73 
     74 
     75 	// We increment the global counter in one second intervals. If we
     76 	// are in our own address space, this will have no influence on
     77 	// the parent!
     78 	while (counter < 4) {
     79 		// syscall_write("child counter = ", counter);
     80 		counter++;
     81 		sleep(1);
     82 	}
     83 
     84 	return 0;
     85 }
     86 
     87 
     88 int main(int argc, char *argv[]) {
     89 	if (argc != 2) {
     90 		printf("usage: %s MODE\n", argv[0]);
     91 		printf("MODE:\n");
     92 		printf("  - fork    -- emulate fork with clone\n");
     93 		printf("  - chimera -- create process/thread chimera\n");
     94 		printf("  - thread  -- create a new thread in a process\n");
     95 		printf("  - user    -- create a new process and alter its UID namespace\n");
     96 		return -1;
     97 	}
     98 
     99 	syscall_write("> Hello from main!", 0);
    100 	syscall_write("> getppid() = ", getppid());
    101 	syscall_write("> getpid()  = ", getpid());
    102 	syscall_write("> gettid()  = ", gettid());
    103 	syscall_write("> getuid()  = ", getuid());
    104 
    105 	int flags = 0;
    106 	void *arg = NULL;
    107 	if (!strcmp(argv[1], "fork")) {
    108 		// Receive a SIGCHLD if the child terminates.
    109 		flags = SIGCHLD;
    110 	} else if (!strcmp(argv[1], "chimera")) {
    111 		// Process-thread chimera.
    112 		// Child process shares virtual memory with the parent.
    113 		flags = SIGCHLD | CLONE_VM;
    114 	} else if (!strcmp(argv[1], "thread")) {
    115 		// Thread
    116 		// Child shares virtual memory with parent.
    117 		// Child is in the same thread group as parent.
    118 		// Child shares the signal handler table with the parent.
    119 		flags = CLONE_VM | CLONE_THREAD | CLONE_SIGHAND;
    120 	} else if (!strcmp(argv[1], "user")) {
    121 		// TODO
    122 		flags = SIGCHLD;
    123 	} else {
    124 		printf("Invalid clone() mode: %s\n", argv[1]);
    125 		return -1;
    126 	}
    127 
    128 	pid_t pid = clone(child_entry, &stack[sizeof(stack)-1], flags, arg);
    129 	if (pid == -1) {
    130 		perror("clone");
    131 		return -1;
    132 	}
    133 	syscall_write("> clone() returned ", pid);
    134 
    135 	syscall_write("\n!!!!! Press C-c to terminate. !!!!!", 0);
    136 	while(counter < 4) {
    137 		syscall_write("counter = ", counter);
    138 		sleep(1);
    139 	}
    140 
    141 	return 0;
    142 }