rs@uunet.UU.NET (Rich Salz) (06/24/87)
Submitted by: Simon Brown <simon@its63b.ed.ac.uk> Archive-name: sxt-sh-jobs/Part02 Mod.sources: Volume 10, Number 19 Part 2 of context diffs for implementing job-control in the System V Bourne shell. Simon Brown Department of Computer Science University of Edinburgh Scotland, UK. simon@its63b.ed.ac.uk simon@cstvax.ed.ac.uk seismo!mcvax!ukc!{its63b,cstvax}!simon -------------cut here --------------cut here ------------ cut here ------- #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # proc.c # service.c.Diff # sh.1.Diff # string.c.Diff # sxtalloc.8 # sxtalloc.c # word.c.Diff # xec.c.Diff # This archive created: Fri Feb 20 17:43:13 1987 export PATH; PATH=/bin:/usr/bin:$PATH echo shar: "extracting 'proc.c'" '(22373 characters)' if test -f 'proc.c' then echo shar: "will not over-write existing file 'proc.c'" else cat << \SHAR_EOF > 'proc.c' /* * SH Shell. * * * Copyright (c) S. Brown, 1987 * * * wait stuff, for processes to be waited for. * * also, job-control. */ #include "defs.h" extern char *sysmsg[]; /* signal messages */ #ifdef JOB /* * JOB-CONTROL */ #ifdef JOBSXT #undef input #include <sys/types.h> #include <sys/tty.h> #include <sys/sxt.h> #include <errno.h> #endif JOBSXT #define PROCESS_DOT_C #include "job.h" #include <signal.h> int currentjob = 0; /* current job */ int fgjobs = 0; /* no. of foreground jobs */ int bgjobs = 0; /* no. of background jobs */ int mainpgrp; /* initial process group */ int dfltjob; /* default jobnumber for fg, bg, notify ... */ int bdfltjob; /* back-one default */ int maxpri = 0; /* max. priority */ int extracnt=0; /* number of children dead in queue */ #ifdef JOBSXT int sxtchan = -1; /* sxt channel set */ int savsxtchan = -1; /* saved chan fd */ struct sxtblock sxtblock; /* block masks */ int sxtcontrol = -1; /* controlling sxt-tty */ int sxtkludge = 1; /* real tty virtual offset of controller */ char blkmask; /* virtual blocks */ TTYNOD realtty; /* state of physical tty */ #define BLKBIT(c,n) ((c)&(1<<SXT(n))) #define SIGBLOCK 21 /* fake signal-number for blockages */ int sxt_fd = -1; #endif JOBSXT struct jobnod *jobs[JOB_MAX]; struct extrawait extras[EXTRAPROCS]; /* * empty all of jobs table */ postclr() { register int i; for (i=1; i<JOB_MAX; i++) postjclr(i); bgjobs = fgjobs = 0; } /* * clear out an entry in jobs table */ postjclr(jobno) { register int i; if (jobs[i=jobno]){ free((char *)jobs[i]->jb_dir); free((char *)jobs[i]->jb_cmd); #ifdef JOBSXT if (jobs[i]->jb_sxtfg) close(jobs[i]->jb_sxtfg); blkmask &= ~(1<<SXT(i)); #endif free((char *)jobs[i]); jobs[i] = (struct jobnod *)0; } if (i==dfltjob || i==bdfltjob) setdfj(); } /* * set default job */ setdfj() { register int i; maxpri = 0; bdfltjob = dfltjob; dfltjob = -1; for (i=1; i<JOB_MAX; i++) if (jobs[i] && jobs[i]->jb_pri > maxpri){ maxpri = jobs[i]->jb_pri; bdfltjob = dfltjob; dfltjob = i; } } /* * place process in jobs table */ post(procid) int procid; { register struct jobnod *jp; register int i; jp = jobs[currentjob]; if (jp){ for (i=0; i<PPERJ; i++) if (jp->jb_proc[i].proc_pid==0) break; if (i>PPERJ-1 || (jp->jb_flags.singlebits.lifes&(1<<i))) /*error("cannot post job")*/; /* Never Happens */ jp->jb_proc[i].proc_pid = procid; jp->jb_flags.singlebits.lifes |= (1<<i); } } /* * restart in background */ bg(jno) { if (jobs[jno]){ #ifdef JOBSXT ioctl(sxt_fd,SXTIOCUBLK,SXT(jno)); sxtblock.input &= ~(1<<SXT(jno)); sxtblock.output &= ~(1<<SXT(jno)); blkmask &= ~(1<<SXT(jno)); #endif JOBSXT jobs[jno]->jb_flags.singlebits.back = jobs[jno]->jb_flags.singlebits.run = 1; jobs[jno]->jb_pri = ++maxpri; /* top priority */ bdfltjob = dfltjob; if (jobs[jno]->jb_flags.singlebits.stop){ jobs[jno]->jb_flags.singlebits.stop = 0; if (jobs[jno]->jb_cmd){ prs(jobs[jno]->jb_cmd); prs(" &\n"); } } #ifdef JOBSXT if (sxtchan!=-1) #endif JOBSXT iflags |= jobflg; setjob(0); /* so it'll stop if its reading from tty */ bgjobs++; } } /* * set the "notify" bit in a job */ notify(jno) { if (jobs[jno]) jobs[jno]->jb_flags.singlebits.notify = 1; } /* * bring a job into foreground. */ fg(jno) { if (jobs[jno] && jno>0){ jobs[jno]->jb_flags.singlebits.back = 0; jobs[jno]->jb_flags.singlebits.run = 1; jobs[jno]->jb_pri = ++maxpri; /* top priority */ bdfltjob = dfltjob; dfltjob = jno; #ifdef JOBSXT if (sxtchan!=-1) #endif JOBSXT iflags |= jobflg; /* in case it was forgotten... */ if (jobs[jno]->jb_cmd){ prs(jobs[jno]->jb_cmd); newline(); } if (jobs[jno]->jb_flags.singlebits.stop) jobs[jno]->jb_flags.singlebits.stop = 0; #ifdef JOBSXT ioctl(sxt_fd, SXTIOCBLK, SXT(jno)); sxtblock.input &= ~(1<<SXT(jno)); sxtblock.output &= ~(1<<SXT(jno)); blkmask &= ~(1<<SXT(jno)); #ifdef ERCC_SIGCONT killpg(jobs[jno]->jb_pgrp,SIGCONT); #endif setjob(jno); #endif SysV await(-1,WAIT_FG); } } /* * wait for processes to terminate. * waitflags is an ORing of * WAIT_FG - waiting for foreground jobs * WAIT_BG - waiting for background jobs * WAIT_NOPAUSE - fast wait, don't hang around * WAIT_JOB - waiting for a job-no, not a process-no. */ await(id,waitflags) { register int i; register int gotit=0; #ifdef JOBSXT register int j; struct sxtblock block; register int oldex; #endif if ((waitflags&WAIT_JOB)==0 && id>0) post(id); if (waitflags&WAIT_FG) fgjobs++; flushb(); while (fgjobs>0 || ((waitflags&(WAIT_NOPAUSE|WAIT_BG)) && bgjobs>0)){ if ((trapnote&SIGSET) && fgjobs==0){ listjobs((char *)0); prs("wait: interrupted\n"); break; } #ifdef JOBSXT if (extracnt>0){ gotit=announce(); } else if (sxtchan!=-1 && currentjob<SXTMAX && (waitflags&WAIT_BG)==0){ if (setsxt(currentjob) != SXT(currentjob)) continue; iflags |= sxtwaiting; oldex = extracnt; while (ioctl(sxt_fd,SXTIOCWF,0) == -1) iflags &= ~sxtwaiting; setsxt(currentjob); if (jobs[currentjob]->jb_sxtfg!=(-1)) ioctl(jobs[currentjob]->jb_sxtfg,TCSBRK,1); ioctl(sxt_fd,SXTIOCSTAT,&block); for (i=1; i<SXTMAX; i++){ BOOL blkin = (BLKBIT(block.input,i)!=0); BOOL blkout = (BLKBIT(block.output,i)!=0); if (i!=currentjob){ if (!blkin && !blkout) continue; if (BLKBIT(sxtblock.input,i) || BLKBIT(sxtblock.output,i) || BLKBIT(blkmask,i)) continue; } else { if (extracnt!=oldex) continue; } blkmask |= (1<<SXT(i)); jobs[i]->jb_flags.singlebits.stop = 1; jobs[i]->jb_flags.singlebits.run = 0; jobs[i]->jb_flags.singlebits.deaths = jobs[i]->jb_flags.singlebits.lifes; if (jobs[i]->jb_flags.singlebits.back) bgjobs--; else { fgjobs--; setsxt(0); } for (j=0; j<PPERJ; j++) if (jobs[i]->jb_proc[j].proc_pid) jobs[i]->jb_proc[j].proc_status = (SIGBLOCK<<8)|0177; donotify(i); } sxtblock.input = block.input; sxtblock.output = block.output; } else #endif JOBSXT { /* System-V or V7 without sxt's */ #if defined(SIGCLD) || defined(SIGCHLD) if (extracnt==0 && (waitflags&WAIT_NOPAUSE)==0) pause(); #else extrastack(0,0); #endif SIGCLD or SIGCHLD if (extracnt>0) gotit=announce(); } if ( ((waitflags&WAIT_JOB) && gotit==id) || ((waitflags&WAIT_NOPAUSE) && gotit!=0 && extracnt<=0)) break; } if (fgjobs<0) fgjobs=0; if (extracnt<0) extracnt=0; /* ... */ for (i=1; i<JOB_MAX; i++) if (jobs[i] && jobs[i]->jb_flags.singlebits.deaths==jobs[i]->jb_flags.singlebits.lifes){ jobs[i]->jb_flags.singlebits.deaths = 0; printjob(i); if (jobs[i]->jb_flags.singlebits.stop==0) postjclr(i); } setjob(0); /* restore to control state */ } /* * trap routine for SIGCHLD/SIGCLD */ #if defined(SIGCHLD) || defined(SIGCLD) job_fault() { extrastack(0,0); #ifdef JOBSXT if ((flags&waiting)==0 && sxtchan!=-1 && (iflags&sxtwaiting)) setsxt(-1); else if (flags&waiting) announce(); #endif JOBSXT } #endif SIGCHLD or SIGCLD /* * set current job to "job" */ setjob(jno) { register struct jobnod *jp = jobs[jno<0? 0 : jno]; if ((iflags&jobflg) && jp){ #ifdef JOBSXT setsxt(jno); #endif JOBSXT } currentjob = jno; } /* * set up job stuff in parent, assumimg I/O j-control has already * been set up in the child. */ setparjob(jno) { register struct jobnod *jp = jobs[jno]; if (jp && (iflags&jobflg)){ #ifdef JOBSXT sxtcontrol = jno; #endif JOBSXT } } /* * set up j-control I/O in child */ setchildjob(jno,sxtflag) { register struct jobnod *jp = jobs[jno]; if (jp){ if (jp->jb_pgrp==0) jp->jb_pgrp = getpid(); } if (sxtflag && jno){ #ifdef JOBSXT if (sxtflag==1){ ioctl(sxt_fd,SXTIOCBLK,SXT(jno)); setsxt(jno); } else ioctl(sxt_fd,SXTIOCUBLK,SXT(jno)); #endif JOBSXT } } /* * book a slot in the jobs-table */ bookjob() { register int i; for (i=0; i<JOB_MAX; i++) if (jobs[i]==0){ jobs[currentjob=i] = (struct jobnod *)alloc(sizeof(struct jobnod)); jobs[i]->jb_dir = jobs[i]->jb_cmd = (char *)0; jobs[i]->jb_pgrp = 0; #ifdef JOBSXT jobs[i]->jb_sxtfg = -1; if ((flags&(ttyflg|forked))==(ttyflg)){ if (sxtchan!=-1) iflags |= jobflg; } else iflags &= ~jobflg; #endif JOBSXT return; } /*printf("Jobs Table is Full!\n");*/ /* nobody should have THAT many jobs running! */ } /* * create storage for a new job (already chosen by "bookjob") */ newjob(pid,jcmd,backing,chanid) struct argnod * jcmd; { register struct jobnod *jp; register int j; register char * cmdbuffer; register int cmdlength; struct argnod * storcmd; /* -- This should NEVER happen, so its commented out -- if (currentjob<0 || currentjob>=JOB_MAX || jobs[currentjob]==0){ prs("Job Control Error\n"); return(0); } */ jp = jobs[currentjob]; #ifdef notdef if ( (flags&(forked|ttyflg))!=ttyflg) pid = 0; #endif notdef #ifdef JOBSXT if (sxtchan!=-1) jp->jb_sxtfg = chanid; /* parent sxt channel access */ #endif JOBSXT jp->jb_pgrp = pid ? pid : jobs[0]->jb_pgrp; jp->jb_pri = 0; if (jcmd){ for (cmdlength=0, storcmd=jcmd; storcmd; storcmd=storcmd->argnxt) cmdlength += length(storcmd->argval); cmdbuffer = jp->jb_cmd = (char *)alloc(cmdlength+1); *cmdbuffer = '\0'; for (storcmd=jcmd; storcmd; storcmd=storcmd->argnxt){ cmdbuffer = movstr(storcmd->argval,cmdbuffer); cmdbuffer = movstr(" ",cmdbuffer); } } else jp->jb_cmd = make("(?)"); jp->jb_flags.totalbits = 0; jp->jb_flags.singlebits.run = 1; for (j=0; j<PPERJ; j++){ jp->jb_proc[j].proc_pid=0; jp->jb_proc[j].proc_status=0; } if (!backing){ bdfltjob = dfltjob; dfltjob = currentjob; jp->jb_pri = ++maxpri; } if (pid && pid!=mainpgrp) setparjob(currentjob); return(currentjob); } /* * the "jobs" command */ listjobs(llist) char *llist; { register struct jobnod **jp; register int j; #ifdef JOBSXT ioctl(sxt_fd,SXTIOCSTAT,&sxtblock); #endif for (jp = &jobs[1]; jp< &jobs[JOB_MAX]; jp++) if (*jp){ prc('['); prn(jp-jobs); prs("] "); if (llist){ for (j=0; j<PPERJ; j++) if ((*jp)->jb_proc[j].proc_pid){ prn((*jp)->jb_proc[j].proc_pid); prs((((*jp)->jb_flags.singlebits.lifes&(1<<j)))?" ":"Z "); } prc(' '); } else prs(" "); if (jp==&jobs[dfltjob]) prc('+'); else if (jp==&jobs[bdfltjob]) prc('-'); else prc(' '); prc((*jp)->jb_flags.singlebits.notify ? '!' : ' '); prc(' '); #ifdef JOBSXT if (BLKBIT(sxtblock.input,jp-jobs) || BLKBIT(sxtblock.output,jp-jobs)) if ((*jp)->jb_flags.singlebits.stop==0){ if ((*jp)->jb_flags.singlebits.back) bgjobs--; else fgjobs--; (*jp)->jb_flags.singlebits.stop=1; } #endif JOBSXT if ((*jp)->jb_flags.singlebits.stop) prstop(jp-jobs, llist!=(char *)0); else if ((*jp)->jb_flags.singlebits.run) prs("Running\t\t"); else prs("(?)\t\t"); prc('\t'); prs((*jp)->jb_cmd ? (*jp)->jb_cmd : "(?)"); newline(); } } /* * print stop-state */ prstop(jno,longlist) { #ifdef JOBSXT register BOOL blkin, blkout; prs("Blocked"); blkin = BLKBIT(sxtblock.input,jno); blkout = BLKBIT(sxtblock.output,jno); if (blkin && blkout) prs(" (tty i/o)"); else if (blkin) prs(" (tty input)"); else if (blkout) prs(" (tty output)"); else prs(longlist?"\t\t":"\t"); #endif JOBSXT } /* * print info about jobs, and clean out if dead */ donotify(jobno) { register int i; if ((i=jobno) > 0){ if (jobs[i] && (jobs[i]->jb_flags.singlebits.deaths)){ jobs[i]->jb_flags.singlebits.deaths=0; if (jobs[i]->jb_flags.singlebits.stop){ newline(); printjob(i); } else { printjob(i); postjclr(i); } } } } /* * If any processes have terminated, do the works. */ announce() { BOOL stoppage=FALSE; register int i, j; int w, status; if (extracnt>0){ extracnt--; w = extras[extracnt].pid; status = extras[extracnt].status; } else { /* This can NEVER happen! (ha ha) */ #ifdef JOBSXT /*printf("no wait/extra\n");*/ #endif JOBSXT w = 0; } if (w==0 || w==-1){ /* Can this happen? Does it matter if it does? No. */ /*printf("Process Failure:no wait\n");*/ return 0; } for (i=1; i<JOB_MAX; i++){ if (jobs[i]==0) continue; for (j=0; j<PPERJ; j++) if (jobs[i]->jb_proc[j].proc_pid == w){ jobs[i]->jb_flags.singlebits.deaths |= (1<<j); jobs[i]->jb_flags.singlebits.stop = stoppage ? 1 : 0; jobs[i]->jb_proc[j].proc_status = status; if (jobs[i]->jb_flags.singlebits.deaths == jobs[i]->jb_flags.singlebits.lifes){ if (jobs[i]->jb_flags.singlebits.run) if (jobs[i]->jb_flags.singlebits.back) bgjobs--; else fgjobs--; jobs[i]->jb_flags.singlebits.run = 0; donotify(i); } else if (stoppage==0){ jobs[i]->jb_flags.singlebits.lifes &= ~(01<<j); jobs[i]->jb_flags.singlebits.deaths &= ~(01<<j); } return(i); } } /* it wasn't a known process - */ /* so forget about it. */ /* printf("unknown process has died\n"); */ /* extras[extracnt].pid = w; */ /* extras[extracnt].status = status; */ /* extracnt++; */ return(0); } /* * stacking function for putting terminated jobs onto extras when * they die - 'cos they must be waited for immediately, but its * not safe to do an announce(). */ extrastack(p,s) { int w, status; if (p==0){ w = wait(&status); } else { w=p; status=s; } if (w>0){ extras[extracnt].pid = w; extras[extracnt].status = status; extracnt++; } /* else printf("stack: wait error\n"); */ } /* * initialize jobs table * (and set up sxt's under Sys-V) */ initjobs() { if (jobs[0]) postjclr(0); bookjob(); newjob(mainpgrp,(struct argnod *)0,0,0); /* this had better be job[0] */ jobs[0]->jb_pgrp = mainpgrp; #ifdef JOBSXT terminal(&realtty); realtty.c_cc[VSWTCH] = 0377; realtty.c_cflag &= ~LOBLK; sxtinit(); #endif } /* * Something has happened to a job. * Print out info and clean up if necessary. */ printjob(jno) { register struct jobnod *jp = jobs[jno]; register int j; BOOL fgdone = FALSE; int wx=0, rc=0; int sig=0; int status=0; char * msg=0; if (jp){ for (j=0; j<PPERJ; j++){ int ww, ss, ww_hi; if (jp->jb_proc[j].proc_pid==0) continue; ww = jp->jb_proc[j].proc_status; ww_hi = (ww>>8)&LOBYTE; ss = (ww&0177); if (ss==0177){ ss = ww_hi; } wx |= ww; if (rc==0){ if (ss) sig = ss; if (jp->jb_flags.singlebits.back) continue; /* just wanted the signal */ rc = ss ? ss|SIGFLG : ww_hi; status = ww; fgdone = TRUE; } } if (jp->jb_flags.singlebits.stop==0 && jp->jb_flags.singlebits.back==0 && (sig==SIGINT || sig==SIGQUIT)) newline(); if (jp->jb_flags.singlebits.stop==0 && jp->jb_flags.singlebits.back==0){ TTYNOD tt; #ifdef JOBSXT if (jp->jb_sxtfg!=(-1) && (flags&ttyflg) && sig!=SIGHUP){ if (ioctl(jp->jb_sxtfg,TCGETA,&tt)==0){ tt.c_cc[VSWTCH] = 0377; /* ^Z disabled */ tt.c_cflag &= ~LOBLK; /* no blocking */ ioctl(0,TCSETA,&tt); } if (sig==SIGINT || sig==SIGQUIT){ /* Yeauch! */ fault(sig); /* simulate shell's tty pgrp */ if (j==0 && jp->jb_proc[1].proc_pid) kill(jp->jb_proc[1].proc_pid,sig); } } #endif JOBSXT } if ( (msg=(sig==SIGBLOCK?"Blocked":sysmsg[sig])) || jp->jb_flags.singlebits.notify || (ntfynod.namval) ){ if (msg==0 && !fgdone) msg=nullstr; if (msg && (flags&stdflg)){ #if defined(JOBSXT) && defined(BLK_BOTTOM) if (jp->jb_flags.singlebits.stop) blk_bottom(); #endif (JOBSXT && BLK_BOTTOM) if (jp->jb_flags.singlebits.back || jp->jb_flags.singlebits.stop){ prc('['); prn(jno); prs("]\t"); } if (jp->jb_cmd){ register char *p = jp->jb_cmd; while (*p && *p!=' ') prc(*p++); prc('\t'); } prs(*msg ? msg : "Done"); if (status&0200) prs(coredump); newline(); flushb(); } } if (fgdone) if (wx && (flags&errflg)) exitsh(rc); else { exitval=rc; exitset(); } } /*else printf("trying to report unknown job\n");*/ } /* * find a job-number from a describing "%..." string */ findjob(str) char *str; { int number = -1; register struct jobnod **jpp; register char *p; int found = 0; int nlen; if (str==0 || *str=='\0'){ number=dfltjob; if (number<1 || jobs[number]==0) error("No appropriate jobs"); } else { if (*str=='%') str++; if (*str=='%' || *str=='\0' || *str=='+') number=dfltjob; else if (*str=='-') number=bdfltjob; else if (digit(*str)) number=stoi(str); else if (*str=='?'){ nlen = length(++str)-1; for (jpp=jobs; jpp < &jobs[JOB_MAX]; jpp++) if (*jpp) for (p=(*jpp)->jb_cmd; *p; p++) if (cfn(str,p,nlen)==0){ number = jpp-jobs; if (found++) failed(str,"ambiguous"); break; } if (!found) failed(str,"no such job"); } else { nlen = length(str)-1; for (jpp=jobs; jpp < &jobs[JOB_MAX]; jpp++) if (*jpp && cfn(str,(*jpp)->jb_cmd,nlen)==0){ number = jpp-jobs; if (found++) failed(str,"ambiguous"); } if (!found) failed(str,"no such job"); } } if (number<1 || number>=JOB_MAX || jobs[number]==0) failed(str,"no such job"); return(number); } /* * check for stopped jobs */ stpjobs() { register int i; for(i=0; i<JOB_MAX; i++) if (jobs[i] && jobs[i]->jb_flags.singlebits.stop) return(TRUE); return(FALSE); } /* * zap 'em */ zapjobs() { register int i; for (i=1; i<JOB_MAX; i++) if (jobs[i] && jobs[i]->jb_flags.singlebits.stop){ killpg(jobs[i]->jb_pgrp, SIGHUP); jobs[i]->jb_flags.singlebits.stop = 0; jobs[i]->jb_flags.singlebits.back = 1; /* so that it gets reported properly */ jobs[i]->jb_proc[0].proc_status = SIGHUP; printjob(i); } } #ifdef JOBSXT #ifdef JOBSXT_HUP char Mstart[] = "echo \"\nsh was killed off, so I hung up the following processes:\n"; char Mend[] = "\n\" | mail -s sh_was_killed $LOGNAME"; /* good meta-usage */ /* * HUP has happened! Send mail and go away. */ hupmail() { register int i; char maillist[1024]; register char *mpoint; int mcnt = 0; sxtchan = -1; flags &= ~ttyflg; /* just in case ... */ mpoint = movstr(Mstart, maillist); for (i=1; i<JOB_MAX; i++) if (jobs[i] && jobs[i]->jb_flags.singlebits.back==0){ killpg(jobs[i]->jb_pgrp,SIGHUP); if (jobs[i]->jb_cmd){ mpoint = movstr("\t", mpoint); mpoint = movstr(jobs[i]->jb_cmd,mpoint); mpoint = movstr("\n", mpoint); } else mpoint = movstr("\t?\n", mpoint); mcnt++; } movstr(Mend, mpoint); if (mcnt) execexp(maillist,(char * *)0); /* bye! */ } #endif JOBSXT_HUP /* * set current job using sxt's */ setsxt(jno) { if (sxtchan != -1 && jno<SXTMAX){ jno = SXT(jno); sxtcontrol = -1; do { if (ioctl(sxt_fd,SXTIOCSWTCH,jno) == 0) sxtcontrol = jno; /*else if (errno!=EINTR) printf("sxt switch (errno %d)\n", errno);*/ } while (sxtcontrol!=jno && errno==EINTR); return(sxtcontrol); } else return(-1); } /* * block an sxt file */ sxtblk(jno) { ioctl(sxt_fd, SXTIOCBLK, SXT(jno)); } #endif JOBSXT /* * send a signal to all processes in a tty group. * actually, it only sends to the first and last in the pipe-order, * 'cos it only knows the pid's for these (jb_proc[0] ... jb_proc[PPERJ]). * This exists as a system-call under BSD. * Luckily it knows about negative pids. */ killpg(gid,sig) { register struct jobnod **jp; register int i=0; int err = 0, proc;; for (jp=jobs; jp < &jobs[JOB_MAX]; jp++) if (*jp && (*jp)->jb_pgrp==gid) for (i=0; i<PPERJ; i++){ proc = (*jp)->jb_proc[i].proc_pid; if (proc && kill(-proc,sig)==-1) err = errno; } /* Panic Stations! It didn't work, so get really desparate... */ if (err!=0) if (kill(-gid,sig)==-1 && kill(gid,sig)==-1) err = errno; else err=0; return(err); } #if defined(JOBSXT) && defined(BLK_BOTTOM) /* * move cursor to bottom of screen - when job is blocked * * if you have TERMCAP defined, it does it properly be sending * the "ll" string and clearing to end-of-line, otherwise it is * pretty dumb. * in fact, TERMCAP implies a lot of other stuff I havn't included * here, so tough. * simon@uk.ac.ed.its63b */ blk_bottom() { # ifdef TERMCAP STRING it, p; if (p=tcapstr[TCAP_LL]) tputs(p,1,prc); else if (it=tcapstr[TCAP_CM]){ p=tgoto(it,0,tcapflag[TCAP_ROW]); tputs(p,1,prc); /* move to bottom */ } else # endif TERMCAP { register int n = 24; /* bound to have 24 lines */ while (n--) newline(); /* chug chug chug ... */ } # ifdef TERMCAP if (p=tcapstr[TCAP_CE]) tputs(p,1,prc); /* clear to eoln */ # endif TERMCAP } #endif (JOBSXT && BLK_BOTTOM) #else !JOB /* * for processes to be waited for */ #define MAXP 20 static int pwlist[MAXP]; static int pwc; postclr() { register int *pw = pwlist; while (pw <= &pwlist[pwc]) *pw++ = 0; pwc = 0; } post(pcsid) int pcsid; { register int *pw = pwlist; if (pcsid) { while (*pw) pw++; if (pwc >= MAXP - 1) pw--; else pwc++; *pw = pcsid; } } await(i, bckg) int i, bckg; { int rc = 0, wx = 0; int w; int ipwc = pwc; post(i); while (pwc) { register int p; register int sig; int w_hi; int found = 0; { register int *pw = pwlist; p = wait(&w); if (wasintr) { wasintr = 0; if (bckg) { break; } } while (pw <= &pwlist[ipwc]) { if (*pw == p) { *pw = 0; pwc--; found++; } else pw++; } } if (p == -1) { if (bckg) { register int *pw = pwlist; while (pw <= &pwlist[ipwc] && i != *pw) pw++; if (i == *pw) { *pw = 0; pwc--; } } continue; } w_hi = (w >> 8) & LOBYTE; if (sig = w & 0177) { if (sig == 0177) /* ptrace! return */ { prs("ptrace: "); sig = w_hi; } if (sysmsg[sig]) { if (i != p || (flags & prompt) == 0) { prp(); prn(p); blank(); } prs(sysmsg[sig]); if (w & 0200) prs(coredump); } newline(); } if (rc == 0 && found != 0) rc = (sig ? sig | SIGFLG : w_hi); wx |= w; if (p == i) { break; } } if (wx && flags & errflg) exitsh(rc); flags |= eflag; exitval = rc; exitset(); } #endif JOB SHAR_EOF fi echo shar: "extracting 'service.c.Diff'" '(885 characters)' if test -f 'service.c.Diff' then echo shar: "will not over-write existing file 'service.c.Diff'" else cat << \SHAR_EOF > 'service.c.Diff' *** service.c Mon Nov 24 17:49:29 1986 --- /cs/simon/c/shdiff-sources/service.c Fri Feb 20 12:41:00 1987 *************** *** 261,266 } /* * for processes to be waited for */ --- 261,268 ----- } + #ifndef JOB + /* * This should really be in "proc.c", but I left it here to keep the * size of the diff-listing small. *************** *** 262,267 /* * for processes to be waited for */ #define MAXP 20 --- 264,276 ----- #ifndef JOB /* + * This should really be in "proc.c", but I left it here to keep the + * size of the diff-listing small. + * + * simon@uk.ac.ed.its63b + */ + + /* * for processes to be waited for */ #define MAXP 20 *************** *** 385,390 exitval = rc; exitset(); } BOOL nosubst; --- 394,401 ----- exitval = rc; exitset(); } + + #endif JOB BOOL nosubst; SHAR_EOF fi echo shar: "extracting 'sh.1.Diff'" '(8882 characters)' if test -f 'sh.1.Diff' then echo shar: "will not over-write existing file 'sh.1.Diff'" else cat << \SHAR_EOF > 'sh.1.Diff' *** /cs/simon/src/util/sh.sysV/sh.1 Thu Feb 19 19:12:25 1987 --- ../shdiff-sources/sh.1 Thu Feb 19 18:12:00 1987 *************** *** 31,36 .TH SH 1 .SH NAME sh, rsh \- shell, the standard/restricted command programming language .SH SYNOPSIS .B sh [ --- 31,37 ----- .TH SH 1 .SH NAME sh, rsh \- shell, the standard/restricted command programming language + (job-control version) .SH SYNOPSIS .B sh [ *************** *** 552,557 below) for this name. If it is found and there is an 'r' in the file name part of its value, the shell becomes a restricted shell. .PD .RE .PP --- 553,578 ----- below) for this name. If it is found and there is an 'r' in the file name part of its value, the shell becomes a restricted shell. + .TP + .B + .SM NOTIFY + If this is set, then you will receive asynchronous notification of the + termination of background jobs \- it is equivalent to an automatic + \f2notify\^\fP being applied to all jobs. + .TP + .B + .SM REALTTY + If job-control is being used, this parameter will hold the name of the + real (physical) terminal you are logged in on; the entry in \f2utmp\^\fP + is reset to the name of the \f2sxt\^\fP device you are using. + It is automatically read-only and exported. + .TP + .B + .SM SXT_CONTROL + This may be available in sub-processes in order to do their own + job-control. The parameter holds a number on which \f2ioctl\^\fP calls + can be done. To suspend itself, a process need only execute the + \f2\s-1SXTIOCSWTCH\s+1\fP ioctl with a channel-parameter of zero. .PD .RE .PP *************** *** 557,562 .PP The shell gives default values to \f3\s-1PATH\s+1\fP, \f3\s-1PS1\s+1\fP, \f3\s-1PS2\s+1\fP, \f3\s-1MAILCHECK\s+1\fP and \f3\s-1IFS\s+1\fP. .SM .B HOME and --- 578,585 ----- .PP The shell gives default values to \f3\s-1PATH\s+1\fP, \f3\s-1PS1\s+1\fP, \f3\s-1PS2\s+1\fP, \f3\s-1MAILCHECK\s+1\fP and \f3\s-1IFS\s+1\fP. + If job-control is being used, it will also set some of + \f3\s-1REALTTY\s+1\fP and \f3\s-1SXT_CONTROL\s+1\fP. .SM .B HOME and *************** *** 880,885 the .B trap command below). .SS Execution Each time a command is executed, the above substitutions are carried out. --- 903,940 ----- the .B trap command below). + .SS "Job Control" + Whenever a command is executed which results in the shell forking off + a subprocess, this subprocess is entered in a table of "jobs". Individual + jobs may then be manipulated using special job-control commands which + are built-ins in the shell. + The command \f3fg\fP brings a job into the foreground (from either the + background or "limbo"), \f3bg\fP resumes a blocked job in the background, + \f3jobs\fP lists the jobs-table, \f3notify\fP allows a job to give + asynchronous notification if it terminates from the background, + \f3stop\fP allows a job to be blocked from doing any output when it is + not in the foreground (see \f3loblk\fP in \f2stty\^\fP(1)), and + \f3sxt1\fP allows escape from the job-control regime if this becomes + necessary. + The \f3kill\fP command is now a shell built-in and can take + job-specifiers as parameters, as well as the usual process-id numbers. + .PP + All of these commands use job-specifiers to name jobs. A job-specifier + consists of a percent-sign (\f3%\fP) followed by some unique string. + If the string does not name a unique job, this is considered to be + an error (which should perhaps not be the case \- "wildcard" globbing + of job-specifiers would be nice). + The following strings are recognized: + .RS + .nf + %\f2n\^\fP \| the job numbered \f2n\^\fP + %+ \| the current job + %- \| the previous job + %% \| a symnonym for %+ + %\f2name\^\fP \| the job whose command-name starts with \f2name^\fP + %?\f2string\^\fP \| the job whose name contains the named \f2string\^\fP + .fi + .RE .SS Execution Each time a command is executed, the above substitutions are carried out. *************** *** 969,974 is used to find the directory containing .IR file . .TP \f3break\fP \*(OK \f2n\^\fP \*(CK Exit from the enclosing \f3for\fP or .B while --- 1024,1032 ----- is used to find the directory containing .IR file . .TP + \f3bg\fP \*(OK \f2%job\^\fP \*CK + Resume the named (or "current") job in the background. + .TP \f3break\fP \*(OK \f2n\^\fP \*(CK Exit from the enclosing \f3for\fP or .B while *************** *** 1068,1073 .I not be exported. .TP \f3hash\fP \*(OK \f3\-r\fP \*(CK \*(OK \fIname\^\fP .\|.\|. \*(CK For each .IR name\^ , --- 1126,1134 ----- .I not be exported. .TP + \f3fg\fP \*(OK \f2%job\^\fP \*(CK + Resume the named (or default) job in the foreground. + .TP \f3hash\fP \*(OK \f3\-r\fP \*(CK \*(OK \fIname\^\fP .\|.\|. \*(CK For each .IR name\^ , *************** *** 1088,1093 adjacent to the \f2hits\fR information. \f2Cost\fR will be incremented when the recalculation is done. .TP \f3newgrp\fP \*(OK \f2arg\^\fP .\|.\|. \*(CK Equivalent to .BI "exec newgrp" " arg\^" --- 1149,1178 ----- adjacent to the \f2hits\fR information. \f2Cost\fR will be incremented when the recalculation is done. .TP + \f3jobs\fP \*(OK \f3-l\^\fP \*(CK + List all currently active (running or blocked) jobs. + Each job will be listed by giving its job-number within square + brackets, followed by a identifying character, followed + by a status-word (either "Blocked" or "Running"), followed by a the name of + the command. If the \f3-l\fP option is used, then a list of process-id + numbers associated with that job is printed too. + .br + The identifying-character will be one of <blank>, \f3+\fP or \f3-\fP. + The job "%+" is the current "default job", wheras "%-" is the previous + current job. + .br + The status word "Running" means that it is a pure background process, + "Blocked" means that it is possibly executing but will block as soon + as it tries to do any input (or possibly output \- see the \f3loblk\fP + option to \f2stty\^\fP or the \f3stop\fP command) from the terminal. + A physically blocked job will have either "(tty input)" or "(tty output)" + following the word "Blocked". + .TP + \f3kill\fP \*(OK \f2-sig\^\fP \*(CK \f2{process,%job} ...\^\fP + Kill the named processes or jobs by sending the named signal. If + no signal is named, then \f2SIGTERM\^\fP is sent \- signal 15. + It is not possible to give signal-name mnemonics to specify signal numbers. + .TP \f3newgrp\fP \*(OK \f2arg\^\fP .\|.\|. \*(CK Equivalent to .BI "exec newgrp" " arg\^" *************** *** 1096,1101 .IR newgrp (1) for usage and description. .TP \f3pwd\fP Print the current working directory. See --- 1181,1190 ----- .IR newgrp (1) for usage and description. .TP + \f3notify\fP \*(OK \f2%job\^\fP \*(CK + Arrange for asynchronous notification when background jobs terminate. + If no job is given, then all subsequent jobs will asynchronously notify. + .TP \f3pwd\fP Print the current working directory. See *************** *** 1208,1213 .I n\^ is not given, it is assumed to be 1. .TP \f3test\fP .br Evaluate conditional expressions. See --- 1297,1314 ----- .I n\^ is not given, it is assumed to be 1. .TP + \f3stop\fP \*(OK \f2%job\^\fP \*(CK + Stop a given (or the "current") running background job. A job that is + "stopped" will continue to execute until it tries to do input or output + from the terminal, and it will block as soon as this occurs. + .TP + \f3sxt1 \f2command args ...\^\fP + Execute a command without using job-control. This is useful for commands + which rely on being able to find out the name of their terminal from the + \f2utmp\^\fP file \- such as \f2who\^\fP, \f2mesg\^\fP, \f2tty\^\fP + or quite a few others. This is only necessary because of the simplisitic + nature of \f2utmp\^\fP. + .TP \f3test\fP .br Evaluate conditional expressions. See *************** *** 1476,1481 exec(2), fork(2), pipe(2), signal(2), ulimit(2), umask(2), --- 1577,1583 ----- exec(2), fork(2), pipe(2), + setpgrp(2), signal(2), ulimit(2), umask(2), *************** *** 1482,1488 wait(2), a.out(4), profile(4), ! environ(5) in the \f2\s-1UNIX\s+1 System Programmer Reference Manual\fR. .SH CAVEATS .PP --- 1584,1591 ----- wait(2), a.out(4), profile(4), ! environ(5), ! sxt(7) in the \f2\s-1UNIX\s+1 System Programmer Reference Manual\fR. .SH CAVEATS .PP *************** *** 1502,1504 .B cd\^ command with a full path name to correct this situation. --- 1605,1614 ----- .B cd\^ command with a full path name to correct this situation. + .SH ADDITIONS + .PP + This version of the shell has additional job-control features + for the \f2sxt\^\fP(7) driver, + added by Simon Brown at the University of Edinburgh. + (Send any bug reports to \f3simon@its63b.edinburgh.ac.uk\fP + or \f3seismo!mcvax!ukc!its63b!simon\fP). SHAR_EOF fi echo shar: "extracting 'string.c.Diff'" '(457 characters)' if test -f 'string.c.Diff' then echo shar: "will not over-write existing file 'string.c.Diff'" else cat << \SHAR_EOF > 'string.c.Diff' *** string.c Mon Nov 24 17:49:30 1986 --- /cs/simon/c/shdiff-sources/string.c Wed Feb 18 18:59:11 1987 *************** *** 43,48 return(*--s1 - *s2); } length(as) char *as; { --- 43,60 ----- return(*--s1 - *s2); } + #ifdef JOB + cfn(s1,s2,n) + register char *s1, *s2; + int n; + { + while (--n>=0 && *s1++ == *s2) + if (*s2++ == 0) + return(0); + return(n<0 ? 0 : *--s1 - *s2); + } + #endif JOB + length(as) char *as; { SHAR_EOF fi echo shar: "extracting 'sxtalloc.8'" '(1774 characters)' if test -f 'sxtalloc.8' then echo shar: "will not over-write existing file 'sxtalloc.8'" else cat << \SHAR_EOF > 'sxtalloc.8' .TH SXTALLOC 7 "Edinburgh Univ, June 6 1986" .SH NAME sxtalloc - allocate/deallocate sxt channels .SH SYNOPSIS .B /usr/lib/ssh/sxtalloc .br .B /usr/lib/ssh/sxtalloc \fIttyname status\fx .SH DESCRIPTION When called with no parameters, .I sxtalloc finds a non-busy sxt channel-zero in /dev/sxt and chowns all its subchannels to the real user-id of the caller so that the sxt is then available for use by that user. It replaces the users original terminal name in the .I utmp file with the new sxt device. It prints the name of the original terminal on the standard output. .PP If called with two parameters, it attempts to undo the damage caused by allocating the device. It replaces the sxt name in the .I utmp file with the .I ttyname parameter, and exits with exit-status .IR status . Checks are made that you are entitled to have .I ttyname as your terminal. .PP The use of .I sxtalloc may be restricted to certain usernames by creating files .I sxt.allow or .I sxt.deny in the directory .IR /usr/lib/ssh . If an .I allow file exists, then only those people whose name occurs in this file are permitted to allocate sxt channels; if a .I deny file exists, then everyone execept the people named in this file may allocate sxt's. If neither exist, then no restrictions on use are in force. .SH "STATUS RETURNS" When allocating an sxt device, it returns status of 0 if it succeeds, otherwise 1. When deallocating, it exits with the given exit-status, unless it fails for some reason (in which case it exits with status 1). .SH "SEE ALSO" ssh(1), sxt(7), utmp(4). .SH "FILES" /usr/lib/ssh/sxtalloc - binary .br /dev/sxt/??? - sxt devices .br /etc/utmp - utmp file .br /usr/lib/ssh/sxt.allow, /usr/lib/ssh/sxt.deny .SH BUGS It has to be set-user-id root. .SH AUTHOR Simon Brown SHAR_EOF fi echo shar: "extracting 'sxtalloc.c'" '(5826 characters)' if test -f 'sxtalloc.c' then echo shar: "will not over-write existing file 'sxtalloc.c'" else cat << \SHAR_EOF > 'sxtalloc.c' /* * Allocate a sxt-channel for a non-priveliged user, by * chowning an unused one, so the user can pick it up later. * Probably needs locks to prevent races, but too much effort to do that. * * MUST BE SET-USER-ID "root". * * Used by the "ssh" shell to do job-control. * * Usage: * sxtalloc - allocate sxt, print old ttyname on * stdout; * exit status: 0=success, 1=failure. * sxtalloc realtty status - de-allocate sxt, change utmp entry * back to "realtty"; * exit status: "status"." * Copyright (c) S. Brown, 1987 */ #include <errno.h> #include <sys/types.h> #include <sys/tty.h> #include <sys/sxt.h> #include <fcntl.h> #include <utmp.h> #include <sys/stat.h> #include <stdio.h> #include <ctype.h> #include <pwd.h> char sxt[] = "/dev/sxt/000"; /* virtual tty devices, channel 0 */ #define SXT_CHAN 9 char devsxt[] = "sxt000"; /* utmp dummy */ char devreal[50]; extern struct utmp *getutline(); extern char *ttyname(); extern char *strrchr(); char *getuser(); char *progname; /* * The "allow/deny" files, a la cron/at. */ #define ALLOW "/usr/lib/ssh/sxt.allow" #define DENY "/usr/lib/ssh/sxt.deny" #define UNAMESIZE 64 /* FAR too big! */ /* * Verbose mode */ /*#define VERBOSE*/ main(argc,argv) char **argv; { int exitstat; progname = argv[0]; if (argc==3){ release(argv[1]); exitstat = atoi(argv[2]); } else if (argc==1) exitstat = allocate(); else { fprintf(stderr, "%s: usage\n", argv[0]); exitstat = -1; } exit(exitstat); /*NOTREACHED*/ } allocate() { int fd; int chan = 0; int uid, gid; char *tty; int mode; struct utmp *u_entry; struct utmp u_start; register int i; struct stat statb; char *username; uid=getuid(); username = getuser(uid); if (username==NULL){ /* you don't exist! */ #ifdef VERBOSE fprintf(stderr,"You don't exist. go away!\n"); #endif return(1); } if (allowed(username,ALLOW,DENY)==0){ #ifdef VERBOSE fprintf(stderr,"You are not allowed to use sxt devices\n"); #endif return(2); /* 2 means "not permitted" */ } do { fd = open(sxt,O_RDONLY|O_EXCL); if (fd==-1){ if (errno!=EBUSY){ fprintf(stderr,"%s: sxt failure on %s (errno %d)\n", progname, sxt,errno); return(1); /* some strange error, like no chans unused */ } chan++; /* try next device */ sxt[SXT_CHAN] = (chan/10)+'0'; sxt[SXT_CHAN+1] = (chan%10)+'0'; } } while (fd==-1); /* continue until found an unused dev */ gid=getgid(); tty=ttyname(0); if (tty && stat(tty,&statb)==0){ mode = statb.st_mode&0777; tty = strrchr(tty,'/'); if (tty){ tty++; strcpy(u_start.ut_line,tty); if ((u_entry=getutline(&u_start))!=(struct utmp *)0){ strcpy(&devsxt[3], &sxt[SXT_CHAN]); devsxt[5] = '1'; /* chan 1 is control */ strcpy(devreal, u_entry->ut_line); strcpy(u_entry->ut_line, devsxt); pututline(u_entry); endutent(); } #ifdef VERBOSE else fprintf(stderr,"%s: can't change utmp\n", progname); #endif } else fprintf(stderr,"%s: tty has silly name\n", progname); } else { fprintf(stderr,"%s: warning - can't seem to find tty name\n", progname); mode = 0622; } for (i=0; i<MAXPCHAN; i++){ /* chown all the channels for that dev */ sxt[SXT_CHAN+2] = i+'0'; if (chown(sxt,uid,gid) != 0){ /* "real" id's */ fprintf(stderr,"%s: chown failure on %s (errno %d)\n", progname, sxt, errno); return(1); /* no point haning about */ } if (chmod(sxt,mode) != 0){ fprintf(stderr,"%s: chmod failure on %s (errno %d)\n", progname, sxt, errno); return(1); /* no point haning about */ } } printf("%s\n", devreal); return(0); } release(realtty) char *realtty; { char *tty; int mode; struct utmp u_start; struct utmp *u_entry; struct stat statb; int uid; uid=getuid(); strcpy(devreal,"/dev/"); if (realtty && *realtty){ strcat(devreal,realtty); if (stat(devreal,&statb)!=0 || statb.st_uid!=uid || (statb.st_mode&S_IFMT)!=S_IFCHR){ fprintf(stderr,"%s: %s is not a tty belonging to you!\n", progname, realtty); return; } } else { fprintf(stderr,"%s: null tty name given\n", progname); return; } tty=ttyname(0); if (tty && stat(tty,&statb)==0){ mode = statb.st_mode&0777; tty = strrchr(tty,'/'); if (tty){ tty++; strcpy(u_start.ut_line,tty); if ((u_entry=getutline(&u_start))!=(struct utmp *)0){ strcpy(u_entry->ut_line, realtty); pututline(u_entry); endutent(); } else fprintf(stderr,"%s: can't restore utmp\n", progname); } else fprintf(stderr,"%s: tty has silly name\n",progname); } else { fprintf(stderr,"%s: warning - can't seem to find tty name\n", progname); mode = 0622; } if (chmod(devreal,mode)!=0) fprintf(stderr,"%s: cannot reset mode of real tty\n", progname);} /* * -------------------------------------------------------- * * The following stuff checks in the files * sxt.allow and sxt.deny files to control access * */ struct stat globstat; #define exists(file) (stat(file,&globstat) == 0) char *getuser(uid) int uid; { struct passwd *nptr,*getpwuid(); if ((nptr=getpwuid(uid)) == NULL) { return(NULL); } return(nptr->pw_name); } allowed(user,allow,deny) char *user,*allow,*deny; { if (getuid() == 0) return(1); /* root is a good guy */ if ( exists(allow) ) { if ( within(user,allow) ) return(1); } else if ( exists(deny) ) { if ( within(user,deny) ) return(0); else return(1); } else return(1); return(0); } within(username,filename) char *username,*filename; { char line[UNAMESIZE]; FILE *cap; int i; if((cap = fopen(filename,"r")) == NULL) return(0); while ( fgets(line,UNAMESIZE,cap) != NULL ) { for ( i=0 ; line[i] != '\0' ; i++ ) { if ( isspace(line[i]) ) { line[i] = '\0'; break; } } if ( strcmp(line,username)==0 ) { fclose(cap); return(1); } } fclose(cap); return(0); } SHAR_EOF fi echo shar: "extracting 'word.c.Diff'" '(459 characters)' if test -f 'word.c.Diff' then echo shar: "will not over-write existing file 'word.c.Diff'" else cat << \SHAR_EOF > 'word.c.Diff' *** word.c Mon Nov 24 17:49:31 1986 --- /cs/simon/c/shdiff-sources/word.c Thu Feb 19 17:56:50 1987 *************** *** 232,237 chktrap(); clearup(); } } while ((len = read(f->fdes, f->fbuf, f->fsiz)) < 0 && trapnote); return(len); } --- 232,240 ----- chktrap(); clearup(); } + #ifdef JOB + trapnote &= ~DEVINTERRUPT; + #endif JOB } while ((len = read(f->fdes, f->fbuf, f->fsiz)) < 0 && trapnote); return(len); } SHAR_EOF fi echo shar: "extracting 'xec.c.Diff'" '(13610 characters)' if test -f 'xec.c.Diff' then echo shar: "will not over-write existing file 'xec.c.Diff'" else cat << \SHAR_EOF > 'xec.c.Diff' *** xec.c Mon Nov 24 17:49:32 1986 --- /cs/simon/c/shdiff-sources/xec.c Fri Feb 20 14:36:02 1987 *************** *** 13,18 #include "sym.h" #include "hash.h" static int parent; /* ======== command execution ========*/ --- 13,22 ----- #include "sym.h" #include "hash.h" + #ifdef JOB + #include "job.h" + #endif JOB + static int parent; *************** *** 15,20 static int parent; /* ======== command execution ========*/ --- 19,25 ----- static int parent; + /* ======== command execution ========*/ *************** *** 87,92 struct ionod *io = t->treio; short cmdhash; short comtype; exitval = 0; --- 92,100 ----- struct ionod *io = t->treio; short cmdhash; short comtype; + #ifdef JOB + char *fgcom[3]; + #endif exitval = 0; *************** *** 95,100 com = scan(argn); a1 = com[1]; gchain = schain; if (argn != 0) cmdhash = pathlook(com[0], 1, comptr(t)->comset); --- 103,118 ----- com = scan(argn); a1 = com[1]; gchain = schain; + #ifdef JOB + if (com[0][0]=='%'){ + /* this is incredibly kludgy! */ + fgcom[0] = "fg"; + fgcom[1] = com[0]; + fgcom[2] = 0; + com = fgcom; + argn=2; + } + #endif JOB if (argn != 0) cmdhash = pathlook(com[0], 1, comptr(t)->comset); *************** *** 165,171 break; case SYSEXIT: ! flags |= forked; /* force exit */ exitsh(a1 ? stoi(a1) : retval); case SYSNULL: --- 183,192 ----- break; case SYSEXIT: ! #ifdef JOBSXT ! if ((flags&ttyflg)==ttyflg && stpjobs()) ! error("There are blocked jobs"); ! #endif JOBSXT exitsh(a1 ? stoi(a1) : retval); case SYSNULL: *************** *** 250,255 #ifdef RES /* Research includes login as part of the shell */ case SYSLOGIN: oldsigs(); execa(com, -1); done(); --- 271,283 ----- #ifdef RES /* Research includes login as part of the shell */ case SYSLOGIN: + #ifdef JOBSXT + if ((flags&ttyflg)==ttyflg && stpjobs()) + error("There are blocked jobs"); + sxtrelease(0); + nattrib(&TTYnod,N_VAR|N_EXPORT); + #endif JOBSXT + flags |= forked; oldsigs(); execa(com, -1); done(); *************** *** 260,265 failed(com[0], restricted); else { flags |= forked; /* force bad exec to terminate shell */ oldsigs(); execa(com, -1); --- 288,299 ----- failed(com[0], restricted); else { + #ifdef JOBSXT + if ((flags&ttyflg)==ttyflg && stpjobs()) + error("There are blocked jobs"); + sxtrelease(0); + nattrib(&TTYnod,N_EXPORT); + #endif JOBSXT flags |= forked; /* force bad exec to terminate shell */ oldsigs(); execa(com, -1); *************** *** 335,340 break; case SYSWAIT: await(a1 ? stoi(a1) : -1, 1); break; --- 369,377 ----- break; case SYSWAIT: + #ifdef JOB + await(a1 ? stoi(a1) : -1, WAIT_BG); + #else await(a1 ? stoi(a1) : -1, 1); #endif break; *************** *** 336,341 case SYSWAIT: await(a1 ? stoi(a1) : -1, 1); break; case SYSREAD: --- 373,379 ----- await(a1 ? stoi(a1) : -1, WAIT_BG); #else await(a1 ? stoi(a1) : -1, 1); + #endif break; case SYSREAD: *************** *** 547,552 unset_name(*com); } break; default: prs_buff("unknown builtin\n"); --- 585,615 ----- unset_name(*com); } break; + #ifdef JOB + case SYSKILL: + zipkill(argn,com); + break; + case SYSSTOP: + zipstop(argn,com); + break; + case SYSJOBS: + listjobs(com[1]); + break; + case SYSFG: + # ifdef JOBSXT + if (sxtchan==-1) + failed(com[0],badexec); + else + # endif JOBSXT + fg(findjob(com[1])); + break; + case SYSBG: + bg(findjob(com[1])); + break; + case SYSNOTIFY: + notify(findjob(com[1])); + break; + # ifdef JOBSXT case SYSSXT1: { *************** *** 548,553 } break; default: prs_buff("unknown builtin\n"); } --- 611,632 ----- break; # ifdef JOBSXT + case SYSSXT1: + { + int chsxt; + extern int crisps; + + if (com[1]){ + chsxt = hide_sxt(); + execexp(com[1],&com[2]); + if (chsxt) + restore_sxt(); + } + } + break; + # endif JOBSXT + #endif JOB + default: prs_buff("unknown builtin\n"); } *************** *** 586,591 } case TFORK: exitval = 0; if (execflg && (treeflgs & (FAMP | FPOU)) == 0) parent = 0; --- 665,674 ----- } case TFORK: + #ifdef JOB + forkprocess(t,treeflgs,com,pf1,pf2,type,execflg,errorflg,pos,exec_link,linked); + break; + #else exitval = 0; if (execflg && (treeflgs & (FAMP | FPOU)) == 0) parent = 0; *************** *** 719,724 } done(); } case TPAR: execute(parptr(t)->partre, exec_link, errorflg); --- 802,808 ----- } done(); } + #endif JOB case TPAR: execute(parptr(t)->partre, exec_link, errorflg); *************** *** 857,862 int f; { struct fileblk fb; push(&fb); if (s) --- 941,949 ----- int f; { struct fileblk fb; + #ifdef JOBSXT + BOOL chsxt=FALSE; + #endif JOBSXT push(&fb); if (s) *************** *** 864,870 estabf(s); fb.feval = (char **)(f); } ! else if (f >= 0) initf(f); execute(cmd(NL, NLFLG | MTFLG), 0, (int)(flags & errflg)); pop(); --- 951,957 ----- estabf(s); fb.feval = (char **)(f); } ! else if (f >= 0){ initf(f); #ifdef JOBSXT chsxt = hide_sxt(); *************** *** 866,871 } else if (f >= 0) initf(f); execute(cmd(NL, NLFLG | MTFLG), 0, (int)(flags & errflg)); pop(); } --- 953,963 ----- } else if (f >= 0){ initf(f); + #ifdef JOBSXT + chsxt = hide_sxt(); + #endif JOBSXT + } + else error("execexp: no args"); execute(cmd(NL, NLFLG | MTFLG), 0, (int)(flags & errflg)); pop(); #ifdef JOBSXT *************** *** 868,873 initf(f); execute(cmd(NL, NLFLG | MTFLG), 0, (int)(flags & errflg)); pop(); } execprint(com) --- 960,969 ----- else error("execexp: no args"); execute(cmd(NL, NLFLG | MTFLG), 0, (int)(flags & errflg)); pop(); + #ifdef JOBSXT + if (chsxt) + restore_sxt(); + #endif JOBSXT } *************** *** 870,875 pop(); } execprint(com) char **com; { --- 966,974 ----- #endif JOBSXT } + + + execprint(com) char **com; { *************** *** 885,887 newline(); } --- 984,1275 ----- newline(); } + + #ifdef JOB + /* + * service-things for job-control. + */ + /*ARGSUSED*/ + zipkill(argc,argv) + char **argv; + { + int signo, procid, ret; + char * a1 = argv[1]; + extern char * sys_errlist[]; + + if (!a1) failed(argv[0],synmsg); + if (*a1=='-'){ + a1++; + signo=stoi(a1); + argv++; + } else signo=SIGTERM; + while (*++argv){ + if (**argv=='%'){ + procid = jobs[findjob(argv[0])]->jb_pgrp; + ret = killpg(procid,signo); + } else { + procid = stoi(*argv); + ret = kill(procid,signo); + if (ret==-1) ret=errno; + } + if (ret!=0){ + prn_buff(procid); + prs_buff(" "); + prs_buff(procid,sys_errlist[ret]); + newline(); + } + } + } + + /* ------------------------------- stop ------------------------------ */ + /*ARGSUSED*/ + zipstop(argc,argv) + char **argv; + { + int id; + + if (argv[1]==0){ + register int i; + + for (i=1; i<JOB_MAX; i++) + if (jobs[i]){ + # ifdef JOBSXT + sxtblk(i); + # endif JOBSXT + } + } else { + argv++; + while (argv[1]){ + if (jobs[id=findjob(argv[1])]){ + # ifdef JOBSXT + sxtblk(id); + # endif JOBSXT + } + argv++; + } + } + } + + + #ifdef JOBSXT + #undef input + #include <sys/types.h> + #include <sys/tty.h> + #include <sys/sxt.h> + #include <sys/ipc.h> + #include <sys/msg.h> + #endif JOBSXT + + /* + * fork a process, exec the child and deal with job initialization, etc... + * (the parameter-passing is a bit crude) + */ + forkprocess(t,treeflgs,com,pf1,pf2,type,execflg,errorflg,pos,exec_link,linked) + register struct trenod * t; + register int treeflgs; + register char * *com; + int *pf1, *pf2; + int type,errorflg,pos,exec_link,linked; + { + #ifdef JOBSXT + struct msgbuf msgbuffer; + int msgqid = -1; + #endif JOBSXT + + exitval=0; + if (execflg && (treeflgs&(FAMP|FPOU))==0) + parent=0; + else { + int forkcnt = 1; + + if (treeflgs & (FAMP | FPOU)) + { + link_iodocs(iotemp); + linked = 1; + } + if ((treeflgs&FPIN)==0){ + bookjob(); /* set up a job slot */ + #ifdef JOBSXT + if (sxtchan!=-1) + msgqid = msgget(IPC_PRIVATE,0); + #endif JOBSXT + } + while ((parent=fork()) == -1){ + if ((forkcnt = (forkcnt * 2)) > FORKLIM) /* 32 */ + { + switch (errno) + { + case ENOMEM: + error(noswap); + break; + default: + case EAGAIN: + error(nofork); + break; + } + } + if (trapnote&SIGSET){ + postjclr(currentjob); + #ifdef JOBSXT + if (sxtchan!=-1) + msgctl(msgqid, IPC_RMID, (struct msqid_ds *)0); + #endif JOBSXT + sigchk(); + } + alarm(forkcnt); + pause(); + } + } + + + if (parent){ + /* This is the parent branch of fork; */ + /* it may or may not wait for the child. */ + struct argnod *Dcom; + + if (treeflgs&FPCL) closepipe(pf1); + while (type==TLST || type==TFIL || type==TFORK){ + if (type==TFORK) + t=forkptr(t)->forktre; + else t=lstptr(t)->lstlef; + type=(t->tretyp)&COMMSK; + } + if (type==TCOM) + Dcom = comptr(t)->comarg; + else Dcom = 0; + if ((treeflgs&FPIN)==0){ + #ifdef JOBSXT + if (sxtchan!=-1){ + msgrcv(msgqid, &msgbuffer,0,0,0); + msgctl(msgqid, IPC_RMID, (struct msqid_ds *)0); + sxtchan = sxtopen(SXT(currentjob)); + newjob(parent,Dcom,treeflgs&FAMP,sxtchan); + if (sxtchan==-1) sxtchan=0; /* fix */ + } else newjob(parent,Dcom,treeflgs&FAMP,-1); + #else + newjob(parent,Dcom,treeflgs&FAMP,0); + #endif JOBSXT + } + if (treeflgs&FPRS && (flags&(ttyflg|prompt))==(ttyflg|prompt)){ + prc('['); + prn(currentjob); + prs("]\t"); + prn(parent); + newline(); + } + if ((treeflgs&(FAMP|FPOU))==0) + await(parent,WAIT_FG); /* a process, in the foreground */ + else if ((treeflgs&FAMP)==0) + post(parent); + else { + assnum(&pcsadr, parent); + post(parent); + bg(currentjob); /* set it running in bg, and reset current job to 0 */ + } + + chktrap(); + return; + + + } else { /* this is the forked branch (child) of execute */ + flags |= forked; + fiotemp = 0; + + if (linked == 1) + { + swap_iodoc_nm(iotemp); + exec_link |= 06; + } + else if (linked == 0) + iotemp = 0; + + #ifdef ACCT + suspacct(); + #endif + + setchildjob(currentjob,0); + postclr(); + settmp(); + + /* Turn off INTR and QUIT if `FINT' */ + /* Reset ramaining signals to parent */ + /* except for those `lost' by trap */ + oldsigs(); + fixsigs(); + #ifdef JOBSXT + if ((iflags&jobflg)==0) + #endif JOBSXT + if (treeflgs&FINT){ + signal(SIGINT,SIG_IGN); + signal(SIGQUIT,SIG_IGN); + #ifdef NICE + if (treeflgs&FAMP) + nice(NICEVAL); + #endif + } + + #ifdef JOBSXT + /* reset fildes 0,1,2 through sxt channel */ + if (sxtchan!=-1){ + if (currentjob<SXTMAX) + setpgrp(); + sxtchan=sxtopen(SXT(currentjob)); + if ((treeflgs&(FPIN|FPOU))!=FPOU) /*pipes once*/ + setchildjob(currentjob, ((treeflgs&FAMP)==0)?1:2); + if (sxtchan != -1){ + close(0); close(1); close(2); + dup(sxtchan); + dup(0); dup(0); /* stdout and stderr */ + } + sxtchan = -1; /* prevent subshell jobs */ + #ifdef SXT_SUSPEND + if (treeflgs&FAMP) + #endif + close(sxt_fd); + msgbuffer.mtype = 1; + msgsnd(msgqid, msgbuffer, 0, 0); + msgctl(msgqid, IPC_RMID, (struct msqid_ds *)0); /* delete in kid too */ + } + if ((iflags&jobflg) && (treeflgs&(FPIN|FPOU))==FPOU){ + alarm(1); + pause(); /* Horrendous Kludge! */ + } + #endif JOBSXT + + /* pipe in or out */ + if (treeflgs&FPIN){ + rename(pf1[INPIPE],0); + close(pf1[OTPIPE]); + } + if (treeflgs&FPOU){ + rename(pf2[OTPIPE],1); + close(pf2[INPIPE]); + } + + /* default std input for & */ + #ifdef JOBSXT + if ((iflags&jobflg)==0) /* only if no job-control */ + #endif JOBSXT + if ((treeflgs&FINT) && ioset==0){ + int devvy = chkopen(devnull); + rename(devvy,0); + } + + iflags &= ~jobflg; + + /* io redirection */ + initio(t->treio,0); + if (type!=TCOM){ + execute(forkptr(t)->forktre,exec_link|01,errorflg); + } else if (com[0]!=ENDARGS){ + eflag = 0; + setlist(comptr(t)->comset,N_EXPORT); + rmtemp(0); + execa(com,pos); + } + flags &= ~ttyflg; /* don't reset tty pgrp */ + done(); + } + } + + #endif JOB SHAR_EOF fi exit 0 # End of shell archive