Monday, September 15, 2014

Unix Prog: exec functions

1. exec functions
When a process calls one of exec functions, the process is completely replaced by the new program, and the new program starts executing from the main function. exec merely replaces the current process, text, data, heap and stack segments while retaining the process ID.

System Definition:
 ubuntu@ip-172-31-23-227:~$ less /usr/include/unistd.h  
 ......  
 /* Replace the current process, executing PATH with arguments ARGV and  
   environment ENVP. ARGV and ENVP are terminated by NULL pointers. */  
 extern int execve (const char *__path, char *const __argv[],  
           char *const __envp[]) __THROW __nonnull ((1, 2));  
 ......  
 /* Execute PATH with arguments ARGV and environment from `environ'. */  
 extern int execv (const char *__path, char *const __argv[])  
    __THROW __nonnull ((1, 2));  
   
 /* Execute PATH with all arguments after PATH until a NULL pointer,  
   and the argument after that for environment. */  
 extern int execle (const char *__path, const char *__arg, ...)  
    __THROW __nonnull ((1, 2));  
   
 /* Execute PATH with all arguments after PATH until  
   a NULL pointer and environment from `environ'. */  
 extern int execl (const char *__path, const char *__arg, ...)  
    __THROW __nonnull ((1, 2));  
   
 /* Execute FILE, searching in the `PATH' environment variable if it contains  
   no slashes, with arguments ARGV and environment from `environ'. */  
 extern int execvp (const char *__file, char *const __argv[])  
    __THROW __nonnull ((1, 2));  
   
 /* Execute FILE, searching in the `PATH' environment variable if  
   it contains no slashes, with all arguments after FILE until a  
   NULL pointer and environment from `environ'. */  
 extern int execlp (const char *__file, const char *__arg, ...)  
    __THROW __nonnull ((1, 2));  
 ......  

If the execution file is not executable, then it will launch /bin/sh to run against the file.

If we don't pass the environment variable then it will use global variable "environ" of the current process.

For each file descriptor of current process, if its FD_CLOEXEC flag is on, it will be closed after running the exec, otherwise it will keep open.

Real user id and real group id will keep same, but the effective user id and effective group id will depend on the "set_user_id", "set_group_id" flag of executable file.

Only execve is the system call, the calling relationship of different functions:
execlp -(build argv)-> execvp
execl -(build argv)-> execv
execle -(build argv)-> execve
execvp->execv->execve(system call)

2. Example

echoall.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<unistd.h>  
   
 int main(int argc, char* argv[])  
 {  
  int i;  
  char **ptr;  
   
  for(i = 0; i < argc; i++)  
   printf("argv[%d]: %s\n", i, argv[i]);  
   
  for(ptr = __environ; *ptr != 0; ptr++)  
   printf("%s\n", *ptr);  
   
  exit(0);  
 }  

shell:
 ubuntu@ip-172-31-23-227:~$ ./echoall.out Hello world  
 argv[0]: ./echoall.out  
 argv[1]: Hello  
 argv[2]: world  
 XDG_SESSION_ID=60  
 TERM=xterm  
 SHELL=/bin/bash  
 SSH_CLIENT=67.243.178.100 50301 22  
 SSH_TTY=/dev/pts/0  
 USER=ubuntu  
 ......  

proc.c:
 #include<stdio.h>  
 #include<stdlib.h>  
 #include<unistd.h>  
   
 char *env_init[]={"USER=unknown", "PATH=/tmp", NULL};  
   
 int main(int argc, char* argv[])  
 {  
  pid_t pid;  
   
  if((pid = fork()) < 0) {  
   printf("fork error!\n");  
   exit(1);  
  }  
  else if (pid == 0) {  
   // Note: we have to convert 0 to (char*), if in current system  
   // memory usage for int and pointer are different, then it would  
   // cause error  
   if(execle("./echoall.out", "First Arg", "Second Arg", (char*)0, env_init) < 0) {  
    printf("execle error!\n");  
    exit(3);  
   }  
   exit(0);  
  }  
   
  // Wait for the child process finish firstly  
  if(waitpid(pid, NULL, 0) < 0) {  
   printf("waitpid error!\n");  
   exit(2);  
  }  
   
  if((pid = fork()) < 0) {  
   printf("fork error!\n");  
   exit(1);  
  }  
  else if (pid == 0) {  
   if(execlp("echoall.out", "First Arg", "Second Arg", (char*) 0) < 0) {  
    printf("execlp error!\n");  
    exit(3);  
   }  
  }  
   
  // Wait for the child process finish firstly  
  if(waitpid(pid, NULL, 0) < 0) {  
   printf("waitpid error!\n");  
   exit(2);  
  }  
   
  exit(0);  
 }  

shell:
Note:
If running the program from the shell, the shell will put first argument as the program name automatically. In this case ,we just provide the "First Arg", "Second Arg" as two arguments, so we can't see the program name as argument.
 ubuntu@ip-172-31-23-227:~$ ./proc.out  
 argv[0]: First Arg  
 argv[1]: Second Arg  
 USER=unknown  
 PATH=/tmp  
 argv[0]: First Arg  
 argv[1]: Second Arg  
 XDG_SESSION_ID=60  
 TERM=xterm  
 SHELL=/bin/bash  
 SSH_CLIENT=67.243.178.100 50301 22  
 SSH_TTY=/dev/pts/0  
 USER=ubuntu  
 ......  

No comments:

Post a Comment