1 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,249 @@ |
1 |
+/* |
|
2 |
+ * libexec.c |
|
3 |
+ * |
|
4 |
+ * Small wrapper for execve() |
|
5 |
+ * |
|
6 |
+ * (c) 2024 Dario Rodriguez <antartica@whereismybit.com> |
|
7 |
+ * This file is in the public domain. |
|
8 |
+ */ |
|
9 |
+ |
|
10 |
+#include <stdio.h> |
|
11 |
+#include <stdlib.h> |
|
12 |
+#include <unistd.h> |
|
13 |
+#include <string.h> |
|
14 |
+#include <signal.h> |
|
15 |
+#include <sys/wait.h> |
|
16 |
+#include <sys/ioctl.h> |
|
17 |
+#ifndef FIONREAD |
|
18 |
+#include <sys/filio.h> |
|
19 |
+#endif |
|
20 |
+#include <stdarg.h> |
|
21 |
+#include "libexec.h" |
|
22 |
+ |
|
23 |
+#ifndef RD |
|
24 |
+#define RD 0 |
|
25 |
+#endif |
|
26 |
+ |
|
27 |
+#ifndef WR |
|
28 |
+#define WR 1 |
|
29 |
+#endif |
|
30 |
+ |
|
31 |
+exec_t * |
|
32 |
+exec_init(char *name, char *cmdwithstringsub, char **envp) |
|
33 |
+{ |
|
34 |
+ exec_t *exec; |
|
35 |
+ char *ptr; |
|
36 |
+ int n; |
|
37 |
+ if((exec=malloc(sizeof(exec_t)))==NULL) |
|
38 |
+ return(NULL); /* insuf. mem. */ |
|
39 |
+ memset(exec,0,sizeof(exec_t)); |
|
40 |
+ exec->pid=-1; |
|
41 |
+ exec->laststatus=-1; |
|
42 |
+ exec->fdin=exec->fdout=exec->fderr=-1; |
|
43 |
+ exec->envp=envp; |
|
44 |
+ if(name!=NULL && (exec->name=strdup(name))==NULL) { |
|
45 |
+ exec_free(exec),exec=NULL; |
|
46 |
+ return(NULL); /* insuf. mem. */ |
|
47 |
+ } |
|
48 |
+ for(ptr=strchr(cmdwithstringsub,'%') |
|
49 |
+ ;ptr!=NULL && ptr[1]=='s' |
|
50 |
+ ;ptr=strchr(ptr+1,'%') |
|
51 |
+ ) { |
|
52 |
+ for(n=0;(ptr-(n+1))>=cmdwithstringsub && ptr[-(n+1)]=='%';) { |
|
53 |
+ n++; |
|
54 |
+ } |
|
55 |
+ if(((n+1)%2)==0) |
|
56 |
+ continue; /* this '%' is escaped (duplicated) */ |
|
57 |
+ break; /* found the first %s */ |
|
58 |
+ } |
|
59 |
+ exec->has_param=(ptr!=NULL)?1:0; |
|
60 |
+ if(!(exec->has_param)) { |
|
61 |
+ exec->prebuf=strdup(cmdwithstringsub); |
|
62 |
+ exec->postbuf=strdup(""); |
|
63 |
+ } else { |
|
64 |
+ if((exec->prebuf=malloc((ptr-cmdwithstringsub)+1))!=NULL) { |
|
65 |
+ memcpy(exec->prebuf,cmdwithstringsub,ptr-cmdwithstringsub); |
|
66 |
+ exec->prebuf[ptr-cmdwithstringsub]='\0'; |
|
67 |
+ } |
|
68 |
+ exec->postbuf=strdup(ptr+2); |
|
69 |
+ } |
|
70 |
+ if(exec->prebuf==NULL || exec->postbuf==NULL) { |
|
71 |
+ exec_free(exec),exec=NULL; |
|
72 |
+ return(NULL); /* insuf. mem. */ |
|
73 |
+ } |
|
74 |
+ return(exec); |
|
75 |
+} |
|
76 |
+ |
|
77 |
+void |
|
78 |
+exec_free(exec_t *exec) |
|
79 |
+{ |
|
80 |
+ if(exec==NULL) |
|
81 |
+ return; |
|
82 |
+ if(exec->pid!=-1) |
|
83 |
+ exec_close(exec); |
|
84 |
+ if(exec->name!=NULL) |
|
85 |
+ free(exec->name),exec->name=NULL; |
|
86 |
+ if(exec->prebuf!=NULL) |
|
87 |
+ free(exec->prebuf),exec->prebuf=NULL; |
|
88 |
+ if(exec->postbuf!=NULL) |
|
89 |
+ free(exec->postbuf),exec->postbuf=NULL; |
|
90 |
+ if(exec->fdin!=-1) |
|
91 |
+ close(exec->fdin),exec->fdin=-1; |
|
92 |
+ if(exec->fdout!=-1) |
|
93 |
+ close(exec->fdout),exec->fdout=-1; |
|
94 |
+ if(exec->fderr!=-1) |
|
95 |
+ close(exec->fderr),exec->fderr=-1; |
|
96 |
+ free(exec),exec=NULL; |
|
97 |
+ return; |
|
98 |
+} |
|
99 |
+ |
|
100 |
+int |
|
101 |
+exec_open(exec_t *exec, char *stringsub) |
|
102 |
+{ |
|
103 |
+ char cmd[1024]; |
|
104 |
+ int i,n; |
|
105 |
+ int pipein[2],pipeout[2],pipeerr[2]; |
|
106 |
+ int in_error; |
|
107 |
+ if(exec==NULL) |
|
108 |
+ return(-1); /* sanity check error */ |
|
109 |
+ if(exec->pid!=-1) |
|
110 |
+ return(-1); /* there is already a running child */ |
|
111 |
+ snprintf(cmd,sizeof(cmd),"%s%s%s",exec->prebuf,(exec->has_param && stringsub!=NULL)?stringsub:"",exec->postbuf); |
|
112 |
+ cmd[sizeof(cmd)-1]='\0'; |
|
113 |
+ pipein[RD]=pipein[WR]=-1; |
|
114 |
+ pipeout[RD]=pipeout[WR]=-1; |
|
115 |
+ pipeerr[RD]=pipeerr[WR]=-1; |
|
116 |
+ in_error=0; |
|
117 |
+ if(pipe(pipein)==-1 || pipe(pipeout)==-1 || pipe(pipeerr)==-1) |
|
118 |
+ in_error=1; |
|
119 |
+ if(in_error==0 && (exec->pid=fork())==0) { |
|
120 |
+ char *params[1024]; |
|
121 |
+ int usedparams; |
|
122 |
+ char *ptr,*end,*realend; |
|
123 |
+ char endchar; |
|
124 |
+ realend=cmd+strlen(cmd); |
|
125 |
+ for(usedparams=0,ptr=cmd |
|
126 |
+ ;ptr!=realend && usedparams<((sizeof(params)/sizeof(params[0]))-1) |
|
127 |
+ ; |
|
128 |
+ ) { |
|
129 |
+ endchar=(ptr[0]=='\"')?'\"':' '; |
|
130 |
+ ptr+=(endchar=='\"')?1:0; |
|
131 |
+ params[usedparams++]=ptr; |
|
132 |
+ for(end=strchr(ptr,endchar) |
|
133 |
+ ;end!=NULL |
|
134 |
+ ;end=strchr(end+1,endchar) |
|
135 |
+ ) { |
|
136 |
+ for(n=0;(end-(n+1))>=ptr && end[-(n+1)]=='\\';) { |
|
137 |
+ n++; |
|
138 |
+ } |
|
139 |
+ if((n%2)==1) |
|
140 |
+ continue; /* this '\"' or ' ' is escaped */ |
|
141 |
+ break; /* found the first unescaped '\"' or ' ' */ |
|
142 |
+ } |
|
143 |
+ end=(end==NULL)?realend:end; |
|
144 |
+ *end='\0'; |
|
145 |
+ ptr=(end<realend)?end+1:realend; |
|
146 |
+ if(endchar=='\"' && ptr[0]==' ') |
|
147 |
+ ptr++; |
|
148 |
+ } |
|
149 |
+ params[usedparams]=NULL; |
|
150 |
+ if(usedparams==0) |
|
151 |
+ exit(1); /* nothing to execute */ |
|
152 |
+ dup2(pipein[RD],0); |
|
153 |
+ dup2(pipeout[WR],1); |
|
154 |
+ dup2(pipeerr[WR],2); |
|
155 |
+ for(i=3;i<1024;i++) |
|
156 |
+ close(i); |
|
157 |
+ execve(params[0],params,exec->envp); |
|
158 |
+ exit(2); /* couldn't execve */ |
|
159 |
+ } else if(in_error==0 && exec->pid>0) { |
|
160 |
+ close(pipein[RD]),pipein[RD]=-1; |
|
161 |
+ close(pipeout[WR]),pipeout[WR]=-1; |
|
162 |
+ close(pipeerr[WR]),pipeerr[WR]=-1; |
|
163 |
+ exec->fdin=pipein[WR],pipein[WR]=-1; |
|
164 |
+ exec->fdout=pipeout[RD],pipeout[RD]=-1; |
|
165 |
+ exec->fderr=pipeerr[RD],pipeerr[RD]=-1; |
|
166 |
+ } else { |
|
167 |
+ in_error=1; |
|
168 |
+ } |
|
169 |
+ if(in_error) { |
|
170 |
+ int *curfd[]={pipein,pipein+1,pipeout,pipeout+1,pipeerr,pipeerr+1}; |
|
171 |
+ for(i=0;i<(sizeof(curfd)/sizeof(curfd[0]));i++) { |
|
172 |
+ if(*(curfd[i])!=-1) |
|
173 |
+ close(*(curfd[i])),*(curfd[i])=-1; |
|
174 |
+ } |
|
175 |
+ return(-1); /* was in error */ |
|
176 |
+ } |
|
177 |
+ return(0); |
|
178 |
+} |
|
179 |
+ |
|
180 |
+int |
|
181 |
+exec_close(exec_t *exec) |
|
182 |
+{ |
|
183 |
+ if(exec==NULL) |
|
184 |
+ return(-1); /* sanity check failed */ |
|
185 |
+ if(exec->pid==-1) |
|
186 |
+ return(-1); /* nothing to do as child is not started */ |
|
187 |
+ kill(exec->pid,SIGTERM); |
|
188 |
+ return(0); |
|
189 |
+} |
|
190 |
+ |
|
191 |
+int |
|
192 |
+exec_reap(exec_t *exec1, /* exec_t *exec2, */ ...) |
|
193 |
+{ |
|
194 |
+ int status; |
|
195 |
+ int flag_reaped; |
|
196 |
+ exec_t *exec; |
|
197 |
+ va_list ap; |
|
198 |
+ flag_reaped=0; |
|
199 |
+ va_start(ap, exec1); |
|
200 |
+ for(exec=exec1;exec!=NULL;exec=va_arg(ap,exec_t *)) { |
|
201 |
+ if(exec->pid==-1) |
|
202 |
+ continue; |
|
203 |
+ if(waitpid(exec->pid,&status,WNOHANG)==exec->pid) { |
|
204 |
+ exec->pid=-1; |
|
205 |
+ exec->laststatus=status; |
|
206 |
+ close(exec->fdin),exec->fdin=-1; |
|
207 |
+ close(exec->fdout),exec->fdout=-1; |
|
208 |
+ close(exec->fderr),exec->fderr=-1; |
|
209 |
+ flag_reaped=1; |
|
210 |
+ continue; |
|
211 |
+ } |
|
212 |
+ } |
|
213 |
+ va_end(ap); |
|
214 |
+ return((flag_reaped==0)?-1:0); /* -1: no child has exited, 0: at least one child exited */ |
|
215 |
+} |
|
216 |
+ |
|
217 |
+int |
|
218 |
+executil_installsignal(int sig, void (*f)(int)) |
|
219 |
+{ |
|
220 |
+ struct sigaction sa; |
|
221 |
+ sa.sa_handler=f; |
|
222 |
+ sigemptyset(&sa.sa_mask); |
|
223 |
+ sa.sa_flags=0; |
|
224 |
+ return(sigaction(sig,&sa,NULL)); |
|
225 |
+} |
|
226 |
+ |
|
227 |
+int |
|
228 |
+executil_queued(int fd) |
|
229 |
+{ |
|
230 |
+ int n; |
|
231 |
+ if(ioctl(fd,FIONREAD,&n)!=0) |
|
232 |
+ return(-1); |
|
233 |
+ return(n); |
|
234 |
+} |
|
235 |
+ |
|
236 |
+exec_t * |
|
237 |
+executil_fd2exec(int fd, exec_t *exec1, /* exec_t *exec2, */ ...) |
|
238 |
+{ |
|
239 |
+ exec_t *exec; |
|
240 |
+ va_list ap; |
|
241 |
+ va_start(ap, exec1); |
|
242 |
+ for(exec=exec1;exec!=NULL;exec=va_arg(ap,exec_t *)) { |
|
243 |
+ if(exec->fdin==fd || exec->fdout==fd || exec->fderr==fd) |
|
244 |
+ break; |
|
245 |
+ } |
|
246 |
+ va_end(ap); |
|
247 |
+ return(exec); |
|
248 |
+} |
|
249 |
+ |