1 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,13 @@ |
1 |
+CC=gcc |
|
2 |
+CFLAGS=-Wall -g |
|
3 |
+LDFLAGS= |
|
4 |
+ |
|
5 |
+all: test_libexec |
|
6 |
+ |
|
7 |
+libexec.o: libexec.c libexec.h |
|
8 |
+ |
|
9 |
+test_libexec: test_libexec.c libexec.o |
|
10 |
+ $(CC) $(CFLAGS) $(LDFLAGS) test_libexec.c libexec.o -o test_libexec |
|
11 |
+ |
|
12 |
+clean: |
|
13 |
+ rm -f libexec.o test_libexec |
0 | 2 |
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 |
+ |
0 | 250 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,43 @@ |
1 |
+/* |
|
2 |
+ * libexec.h |
|
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 |
+typedef struct exec_t { |
|
11 |
+ char *name; |
|
12 |
+ char **envp; |
|
13 |
+ char *prebuf; |
|
14 |
+ char *postbuf; |
|
15 |
+ int has_param; |
|
16 |
+ int fdin; |
|
17 |
+ int fdout; |
|
18 |
+ int fderr; |
|
19 |
+ int pid; |
|
20 |
+ int laststatus; |
|
21 |
+} exec_t; |
|
22 |
+ |
|
23 |
+exec_t *exec_init(char *name, char *commandwithstringsub, char **envp); |
|
24 |
+void exec_free(exec_t *exec); |
|
25 |
+ |
|
26 |
+int exec_open(exec_t *exec, char *stringsub); |
|
27 |
+int exec_close(exec_t *exec); |
|
28 |
+ |
|
29 |
+#ifndef __GNUC__ |
|
30 |
+int exec_reap(exec_t *exec1, /* exec_t *exec2, */ ...); |
|
31 |
+#else |
|
32 |
+ int exec_reap(exec_t *exec1, /* exec_t *exec2, */ ...) __attribute__((sentinel)); |
|
33 |
+#endif |
|
34 |
+ |
|
35 |
+int executil_installsignal(int sig, void (*f)(int)); |
|
36 |
+int executil_queued(int fd); |
|
37 |
+#ifndef __GNUC__ |
|
38 |
+exec_t *executil_fd2exec(int fd, exec_t *exec1, /* exec_t *exec2, */ ...); |
|
39 |
+#else |
|
40 |
+ exec_t *executil_fd2exec(int fd, exec_t *exec1, ...) __attribute__((sentinel)); |
|
41 |
+#endif |
|
42 |
+ |
|
43 |
+ |
0 | 44 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,132 @@ |
1 |
+/* |
|
2 |
+ * simple test program for libexec |
|
3 |
+ * |
|
4 |
+ * (c) 2024 Dario Rodriguez <antartica@whereismybit.com> |
|
5 |
+ * This file is in the public domain. |
|
6 |
+ */ |
|
7 |
+ |
|
8 |
+#include <stdio.h> |
|
9 |
+#include <stdlib.h> |
|
10 |
+#include <unistd.h> |
|
11 |
+#include <string.h> |
|
12 |
+#include <signal.h> |
|
13 |
+#include <time.h> |
|
14 |
+#include <sys/select.h> |
|
15 |
+#include "libexec.h" |
|
16 |
+ |
|
17 |
+volatile int flag_sigint; |
|
18 |
+volatile int flag_sigchld; |
|
19 |
+void sigchld(int sig); |
|
20 |
+void sigint(int sig); |
|
21 |
+ |
|
22 |
+ |
|
23 |
+int |
|
24 |
+main(int argc, char *argv[], char *envp[]) |
|
25 |
+{ |
|
26 |
+ struct timeval tv; |
|
27 |
+ exec_t *dateexec,*echoexec,*catexec; |
|
28 |
+ exec_t *exec; |
|
29 |
+ fd_set readset,writeset; |
|
30 |
+ flag_sigchld=flag_sigint=0; |
|
31 |
+ int maxfd; |
|
32 |
+ int i; |
|
33 |
+ int queued,nread; |
|
34 |
+ int fd; |
|
35 |
+ char *fdname; |
|
36 |
+ time_t curtimet,lasttimet; |
|
37 |
+ executil_installsignal(SIGINT,sigint); |
|
38 |
+ executil_installsignal(SIGCHLD,sigchld); |
|
39 |
+ dateexec=exec_init("date","/bin/sh -c \"while true ; do sleep 1 ; date ; echo . >&2 ; done\"",envp); |
|
40 |
+ echoexec=exec_init("echo","/bin/echo \"%s\"",envp); |
|
41 |
+ catexec=exec_init("cat","/bin/cat",envp); |
|
42 |
+ exec_open(dateexec,NULL); |
|
43 |
+ exec_open(catexec,NULL); |
|
44 |
+ lasttimet=0; |
|
45 |
+ while(flag_sigint==0) { |
|
46 |
+ int *readfds[]={ |
|
47 |
+ &(dateexec->fdout), |
|
48 |
+ &(dateexec->fderr), |
|
49 |
+ &(echoexec->fdout), |
|
50 |
+ &(echoexec->fderr), |
|
51 |
+ &(catexec->fdout), |
|
52 |
+ &(catexec->fderr), |
|
53 |
+ }; |
|
54 |
+ if(flag_sigchld) { |
|
55 |
+ exec_t *execs[]={dateexec,echoexec,catexec}; |
|
56 |
+ flag_sigchld=0; |
|
57 |
+ exec_reap(dateexec,echoexec,catexec,NULL); |
|
58 |
+ for(i=0;i<(sizeof(execs)/sizeof(execs[0]));i++) { |
|
59 |
+ exec=execs[i]; |
|
60 |
+ if(exec->laststatus!=-1) |
|
61 |
+ fprintf(stderr,"%s: exited with status %i\n",exec->name,exec->laststatus),exec->laststatus=-1; |
|
62 |
+ } |
|
63 |
+ } |
|
64 |
+ tv.tv_sec=1,tv.tv_usec=0; |
|
65 |
+ maxfd=0; |
|
66 |
+ FD_ZERO(&readset); |
|
67 |
+ for(i=0;i<(sizeof(readfds)/sizeof(readfds[0]));i++) { |
|
68 |
+ if(*(readfds[i])==-1) |
|
69 |
+ continue; |
|
70 |
+ FD_SET(*(readfds[i]),&readset); |
|
71 |
+ maxfd=(maxfd<*(readfds[i]))?*(readfds[i]):maxfd; |
|
72 |
+ } |
|
73 |
+ FD_ZERO(&writeset); |
|
74 |
+ curtimet=time(NULL); |
|
75 |
+ if(catexec->fdin!=-1 && curtimet!=lasttimet) { |
|
76 |
+ FD_SET(catexec->fdin,&writeset); |
|
77 |
+ maxfd=(maxfd<catexec->fdin)?catexec->fdin:maxfd; |
|
78 |
+ } |
|
79 |
+ select(maxfd+1,&readset,&writeset,NULL,&tv); |
|
80 |
+#if 1 |
|
81 |
+fprintf(stderr,"ITERATION\n"); |
|
82 |
+#endif |
|
83 |
+ for(i=0;i<(sizeof(readfds)/sizeof(readfds[0]));i++) { |
|
84 |
+ if(*(readfds[i])==-1 || !FD_ISSET(*(readfds[i]),&readset) || (exec=executil_fd2exec(*(readfds[i]),dateexec,echoexec,catexec,NULL))==NULL) |
|
85 |
+ continue; |
|
86 |
+ fd=*(readfds[i]); |
|
87 |
+ fdname=(fd==exec->fdout)?"stdout":"stderr"; |
|
88 |
+ queued=executil_queued(fd); |
|
89 |
+ if(queued<=0) { |
|
90 |
+ /* remote closed connection */ |
|
91 |
+ fprintf(stderr,"%s:%s: remote closed connection (queued:%i)\n",exec->name,fdname,queued); |
|
92 |
+ exec_close(exec); |
|
93 |
+ continue; |
|
94 |
+ } else { |
|
95 |
+ char buf[1024]; |
|
96 |
+ queued=(queued>(sizeof(buf)-1))?(sizeof(buf)-1):queued; |
|
97 |
+ if((nread=read(fd,buf,queued))<queued) { |
|
98 |
+ fprintf(stderr,"%s:%s short read (%i<%i)\n",exec->name,fdname,nread,queued); |
|
99 |
+ exec_close(exec); |
|
100 |
+ } |
|
101 |
+ buf[nread]='\0'; |
|
102 |
+ fprintf(stderr,"%s:%s: read \"",exec->name,fdname); |
|
103 |
+ fwrite(buf,1,nread,stderr); |
|
104 |
+ fprintf(stderr,"\"\n"); |
|
105 |
+ } |
|
106 |
+ } |
|
107 |
+ if(catexec->fdin!=-1 && FD_ISSET(catexec->fdin,&writeset)) { |
|
108 |
+ char msg[32]; |
|
109 |
+ snprintf(msg,sizeof(msg),"%li\n",(long)curtimet); |
|
110 |
+ msg[sizeof(msg)-1]='\0'; |
|
111 |
+ write(catexec->fdin,msg,strlen(msg)); |
|
112 |
+ lasttimet=curtimet; |
|
113 |
+ } |
|
114 |
+ if((time(NULL)%5)==0) { |
|
115 |
+ exec_open(echoexec,"multi 5s"); |
|
116 |
+ } |
|
117 |
+ } |
|
118 |
+ return(0); |
|
119 |
+} |
|
120 |
+ |
|
121 |
+void |
|
122 |
+sigint(int sig) |
|
123 |
+{ |
|
124 |
+ flag_sigint++; |
|
125 |
+} |
|
126 |
+ |
|
127 |
+void |
|
128 |
+sigchld(int sig) |
|
129 |
+{ |
|
130 |
+ flag_sigchld++; |
|
131 |
+} |
|
132 |
+ |