Browse code

Initial commit

Dario Rodriguez authored on 02/06/2024 20:53:59
Showing 5 changed files
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 14
new file mode 100644
... ...
@@ -0,0 +1 @@
1
+libexec is a simple wrapper over execve
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
+