/* * libexec.c * * Small wrapper for execve() * * (c) 2024 Dario Rodriguez * This file is in the public domain. */ #include #include #include #include #include #include #include #ifndef FIONREAD #include #endif #include #include "libexec.h" #ifndef RD #define RD 0 #endif #ifndef WR #define WR 1 #endif exec_t * exec_init(char *name, char *cmdwithstringsub, char **envp) { exec_t *exec; char *ptr; int n; if((exec=malloc(sizeof(exec_t)))==NULL) return(NULL); /* insuf. mem. */ memset(exec,0,sizeof(exec_t)); exec->pid=-1; exec->laststatus=-1; exec->fdin=exec->fdout=exec->fderr=-1; exec->envp=envp; if(name!=NULL && (exec->name=strdup(name))==NULL) { exec_free(exec),exec=NULL; return(NULL); /* insuf. mem. */ } for(ptr=strchr(cmdwithstringsub,'%') ;ptr!=NULL && ptr[1]=='s' ;ptr=strchr(ptr+1,'%') ) { for(n=0;(ptr-(n+1))>=cmdwithstringsub && ptr[-(n+1)]=='%';) { n++; } if(((n+1)%2)==0) continue; /* this '%' is escaped (duplicated) */ break; /* found the first %s */ } exec->has_param=(ptr!=NULL)?1:0; if(!(exec->has_param)) { exec->prebuf=strdup(cmdwithstringsub); exec->postbuf=strdup(""); } else { if((exec->prebuf=malloc((ptr-cmdwithstringsub)+1))!=NULL) { memcpy(exec->prebuf,cmdwithstringsub,ptr-cmdwithstringsub); exec->prebuf[ptr-cmdwithstringsub]='\0'; } exec->postbuf=strdup(ptr+2); } if(exec->prebuf==NULL || exec->postbuf==NULL) { exec_free(exec),exec=NULL; return(NULL); /* insuf. mem. */ } return(exec); } void exec_free(exec_t *exec) { if(exec==NULL) return; if(exec->pid!=-1) exec_close(exec); if(exec->name!=NULL) free(exec->name),exec->name=NULL; if(exec->prebuf!=NULL) free(exec->prebuf),exec->prebuf=NULL; if(exec->postbuf!=NULL) free(exec->postbuf),exec->postbuf=NULL; if(exec->fdin!=-1) close(exec->fdin),exec->fdin=-1; if(exec->fdout!=-1) close(exec->fdout),exec->fdout=-1; if(exec->fderr!=-1) close(exec->fderr),exec->fderr=-1; free(exec),exec=NULL; return; } int exec_open(exec_t *exec, char *stringsub) { char cmd[1024]; int i,n; int pipein[2],pipeout[2],pipeerr[2]; int in_error; if(exec==NULL) return(-1); /* sanity check error */ if(exec->pid!=-1) return(-1); /* there is already a running child */ snprintf(cmd,sizeof(cmd),"%s%s%s",exec->prebuf,(exec->has_param && stringsub!=NULL)?stringsub:"",exec->postbuf); cmd[sizeof(cmd)-1]='\0'; pipein[RD]=pipein[WR]=-1; pipeout[RD]=pipeout[WR]=-1; pipeerr[RD]=pipeerr[WR]=-1; in_error=0; if(pipe(pipein)==-1 || pipe(pipeout)==-1 || pipe(pipeerr)==-1) in_error=1; if(in_error==0 && (exec->pid=fork())==0) { char *params[1024]; int usedparams; char *ptr,*end,*realend; char endchar; realend=cmd+strlen(cmd); for(usedparams=0,ptr=cmd ;ptr!=realend && usedparams<((sizeof(params)/sizeof(params[0]))-1) ; ) { endchar=(ptr[0]=='\"')?'\"':' '; ptr+=(endchar=='\"')?1:0; params[usedparams++]=ptr; for(end=strchr(ptr,endchar) ;end!=NULL ;end=strchr(end+1,endchar) ) { for(n=0;(end-(n+1))>=ptr && end[-(n+1)]=='\\';) { n++; } if((n%2)==1) continue; /* this '\"' or ' ' is escaped */ break; /* found the first unescaped '\"' or ' ' */ } end=(end==NULL)?realend:end; *end='\0'; ptr=(endenvp); exit(2); /* couldn't execve */ } else if(in_error==0 && exec->pid>0) { close(pipein[RD]),pipein[RD]=-1; close(pipeout[WR]),pipeout[WR]=-1; close(pipeerr[WR]),pipeerr[WR]=-1; exec->fdin=pipein[WR],pipein[WR]=-1; exec->fdout=pipeout[RD],pipeout[RD]=-1; exec->fderr=pipeerr[RD],pipeerr[RD]=-1; } else { in_error=1; } if(in_error) { int *curfd[]={pipein,pipein+1,pipeout,pipeout+1,pipeerr,pipeerr+1}; for(i=0;i<(sizeof(curfd)/sizeof(curfd[0]));i++) { if(*(curfd[i])!=-1) close(*(curfd[i])),*(curfd[i])=-1; } return(-1); /* was in error */ } return(0); } int exec_close(exec_t *exec) { if(exec==NULL) return(-1); /* sanity check failed */ if(exec->pid==-1) return(-1); /* nothing to do as child is not started */ kill(exec->pid,SIGTERM); return(0); } int exec_reap(exec_t *exec1, /* exec_t *exec2, */ ...) { int status; int flag_reaped; exec_t *exec; va_list ap; flag_reaped=0; va_start(ap, exec1); for(exec=exec1;exec!=NULL;exec=va_arg(ap,exec_t *)) { if(exec->pid==-1) continue; if(waitpid(exec->pid,&status,WNOHANG)==exec->pid) { exec->pid=-1; exec->laststatus=status; close(exec->fdin),exec->fdin=-1; close(exec->fdout),exec->fdout=-1; close(exec->fderr),exec->fderr=-1; flag_reaped=1; continue; } } va_end(ap); return((flag_reaped==0)?-1:0); /* -1: no child has exited, 0: at least one child exited */ } int executil_installsignal(int sig, void (*f)(int)) { struct sigaction sa; sa.sa_handler=f; sigemptyset(&sa.sa_mask); sa.sa_flags=0; return(sigaction(sig,&sa,NULL)); } int executil_queued(int fd) { int n; if(ioctl(fd,FIONREAD,&n)!=0) return(-1); return(n); } exec_t * executil_fd2exec(int fd, exec_t *exec1, /* exec_t *exec2, */ ...) { exec_t *exec; va_list ap; va_start(ap, exec1); for(exec=exec1;exec!=NULL;exec=va_arg(ap,exec_t *)) { if(exec->fdin==fd || exec->fdout==fd || exec->fderr==fd) break; } va_end(ap); return(exec); }