Computer Systems Fundamentals

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void) {
   printf("Starting process...\n");
   printf("P: my pid is %d.\n", getpid()); 
   printf("P: my parent is %d.\n", getppid());
   return 0;
}
print all environment variables
#include <stdio.h>

int main(void) {
    // print all environment variables
    extern char **environ;

    for (int i = 0; environ[i] != NULL; i++) {
        printf("%s\n", environ[i]);
    }
}
#include <stdio.h>
#include <stdlib.h>
int main(void) {
    char *value = getenv("PATH");
    printf("Environment variable 'PATH' has value '%s'\n", value);
    return 0;
}
$ dcc fork.c
$ a.out

I am the parent because fork() returned 2884551.
I am the child because fork() returned 0. $

#include <stdio.h>
#include <unistd.h>

int main(void) {

    // fork creates 2 identical copies of program
    // only return value is different

    pid_t pid = fork();

    if (pid == -1) {
         perror("fork");  // print why the fork failed
    } else if (pid == 0) {
        printf("I am the child because fork() returned %d.\n", pid);
    } else {
        printf("I am the parent because fork() returned %d.\n", pid);
    }

    return 0;
}
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <signal.h>

int main(void)
{
   pid_t pid = fork();
   if (pid < 0){
   	  perror("I am the parent and fork() failed");

   } else if (pid != 0) {
	  
      printf("I am the parent.\n");
      printf("P: my pid is %d.\n", getpid());
      printf("P: my child is %d.\n", pid);
      printf("P: my parent is %d.\n", getppid());
      sleep(5);
   }
   else {
   
      printf("I am the child.\n");
      printf("C: my pid is %d.\n", getpid());
      printf("C: my parent is %d.\n", getppid());
      sleep(5);
   }
   return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void) {
   //Child process will get a copy of all the variables that exist
   //in the parent when fork is called.
   int x = 10;
   int *a = malloc(100);
   a[0] = 100;
   pid_t pid;
   pid = fork();
   if (pid < 0)
      perror("fork() failed");
   else if (pid == 0){
      x++;
      a[0]++;
      printf("I am the child. %d %d\n",x,a[0]);
      free(a);
   }else {
      x--;
      a[0] = a[0] * 2;
      printf("I am the parent. %d %d\n",x,a[0]);
      free(a);
   }
   return 0;
}

#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

//ps -ef | grep forkEx
int main(void) {
    printf("hello world...\n");
    sleep(10);
    fork(); 
    printf("Hello 1\n"); 
    sleep(10);   
    fork();   
    printf("hello 2\n");
    sleep(10); 
    fork();   
    printf("hello 3\n");
    sleep(20);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
int main(void){
   printf("Hello");
   exit(0);  //return 0
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(void){
   printf("Hello");
   _exit(0);  //does not
              //flush stdio buffers
}

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void)
{
   pid_t pid;
   pid = fork();
   if (pid == 0) {
      sleep(5);
      printf("I am the child.\n");
   }
   else {
      
      printf("I will wait for my child\n");
      wait(NULL);
      printf("I am the parent.\n");
      sleep(2);    
   }
   return 0;
}
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void){
   int status;
   pid_t pid;
   pid = fork();
   if (pid == 0) {
      sleep(5);
      printf("I am the child.\n");
      int *p = NULL;
      *p = *p + 1;
      return 2; //exit(2)
   }
   else {
      wait(&status);
      printf("%d\n",status); 
     
      if(WIFEXITED(status)){
         printf("Status was %d %d %08x\n",WEXITSTATUS(status), status,status);
      } else {
	 printf("Did not exit %d %08x\n",status,status);
      }
      
      printf("I am the parent.\n");
   }
   return 0;
}
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>

extern char **environ;

int main(void) {
   pid_t pid;
   int stat;

   printf("Parent process (%d) starts ...\n", getpid());
   if ((pid = fork()) != 0) {
      printf("Parent is waiting ...\n");
      pid = wait(&stat);
      printf("Child (%d) status = %08x\n", pid, stat);
      printf("%d\n",stat);
      if(WIFEXITED(stat)){
          printf("Exit Status was %d\n",WEXITSTATUS(stat));
      }
   }
   else {
      printf("Child process (%d) has parent %d\n",
             getpid(), getppid());

      char *args[2]; 
      args[0] = "./hello"; 
      args[1] = NULL;
      stat = execve(args[0], args, environ);
      printf("In child %d\n",stat);
      // only reach here if exec fails
      perror("Exec failed");
      return 1;
   }
   return 0;
}
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
//#include <sys/wait.h>
//#include <sys/types.h>

int main(void)
{
   printf("Hello! ");
   printf("Exec'd process (%d) has parent %d\n",
          getpid(), getppid());
          
   char *value = getenv("HOME");
   printf("Environment variable 'HOME' has value '%s'\n", value);

   sleep(10);
   return 2; //exit(2);
}
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>

extern char **environ;  

int main(void) {
   pid_t pid;
   int stat;

   printf("Parent process (%d) starts ...\n", getpid());
   if ((pid = fork()) != 0) {
      printf("Parent is waiting ...\n");
      pid = wait(&stat); 
      if(WIFEXITED(stat)){
          printf("Exit Status was %d\n",WEXITSTATUS(stat));
      }
   }
   else {
      printf("Child process (%d) has parent %d\n",
             getpid(), getppid());

      char *args[3] = {"/usr/bin/ls","-l", NULL} ; 
    
      stat = execve(args[0], args, environ);
      printf("In child %d\n",stat);
      // only reach here if exec fails
      perror("Exec failed");
      return 1;
   }
   return 0;
}
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>

extern char **environ; //array of strings
int main(void) {
   pid_t pid;
   int stat;

   printf("Parent process starts ...\n");
   if ((pid = fork()) != 0) {
      printf("Parent is waiting ...\n");
      wait(&stat);
      printf("Child status = %08x %d\n", stat,WEXITSTATUS(stat));
   }
   else { 
      int   i = 0;
      char  line[50];       // input line buffer
      char *args[100];      // cmd line args
      printf("Give me the argv list (1 per line):\n");
      while (fgets(line, 50, stdin) != NULL) {
         line[strlen(line)-1] = '\0'; // strip '\n'
         args[i] = strdup(line); //a malloc then a strcpy
         i++;
      }
      
      args[i] = NULL;
     
     
      stat = execve(args[0], args, environ);
      // only reach here if exec fails
      printf("Tried to exec %s\n", args[0]);
      perror("Exec failed");
      //should free the strdup strings to avoid a memory leak
      return 1;
     
   }
   return 0;
}
$ dcc spawn.c
$ a.out

Tue 3 Nov 23:51:27 UTC 2022 /bin/date exit status was 0

simple example of posix_spawn run date --utc to print current UTC
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <spawn.h>
#include <sys/wait.h>

int main(void) {

    pid_t pid;
    extern char **environ;
    char *date_argv[] = {"/bin/date", "--utc", NULL};

    // spawn "/bin/date" as a separate process
    if (posix_spawn(&pid, "/bin/date", NULL, NULL, date_argv, environ) != 0) {
        perror("spawn");
        exit(1);
    }

    // wait for spawned processes to finish
    int exit_status;
    if (waitpid(pid, &exit_status, 0) == -1) {
        perror("waitpid");
        exit(1);
    }

    printf("/bin/date exit status was %d\n", exit_status);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <spawn.h>
#include <sys/wait.h>

extern char **environ;

int main(void) {

    pid_t pid;
    
    char *args[] = {"/usr/bin/ls", "-l", NULL};
    

    // Similar to doing a fork then an execve
    // spawn "/bin/ls" as a separate process
    if (posix_spawn(&pid, args[0], NULL, NULL, args, environ) != 0) {
        perror("spawn");
        exit(1);
    }

    int status;
    // wait for spawned processes to finish
    if (wait(&status) == -1) {
        perror("waitpid");
        exit(1);
    }

    if(WIFEXITED(status)){
          printf("/bin/ls exited normally with status %d\n",WEXITSTATUS(status));
    }
    return 0;
}
spawn ls -ld adding as argument the arguments we have been given
#include <stdio.h>
#include <stdlib.h>
#include <spawn.h>
#include <sys/wait.h>

int main(int argc, char *argv[]) {
    char *ls_argv[argc + 2];
    ls_argv[0] = "/bin/ls";
    ls_argv[1] = "-ld";
    for (int i = 1; i <= argc; i++) {
        ls_argv[i + 1] = argv[i];
    }

    pid_t pid;
    extern char **environ;
    if (posix_spawn(&pid, "/bin/ls", NULL, NULL, ls_argv, environ) != 0) {
        perror("spawn");
        exit(1);
    }

    int exit_status;
    if (waitpid(pid, &exit_status, 0) == -1) {
        perror("waitpid");
        exit(1);
    }

    // exit with whatever status ls exited with
    return exit_status;
}


$ dcc get_status.c -o get_status $ STATUS=ok ./get_status
Environment variable 'STATUS' has value 'ok' $

#include <stdio.h>
#include <stdlib.h>

// simple example of accessing an environment variable
int main(void) {
    // print value of environment variable STATUS
    char *value = getenv("STATUS");
    printf("Environment variable 'STATUS' has value '%s'\n", value);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <spawn.h>
#include <sys/wait.h>

extern char **environ;

// simple example of setting an environment variable
int main(void) {

    // set environment variable STATUS
    setenv("STATUS", "great", 1);

    char *getenv_argv[] = {"./get_status", NULL};
    pid_t pid;
    
    if (posix_spawn(&pid, "./get_status", NULL, NULL,
        getenv_argv, environ) != 0) {
        perror("spawn");
        exit(1);
    }
    int exit_status;
    if (waitpid(pid, &exit_status, 0) == -1) {
        perror("waitpid");
        exit(1);
    }

    // exit with whatever status s exited with
    return exit_status;
}
simple example of using environment variableto change program behaviour run date -to print time
Perth time printed, due to TZ environment variable
#include <stdio.h>
#include <unistd.h>
#include <spawn.h>
#include <sys/wait.h>

int main(void) {
    pid_t pid;

    char *date_argv[] = { "/bin/date", NULL };
    char *date_environment[] = { "TZ=Australia/Perth", NULL };
    // print time in Perth
    if (posix_spawn(&pid, "/bin/date", NULL, NULL, date_argv,
                    date_environment) != 0) {
        perror("spawn");
        return 1;
    }

    int exit_status;
    if (waitpid(pid, &exit_status, 0) == -1) {
        perror("waitpid");
        return 1;
    }

    printf("/bin/date exit status was %d\n", exit_status);
    return 0;
}
simple example of system
#include <stdio.h>
#include <stdlib.h>

int main(void) {

    // system passes string to a shell for evaluation
    // brittle and highly vulnerable to security exploits
    // system is suitable for quick debugging and throw-away programs only
    // run date --utc to print current UTC
    int exit_status = system("/bin/date --utc");
    printf("/bin/date exit status was %d\n", exit_status);
    return 0;
}

Simple pipe example
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>

int main(void) {
   int fd[2];
   pid_t pid;  
   char buffer[10] = {0};
   if( pipe(fd) < 0){
        perror(NULL);
        exit(1);
   }
   pid = fork();
   assert(pid >= 0);
   if (pid != 0) {  // parent
      
      close(fd[0]); // writer, don't need fd[0]
      write(fd[1], "123456789", 10);
      write(fd[1], "abcdefghi", 10);
      close(fd[1]);
   }
   else {       
     
      // child
      close(fd[1]); // reader, don't need fd[1]
      read(fd[0], buffer, sizeof(buffer));
      printf("got \"%s\"\n", buffer);
      read(fd[0], buffer, sizeof(buffer));
      printf("got \"%s\"\n", buffer);
      close(fd[0]);
   }
   return 0;
}

Simple pipe example
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>

int main(void) {
   int fd[2];
   pid_t pid;  
   char buffer[10] = {0};
   if( pipe(fd) < 0){
        perror(NULL);
        exit(1);
   }
   pid = fork();
   assert(pid >= 0);
   if (pid != 0) {  // parent
      
      close(fd[0]); // writer, don't need fd[0]
      write(fd[1], "123456789", 10);
      write(fd[1], "abcdefghi", 10);
      close(fd[1]);
   }
   else {       
     
      // child
      close(fd[1]); // reader, don't need fd[1]
      read(fd[0], buffer, sizeof(buffer));
      printf("got \"%s\"\n", buffer);
      read(fd[0], buffer, sizeof(buffer));
      printf("got \"%s\"\n", buffer);
      close(fd[0]);
   }
   return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

//This is not a good idea
//Should create 2 pipes rather than use 1 bidirectional pipe
int main(void){
    int pipefds[2];
    
    pipe(pipefds);
    pid_t id = fork();
    if( id == 0){
        //I am the child
        char message[] = "Hi mum!\n";
        char buf[100] = {};
        read(pipefds[0],buf,100);
        write(pipefds[1],message,strlen(message));
        printf("I am the child and I got the message %s\n",buf);
        close(pipefds[0]);
    } else {
         
        //I am the parent
        char message[] = "Hello my child\n";        
        write(pipefds[1],message,strlen(message));
        char buf[100];
        read(pipefds[0],buf,100);       
        printf("I am the parent and I got the message %s\n",buf);
        close(pipefds[1]);
    }
    return 0;
}
#include <stdio.h>
#include <error.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>

int main(void){
    int fdsPC[2];  //Parent -> Child
    int fdsCP[2];  //Child -> Parent
    if(pipe(fdsPC) < 0){
        error(errno,errno,NULL);
    }
    
    if(pipe(fdsCP) < 0){
        error(errno,errno,NULL);
    }
    
    pid_t pid= fork();
    if(pid < 0){
        error(errno,errno,NULL);
    } else if (pid == 0){
        close(fdsPC[1]);
        close(fdsCP[0]);
        //I am the child
        char buffer[100] = {0};
        read(fdsPC[0],buffer,99);       
        printf("%s\n",buffer);
        close(fdsPC[0]);
        write(fdsCP[1],"Hello dad",strlen("Hello dad"));
        close(fdsCP[1]);
       
    } else {
        close(fdsPC[0]);
        close(fdsCP[1]);
        write(fdsPC[1],"Hello Child",strlen("Hello Child"));
        close(fdsPC[1]);
        char buffer[100] = {0};
        read(fdsCP[0],buffer,99);
        printf("%s\n",buffer);
        close(fdsCP[0]);  
    }

    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>


int main(void){
    FILE * fd = popen("ls -al", "r");
    char buffer[100];
    
    while( fgets(buffer,100,fd) != NULL){
        printf("I just got %s\n",buffer);
    }
    fclose(fd);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

//pipe
//fork
//execve

int main(void){
    FILE * fd = popen("wc", "w"); //writing to the pipe
    fprintf(fd,"0123456789\n");  
    fprintf(fd,"aksdflaksdf\n");
    fclose(fd);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <sys/types.h>

int main(void)
{
   FILE *p = popen("ls -l","r");
    
   assert(p != NULL);
   char line[200], a[20],b[20],c[20],d[20];
   long int tot = 0, size = 0;
   while (fgets(line,200,p) != NULL) {
      sscanf(line, "%s %s %s %s %ld",
                     a, b, c, d, &size);
      fputs(line, stdout);
      if (a[0] == '-') tot += size;
   }
   printf("Total: %ld\n", tot);
   pclose(p);
}
simple example using a pipe with posix_spawn to capture output from spawned process
#include <stdio.h>
#include <unistd.h>
#include <spawn.h>
#include <sys/wait.h>

int main(void) {
    // create a pipe
    int pipe_file_descriptors[2];
    if (pipe(pipe_file_descriptors) == -1) {
        perror("pipe");
        return 1;
    }

    // create a list of file actions to be carried out on spawned process
    posix_spawn_file_actions_t actions;
    if (posix_spawn_file_actions_init(&actions) != 0) {
        perror("posix_spawn_file_actions_init");
        return 1;
    }

    // tell spawned process to close unused read end of pipe
    // without this - spawned process would not receive EOF
    // when read end of the pipe is closed below,
    if (posix_spawn_file_actions_addclose(&actions, pipe_file_descriptors[0]) != 0) {
        perror("posix_spawn_file_actions_init");
        return 1;
    }

    // tell spawned process to replace file descriptor 1 (stdout)
    // with write end of the pipe
    if (posix_spawn_file_actions_adddup2(&actions, pipe_file_descriptors[1], 1) != 0) {
        perror("posix_spawn_file_actions_adddup2");
        return 1;
    }

    pid_t pid;
    extern char **environ;
    char *date_argv[] = {"/bin/date", "--utc", NULL};
    if (posix_spawn(&pid, "/bin/date", &actions, NULL, date_argv, environ) != 0) {
        perror("spawn");
        return 1;
    }

    // close unused write end of pipe
    // in some case processes will deadlock without this
    // not in this case, but still good practice
    close(pipe_file_descriptors[1]);

    // create a stdio stream from read end of pipe
    FILE *f = fdopen(pipe_file_descriptors[0], "r");
    if (f == NULL) {
        perror("fdopen");
        return 1;
    }

    // read a line from read-end of pipe
    char line[256];
    if (fgets(line, sizeof line, f) == NULL) {
        fprintf(stderr, "no output from date\n");
        return 1;
    }

    printf("output captured from /bin/date was: '%s'\n", line);

    // close read-end of the pipe
    // spawned process will now receive EOF if attempts to read input
    fclose(f);

    int exit_status;
    if (waitpid(pid, &exit_status, 0) == -1) {
        perror("waitpid");
        return 1;
    }
    printf("/bin/date exit status was %d\n", exit_status);

    // free the list of file actions
    posix_spawn_file_actions_destroy(&actions);

    return 0;
}
simple example of using a pipe to with posix_spawn to sending input to spawned process
#include <stdio.h>
#include <unistd.h>
#include <spawn.h>
#include <sys/wait.h>

int main(void) {
    // create a pipe
    int pipe_file_descriptors[2];
    if (pipe(pipe_file_descriptors) == -1) {
        perror("pipe");
        return 1;
    }

    // create a list of file actions to be carried out on spawned process
    posix_spawn_file_actions_t actions;
    if (posix_spawn_file_actions_init(&actions) != 0) {
        perror("posix_spawn_file_actions_init");
        return 1;
    }

    // tell spawned process to close unused write end of pipe
    // without this - spawned process will not receive EOF
    // when write end of the pipe is closed below,
    // because spawned process also has the write-end open
    // deadlock will result
    if (posix_spawn_file_actions_addclose(&actions, pipe_file_descriptors[1]) != 0) {
        perror("posix_spawn_file_actions_init");
        return 1;
    }

    // tell spawned process to replace file descriptor 0 (stdin)
    // with read end of the pipe
    if (posix_spawn_file_actions_adddup2(&actions, pipe_file_descriptors[0], 0) != 0) {
        perror("posix_spawn_file_actions_adddup2");
        return 1;
    }


    // create a process running /usr/bin/sort
    // sort reads lines from stdin and prints them in sorted order
    char *sort_argv[] = {"sort", NULL};
    pid_t pid;
    extern char **environ;
    if (posix_spawn(&pid, "/usr/bin/sort", &actions, NULL, sort_argv, environ) != 0) {
        perror("spawn");
        return 1;
    }

    // close unused read end of pipe
    close(pipe_file_descriptors[0]);

    // create a stdio stream from write-end of pipe
    FILE *f = fdopen(pipe_file_descriptors[1], "w");
    if (f == NULL) {
        perror("fdopen");
        return 1;
    }

    // send some input to the /usr/bin/sort process
    //sort with will print the lines to stdout in sorted order
    fprintf(f, "sort\nwords\nplease\nthese\n");

    // close write-end of the pipe
    // without this sort will hang waiting for more input
    fclose(f);

    int exit_status;
    if (waitpid(pid, &exit_status, 0) == -1) {
        perror("waitpid");
        return 1;
    }
    printf("/usr/bin/sort exit status was %d\n", exit_status);

    // free the list of file actions
    posix_spawn_file_actions_destroy(&actions);

    return 0;
}