/* $RCSfile: doio.c,v $$Revision: 4.0.1.6 $$Date: 92/06/11 21:08:16 $
* Copyright (c) 1991, Larry Wall
* You may distribute under the terms of either the GNU General Public
* License or the Artistic License, as specified in the README file.
* Revision 4.0.1.6 92/06/11 21:08:16 lwall
* patch34: some systems don't declare h_errno extern in header files
* Revision 4.0.1.5 92/06/08 13:00:21 lwall
* patch20: some machines don't define ENOTSOCK in errno.h
* patch20: new warnings for failed use of stat operators on filenames with \n
* patch20: wait failed when STDOUT or STDERR reopened to a pipe
* patch20: end of file latch not reset on reopen of STDIN
* patch20: seek(HANDLE, 0, 1) went to eof because of ancient Ultrix workaround
* patch20: fixed memory leak on system() for vfork() machines
* patch20: get*by* routines now return something useful in a scalar context
* patch20: h_errno now accessible via $?
* Revision 4.0.1.4 91/11/05 16:51:43 lwall
* patch11: prepared for ctype implementations that don't define isascii()
* patch11: perl mistook some streams for sockets because they return mode 0 too
* patch11: reopening STDIN, STDOUT and STDERR failed on some machines
* patch11: certain perl errors should set EBADF so that $! looks better
* patch11: truncate on a closed filehandle could dump
* patch11: stats of _ forgot whether prior stat was actually lstat
* patch11: -T returned true on NFS directory
* Revision 4.0.1.3 91/06/10 01:21:19 lwall
* patch10: read didn't work from character special files open for writing
* patch10: close-on-exec wrongly set on system file descriptors
* Revision 4.0.1.2 91/06/07 10:53:39 lwall
* patch4: new copyright notice
* patch4: system fd's are now treated specially
* patch4: added $^F variable to specify maximum system fd, default 2
* patch4: character special files now opened with bidirectional stdio buffers
* patch4: taintchecks could improperly modify parent in vfork()
* patch4: many, many itty-bitty portability fixes
* Revision 4.0.1.1 91/04/11 17:41:06 lwall
* patch1: hopefully straightened out some of the Xenix mess
* Revision 4.0 91/03/20 01:07:06 lwall
#if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM)
static char* warn_nl
= "Unsuccessful %s on filename containing newline";
register STIO
*stio
= stab_io(stab
);
char *myname
= savestr(name
);
char mode
[3]; /* stdio file mode ("r\0" or "r+\0") */
mode
[0] = mode
[1] = mode
[2] = '\0';
forkprocess
= 1; /* assume true if no fork */
while (len
&& isSPACE(name
[len
-1]))
stio
= stab_io(stab
) = stio_new();
else if (fd
<= maxsysfd
) {
else if (stio
->type
== '|')
result
= mypclose(stio
->ifp
);
else if (stio
->ifp
!= stio
->ofp
) {
result
= fclose(stio
->ofp
);
fclose(stio
->ifp
); /* clear stdio, fd already closed */
result
= fclose(stio
->ifp
);
result
= fclose(stio
->ifp
);
if (result
== EOF
&& fd
> maxsysfd
)
fprintf(stderr
,"Warning: unable to close filehandle %s properly.\n",
stio
->ofp
= stio
->ifp
= Nullfp
;
if (*name
== '+' && len
> 1 && name
[len
-1] != '|') { /* scary */
for (name
++; isSPACE(*name
); name
++) ;
taintproper("Insecure dependency in piped open");
taintproper("Insecure dependency in open");
mode
[0] = stio
->type
= 'a';
stab
= stabent(name
,FALSE
);
if (!stab
|| !stab_io(stab
)) {
if (stab_io(stab
) && stab_io(stab
)->ifp
) {
fd
= fileno(stab_io(stab
)->ifp
);
if (stab_io(stab
)->type
== 's')
if (!(fp
= fdopen(fd
= dup(fd
),mode
))) {
else if (name
[len
-1] == '|') {
taintproper("Insecure dependency in piped open");
while (len
&& isSPACE(name
[len
-1]))
for (; isSPACE(*name
); name
++) ;
for (; isSPACE(*name
); name
++) ;
if (dowarn
&& stio
->type
== '<' && index(name
, '\n'))
stio
->type
!= '|' && stio
->type
!= '-') {
if (fstat(fileno(fp
),&statbuf
) < 0) {
if (S_ISSOCK(statbuf
.st_mode
))
stio
->type
= 's'; /* in case a socket was passed in to us */
!(statbuf
.st_mode
& S_IFMT
)
int buflen
= sizeof tokenbuf
;
if (getsockname(fileno(fp
), tokenbuf
, &buflen
) >= 0
stio
->type
= 's'; /* some OS's return 0 on fstat()ed socket */
/* but some return 0 for streams too, sigh */
if (saveifp
) { /* must use old fp? */
fflush(saveofp
); /* emulate fclose() */
if (saveofp
!= saveifp
) { /* was a socket? */
str
= afetch(fdpid
,fileno(fp
),TRUE
);
pid
= str
->str_u
.str_useful
;
str
->str_u
.str_useful
= 0;
str
= afetch(fdpid
,fd
,TRUE
);
str
->str_u
.str_useful
= pid
;
#if defined(HAS_FCNTL) && defined(F_SETFD)
fcntl(fd
,F_SETFD
,fd
> maxsysfd
);
|| (stio
->type
== '>' && S_ISCHR(statbuf
.st_mode
)) ) {
if (!(stio
->ofp
= fdopen(fileno(fp
),"w"))) {
argvoutstab
= stabent("ARGVOUT",TRUE
);
if (filemode
& (S_ISUID
|S_ISGID
)) {
fflush(stab_io(argvoutstab
)->ifp
); /* chmod must follow last write */
(void)fchmod(lastfd
,filemode
);
(void)chmod(oldname
,filemode
);
while (alen(stab_xarray(stab
)) >= 0) {
str
= ashift(stab_xarray(stab
));
str_sset(stab_val(stab
),str
);
oldname
= str_get(stab_val(stab
));
if (do_open(stab
,oldname
,stab_val(stab
)->str_cur
)) {
taintproper("Insecure dependency in inplace open");
if (strEQ(oldname
,"-")) {
defoutstab
= stabent("STDOUT",TRUE
);
return stab_io(stab
)->ifp
;
filedev
= statbuf
.st_dev
;
fileino
= statbuf
.st_ino
;
filemode
= statbuf
.st_mode
;
fileuid
= statbuf
.st_uid
;
filegid
= statbuf
.st_gid
;
if (!S_ISREG(filemode
)) {
warn("Can't do inplace edit: %s is not a regular file",
if (stat(str
->str_ptr
,&statbuf
) >= 0
&& statbuf
.st_dev
== filedev
&& statbuf
.st_ino
== fileino
) {
warn("Can't do inplace edit: %s > 14 characters",
if (rename(oldname
,str
->str_ptr
) < 0) {
warn("Can't rename %s to %s: %s, skipping file",
oldname
, str
->str_ptr
, strerror(errno
) );
(void)unlink(str
->str_ptr
);
(void)rename(oldname
,str
->str_ptr
);
do_open(stab
,str
->str_ptr
,stab_val(stab
)->str_cur
);
(void)UNLINK(str
->str_ptr
);
if (link(oldname
,str
->str_ptr
) < 0) {
warn("Can't rename %s to %s: %s, skipping file",
oldname
, str
->str_ptr
, strerror(errno
) );
if (UNLINK(oldname
) < 0) {
warn("Can't rename %s to %s: %s, skipping file",
oldname
, str
->str_ptr
, strerror(errno
) );
fatal("Can't do inplace edit without backup");
errno
= 0; /* in case sprintf set errno */
if (!do_open(argvoutstab
,str
->str_ptr
,str
->str_cur
)) {
warn("Can't do inplace edit on %s: %s",
oldname
, strerror(errno
) );
defoutstab
= argvoutstab
;
lastfd
= fileno(stab_io(argvoutstab
)->ifp
);
(void)fstat(lastfd
,&statbuf
);
(void)fchmod(lastfd
,filemode
);
(void)chmod(oldname
,filemode
);
if (fileuid
!= statbuf
.st_uid
|| filegid
!= statbuf
.st_gid
) {
(void)fchown(lastfd
,fileuid
,filegid
);
(void)chown(oldname
,fileuid
,filegid
);
return stab_io(stab
)->ifp
;
fprintf(stderr
,"Can't open %s: %s\n",str_get(str
), strerror(errno
));
(void)do_close(argvoutstab
,FALSE
);
defoutstab
= stabent("STDOUT",TRUE
);
do_pipe(str
, rstab
, wstab
)
rstio
= stab_io(rstab
) = stio_new();
wstio
= stab_io(wstab
) = stio_new();
rstio
->ifp
= fdopen(fd
[0], "r");
wstio
->ofp
= fdopen(fd
[1], "w");
if (!rstio
->ifp
|| !wstio
->ofp
) {
if (rstio
->ifp
) fclose(rstio
->ifp
);
if (wstio
->ofp
) fclose(wstio
->ofp
);
str_sset(str
,&str_undef
);
if (!stio
) { /* never opened */
warn("Close on unopened file <%s>",stab_ename(stab
));
status
= mypclose(stio
->ifp
);
statusvalue
= (unsigned short)status
& 0xffff;
else if (stio
->type
== '-')
if (stio
->ofp
&& stio
->ofp
!= stio
->ifp
) { /* a socket */
retval
= (fclose(stio
->ofp
) != EOF
);
fclose(stio
->ifp
); /* clear stdio, fd already closed */
retval
= (fclose(stio
->ifp
) != EOF
);
stio
->ofp
= stio
->ifp
= Nullfp
;
stio
= stab_io(argvstab
);
#ifdef STDSTDIO /* (the code works without this) */
if (stio
->ifp
->_cnt
> 0) /* cheat a little, since */
return FALSE
; /* this is the most usual case */
(void)ungetc(ch
, stio
->ifp
);
if (stio
->ifp
->_cnt
< -1)
if (!stab
) { /* not necessarily a real EOF yet? */
if (!nextargv(argvstab
)) /* get another fp handy */
return TRUE
; /* normal fp, definitely end of file */
#ifdef ULTRIX_STDIO_BOTCH
(void)fseek (stio
->ifp
, 0L, 2); /* ultrix 1.2 workaround */
warn("tell() on unopened file");
do_seek(stab
, pos
, whence
)
#ifdef ULTRIX_STDIO_BOTCH
(void)fseek (stio
->ifp
, 0L, 2); /* ultrix 1.2 workaround */
return fseek(stio
->ifp
, pos
, whence
) >= 0;
warn("seek() on unopened file");
do_ctl(optype
,stab
,func
,argstr
)
if (!stab
|| !argstr
|| !(stio
= stab_io(stab
)) || !stio
->ifp
) {
errno
= EBADF
; /* well, sort of... */
if (argstr
->str_pok
|| !argstr
->str_nok
) {
#define IOCPARM_LEN(x) (((x) >> 16) & IOCPARM_MASK)
retval
= IOCPARM_LEN(func
); /* on BSDish systes we're safe */
retval
= 256; /* otherwise guess at what's safe */
if (argstr
->str_cur
< retval
) {
Str_Grow(argstr
,retval
+1);
argstr
->str_cur
= retval
;
s
[argstr
->str_cur
] = 17; /* a little sanity check here */
retval
= (int)str_gnum(argstr
);
s
= (char*)(long)retval
; /* ouch */
s
= (char*)retval
; /* ouch */
retval
= ioctl(fileno(stio
->ifp
), func
, s
);
fatal("fcntl is not implemented");
retval
= fcntl(fileno(stio
->ifp
), func
, s
);
fatal("fcntl is not implemented");
if (s
[argstr
->str_cur
] != 17)
fatal("Return value overflowed string");
s
[argstr
->str_cur
] = 0; /* put our null back */
do_stat(str
,arg
,gimme
,arglast
)
register ARRAY
*ary
= stack
;
register int sp
= arglast
[0] + 1;
if ((arg
[1].arg_type
& A_MASK
) == A_WORD
) {
tmpstab
= arg
[1].arg_ptr
.arg_stab
;
if (tmpstab
!= defstab
) {
if (!stab_io(tmpstab
) || !stab_io(tmpstab
)->ifp
||
fstat(fileno(stab_io(tmpstab
)->ifp
),&statcache
) < 0) {
else if (laststatval
< 0)
str_set(statname
,str_get(ary
->ary_array
[sp
]));
laststype
= arg
->arg_type
;
if (arg
->arg_type
== O_LSTAT
)
laststatval
= lstat(str_get(statname
),&statcache
);
laststatval
= stat(str_get(statname
),&statcache
);
if (dowarn
&& index(str_get(statname
), '\n'))
str_sset(str
,&str_undef
);
ary
->ary_array
[sp
] = str
;
str_2mortal(str_nmake((double)statcache
.st_dev
)));
str_2mortal(str_nmake((double)statcache
.st_ino
)));
str_2mortal(str_nmake((double)statcache
.st_mode
)));
str_2mortal(str_nmake((double)statcache
.st_nlink
)));
str_2mortal(str_nmake((double)statcache
.st_uid
)));
str_2mortal(str_nmake((double)statcache
.st_gid
)));
str_2mortal(str_nmake((double)statcache
.st_rdev
)));
str_2mortal(str_nmake((double)statcache
.st_size
)));
str_2mortal(str_nmake((double)statcache
.st_atime
)));
str_2mortal(str_nmake((double)statcache
.st_mtime
)));
str_2mortal(str_nmake((double)statcache
.st_ctime
)));
str_2mortal(str_nmake((double)statcache
.st_blksize
)));
str_2mortal(str_nmake((double)statcache
.st_blocks
)));
str_2mortal(str_make("",0)));
str_2mortal(str_make("",0)));
(void)astore(ary
,++sp
,str_nmake(0.0));
#if !defined(HAS_TRUNCATE) && !defined(HAS_CHSIZE) && defined(F_FREESP)
/* code courtesy of William Kucharski */
int fd
; /* file descriptor */
off_t length
; /* length to set file to */
if (fstat(fd
, &filebuf
) < 0)
if (filebuf
.st_size
< length
) {
if ((lseek(fd
, (length
- 1), 0)) < 0)
if ((write(fd
, "", 1)) != 1)
fl
.l_type
= F_WRLCK
; /* write lock on file space */
* This relies on the UNDOCUMENTED F_FREESP argument to
* fcntl(2), which truncates the file so that it ends at the
* position indicated by fl.l_start.
* Will minor miracles never cease?
if (fcntl(fd
, F_FREESP
, &fl
) < 0)
do_truncate(str
,arg
,gimme
,arglast
)
register ARRAY
*ary
= stack
;
register int sp
= arglast
[0] + 1;
off_t len
= (off_t
)str_gnum(ary
->ary_array
[sp
+1]);
#if defined(HAS_TRUNCATE) || defined(HAS_CHSIZE)
if ((arg
[1].arg_type
& A_MASK
) == A_WORD
) {
tmpstab
= arg
[1].arg_ptr
.arg_stab
;
if (!stab_io(tmpstab
) || !stab_io(tmpstab
)->ifp
||
ftruncate(fileno(stab_io(tmpstab
)->ifp
), len
) < 0)
else if (truncate(str_get(ary
->ary_array
[sp
]), len
) < 0)
if ((arg
[1].arg_type
& A_MASK
) == A_WORD
) {
tmpstab
= arg
[1].arg_ptr
.arg_stab
;
if (!stab_io(tmpstab
) || !stab_io(tmpstab
)->ifp
||
chsize(fileno(stab_io(tmpstab
)->ifp
), len
) < 0)
if ((tmpfd
= open(str_get(ary
->ary_array
[sp
]), 0)) < 0)
if (chsize(tmpfd
, len
) < 0)
str_sset(str
,&str_undef
);
ary
->ary_array
[sp
] = str
;
fatal("truncate not implemented");
if (*s
== '+' || *s
== '-')
else if (s
== str
->str_ptr
)
if (*s
== 'e' || *s
== 'E') {
if (*s
== '+' || *s
== '-')
warn("print to unopened file");
((str
->str_nok
&& str
->str_u
.str_nval
!= 0.0)
|| (looks_like_number(str
) && str_gnum(str
) != 0.0) ) ) {
fprintf(fp
, ofmt
, str
->str_u
.str_nval
);
if (*tmps
== 'S' && tmps
[1] == 't' && tmps
[2] == 'B' && tmps
[3] == '\0'
&& str
->str_cur
== sizeof(STBP
) && strlen(tmps
) < str
->str_cur
) {
STR
*tmpstr
= str_mortal(&str_undef
);
stab_efullname(tmpstr
,((STAB
*)str
));/* a stab value, be nice */
if (str
->str_cur
&& (fwrite(tmps
,1,str
->str_cur
,fp
) == 0 || ferror(fp
)))
do_aprint(arg
,fp
,arglast
)
register STR
**st
= stack
->ary_array
;
register int sp
= arglast
[1];
register int items
= arglast
[2] - sp
;
warn("print to unopened file");
if (arg
->arg_type
== O_PRTF
) {
do_sprintf(arg
->arg_ptr
.arg_str
,items
,st
);
retval
= do_print(arg
->arg_ptr
.arg_str
,fp
);
for (; items
> 0; items
--,st
++) {
if (fwrite(ofs
, 1, ofslen
, fp
) == 0 || ferror(fp
)) {
if (!(retval
= do_print(*st
, fp
)))
if (fwrite(ors
, 1, orslen
, fp
) == 0 || ferror(fp
))
if (arg
[1].arg_type
& A_DONT
) {
stio
= stab_io(arg
[1].arg_ptr
.arg_stab
);
statstab
= arg
[1].arg_ptr
.arg_stab
;
return (laststatval
= fstat(fileno(stio
->ifp
), &statcache
));
if (arg
[1].arg_ptr
.arg_stab
== defstab
)
warn("Stat on unopened file <%s>",
stab_ename(arg
[1].arg_ptr
.arg_stab
));
return (laststatval
= -1);
str_set(statname
,str_get(str
));
laststatval
= stat(str_get(str
),&statcache
);
if (laststatval
< 0 && dowarn
&& index(str_get(str
), '\n'))
if (arg
[1].arg_type
& A_DONT
) {
if (arg
[1].arg_ptr
.arg_stab
== defstab
) {
if (laststype
!= O_LSTAT
)
fatal("The stat preceding -l _ wasn't an lstat");
fatal("You can't use -l on a filehandle");
str_set(statname
,str_get(str
));
laststatval
= lstat(str_get(str
),&statcache
);
laststatval
= stat(str_get(str
),&statcache
);
if (laststatval
< 0 && dowarn
&& index(str_get(str
), '\n'))
if (arg
[1].arg_type
& A_DONT
) {
if (arg
[1].arg_ptr
.arg_stab
== defstab
) {
stio
= stab_io(statstab
);
statstab
= arg
[1].arg_ptr
.arg_stab
;
stio
= stab_io(statstab
);
#if defined(STDSTDIO) || defined(atarist) /* this will work with atariST */
fstat(fileno(stio
->ifp
),&statcache
);
if (S_ISDIR(statcache
.st_mode
)) /* handle NFS glitch */
return arg
->arg_type
== O_FTTEXT
? &str_no
: &str_yes
;
if (stio
->ifp
->_cnt
<= 0) {
(void)ungetc(i
,stio
->ifp
);
if (stio
->ifp
->_cnt
<= 0) /* null file is anything */
len
= stio
->ifp
->_cnt
+ (stio
->ifp
->_ptr
- stio
->ifp
->_base
);
fatal("-T and -B not implemented on filehandles");
warn("Test on unopened file <%s>",
stab_ename(arg
[1].arg_ptr
.arg_stab
));
str_set(statname
,str_get(str
));
i
= open(str_get(str
),0);
if (dowarn
&& index(str_get(str
), '\n'))
if (S_ISDIR(statcache
.st_mode
) && arg
->arg_type
== O_FTTEXT
)
return &str_no
; /* special case NFS directories */
return &str_yes
; /* null file is anything */
/* now scan s to look for textiness */
for (i
= 0; i
< len
; i
++,s
++) {
if (!*s
) { /* null never allowed in text */
*s
!= '\n' && *s
!= '\r' && *s
!= '\b' &&
*s
!= '\t' && *s
!= '\f' && *s
!= 27)
if ((odd
* 10 > len
) == (arg
->arg_type
== O_FTTEXT
)) /* allow 10% odd */
static char **Argv
= Null(char **);
static char *Cmd
= Nullch
;
register STR
**st
= stack
->ary_array
;
register int sp
= arglast
[1];
register int items
= arglast
[2] - sp
;
New(401,Argv
, items
+1, char*);
for (st
+= ++sp
; items
> 0; items
--,st
++) {
if (*Argv
[0] != '/') /* will execvp use PATH? */
taintenv(); /* testing IFS here is overkill, probably */
if (really
&& *(tmps
= str_get(really
)))
/* save an extra exec if possible */
if (strnEQ(cmd
,cshname
,cshlen
) && strnEQ(cmd
+cshlen
," -c",3)) {
execl(cshname
,"csh", flags
,ncmd
,(char*)0);
/* see if there are shell metacharacters in it */
for (s
= cmd
; *s
&& isALPHA(*s
); s
++) ; /* catch VAR=val gizmo */
if (*s
!= ' ' && !isALPHA(*s
) && index("$&*(){}[]'\";\\|?<>~`\n",*s
)) {
if (*s
== '\n' && !s
[1]) {
execl("/bin/sh","sh","-c",cmd
,(char*)0);
New(402,Argv
, (s
- cmd
) / 2 + 2, char*);
Cmd
= nsavestr(cmd
, s
-cmd
);
while (*s
&& isSPACE(*s
)) s
++;
while (*s
&& !isSPACE(*s
)) s
++;
if (errno
== ENOEXEC
) { /* for system V NIH syndrome */
register STR
**st
= stack
->ary_array
;
register int sp
= arglast
[1];
int domain
, type
, protocol
, fd
;
stio
= stab_io(stab
) = stio_new();
domain
= (int)str_gnum(st
[++sp
]);
type
= (int)str_gnum(st
[++sp
]);
protocol
= (int)str_gnum(st
[++sp
]);
taintproper("Insecure dependency in socket");
fd
= socket(domain
,type
,protocol
);
stio
->ifp
= fdopen(fd
, "r"); /* stdio gets confused about sockets */
stio
->ofp
= fdopen(fd
, "w");
if (!stio
->ifp
|| !stio
->ofp
) {
if (stio
->ifp
) fclose(stio
->ifp
);
if (stio
->ofp
) fclose(stio
->ofp
);
if (!stio
->ifp
&& !stio
->ofp
) close(fd
);
register STR
**st
= stack
->ary_array
;
register int sp
= arglast
[1];
addr
= str_get(st
[++sp
]);
taintproper("Insecure dependency in bind");
return bind(fileno(stio
->ifp
), addr
, st
[sp
]->str_cur
) >= 0;
warn("bind() on closed fd");
do_connect(stab
, arglast
)
register STR
**st
= stack
->ary_array
;
register int sp
= arglast
[1];
addr
= str_get(st
[++sp
]);
taintproper("Insecure dependency in connect");
return connect(fileno(stio
->ifp
), addr
, st
[sp
]->str_cur
) >= 0;
warn("connect() on closed fd");
register STR
**st
= stack
->ary_array
;
register int sp
= arglast
[1];
backlog
= (int)str_gnum(st
[++sp
]);
return listen(fileno(stio
->ifp
), backlog
) >= 0;
warn("listen() on closed fd");
do_accept(str
, nstab
, gstab
)
if (!gstio
|| !gstio
->ifp
)
nstio
= stab_io(nstab
) = stio_new();
fd
= accept(fileno(gstio
->ifp
),(struct sockaddr
*)buf
,&len
);
nstio
->ifp
= fdopen(fd
, "r");
nstio
->ofp
= fdopen(fd
, "w");
if (!nstio
->ifp
|| !nstio
->ofp
) {
if (nstio
->ifp
) fclose(nstio
->ifp
);
if (nstio
->ofp
) fclose(nstio
->ofp
);
if (!nstio
->ifp
&& !nstio
->ofp
) close(fd
);
warn("accept() on closed fd");
str_sset(str
,&str_undef
);
do_shutdown(stab
, arglast
)
register STR
**st
= stack
->ary_array
;
register int sp
= arglast
[1];
how
= (int)str_gnum(st
[++sp
]);
return shutdown(fileno(stio
->ifp
), how
) >= 0;
warn("shutdown() on closed fd");
do_sopt(optype
, stab
, arglast
)
register STR
**st
= stack
->ary_array
;
register int sp
= arglast
[1];
lvl
= (unsigned int)str_gnum(st
[sp
+1]);
optname
= (unsigned int)str_gnum(st
[sp
+2]);
st
[sp
] = str_2mortal(Str_new(22,257));
if (getsockopt(fd
, lvl
, optname
, st
[sp
]->str_ptr
,
(int*)&st
[sp
]->str_cur
) < 0)
if (setsockopt(fd
, lvl
, optname
, st
[sp
]->str_ptr
, st
[sp
]->str_cur
) < 0)
warn("[gs]etsockopt() on closed fd");
do_getsockname(optype
, stab
, arglast
)
register STR
**st
= stack
->ary_array
;
register int sp
= arglast
[1];
st
[sp
] = str_2mortal(Str_new(22,257));
if (getsockname(fd
, st
[sp
]->str_ptr
, (int*)&st
[sp
]->str_cur
) < 0)
if (getpeername(fd
, st
[sp
]->str_ptr
, (int*)&st
[sp
]->str_cur
) < 0)
warn("get{sock,peer}name() on closed fd");
do_ghent(which
,gimme
,arglast
)
register ARRAY
*ary
= stack
;
register int sp
= arglast
[0];
struct hostent
*gethostbyname();
struct hostent
*gethostbyaddr();
struct hostent
*gethostent();
if (which
== O_GHBYNAME
) {
char *name
= str_get(ary
->ary_array
[sp
+1]);
hent
= gethostbyname(name
);
else if (which
== O_GHBYADDR
) {
STR
*addrstr
= ary
->ary_array
[sp
+1];
int addrtype
= (int)str_gnum(ary
->ary_array
[sp
+2]);
char *addr
= str_get(addrstr
);
hent
= gethostbyaddr(addr
,addrstr
->str_cur
,addrtype
);
fatal("gethostent not implemented");
statusvalue
= (unsigned short)h_errno
& 0xffff;
astore(ary
, ++sp
, str
= str_mortal(&str_undef
));
if (which
== O_GHBYNAME
) {
str_nset(str
, *hent
->h_addr
, hent
->h_length
);
str_nset(str
, hent
->h_addr
, hent
->h_length
);
str_set(str
, hent
->h_name
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_set(str
, hent
->h_name
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
for (elem
= hent
->h_aliases
; *elem
; elem
++) {
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_numset(str
, (double)hent
->h_addrtype
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_numset(str
, (double)len
);
for (elem
= hent
->h_addr_list
; *elem
; elem
++) {
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_nset(str
, *elem
, len
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_nset(str
, hent
->h_addr
, len
);
(void)astore(ary
, ++sp
, str_mortal(&str_no
));
do_gnent(which
,gimme
,arglast
)
register ARRAY
*ary
= stack
;
register int sp
= arglast
[0];
struct netent
*getnetbyname();
struct netent
*getnetbyaddr();
struct netent
*getnetent();
if (which
== O_GNBYNAME
) {
char *name
= str_get(ary
->ary_array
[sp
+1]);
nent
= getnetbyname(name
);
else if (which
== O_GNBYADDR
) {
unsigned long addr
= U_L(str_gnum(ary
->ary_array
[sp
+1]));
int addrtype
= (int)str_gnum(ary
->ary_array
[sp
+2]);
nent
= getnetbyaddr((long)addr
,addrtype
);
astore(ary
, ++sp
, str
= str_mortal(&str_undef
));
str_numset(str
, (double)nent
->n_net
);
str_set(str
, nent
->n_name
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_set(str
, nent
->n_name
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
for (elem
= nent
->n_aliases
; *elem
; elem
++) {
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_numset(str
, (double)nent
->n_addrtype
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_numset(str
, (double)nent
->n_net
);
(void)astore(ary
, ++sp
, str_mortal(&str_no
));
do_gpent(which
,gimme
,arglast
)
register ARRAY
*ary
= stack
;
register int sp
= arglast
[0];
struct protoent
*getprotobyname();
struct protoent
*getprotobynumber();
struct protoent
*getprotoent();
if (which
== O_GPBYNAME
) {
char *name
= str_get(ary
->ary_array
[sp
+1]);
pent
= getprotobyname(name
);
else if (which
== O_GPBYNUMBER
) {
int proto
= (int)str_gnum(ary
->ary_array
[sp
+1]);
pent
= getprotobynumber(proto
);
astore(ary
, ++sp
, str
= str_mortal(&str_undef
));
str_numset(str
, (double)pent
->p_proto
);
str_set(str
, pent
->p_name
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_set(str
, pent
->p_name
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
for (elem
= pent
->p_aliases
; *elem
; elem
++) {
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_numset(str
, (double)pent
->p_proto
);
(void)astore(ary
, ++sp
, str_mortal(&str_no
));
do_gsent(which
,gimme
,arglast
)
register ARRAY
*ary
= stack
;
register int sp
= arglast
[0];
struct servent
*getservbyname();
struct servent
*getservbynumber();
struct servent
*getservent();
if (which
== O_GSBYNAME
) {
char *name
= str_get(ary
->ary_array
[sp
+1]);
char *proto
= str_get(ary
->ary_array
[sp
+2]);
sent
= getservbyname(name
,proto
);
else if (which
== O_GSBYPORT
) {
int port
= (int)str_gnum(ary
->ary_array
[sp
+1]);
char *proto
= str_get(ary
->ary_array
[sp
+2]);
sent
= getservbyport(port
,proto
);
astore(ary
, ++sp
, str
= str_mortal(&str_undef
));
if (which
== O_GSBYNAME
) {
str_numset(str
, (double)ntohs(sent
->s_port
));
str_numset(str
, (double)(sent
->s_port
));
str_set(str
, sent
->s_name
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_set(str
, sent
->s_name
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
for (elem
= sent
->s_aliases
; *elem
; elem
++) {
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_numset(str
, (double)ntohs(sent
->s_port
));
str_numset(str
, (double)(sent
->s_port
));
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_set(str
, sent
->s_proto
);
(void)astore(ary
, ++sp
, str_mortal(&str_no
));
register STR
**st
= stack
->ary_array
;
register int sp
= arglast
[0];
struct timeval
*tbuf
= &timebuf
;
#if BYTEORDER != 0x1234 && BYTEORDER != 0x12345678
#define ORDERBYTE (0x88888888 - BYTEORDER)
#define ORDERBYTE (0x4444 - BYTEORDER)
for (i
= 1; i
<= 3; i
++) {
#if BYTEORDER == 0x1234 || BYTEORDER == 0x12345678
growsize
= maxlen
; /* little endians can use vecs directly */
masksize
= NFDBITS
/ NBBY
;
masksize
= sizeof(long); /* documented int, everyone seems to use long */
growsize
= maxlen
+ (masksize
- (maxlen
% masksize
));
Zero(&fd_sets
[0], 4, char*);
for (i
= 1; i
<= 3; i
++) {
while (++j
<= growsize
) {
#if BYTEORDER != 0x1234 && BYTEORDER != 0x12345678
New(403, fd_sets
[i
], growsize
, char);
for (offset
= 0; offset
< growsize
; offset
+= masksize
) {
for (j
= 0, k
=ORDERBYTE
; j
< masksize
; j
++, (k
>>= 4))
fd_sets
[i
][j
+offset
] = s
[(k
% masksize
) + offset
];
if (str
->str_nok
|| str
->str_pok
) {
timebuf
.tv_sec
= (long)value
;
value
-= (double)timebuf
.tv_sec
;
timebuf
.tv_usec
= (long)(value
* 1000000.0);
tbuf
= Null(struct timeval
*);
#if BYTEORDER == 0x1234 || BYTEORDER == 0x12345678
for (i
= 1; i
<= 3; i
++) {
for (offset
= 0; offset
< growsize
; offset
+= masksize
) {
for (j
= 0, k
=ORDERBYTE
; j
< masksize
; j
++, (k
>>= 4))
s
[(k
% masksize
) + offset
] = fd_sets
[i
][j
+offset
];
st
[++sp
] = str_mortal(&str_no
);
str_numset(st
[sp
], (double)nfound
);
if (gimme
== G_ARRAY
&& tbuf
) {
value
= (double)(timebuf
.tv_sec
) +
(double)(timebuf
.tv_usec
) / 1000000.0;
st
[++sp
] = str_mortal(&str_no
);
str_numset(st
[sp
], value
);
do_spair(stab1
, stab2
, arglast
)
register STR
**st
= stack
->ary_array
;
register int sp
= arglast
[2];
int domain
, type
, protocol
, fd
[2];
stio1
= stab_io(stab1
) = stio_new();
stio2
= stab_io(stab2
) = stio_new();
domain
= (int)str_gnum(st
[++sp
]);
type
= (int)str_gnum(st
[++sp
]);
protocol
= (int)str_gnum(st
[++sp
]);
taintproper("Insecure dependency in socketpair");
if (socketpair(domain
,type
,protocol
,fd
) < 0)
fatal("Socketpair unimplemented");
stio1
->ifp
= fdopen(fd
[0], "r");
stio1
->ofp
= fdopen(fd
[0], "w");
stio2
->ifp
= fdopen(fd
[1], "r");
stio2
->ofp
= fdopen(fd
[1], "w");
if (!stio1
->ifp
|| !stio1
->ofp
|| !stio2
->ifp
|| !stio2
->ofp
) {
if (stio1
->ifp
) fclose(stio1
->ifp
);
if (stio1
->ofp
) fclose(stio1
->ofp
);
if (!stio1
->ifp
&& !stio1
->ofp
) close(fd
[0]);
if (stio2
->ifp
) fclose(stio2
->ifp
);
if (stio2
->ofp
) fclose(stio2
->ofp
);
if (!stio2
->ifp
&& !stio2
->ofp
) close(fd
[1]);
do_gpwent(which
,gimme
,arglast
)
register ARRAY
*ary
= stack
;
register int sp
= arglast
[0];
struct passwd
*getpwnam();
struct passwd
*getpwuid();
struct passwd
*getpwent();
char *name
= str_get(ary
->ary_array
[sp
+1]);
else if (which
== O_GPWUID
) {
int uid
= (int)str_gnum(ary
->ary_array
[sp
+1]);
astore(ary
, ++sp
, str
= str_mortal(&str_undef
));
str_numset(str
, (double)pwent
->pw_uid
);
str_set(str
, pwent
->pw_name
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_set(str
, pwent
->pw_name
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_set(str
, pwent
->pw_passwd
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_numset(str
, (double)pwent
->pw_uid
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_numset(str
, (double)pwent
->pw_gid
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_numset(str
, (double)pwent
->pw_change
);
str_numset(str
, (double)pwent
->pw_quota
);
str_set(str
, pwent
->pw_age
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_set(str
,pwent
->pw_class
);
str_set(str
, pwent
->pw_comment
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_set(str
, pwent
->pw_gecos
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_set(str
, pwent
->pw_dir
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_set(str
, pwent
->pw_shell
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_numset(str
, (double)pwent
->pw_expire
);
fatal("password routines not implemented");
do_ggrent(which
,gimme
,arglast
)
register ARRAY
*ary
= stack
;
register int sp
= arglast
[0];
struct group
*getgrnam();
struct group
*getgrgid();
struct group
*getgrent();
char *name
= str_get(ary
->ary_array
[sp
+1]);
else if (which
== O_GGRGID
) {
int gid
= (int)str_gnum(ary
->ary_array
[sp
+1]);
astore(ary
, ++sp
, str
= str_mortal(&str_undef
));
str_numset(str
, (double)grent
->gr_gid
);
str_set(str
, grent
->gr_name
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_set(str
, grent
->gr_name
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_set(str
, grent
->gr_passwd
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
str_numset(str
, (double)grent
->gr_gid
);
(void)astore(ary
, ++sp
, str
= str_mortal(&str_no
));
for (elem
= grent
->gr_mem
; *elem
; elem
++) {
fatal("group routines not implemented");
do_dirop(optype
,stab
,gimme
,arglast
)
#if defined(DIRENT) && defined(HAS_READDIR)
register ARRAY
*ary
= stack
;
register STR
**st
= ary
->ary_array
;
register int sp
= arglast
[1];
struct DIRENT
*readdir();
register struct DIRENT
*dp
;
if (!(stio
= stab_io(stab
)))
stio
= stab_io(stab
) = stio_new();
if (!stio
->dirp
&& optype
!= O_OPEN_DIR
)
if (!(stio
->dirp
= opendir(str_get(st
[sp
+1]))))
while (dp
= readdir(stio
->dirp
)) {
str_2mortal(str_make(dp
->d_name
,dp
->d_namlen
)));
str_2mortal(str_make(dp
->d_name
,0)));
if (!(dp
= readdir(stio
->dirp
)))
st
[sp
] = str_mortal(&str_undef
);
str_nset(st
[sp
], dp
->d_name
, dp
->d_namlen
);
str_set(st
[sp
], dp
->d_name
);
#if defined(HAS_TELLDIR) || defined(telldir)
st
[sp
] = str_mortal(&str_undef
);
str_numset(st
[sp
], (double)telldir(stio
->dirp
));
#if defined(HAS_SEEKDIR) || defined(seekdir)
st
[sp
] = str_mortal(&str_undef
);
along
= (long)str_gnum(st
[sp
+1]);
(void)seekdir(stio
->dirp
,along
);
#if defined(HAS_REWINDDIR) || defined(rewinddir)
st
[sp
] = str_mortal(&str_undef
);
(void)rewinddir(stio
->dirp
);
st
[sp
] = str_mortal(&str_undef
);
(void)closedir(stio
->dirp
);
fatal("Unimplemented directory operation");
register STR
**st
= stack
->ary_array
;
register int sp
= arglast
[1];
register int items
= arglast
[2] - sp
;
for (st
+= ++sp
; items
--; st
++)
tainted
|= (*st
)->str_tainted
;
taintproper("Insecure dependency in chmod");
val
= (int)str_gnum(st
[++sp
]);
if (chmod(str_get(st
[++sp
]),val
))
taintproper("Insecure dependency in chown");
val
= (int)str_gnum(st
[++sp
]);
val2
= (int)str_gnum(st
[++sp
]);
if (chown(str_get(st
[++sp
]),val
,val2
))
taintproper("Insecure dependency in kill");
if (*s
== 'S' && s
[1] == 'I' && s
[2] == 'G')
if (!(val
= whichsig(s
)))
fatal("Unrecognized signal name \"%s\"",s
);
val
= (int)str_gnum(st
[sp
]);
int proc
= (int)str_gnum(st
[++sp
]);
if (killpg(proc
,val
)) /* BSD */
if (kill(-proc
,val
)) /* SYSV */
if (kill((int)(str_gnum(st
[++sp
])),val
))
taintproper("Insecure dependency in unlink");
else { /* don't let root wipe out directories without -U */
if (lstat(s
,&statbuf
) < 0 || S_ISDIR(statbuf
.st_mode
))
if (stat(s
,&statbuf
) < 0 || S_ISDIR(statbuf
.st_mode
))
taintproper("Insecure dependency in utime");
Zero(&utbuf
, sizeof utbuf
, char);
utbuf
.actime
= (long)str_gnum(st
[++sp
]); /* time accessed */
utbuf
.modtime
= (long)str_gnum(st
[++sp
]); /* time modified */
if (utime(str_get(st
[++sp
]),&utbuf
))
/* Do the permissions allow some operation? Assumes statcache already set. */
cando(bit
, effective
, statbufp
)
register struct stat
*statbufp
;
/* [Comments and code from Len Reed]
* MS-DOS "user" is similar to UNIX's "superuser," but can't write
* to write-protected files. The execute permission bit is set
* by the Miscrosoft C library stat() function for the following:
* All files and directories are readable.
* Directories and special files, e.g. "CON", cannot be
* [Comment by Tom Dinger -- a directory can have the write-protect
* bit set in the file system, but DOS permits changes to
* the directory anyway. In addition, all bets are off
* here for networked software, such as Novell and
/* Atari stat() does pretty much the same thing. we set x_bit_set_in_stat
* too so it will actually look into the files for magic numbers
return (bit
& statbufp
->st_mode
) ? TRUE
: FALSE
;
if ((effective
? euid
: uid
) == 0) { /* root is special */
if (statbufp
->st_mode
& 0111 || S_ISDIR(statbufp
->st_mode
))
return TRUE
; /* root reads and writes anything */
if (statbufp
->st_uid
== (effective
? euid
: uid
) ) {
if (statbufp
->st_mode
& bit
)
return TRUE
; /* ok as "user" */
else if (ingroup((int)statbufp
->st_gid
,effective
)) {
if (statbufp
->st_mode
& bit
>> 3)
return TRUE
; /* ok as "group" */
else if (statbufp
->st_mode
& bit
>> 6)
return TRUE
; /* ok as "other" */
ingroup(testgid
,effective
)
if (testgid
== (effective
? egid
: gid
))
GROUPSTYPE gary
[NGROUPS
];
anum
= getgroups(NGROUPS
,gary
);
if (gary
[anum
] == testgid
)
#if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM)
do_ipcget(optype
, arglast
)
register STR
**st
= stack
->ary_array
;
register int sp
= arglast
[0];
key
= (key_t
)str_gnum(st
[++sp
]);
n
= (optype
== O_MSGGET
) ? 0 : (int)str_gnum(st
[++sp
]);
flags
= (int)str_gnum(st
[++sp
]);
return msgget(key
, flags
);
return semget(key
, n
, flags
);
return shmget(key
, n
, flags
);
#if !defined(HAS_MSG) || !defined(HAS_SEM) || !defined(HAS_SHM)
fatal("%s not implemented", opname
[optype
]);
return -1; /* should never happen */
do_ipcctl(optype
, arglast
)
register STR
**st
= stack
->ary_array
;
register int sp
= arglast
[0];
int id
, n
, cmd
, infosize
, getinfo
, ret
;
id
= (int)str_gnum(st
[++sp
]);
n
= (optype
== O_SEMCTL
) ? (int)str_gnum(st
[++sp
]) : 0;
cmd
= (int)str_gnum(st
[++sp
]);
getinfo
= (cmd
== IPC_STAT
);
if (cmd
== IPC_STAT
|| cmd
== IPC_SET
)
infosize
= sizeof(struct msqid_ds
);
if (cmd
== IPC_STAT
|| cmd
== IPC_SET
)
infosize
= sizeof(struct shmid_ds
);
if (cmd
== IPC_STAT
|| cmd
== IPC_SET
)
infosize
= sizeof(struct semid_ds
);
else if (cmd
== GETALL
|| cmd
== SETALL
)
if (semctl(id
, 0, IPC_STAT
, &semds
) == -1)
getinfo
= (cmd
== GETALL
);
infosize
= semds
.sem_nsems
* sizeof(short);
/* "short" is technically wrong but much more portable
than guessing about u_?short(_t)? */
#if !defined(HAS_MSG) || !defined(HAS_SEM) || !defined(HAS_SHM)
fatal("%s not implemented", opname
[optype
]);
STR_GROW(astr
, infosize
+1);
if (astr
->str_cur
!= infosize
)
int i
= (int)str_gnum(astr
);
a
= (char *)i
; /* ouch */
ret
= msgctl(id
, cmd
, (struct msqid_ds
*)a
);
ret
= semctl(id
, n
, cmd
, a
);
ret
= shmctl(id
, cmd
, (struct shmid_ds
*)a
);
if (getinfo
&& ret
>= 0) {
astr
->str_cur
= infosize
;
astr
->str_ptr
[infosize
] = '\0';
register STR
**st
= stack
->ary_array
;
register int sp
= arglast
[0];
id
= (int)str_gnum(st
[++sp
]);
flags
= (int)str_gnum(st
[++sp
]);
if ((msize
= mstr
->str_cur
- sizeof(long)) < 0) {
return msgsnd(id
, (struct msgbuf
*)mbuf
, msize
, flags
);
fatal("msgsnd not implemented");
register STR
**st
= stack
->ary_array
;
register int sp
= arglast
[0];
int id
, msize
, flags
, ret
;
id
= (int)str_gnum(st
[++sp
]);
msize
= (int)str_gnum(st
[++sp
]);
mtype
= (long)str_gnum(st
[++sp
]);
flags
= (int)str_gnum(st
[++sp
]);
if (mstr
->str_cur
< sizeof(long)+msize
+1) {
STR_GROW(mstr
, sizeof(long)+msize
+1);
ret
= msgrcv(id
, (struct msgbuf
*)mbuf
, msize
, mtype
, flags
);
mstr
->str_cur
= sizeof(long)+ret
;
mstr
->str_ptr
[sizeof(long)+ret
] = '\0';
fatal("msgrcv not implemented");
register STR
**st
= stack
->ary_array
;
register int sp
= arglast
[0];
id
= (int)str_gnum(st
[++sp
]);
if (opsize
< sizeof(struct sembuf
)
|| (opsize
% sizeof(struct sembuf
)) != 0) {
return semop(id
, (struct sembuf
*)opbuf
, opsize
/sizeof(struct sembuf
));
fatal("semop not implemented");
do_shmio(optype
, arglast
)
register STR
**st
= stack
->ary_array
;
register int sp
= arglast
[0];
id
= (int)str_gnum(st
[++sp
]);
mpos
= (int)str_gnum(st
[++sp
]);
msize
= (int)str_gnum(st
[++sp
]);
if (shmctl(id
, IPC_STAT
, &shmds
) == -1)
if (mpos
< 0 || msize
< 0 || mpos
+ msize
> shmds
.shm_segsz
) {
errno
= EFAULT
; /* can't do as caller requested */
shm
= (char*)shmat(id
, (char*)NULL
, (optype
== O_SHMREAD
) ? SHM_RDONLY
: 0);
if (shm
== (char *)-1) /* I hate System V IPC, I really do */
if (optype
== O_SHMREAD
) {
if (mstr
->str_cur
< msize
) {
Copy(shm
+ mpos
, mbuf
, msize
, char);
mstr
->str_ptr
[msize
] = '\0';
if ((n
= mstr
->str_cur
) > msize
)
Copy(mbuf
, shm
+ mpos
, n
, char);
memzero(shm
+ mpos
+ n
, msize
- n
);
fatal("shm I/O not implemented");