[comp.unix.wizards] dup2

schmidt@bastille.ics.uci.edu (Doug Schmidt) (02/08/91)

Hi,

	I'm curious, is it possible to implement the dup2() system
call using only routines available in the standard C library and other
existing system calls?

	Thanks,

		Doug
--
As flies to wanton boys             | Douglas C. Schmidt
Are we to th' gods,                 | (schmidt@ics.uci.edu)
They kill us for their sport.       | (714) 856-4101

pfalstad@phoenix.Princeton.EDU (Paul Falstad) (02/08/91)

schmidt@ics.uci.edu (Doug Schmidt) wrote:
>  I'm curious, is it possible to implement the dup2() system
>call using only routines available in the standard C library and other
>existing system calls?

Two ways, at least.

1: close and dup only

int dup2(int fd1,int fd2)
{
   close(fd2);
   fd1 = dup(fd1);
   if (fd1 != fd2)
      fd1 = movefd(fd1,fd2);
   return fd1;
}

int movefd(int fd1,int fd2)
{
int fe;

   if (fd1 == -1)
      return -1;
   if ((fe = dup(fd1)) != fd2)
      fe = movefd(fe);
   close(fd1);
   return fe;
}

2: fcntl

int dup2(int fd1,int fd2)
{
   close(fd2);
   return(fcntl(fd1,F_DUPFD,fd2));
}

--
Paul Falstad, pfalstad@phoenix.princeton.edu PLink:HYPNOS GEnie:P.FALSTAD
10 PRINT "PRINCETON CS"          | #include <std.disclaimer.h>
20 GOTO 10                       | [Your blood pressure just went up.]
Princeton University would like to apologize to everyone for this article.

chet@odin.INS.CWRU.Edu (Chet Ramey) (02/08/91)

Doug Schmidt writes:

>	I'm curious, is it possible to implement the dup2() system
>call using only routines available in the standard C library and other
>existing system calls?

When you really get down to it.  the kernel is going to have to do the
nitty-gritty duplication for you, otherwise it gets tricky.  If you have
an fcntl(..., F_DUPFD, ...), it's straightforward. 

Here's how we do it in bash:

dup2 (fd1, fd2)
     int fd1, fd2;
{
  if (fcntl (fd1, F_GETFL, 0) == -1)    /* fd1 is an invalid fd */
    return (-1);
  if (fd2 < 0 || fd2 >= getdtablesize ())
    {
      errno = EBADF;
      return (-1);
    }
  if (fd1 == fd2)
    return (0);
  (void) close (fd2);
  return (fcntl (fd1, F_DUPFD, fd2));
}

(getdtablesize() can be replaced with:

	sysconf(_SC_OPEN_MAX)	Posix

	ulimit(4, 0L)		System V.3 and up

	NOFILE			just about anything else

and of course the value can be fetched once into a static variable and
cached.)

Chet
-- 
Chet Ramey				``There's just no surf in
Network Services Group			  Cleveland, U.S.A. ...''
Case Western Reserve University
chet@ins.CWRU.Edu		My opinions are just those, and mine alone.

rbj@uunet.uu.net (Root Boy Jim) (02/08/91)

--text follows this line--

In article <1991Feb8.002436.21328@usenet.ins.cwru.edu> chet@po.CWRU.Edu writes:
>Doug Schmidt writes:
>
>>	I'm curious, is it possible to implement the dup2() system
>>call using only routines available in the standard C library and other
>>existing system calls?
>
>When you really get down to it.  the kernel is going to have to do the
>nitty-gritty duplication for you, otherwise it gets tricky.  If you have
>an fcntl(..., F_DUPFD, ...), it's straightforward. 
>
>Here's how we do it in bash:

Hmmmm. The two left feet don't know what the right hand is doing.
Go check your emacs sources, src/sysdep.c. It's trivial once you
realize the recursive solution.

First, the one from emacs: OOPS! It doesn't work! And I feel
really stupid cuz I sent it to Larry Wall.
Oh well, I just tested this:

dup2(old,new)
{
	register int fd, ret;
	close(new);
	fd = dup(old);
	if (fd == -1) return(-1);
	if (fd == new) return(new);
	ret = dup2(old,new);
	close(fd);
	return(ret);
}

If you want to try it out, here's a driver program:

main(c,v) char **v;
{
	int a,b;
	(c > 2) || (printf("usage: %s srcfd dstfd\n",v[0]), exit(1));  
        a = atoi(v[1]); b = atoi(v[2]);
        printf("dup2(%d,%d) = %d\n",a,b,dup2(a,b)), exit(0);
}
-- 

	Root Boy Jim Cottrell <rbj@uunet.uu.net>
	I got a head full of ideas
	They're driving me insane

rbj@uunet.UU.NET (Root Boy Jim) (02/08/91)

In article <1991Feb8.002436.21328@usenet.ins.cwru.edu> chet@po.CWRU.Edu writes:
>Doug Schmidt writes:
>
>>	I'm curious, is it possible to implement the dup2() system
>>call using only routines available in the standard C library and other
>>existing system calls?
>
>When you really get down to it.  the kernel is going to have to do the
>nitty-gritty duplication for you, otherwise it gets tricky.  If you have
>an fcntl(..., F_DUPFD, ...), it's straightforward. 
>
>Here's how we do it in bash:

[You may see this twice. Sorry if you do]

Well, a clear case of two left feet not knowing what the right hand is
doing. The solution is trivial once you grasp the recursive nature of it.

I was gonna tell y'all to look in ~emacs/src/sysdep.c, but the
version there is broken! I feel like a fool, as I sent it off to
Larry Wall. Dup and dup2 can fail, altho people treat it as if it can't.
I just debugged the following routine. Appended is a test driver.

dup2(old,new)
{
#ifdef F_DUPFD
	close(new);
	return(fcntl(old, F_DUPFD, new));
#else
        register int fd, ret;
        close(new);
        fd = dup(old);
        if (fd == -1) return(-1);
        if (fd == new) return(new);
        ret = dup2(old,new);
        close(fd);
        return(ret);
#endif
}

main(c,v) char *v[];
{
        register int a,b;
        (c > 2) || (printf("usage: %s srcfd dstfd\n",v[0]),exit(1));
        a = atoi(v[1]); b = atoi(v[2]);
        printf("dup2(%d,%d) = %d\n",a,b,dup2(a,b));
}
-- 

	Root Boy Jim Cottrell <rbj@uunet.uu.net>
	I got a head full of ideas
	They're driving me insane

jpr@jpradley.jpr.com (Jean-Pierre Radley) (02/09/91)

In article <27B1CA1C.22559@ics.uci.edu> schmidt@ics.uci.edu (Doug Schmidt) writes:
>	I'm curious, is it possible to implement the dup2() system
>call using only routines available in the standard C library and other
>existing system calls?

From the xcmalt sources:

#if !DUP2		/* For those that do not have dup2() */
#include <fcntl.h>
dup2(oldfd, newfd)
int oldfd, newfd;
{
   if (fcntl(oldfd, F_GETFL, 0) == -1)      /* Valid file descriptor? */
       return (-1);                         /* No, return an error */
   close(newfd);                            /* Ensure newfd is closed */
   return (fcntl(oldfd, F_DUPFD, newfd));   /* Dup oldfd into newfd */
}
#endif /* !DUP2Thanks to Bill Allie CIS: 76703,2061 */

 Jean-Pierre Radley   NYC Public Unix   jpr@jpradley.jpr.com   CIS: 72160,1341

gwyn@smoke.brl.mil (Doug Gwyn) (02/09/91)

In article <27B1CA1C.22559@ics.uci.edu> schmidt@ics.uci.edu (Doug Schmidt) writes:
>	I'm curious, is it possible to implement the dup2() system
>call using only routines available in the standard C library and other
>existing system calls?

/*
	dup2 -- 7th Edition UNIX system call emulation for UNIX System V

	last edit:	11-Feb-1987	D A Gwyn
*/

#include	<errno.h>
#include	<fcntl.h>

extern int	close(), fcntl();

int
dup2( oldfd, newfd )
	int		oldfd;		/* already-open file descriptor */
	int		newfd;		/* desired duplicate descriptor */
{
	register int	ret;		/* for fcntl() return value */
	register int	save;		/* for saving entry errno */

	if ( oldfd == newfd )
		return oldfd;		/* be careful not to close() */

	save = errno;			/* save entry errno */
	(void) close( newfd );		/* in case newfd is open */
	/* (may have just clobbered the original errno value) */

	ret = fcntl( oldfd, F_DUPFD, newfd );	/* dupe it */

	if ( ret >= 0 )
		errno = save;		/* restore entry errno */
	else				/* fcntl() returned error */
		if ( errno == EINVAL )
			errno = EBADF;	/* we think of everything */

	return ret;			/* return file descriptor */
}

rbj@uunet.UU.NET (Root Boy Jim) (02/12/91)

In article <15136@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
>/*
>	dup2 -- 7th Edition UNIX system call emulation for UNIX System V
>*/

Do you mean that V7 had dup2 but System V doesn't?

And what about systems that don't have fcntl?

BTW, folks, I missed the case where newfd==oldfd. My (mis)reading
of the manual led me to believe that you should get nothing but an error.
In any case, it's a trivial fix.
-- 
	Root Boy Jim Cottrell <rbj@uunet.uu.net>
	I got a head full of ideas
	They're driving me insane

richard@locus.com (Richard M. Mathews) (02/12/91)

I have not yet seen a correct version of code for dup2() posted.  The
versions posted by Paul Falsted, Doug Gwyn, and Root Boy Jim all fail
the following test case because they close "fd2" too soon:
	main()
	{
		dup2(-1,fileno(stdout));  /* this returns -1 as expected */
		printf("stdout *should* still be OK\n");
	}
Only the version posted by Chet Ramey gets this right, but only Doug
Gwyn's version does everything right with errno, including mapping
EINVAL to EBADF.

I would post what I consider to be the right answer, but I have licensing
problems.  I can recommend taking ideas from both Gwyn and Ramey; perhaps
one of them would be willing to post the result of such a merge.

Richard M. Mathews			 Freedom for Lithuania
richard@locus.com				Laisve!
lcc!richard@seas.ucla.edu
...!{uunet|ucla-se|turnkey}!lcc!richard

guy@auspex.auspex.com (Guy Harris) (02/13/91)

>Do you mean that V7 had dup2 but System V doesn't?

Yes.  For whatever reason, that particular branch of the family didn't
have "dup2()"; it did, however, have "fcntl()" and the F_DUPFD
subfunction thereof, at least as far back as S3 (I don't remember
whether earlier members of that branch had it).

S5R3 picked up "dup2()", which is in POSIX; 4.2BSD picked up "fcntl()",
which is also in POSIX.

>And what about systems that don't have fcntl?

If you have neither "fcntl()" nor "dup2()", you're not dealing with a
UNIX, or lookalike, that does a reasonable job of looking like a modern
UNIX (it's not as modern as V7, even), in which case 1) you may have a
problem if you need "dup2()"-like functionality (you might have to
repeatedly "dup()" the file descriptor until it reaches the desired
value, and then close all the intermediate descriptors) and 2) you
probably have a bunch of other problems as well. 

chet@odin.INS.CWRU.Edu (Chet Ramey) (02/13/91)

In article <richard.666334818@fafnir.la.locus.com> richard@locus.com (Richard M. Mathews) writes:

>I would post what I consider to be the right answer, but I have licensing
>problems.  I can recommend taking ideas from both Gwyn and Ramey; perhaps
>one of them would be willing to post the result of such a merge.

How about this (apologies for the Gnu coding style)?  (As an aside, I do not
think that the second fcntl will ever return EINVAL, since it only returns
that error for out-of-range values (< 0 or >= getdtablesize()), and that
case is already handled explicitly.)

dup2 (fd1, fd2)
     int fd1, fd2;
{
  int saved_errno, r;

  if (fcntl (fd1, F_GETFL, 0) == -1)    /* fd1 is an invalid fd */
    return (-1);

  if (fd2 < 0 || fd2 >= getdtablesize ())	/* This could be removed. */
    {
      errno = EBADF;
      return (-1);
    }

  if (fd1 == fd2)
    return (0);

  saved_errno = errno;
  (void) close (fd2);
  r = fcntl (fd1, F_DUPFD, fd2);

  if (r >= 0)
    errno = saved_errno;
  else
    {
      if (errno == EINVAL)
	errno = EBADF;
    }

  return (r);
}

Chet
-- 
Chet Ramey				``There's just no surf in
Network Services Group			  Cleveland, U.S.A. ...''
Case Western Reserve University
chet@ins.CWRU.Edu		My opinions are just those, and mine alone.

gwyn@smoke.brl.mil (Doug Gwyn) (02/14/91)

In article <122373@uunet.UU.NET> rbj@uunet.UU.NET (Root Boy Jim) writes:
-In article <15136@smoke.brl.mil> gwyn@smoke.brl.mil (Doug Gwyn) writes:
->/*
->	dup2 -- 7th Edition UNIX system call emulation for UNIX System V
->*/
-Do you mean that V7 had dup2 but System V doesn't?
-And what about systems that don't have fcntl?

Geez, what do you think the comment means, anyway?

sbs@ciara.Frame.COM (Steven Sargent) (02/20/91)

dup2(old, new)
{
	return dup(old+64, new);
}

... depends on an undocumented interface; your mileage may
vary; guaranteed to lose in SunOS 4.1 (but you can use real
dup2 there); doubtless will lose other places that have wide
u.u_ofile; but amusing nonetheless.
-- 
Steven Sargent sbs@frame.com	"Frame's phone bill, my opinions."

forrie@morwyn.UUCP (Forrie Aldrich) (04/17/91)

Brandon S. Allbery writes:
| 
| As quoted from <2180@estevax.UUCP> by iain@estevax.UUCP (Hr Iain Lea):
| +---------------
| | I am porting a program from BSD to a Sys5r2 ish derivative and need the
| | dup2() function call.
| +---------------
| 
| Is this one in the FAQ?
| 
| /*
|  * Near-duplicate for dup2().  ("Near"?  We discussed this for a whole bloody
|  * month --- I don't want to discuss it any more.  BSD and System V have
|  * enough differing errno values that that part is pointless anyway.)
|  *
|  * Caveats: doesn't necessarily return the same errno values on failure; does
|  * not leave f2 open if the dup fails.
|  */
| 
| #include <ioctl.h>
| #include <errno.h>
| 
| #define dup2(f1,f2) (close(f2),fcntl(f1,F_DUPFD,f2))
| 
| ++Brandon
[...]

Well, for reference's sake, here's another version that I caught off the net
a little while ago... hope it helps.

/*
	dup2 -- 7th Edition UNIX system call emulation for UNIX System V

	last edit:	11-Feb-1987	D A Gwyn
*/

#include	<errno.h>
#include	<fcntl.h>

extern int	close(), fcntl();

int
dup2( oldfd, newfd )
	int		oldfd;		/* already-open file descriptor */
	int		newfd;		/* desired duplicate descriptor */
{
	register int	ret;		/* for fcntl() return value */
	register int	save;		/* for saving entry errno */

	if ( oldfd == newfd )
		return oldfd;		/* be careful not to close() */

	save = errno;			/* save entry errno */
	(void) close( newfd );		/* in case newfd is open */
	/* (may have just clobbered the original errno value) */

	ret = fcntl( oldfd, F_DUPFD, newfd );	/* dupe it */

	if ( ret >= 0 )
		errno = save;		/* restore entry errno */
	else				/* fcntl() returned error */
		if ( errno == EINVAL )
			errno = EBADF;	/* we think of everything */

	return ret;			/* return file descriptor */
}

--------------------=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--------------------
Forrest Aldrich, Jr.|   ...uunet!eci!morwyn!forrie       |forrie@morywn.UUCP
                    |           <email paths>            | 
CREATIVE CONNECTIONS|  ...uunet!zinn!eci!morwyn!forrie   |Graphic Illustration
------------------\-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=/------------------
                   \___ PO Box 1541 - Dover, NH  03820 ___/                   
-- 

--------------------=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--------------------
Forrest Aldrich, Jr.|   ...uunet!eci!morwyn!forrie       |forrie@morywn.UUCP
                    |           <email paths>            | 
CREATIVE CONNECTIONS|  ...uunet!zinn!eci!morwyn!forrie   |Graphic Illustration
------------------\-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=/------------------
                   \___ PO Box 1541 - Dover, NH  03820 ___/                   

allbery@NCoast.ORG (Brandon S. Allbery KB8JRR/AA) (04/21/91)

As quoted from <85@morwyn.UUCP> by forrie@morwyn.UUCP (Forrie Aldrich):
+---------------
| Well, for reference's sake, here's another version that I caught off the net
| a little while ago... hope it helps.
+---------------

(Doug Gwyn's dup2() omitted)

Reference's sake, yes --- but that was one that got some remarks about
differing errno values.  (Which is why I had that remark in my comments.)

For almost every use of dup2() I've ever encountered, the macro does the job.
If you absolutely NEED 100% V7 compatibility, use Doug's by all means.

++Brandon
-- 
Me: Brandon S. Allbery			  Ham: KB8JRR/AA on 2m, 220, 440, 1200
Internet: allbery@NCoast.ORG		(QRT on HF until local problems fixed)
America OnLine: KB8JRR // Delphi: ALLBERY   AMPR: kb8jrr.AmPR.ORG [44.70.4.88]
uunet!usenet.ins.cwru.edu!ncoast!allbery          KB8JRR @ WA8BXN.OH

richard@locus.com (Richard M. Mathews) (04/23/91)

allbery@NCoast.ORG (Brandon S. Allbery KB8JRR/AA) writes:

>(Doug Gwyn's dup2() omitted)

>Reference's sake, yes --- but that was one that got some remarks about
>differing errno values.  (Which is why I had that remark in my comments.)

Actually, the problem with Gwyn's version is the behavior when, on entry
to the routine,
	newfd is a valid descriptor for an open file
	oldfd is not a valid descriptor for an open file
A real dup2 (as on V7) will return EBADF and leave newfd alone.
Gwyn's will return EBADF and close newfd.

>For almost every use of dup2() I've ever encountered, the macro does the job.
>If you absolutely NEED 100% V7 compatibility, use Doug's by all means.

I had problems with the super-simple-minded version of dup2 when porting
the BSD4.1 C Shell.  I don't remember if it was the above problem or the
problem with newfd==oldfd (I think it was the latter, and Gwyn's addresses
that).

Richard M. Mathews			Lietuva laisva = Free Lithuania
richard@locus.com			Brivu Latviju  = Free Latvia
lcc!richard@seas.ucla.edu		Eesti vabaks   = Free Estonia
...!{uunet|ucla-se|turnkey}!lcc!richard