.\" -*-nroff-*- .de VS .sp 1 .RS .nf .ft B .. .de VE .ft R .fi .RE .sp 1 .. .de hP .IP .ft B \h'-\w'\\$1\ 'u'\\$1\ \c .ft P .. .ie t .ds o \(bu .el .ds o o .TH mdup 3 "4 January" "Straylight/Edgeware" "mLib utilities library" .SH NAME mdup \- renumber file descriptors .SH SYNOPSIS .B "#include " .BI "int mdup(mdup_fd *" v ", size_t " n ");" .SH DESCRIPTION The .B mdup function renumbers file descriptors, using the .BR dup (2) and .BR dup2 (2) system calls. Its arguments are a pointer .I v to a vector of .B mdup_fd structures, and the length .I n of this vector, in elements. The .B mdup_fd structure is defined as .VS typedef struct mdup_fd { int cur; int want; } mdup_fd; .VE Each `slot' (element) in the vector .I v represents a file. The slot's .B cur member names the current file descriptor for this file; the .B want member is the file descriptor to move it to. In order to keep a file alive when you don't care which descriptor it ends up with, set .I want = \-1. Several slots may specify the same .B cur descriptor; but they all have to declare different .BR want s (except that several slots may have .I want = \-1. .PP On successful exit, the function will have rearranged the file descriptors as requested. To reflect this, the .B cur members will all be set to match the .B want members (except where the latter are \-1). .PP If there is a failure, then some rearrangement may have been performed and some not; the .B cur members are set to reflect which file descriptors are to be used. .PP The old file descriptors are .IR closed . This is different from usual .BR dup (2) behaviour, of course, but essential for reliable error handling. If you want to keep a particular source file descriptor open as well as make a new copy then specify two slots with the same .BR cur , one with .B want " = " cur and one with the desired output descriptor. .PP The .B mdup function is capable of arbitrary file descriptor remappings. In particular, it works correctly even if the desired remappings contain cycles. .SS "Background: the problem that mdup solves" The .B mdup function is intended to be used to adjust file descriptors prior to invoking one of the .B exec system calls. The standard use of .BR dup (2) to establish the child process's standard input/output/error files is prone to errors in the case where the newly opened file in fact already has one of the relevant file descriptors. .PP Consider the case where we want to run a process with separate pipes attached to each of the standard descriptors. Typical code looks like this. .VS #define P_INIT { \-1, \-1 } int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT; pid_t kid = -1; int i; if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error; if ((kid = fork()) < 0) goto error; if (!kid) { if (dup2(p_in[0], STDIN_FILENO) < 0 || dup2(p_out[1], STDOUT_FILENO) < 0 || dup2(p_err[2], STDERR_FILENO) < 0 || close(p_in[0]) || close(p_out[0]) || close(p_err[0]) || close(p_in[1]) || close(p_out[1]) || close(p_err[1])) _exit(127); execvp("/bin/sh", "sh", "-c", "...", (char *)0); } \&... .VE Now suppose that, in the parent process, the standard input, output and error descriptors are all initially closed. After the calls to .BR pipe (2), descriptors 0, 1, and 2 refer to .BR p_in[0] , .BR p_in[1] , and .B p_out[0] respectively. In the child process, the calls to .BR dup2 (2) rearrange these. But then the .BR close (2) calls will immediately close all three descriptors, before .BR exec ing the child. .PP Here's how to rewrite the above function using .BR mdup . .VS #define P_INIT { \-1, \-1 } int p_in[2] = P_INIT, p_out[2] = P_INIT, p_err[2] = P_INIT; pid_t kid = -1; mdup_fd md[3]; int i; if (pipe(p_in) || pipe(p_out) || pipe(p_err)) goto error; if ((kid = fork()) < 0) goto error; if (!kid) { if (close(p_in[1] || close(p_out[0]) || close(p_err[0])) goto _exit(127); md[0].cur = p_in[0]; md[0].want = STDIN_FILENO; md[1].cur = p_out[1]; md[1].want = STDOUT_FILENO; md[2].cur = p_err[1]; md[2].want = STDERR_FILENO; if (mdup(md, 3)) _exit(127); execvp("/bin/sh", "sh", "-c", "...", (char *)0); } \&... .VE One can see that, not only is the resulting program more correct, it's also simpler. Note that we close the unwanted ends of the pipes .I before invoking .BR mdup . Closing them afterwards risks interfering with the newly assigned descriptors which are meant to be passed to the child process. Note also that .B mdup has taken responsibility for closing the other descriptors for the wanted ends of the pipes. .SH "SEE ALSO" .BR dup (2), .BR dup2 (2), .BR mLib (3). .SH AUTHOR Mark Wooding,