static int hist_exceptf(Sfio_t *fp, int type, Sfdisc_t *handle) #endif { UNUSED(data); History_t *hp = (History_t *)handle; if (type == SF_WRITE) { if (errno == ENOSPC || hp->histwfail++ >= 10) return 0; // Write failure could be NFS problem, try to re-open. int newfd = open(hp->histname, O_BINARY | O_APPEND | O_CREAT | O_RDWR | O_CLOEXEC, S_IRUSR | S_IWUSR); int oldfd = sffileno(fp); sh_close(oldfd); if (newfd == -1) goto fail; if (sh_fcntl(newfd, F_DUPFD_CLOEXEC, oldfd) != oldfd) { close(newfd); goto fail; } (void)fcntl(oldfd, F_SETFD, FD_CLOEXEC); close(newfd); if (lseek(oldfd, 0, SEEK_END) < hp->histcnt) { int index = hp->histind; // The return value of this lseek() has historically been ignored. It is unclear if that // is correct. That is, is there any scenario in which this lseek() could fail but the // overall behavior of the shell still be correct if we ignore that failure? The void // cast is to silence Coverity CID #253581. (void)lseek(oldfd, 2, SEEK_SET); hp->histcnt = 2; hp->histind = 1; hp->histcmds[1] = 2; hist_eof(hp); hp->histmarker = hp->histcnt; hp->histind = index; } return 1; } return 0; fail: errormsg(SH_DICT, 2, "History file write error-%d %s: file unrecoverable", errno, hp->histname); return -1; }
/* * open the history file * if HISTNAME is not given and userid==0 then no history file. * if login_sh and HISTFILE is longer than HIST_MAX bytes then it is * cleaned up. * hist_open() returns 1, if history file is open */ int sh_histinit(void *sh_context) { Shell_t *shp = (Shell_t*)sh_context; register int fd; register History_t *hp; register char *histname; char *fname=0; int histmask, maxlines, hist_start=0; register char *cp; register off_t hsize = 0; if(shgd->hist_ptr=hist_ptr) return(1); if(!(histname = nv_getval(HISTFILE))) { int offset = staktell(); if(cp=nv_getval(HOME)) stakputs(cp); stakputs(hist_fname); stakputc(0); stakseek(offset); histname = stakptr(offset); } #ifdef future if(hp=wasopen) { /* reuse history file if same name */ wasopen = 0; shgd->hist_ptr = hist_ptr = hp; if(strcmp(histname,hp->histname)==0) return(1); else hist_free(); } #endif retry: cp = path_relative(shp,histname); if(!histinit) histmode = S_IRUSR|S_IWUSR; if((fd=open(cp,O_BINARY|O_APPEND|O_RDWR|O_CREAT,histmode))>=0) { hsize=lseek(fd,(off_t)0,SEEK_END); } if((unsigned)fd <=2) { int n; if((n=fcntl(fd,F_DUPFD,10))>=0) { close(fd); fd=n; } } /* make sure that file has history file format */ if(hsize && hist_check(fd)) { close(fd); hsize = 0; if(unlink(cp)>=0) goto retry; fd = -1; } if(fd < 0) { #if KSHELL /* don't allow root a history_file in /tmp */ if(shgd->userid) #endif /* KSHELL */ { if(!(fname = pathtmp(NIL(char*),0,0,NIL(int*)))) return(0); fd = open(fname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR); } } if(fd<0) return(0); /* set the file to close-on-exec */ fcntl(fd,F_SETFD,FD_CLOEXEC); if(cp=nv_getval(HISTSIZE)) maxlines = (unsigned)strtol(cp, (char**)0, 10); else maxlines = HIST_DFLT; for(histmask=16;histmask <= maxlines; histmask <<=1 ); if(!(hp=new_of(History_t,(--histmask)*sizeof(off_t)))) { close(fd); return(0); } shgd->hist_ptr = hist_ptr = hp; hp->histshell = (void*)shp; hp->histsize = maxlines; hp->histmask = histmask; hp->histfp= sfnew(NIL(Sfio_t*),hp->histbuff,HIST_BSIZE,fd,SF_READ|SF_WRITE|SF_APPENDWR|SF_SHARE); memset((char*)hp->histcmds,0,sizeof(off_t)*(hp->histmask+1)); hp->histind = 1; hp->histcmds[1] = 2; hp->histcnt = 2; hp->histname = strdup(histname); hp->histdisc = hist_disc; if(hsize==0) { /* put special characters at front of file */ sfwrite(hp->histfp,(char*)hist_stamp,2); sfsync(hp->histfp); } /* initialize history list */ else { int first,last; off_t mark,size = (HIST_MAX/4)+maxlines*HIST_LINE; hp->histind = first = hist_nearend(hp,hp->histfp,hsize-size); histinit = 1; hist_eof(hp); /* this sets histind to last command */ if((hist_start = (last=(int)hp->histind)-maxlines) <=0) hist_start = 1; mark = hp->histmarker; while(first > hist_start) { size += size; first = hist_nearend(hp,hp->histfp,hsize-size); hp->histind = first; } histinit = hist_start; hist_eof(hp); if(!histinit) { sfseek(hp->histfp,hp->histcnt=hsize,SEEK_SET); hp->histind = last; hp->histmarker = mark; } histinit = 0; } if(fname) { unlink(fname); free((void*)fname); } if(hist_clean(fd) && hist_start>1 && hsize > HIST_MAX) { #ifdef DEBUG sfprintf(sfstderr,"%d: hist_trim hsize=%d\n",getpid(),hsize); sfsync(sfstderr); #endif /* DEBUG */ hp = hist_trim(hp,(int)hp->histind-maxlines); } sfdisc(hp->histfp,&hp->histdisc); #if KSHELL (HISTCUR)->nvalue.lp = (&hp->histind); #endif /* KSHELL */ sh_timeradd(1000L*(HIST_RECENT-30), 1, hist_touch, (void*)hp->histname); #if SHOPT_ACCTFILE if(sh_isstate(SH_INTERACTIVE)) acctinit(hp); #endif /* SHOPT_ACCTFILE */ #if SHOPT_AUDIT { char buff[SF_BUFSIZE]; hp->auditfp = 0; if(sh_isstate(SH_INTERACTIVE) && (hp->auditmask=sh_checkaudit(hp,SHOPT_AUDITFILE, buff, sizeof(buff)))) { if((fd=sh_open(buff,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR))>=0 && fd < 10) { int n; if((n = sh_fcntl(fd,F_DUPFD, 10)) >= 0) { sh_close(fd); fd = n; } } if(fd>=0) { fcntl(fd,F_SETFD,FD_CLOEXEC); hp->tty = strdup(ttyname(2)); hp->auditfp = sfnew((Sfio_t*)0,NULL,-1,fd,SF_WRITE); } } } #endif return(1); }
// // Open the history file. If HISTNAME is not given and userid==0 then no history file. If login_sh // and HISTFILE is longer than HIST_MAX bytes then it is cleaned up. // // hist_open() returns 1, if history file is opened. // int sh_histinit(void *sh_context) { Shell_t *shp = sh_context; int fd; History_t *hp; char *histname; char *fname = NULL; int histmask, maxlines, hist_start = 0; char *cp; off_t hsize = 0; shgd->hist_ptr = hist_ptr; if (shgd->hist_ptr) return 1; if (!(histname = nv_getval(HISTFILE))) { int offset = stktell(shp->stk); cp = nv_getval(HOME); if (cp) sfputr(shp->stk, cp, -1); sfputr(shp->stk, hist_fname, 0); stkseek(shp->stk, offset); histname = stkptr(shp->stk, offset); } #if 0 // TODO: Figure out if this should be enabled. Originally excluded via `#ifdef future`. if (hp = wasopen) { // Reuse history file if same name. wasopen = 0; shgd->hist_ptr = hist_ptr = hp; if (strcmp(histname, hp->histname) == 0) { return 1; } else { hist_free(); } } #endif // future retry: cp = path_relative(shp, histname); if (!histinit) histmode = S_IRUSR | S_IWUSR; if ((fd = open(cp, O_BINARY | O_APPEND | O_RDWR | O_CREAT | O_CLOEXEC, histmode)) >= 0) { hsize = lseek(fd, (off_t)0, SEEK_END); } if ((unsigned)fd < 10) { int n; if ((n = sh_fcntl(fd, F_DUPFD_CLOEXEC, 10)) >= 0) { sh_close(fd); fd = n; } } // Make sure that file has history file format. if (hsize && hist_check(fd)) { sh_close(fd); hsize = 0; if (unlink(cp) >= 0) goto retry; fd = -1; } // Don't allow root a history_file in /tmp. if (fd < 0 && shgd->userid) { fname = ast_temp_file(NULL, NULL, &fd, O_APPEND | O_CLOEXEC); if (!fname) return 0; } if (fd < 0) return 0; // Set the file to close-on-exec. (void)fcntl(fd, F_SETFD, FD_CLOEXEC); cp = nv_getval(HISTSIZE); if (cp) { maxlines = (unsigned)strtol(cp, NULL, 10); } else { maxlines = HIST_DFLT; } for (histmask = 16; histmask <= maxlines; histmask <<= 1) { ; // empty loop } histmask -= 1; hp = calloc(1, sizeof(History_t) + histmask * sizeof(off_t)); if (!hp) { sh_close(fd); return 0; } shgd->hist_ptr = hist_ptr = hp; hp->histshell = shp; hp->histsize = maxlines; hp->histmask = histmask; hp->histfp = sfnew(NULL, NULL, HIST_BSIZE, fd, SF_READ | SF_WRITE | SF_APPENDWR | SF_SHARE); hp->histind = 1; hp->histcmds[1] = 2; hp->histcnt = 2; hp->histname = strdup(histname); hp->histdisc = hist_disc; if (hsize == 0) { // Put special characters at front of file. sfwrite(hp->histfp, (char *)hist_stamp, 2); sfsync(hp->histfp); } else { // Initialize history list. int first, last; off_t mark, size = (HIST_MAX / 4) + maxlines * HIST_LINE; hp->histind = first = hist_nearend(hp, hp->histfp, hsize - size); histinit = 1; hist_eof(hp); // this sets histind to last command if ((hist_start = (last = (int)hp->histind) - maxlines) <= 0) hist_start = 1; mark = hp->histmarker; while (first > hist_start) { size += size; first = hist_nearend(hp, hp->histfp, hsize - size); hp->histind = first; } histinit = hist_start; hist_eof(hp); if (!histinit) { sfseek(hp->histfp, hp->histcnt = hsize, SEEK_SET); hp->histind = last; hp->histmarker = mark; } histinit = 0; } if (fname) { unlink(fname); free(fname); } if (hist_clean(fd) && hist_start > 1 && hsize > HIST_MAX) { #ifdef DEBUG sfprintf(sfstderr, "%d: hist_trim hsize=%d\n", getpid(), hsize); sfsync(sfstderr); #endif // DEBUG hp = hist_trim(hp, (int)hp->histind - maxlines); } sfdisc(hp->histfp, &hp->histdisc); STORE_VT((HISTCUR)->nvalue, i32p, &hp->histind); sh_timeradd(1000L * (HIST_RECENT - 30), 1, hist_touch, hp->histname); hp->auditfp = NULL; char buff[SF_BUFSIZE]; if (!sh_isstate(shp, SH_INTERACTIVE)) return 1; hp->auditmask = sh_checkaudit(hp, AUDIT_FILE, buff, sizeof(buff)); if (!hp->auditmask) return 1; if ((fd = sh_open(buff, O_BINARY | O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR)) >= 0 && fd < 10) { int n; if ((n = sh_fcntl(fd, F_DUPFD_CLOEXEC, 10)) >= 0) { sh_close(fd); fd = n; } } if (fd >= 0) { (void)fcntl(fd, F_SETFD, FD_CLOEXEC); hp->tty = strdup(isatty(2) ? ttyname(2) : "notty"); hp->auditfp = sfnew(NULL, NULL, -1, fd, SF_WRITE); } return 1; }
/* * This routine will turn the sftmp() file into a real /tmp file or pipe */ void sh_subtmpfile(int pflag) { Shell_t *shp = &sh; int fds[2]; Sfoff_t off; register struct checkpt *pp = (struct checkpt*)shp->jmplist; register struct subshell *sp = subshell_data->pipe; if(sfset(sfstdout,0,0)&SF_STRING) { register int fd; /* save file descriptor 1 if open */ if((sp->tmpfd = fd = fcntl(1,F_DUPFD,10)) >= 0) { fcntl(fd,F_SETFD,FD_CLOEXEC); shp->fdstatus[fd] = shp->fdstatus[1]|IOCLEX; close(1); shp->fdstatus[1] = IOCLOSE; } else if(errno!=EBADF) { ((struct checkpt*)shp->jmplist)->mode = SH_JMPERREXIT; shp->toomany = 1; errormsg(SH_DICT,ERROR_system(1),e_toomany); } if(shp->subshare || !pflag) { sfdisc(sfstdout,SF_POPDISC); if((fd=sffileno(sfstdout))>=0) { shp->fdstatus[fd] = IOREAD|IOWRITE; sfsync(sfstdout); if(fd==1) fcntl(1,F_SETFD,0); else { sfsetfd(sfstdout,1); shp->fdstatus[1] = shp->fdstatus[fd]; shp->fdstatus[fd] = IOCLOSE; } goto skip; } } } if(sp && (shp->fdstatus[1]==IOCLOSE || (!shp->subshare && !(shp->fdstatus[1]&IONOSEEK)))) { struct stat statb,statx; int fd; sh_pipe(fds); sp->pipefd = fds[0]; sh_fcntl(sp->pipefd,F_SETFD,FD_CLOEXEC); /* write the data to the pipe */ if(off = sftell(sfstdout)) { write(fds[1],sfsetbuf(sfstdout,(Void_t*)sfstdout,0),(size_t)off); sfpurge(sfstdout); } if((sfset(sfstdout,0,0)&SF_STRING) || fstat(1,&statb)<0) statb.st_ino = 0; sfclose(sfstdout); if((sh_fcntl(fds[1],F_DUPFD, 1)) != 1) errormsg(SH_DICT,ERROR_system(1),e_redirect); sh_close(fds[1]); if(statb.st_ino) for(fd=0; fd < 10; fd++) { if(fd==1 || ((shp->fdstatus[fd]&(IONOSEEK|IOSEEK|IOWRITE))!=(IOSEEK|IOWRITE)) || fstat(fd,&statx)<0) continue; if(statb.st_ino==statx.st_ino && statb.st_dev==statx.st_dev) { sh_close(fd); fcntl(1,F_DUPFD, fd); } } skip: sh_iostream(shp,1); sfset(sfstdout,SF_SHARE|SF_PUBLIC,1); sfpool(sfstdout,shp->outpool,SF_WRITE); if(pp && pp->olist && pp->olist->strm == sfstdout) pp->olist->strm = 0; } }