===== Exec… ===== In order to run an executable file, an existing process has to 'exec' into it – that is, the process has to ask the kernel to replace its memory with the code (and data) of the executable file. \\ So, typically to start a new process, one has to ''fork'' and then ''exec…'' in the child:
prog1              ,------.
-------------------| fork |--------------------------------------
pid: x (ppid: y)   `------\
                           \ prog1              ,-------. prog2
                            `-------------------| exec… |--------
                             pid: z (ppid: x)   `-------'
To this end, a family of functions starting with ''exec'' is provided: \\ Needs header:
unistd.h
''int **execlp**(const char *//file//, const char *arg0, ... /*, (char *)**0** */)'' \\ ''int **execl **(const char *//path//, const char *arg0, ... /*, (char *)**0** */)'' \\ ''int **execle**(const char *//path//, const char *arg0, ... /*, (char *)**0**,*/ char *const //envp//[])'' \\ ''int **execvp**(const char *//file//, char *const //argv//[])'' \\ ''int **execv **(const char *//path//, char *const //argv//[])'' \\ ''int **execve**(const char *//path//, char *const //argv//[], char *const //envp//[])'' \\ (Those functions are documented more cleanly in [[https://man7.org/linux/man-pages/man3/exec.3.html|the Linux manual]]). After a successful execution of the ''exec…'' function the next instructions of the process are those of the new executable file. Hence, it is pointless to check the return value of ''exec…'' – it may only return ''-1'', and if the instructions following ''exec…'' do execute, then ''exec…'' must have failed. Importantly, upon executing ''exec…'' the list of open files is retained. \\ Almost all resources are released. See the documentation for other exceptions. ''exec**l**…'' vs ''exec**v**…''\\ The former takes a list of arguments (terminated by a NULL [[https://en.wikipedia.org/wiki/Sentinel_value|sentinel]]), the latter takes a pointer to the argument vector (the last element must be a NULL sentinel as well). char arg0[] = "ls"; char arg2[] = "-a"; char arg1[] = "-l"; char arg3[] = "/tmp"; # Argument list: execlp("ls", arg0, arg1, arg2, arg3, NULL); # Argument vector: char *argv[] = {arg0, arg1, arg2, arg3, NULL}; execvp("ls", argv); Notice that the 0th argument is the program name. ''exec…**p**'' vs ''exec…'' (without ''p''): \\ The latter requires a path to the executable, and the former searches for the executable within directories specified by the ''PATH'' environmental variable if a name rather than a path was provided (//ls// is a name, ///bin/ls// is a path, //./ls// is a path). # succeeds iff there is an executable file named 'ls' in the current working directory execl ("ls", "ls", "-la", "/tmp", NULL); # succeeds iff there is an executable file named 'ls' in one of the directories in the PATH list execlp("ls", "ls", "-la", "/tmp", NULL); # succeeds iff '/bin/ls' is an executable file execl ("/bin/ls", "ls", "-la", "/tmp", NULL); # as above - searching the PATH is abandoned if the argument is a path execlp("/bin/ls", "ls", "-la", "/tmp", NULL); UNIX-like and/or POSIX-compliant operating systems use [[https://en.wikipedia.org/wiki/Environment_variable|environment variables]]. By default the values of all such variables are inherited upon ''exec…''. \\ ''exec…**e**'' functions have an extra argument that should point to an array of the environment variables, allowing thereby to override them for newly exec'ed process. \\ To access an unprocessed array of environment variables for the current process, one must have in the source code the following lines: #include extern char **environ; The ''environ'' external variable and the ''//envp//'' argument of ''exec…e'' functions use a NULL sentinel. \\ (Normally, to access such variables of the running process one shall use the ''[[https://en.cppreference.com/w/cpp/utility/program/getenv|getenv]]'' and ''[[https://pubs.opengroup.org/onlinepubs/9699919799/functions/setenv.html|setenv]]'' functions. \\ The ''environ'' variable is useful when one wants to pass a slightly modified set of environment variables.) \\ ~~Exercise.#~~ Write a program that executes ''ps'' with the argument ''-F''. (Remember that the arguments include the command name.) ~~Exercise.#~~ Write a program ''//prog//'' that, when executed as ''//prog// [//arg//]...'', executes the ''ls -l -t -r [//arg//]...'' command. ~~Exercise.#~~ Write a program ''//prog//'' that, when executed as ''//prog// //cmd// [//arg//]...'', executes and measures the runtime (as wall clock time) of ''//cmd// [//arg//]...''. \\ To measure time, you can use the following C11 code: #include ... struct timespec start, end; timespec_get(&start, TIME_UTC); ... timespec_get(&end, TIME_UTC); double elapsedSec = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1e9; ~~Exercise.#~~ Modify the code of the previous exercise: \\     • close the standard input and output of the parent process right after ''fork'' \\     • write output from parent process to the standard error \\     • output whether the child process executed normally and output its return value ===== Duplicating file descriptors ===== POSIX defines the following functions: \\ Needs header:
unistd.h
''int **dup**(int //fildes//)'' \\ ''int **dup2**(int //fildes//, int //target//)'' \\ to duplicate file descriptors. Duplicating a file descriptor is something different than opening the same file twice. When opening the same file twice, one can choose different set of flags (such as O_RDONLY, O_RDWR, O_APPEND), and the descriptors have a different position in file (byte that will be read/written upon next read/write). Duplicated file descriptors refer to the same state of the file (flags, position etc.). | Hover mouse over lines of code to see what happens in the OS upon ''open''/''dup'':|| | Opening twice: | | || | | Duplicating: | | The ''dup2'' function **atomically** closes the descriptor //target//, and then duplicates the descriptor //fildes// to the descriptor //target//. \\ This is commonly used to replace standard streams, as in the following example (that shows redirection of the standard output):

Each file descriptor has to be closed separately. ~~Exercise.#~~ Write a program that executes ''ps'' with the argument ''-F'' and writes its output to a file ''//output//''. ~~Exercise.#~~ Write a program that, when executed as ''//prog// //fname// [//arg//]...'', will act as ''tr [//arg//]... < fname''. ~~Exercise.#~~ Write a program that: \\     • assigns ''ELOOP'' to ''errno'' \\     • executes ''perror'' function so that the error is output on the terminal \\     • executes ''perror'' function so that the error is written to a file \\     • assigns ''EMFILE'' to ''errno'' \\     • executes ''perror'' function so that the error is output on the terminal \\     • executes ''perror'' function so that the error is written to a file \\ Notice that ''perror'' always writes to standard error, so to make it write to a file, one has to replace the file descriptor ''2''. ~~META: language = en ~~