mrose.uci-750a%rand-relay@sri-unix.UUCP (10/17/83)
From: Marshall Rose <mrose.uci-750a@rand-relay> We're running BSD 4.1a (yes, I know 4.2 is here, give us time), and there are times that I'd like to take a job and re-direct its i/o somewhere else. For instance, it would be nice to be able to tell csh %1 > file & to direct the output of job 1 to the file and put it in the background. I often have the inclination to do this when I start a command which I think is going to a) run fast or b) produce little output, and I don't re-direct the output. Since I run with ``stty tostop'' set to prevent my screen from getting clobbered, this means that I usually interrupt the command and start it again, with re-direction in the background. It could very well be that this problem is just an artifact of my style, so you may not find this very useful to you. In any event 4.1BSD won't let you do this, and I think it unlikely that 4.2 has it. If 4.2 does have this, then don't read the rest of this message, just send me a message saying so. If not, it seems to me we need a dup3() call. The call dup3(pid1, fd1, pid2, fd2) would perform a dup2() from pid1's fd1 to pid2's fd2. Obviously, pid1 and pid2 must exist (you could default a 0 for your process-id); the process executing dup3() must have the same effective user-ids as pid1 and pid2; fd1 must be a valid file-descriptor for pid1; etc., etc. Hence dup2(fd1, fd2) is really dup3(0, fd1, 0, fd2). Has anyone thought about this, or actually implemented this (optimistic, aren't I?) or something similar? /mtr PS - the possibilities are limitless. Think of all the times you wanted to have something go to more after you invoked it. With dup3() you could use %1 | more and *poof*: csh would fork, the child would do the dup3() required to bind %1's stdout to its stdin, and then exec() more. Wow.
mike%brl-vgr@sri-unix.UUCP (10/17/83)
From: Mike Muuss <mike@brl-vgr> Marshal - As far as I can tell so far, 4.2 does not have your wished-for dup3() system call, but it *does* have the ability to "hand off" a file descriptor to another process, using a special PF_UNIX "messsage". Using that capability and some library routines to handle interpreting a special signal, you actually COULD build a dup3() facility which would work as long as the target of the dup3 was willing to cooperate. Neat, huh? -Mike
rehmi@umcp-cs.UUCP (10/19/83)
In the spirit of Bob Brown's setuid program, I cons'd up a setf program which'll open a file and swap pointers to it with some other process. It needs some cleaning up, but... ================================= #include <stdio.h> #include <signal.h> #include <nlist.h> #include <pwd.h> #include <sys/pte.h> #include <sys/param.h> #include <sys/dir.h> #include <sys/user.h> #include <sys/proc.h> #include <sys/vm.h> #include <sys/file.h> #include <sys/inode.h> /* setf - change the file descriptors under a running process * and don't even tell it. Abuse with moderate caution. * Khron the Elder (rehmi@{umd-csd,umcp-cs}) 27 Jan 83 * * inspired and derived partially from Bob Brown's setuid (rlb@purdue) */ #define NAMELIST "/vmunix" #define KERNEL "/dev/kmem" #define MEMORY "/dev/mem" #define SWAP "/dev/drum" #define ROOT 0 /* root user id */ int Memory, Kernel, Swap; struct nlist Names[] = { { "_proc" }, #define X_PROC 0 { "_nproc" }, #define X_NPROC 1 { "_Usrptmap" }, #define X_USRPTMAP 2 { "_usrpt" }, #define X_USRPT 3 { 0 } }; struct proc proc, *Procptr; int nproc; struct pte *Usrptma, *usrpt; union myuser { struct user user; char upages[UPAGES][NBPG]; } User; #define U User.user #ifdef DEBUG # define DEBUGp 1 #else # define DEBUGp 0 #endif int NoWrite; caddr_t procp; getval(fd, addr, buf, n) int fd, addr, n; char *buf; { lseek(fd, addr, 0); return(read(fd, buf, n)); } putval(fd, addr, buf, n) int fd, addr, n; char *buf; { lseek(fd, addr, 0); #ifndef NOWRITE return(write(fd, buf, n)); #else return(n); #endif } getu(user, mproc) union myuser *user; struct proc *mproc; { struct pte apte; int pad1; /* avoid hardware botch */ struct pte arguutl[UPAGES+CLSIZE]; int pad2; /* avoid hardware botch */ register int i; int ncl, size; size = sizeof (struct user); if ((mproc->p_flag & SLOAD) == 0) { if (getval(Swap, ctob(mproc->p_swaddr), &user->user, size) != size) { fprintf(stderr, "can't read u for pid %d from %s\n", mproc->p_pid, SWAP); return (0); } return (1); } if(getval(Kernel, (int)&Usrptma[btokmx(mproc->p_p0br) + mproc->p_szpt - 1], &apte, sizeof apte)!=sizeof(apte)) { printf("can't read indir pte to get u for pid %d from %s\n", mproc->p_pid, KERNEL); return (0); } if (getval(Memory, ctob(apte.pg_pfnum+1)-(UPAGES+CLSIZE)*sizeof (struct pte), arguutl, sizeof arguutl) != sizeof arguutl) { printf("can't read page table for u of pid %d from %s\n", mproc->p_pid, MEMORY); return (0); } ncl = (size + NBPG*CLSIZE - 1) / (NBPG*CLSIZE); while (--ncl >= 0) { i = ncl * CLSIZE; if (getval(Memory, ctob(arguutl[CLSIZE+i].pg_pfnum), user->upages[i], CLSIZE*NBPG)!=CLSIZE*NBPG) { printf("can't read page %x of u of pid %d from %s\n", arguutl[CLSIZE+i].pg_pfnum, mproc->p_pid, MEMORY); return(0); } } return (1); } putu(user, mproc, member, msize) union myuser *user; struct proc *mproc; char *member; int msize; { struct pte apte; int pad1; /* avoid hardware botch */ struct pte arguutl[UPAGES+CLSIZE]; int pad2; /* avoid hardware botch */ register int i; int ncl, size; size = sizeof (struct user); #ifdef DEBUG printf("put:uid %d gid %d ruid %d rgid %d\n", user->user.u_uid, user->user.u_gid, user->user.u_ruid, user->user.u_rgid); #endif if ((mproc->p_flag & SLOAD) == 0) { if (getval(Swap, ctob(mproc->p_swaddr), &user->user, size) != size) { fprintf(stderr, "can't read u for pid %d from %s\n", mproc->p_pid, SWAP); return (0); } return (1); } if(getval(Kernel, (int)&Usrptma[btokmx(mproc->p_p0br) + mproc->p_szpt - 1], &apte, sizeof apte)!=sizeof(apte)) { printf("can't read indir pte to get u for pid %d from %s\n", mproc->p_pid, KERNEL); return (0); } if (getval(Memory, ctob(apte.pg_pfnum+1)-(UPAGES+CLSIZE)*sizeof (struct pte), arguutl, sizeof arguutl) != sizeof arguutl) { printf("can't read page table for u of pid %d from %s\n", mproc->p_pid, MEMORY); return (0); } ncl = (size + NBPG*CLSIZE - 1) / (NBPG*CLSIZE); while (--ncl >= 0) { i = ncl * CLSIZE; #ifdef DEBUG printf("i*NBPG=%x member=%x user=%x memb-user=%x msize=%x\n", i*NBPG, member, user, (int)member-(int)user, msize); #endif if((i+1)*NBPG > ((int)member-(int)user) && i*NBPG <= ((int)member-(int)user+msize)) { int foo, bar; bar=MAX(0, (int)member-(int)user-(i*NBPG)); foo=MIN(CLSIZE*NBPG, (int)member-(int)user+msize-(i*NBPG))-bar; #ifdef DEBUG printf("foo=%04x, bar=%04x, iteration %d, i=%04x\n", foo, bar, ncl, i); #endif if (putval(Memory, ctob(arguutl[CLSIZE+i].pg_pfnum)+bar, (user->upages[i])+bar, foo)!=foo) { printf("can't write page %x of u of pid %d from %s\n", arguutl[CLSIZE+i].pg_pfnum, mproc->p_pid, MEMORY); return(0); } } } return (1); } getw(loc) off_t loc; { long word; lseek(Kernel, loc, 0); if (read(Kernel, &word, sizeof word) != sizeof word) printf("error reading kmem at %x\n", loc); return (word); } getprocu(pid) int pid; { register int i, j, found, ncl; int newflag; Usrptma=(struct pte *)Names[X_USRPTMAP].n_value; usrpt=(struct pte *)Names[X_USRPT].n_value; getval(Kernel, Names[X_PROC].n_value, &procp, sizeof procp); getval(Kernel, Names[X_NPROC].n_value, &nproc, sizeof nproc); found = 0; lseek(Kernel, (char *)procp, 0); for(i = 0; i < nproc; i++){ if(read(Kernel, &proc, sizeof proc) != sizeof proc){ perror("Kernel read"); exit(1); } Procptr = &proc; if(found=(pid == Procptr->p_pid)) break; } if(!found){ fprintf(stderr, "Failed to find pid %d\n", pid); exit(1); } getu(&U, Procptr); } /* * Assignproc - place a value in the proc table for a process * * Inputs: Procptr (global) ... ptr to local copy of proc entry. * Proc (global) ... local copy of entire proc table. * praddr ............ address of field in proc to change. * value .............. pointer to new value to assign. */ assignproc (praddr, value, length) caddr_t praddr, length; char *value; { caddr_t seekto; seekto = (caddr_t)praddr - (caddr_t)Procptr + (caddr_t)procp; lseek(Kernel, seekto, 0); #ifndef NOWRITE if ( write(Kernel, value, length) < 0) { perror("Kernel write"); exit(1); } #endif } /* * openfiles - Open System Files: Paging device (Swap) * Kernel address space (Kernel) * Physical memory (Memory) * * Exits if any error */ openfiles() /* results are global variables */ { #ifdef NOWRITE #define Mode 0 #else #define Mode 2 #endif if((Kernel=open(KERNEL,Mode))<0) { perror("Kernel access"); exit(1); } if((Memory=open(MEMORY,Mode))<0) { perror("Memory access"); exit(1); } if((Swap=open(SWAP,Mode))<0) { perror("Swap access"); exit(1); } } struct nlist nlfoo[] = {{"_u"}, 0}; main(argc, argv) char argc, **argv; { int kmemfd, fd, foo, j; struct user u; struct file f, *t; struct inode i; if(argv[1][0]=='-') { NoWrite=1; argv++; } if(!NoWrite && argc<4) { fprintf(stderr, "usage: %s [-] <pid> <file> <fd>\n", argv[0]); exit(1); } if(getuid()) exit(1); openfiles(); nlist("/vmunix", nlfoo); nlist("/vmunix", Names); setuid(getuid()); fd=open(argv[2], 2); if(fd<0) fd=creat(argv[2], 0644); if(fd<0) perror(argv[2]); lseek(Kernel, nlfoo[0].n_value, 0); read(Kernel, &u, sizeof u); getprocu(atoi(argv[1])); foo=atoi(argv[3]); if(DEBUGp || NoWrite) { printf("name %s old f[%d] %x new f[%d] %x\n", U.u_comm, foo, U.u_ofile[foo], fd, u.u_ofile[fd]); for(j=0;j<NOFILE;j++) printf("u.u_ofile[%2d] = %08x\tU.u_ofile[%2d] = %08x\n", j, u.u_ofile[j], j, U.u_ofile[j]); } t=U.u_ofile[foo]; U.u_ofile[foo]=u.u_ofile[fd]; u.u_ofile[fd]=t; if(!NoWrite) { putu(&U, Procptr, &U.u_ofile[foo], sizeof U.u_ofile[0]); putval(Kernel, nlfoo[0].n_value, &u, sizeof u); } } ==================================== Enjoy(!), -rehmi -- By the fork, spoon, and exec of The Basfour. Arpa: rehmi.umcp-cs@csnet-relay Uucp:...!harpo!seismo!umcp-cs!rehmi ...!{allegra,brl-bmd}!umcp-cs!rehmi
aaw@pyuxss.UUCP (10/25/83)
I have had the same problem and considered the sandwich technique of implementing it. That is: inputControl | theCommand | outputControl inputControl and outputControl would of course have to be a replacement shell, to limit proliferation of processes and interprocess chatter. Consider the consequences: you could have several 'background' processes requesting I/O and the shell (=inputControl) should be able to sort it out, e.g. prompt theCommand: > to say 'theCommand' wants input. Also the major plus is it would allow windowing with several forground processes, due to the shells sorting out the /dev/tty reads/writes Aaron Werman pyuxss!aaw