File Coverage

Spawn.xs
Criterion Covered Total %
statement 105 145 72.4
branch 52 130 40.0
condition n/a
subroutine n/a
pod n/a
total 157 275 57.0


line stmt bran cond sub pod time code
1             #define PERL_NO_GET_CONTEXT
2              
3             #include "EXTERN.h"
4             #include "perl.h"
5             #include "XSUB.h"
6              
7             #include "ppport.h"
8             /* From handy.h since 5.027006 */
9             #ifndef strBEGINs
10             #define strBEGINs(s1,s2) (strncmp(s1,"" s2 "", sizeof(s2)-1) == 0)
11             #endif
12             /* From embed.h, but Perl_signal_* is internal as of 5.38 */
13             #ifndef rsignal_save
14             #define rsignal_save(a,b,c) my_rsignal_save(aTHX_ a,b,c)
15             #define rsignal_restore(a,b) my_rsignal_restore(aTHX_ a,b)
16             #endif
17              
18             #include
19              
20             extern char **environ;
21              
22              
23             /* borrowed from Perl's util.c */
24             int
25 4           my_rsignal_save(pTHX_ int signo, Sighandler_t handler, Sigsave_t *save)
26             {
27             dVAR;
28             struct sigaction act;
29              
30             /* PERL_ARGS_ASSERT_RSIGNAL_SAVE; */
31             assert(save);
32              
33             #ifdef USE_ITHREADS
34             /* only "parent" interpreter can diddle signals */
35             if (PL_curinterp != aTHX)
36             return -1;
37             #endif
38              
39 4           act.sa_handler = (void(*)(int))handler;
40 4           sigemptyset(&act.sa_mask);
41 4           act.sa_flags = 0;
42             #ifdef SA_RESTART
43 4 50         if (PL_signals & PERL_SIGNALS_UNSAFE_FLAG)
44 0           act.sa_flags |= SA_RESTART; /* SVR4, 4.3+BSD */
45             #endif
46             #if defined(SA_NOCLDWAIT) && !defined(BSDish) /* See [perl #18849] */
47 4 50         if (signo == SIGCHLD && handler == (Sighandler_t) SIG_IGN)
    0          
48 0           act.sa_flags |= SA_NOCLDWAIT;
49             #endif
50 4           return sigaction(signo, &act, save);
51             }
52              
53             int
54 3           my_rsignal_restore(pTHX_ int signo, Sigsave_t *save)
55             {
56             dVAR;
57             #ifdef USE_ITHREADS
58             /* only "parent" interpreter can diddle signals */
59             if (PL_curinterp != aTHX)
60             return -1;
61             #endif
62              
63 3           return sigaction(signo, save, (struct sigaction *)NULL);
64             }
65              
66              
67             Pid_t
68 4           do_posix_spawn (const char *cmd, char **argv) {
69             Pid_t pid;
70             posix_spawnattr_t attr;
71 4           short flags = 0;
72              
73 4           posix_spawnattr_init(&attr);
74 4           posix_spawnattr_setflags(&attr, flags);
75 4           errno = posix_spawnp(&pid, cmd, NULL, &attr, argv, environ);
76 4           posix_spawnattr_destroy(&attr);
77              
78 4 100         return errno ? 0 : pid;
79             }
80              
81              
82             /* borrowed from Perl's doio.c: S_exec_failed */
83             static void
84 1           S_posix_spawn_failed (pTHX_ const char *cmd)
85             {
86 1           const int e = errno;
87             /* PERL_ARGS_ASSERT_EXEC_FAILED; */
88             assert(cmd);
89 1 50         if (ckWARN(WARN_EXEC))
90 1           Perl_warner(aTHX_ packWARN(WARN_EXEC), "Can't spawn \"%s\": %s",
91             cmd, Strerror(e));
92 1           }
93              
94              
95             /* borrowed from Perl's doio.c: Perl_do_aexec5 */
96             Pid_t
97 1           do_posix_spawn3 (pTHX_ SV *really, SV **mark, SV **sp)
98             {
99             /* PERL_ARGS_ASSERT_DO_AEXEC5; */
100             assert(mark); assert(sp);
101             assert(sp >= mark);
102 1           ENTER;
103             {
104 1           Pid_t pid = 0;
105             const char **argv, **a;
106 1           const char *tmps = NULL;
107 1 50         Newx(argv, sp - mark + 1, const char*);
108 1           SAVEFREEPV(argv);
109 1           a = argv;
110              
111 5 100         while (++mark <= sp) {
112 4 50         if (*mark) {
113 4           char *arg = savepv(SvPV_nolen_const(*mark));
114 4           SAVEFREEPV(arg);
115 4           *a++ = arg;
116             } else
117 0           *a++ = "";
118             }
119 1           *a = NULL;
120 1 50         if (really) {
121 0           tmps = savepv(SvPV_nolen_const(really));
122 0           SAVEFREEPV(tmps);
123             }
124 1 50         if ((!really && argv[0] && *argv[0] != '/') ||
    50          
    50          
    50          
125 0 0         (really && *tmps != '/')) /* will posix_spawn use PATH? */
126 0 0         TAINT_ENV(); /* testing IFS here is overkill, probably */
127 1           PERL_FPU_PRE_EXEC
128 1 50         if (really && *tmps) {
    0          
129 0           pid = do_posix_spawn(tmps,EXEC_ARGV_CAST(argv));
130 1           return pid;
131 1 50         } else if (argv[0]) {
132 1           pid = do_posix_spawn(argv[0],EXEC_ARGV_CAST(argv));
133 1           return pid;
134             } else {
135 0           SETERRNO(ENOENT,RMS_FNF);
136             }
137 0           PERL_FPU_POST_EXEC
138 0 0         S_posix_spawn_failed(aTHX_ (really ? tmps : argv[0] ? argv[0] : ""));
    0          
139             }
140 0           LEAVE;
141 0           return FALSE;
142             }
143              
144              
145             Pid_t
146 2           do_posix_spawn_shell (const char *path, char *name, char *flags, char *cmd)
147             {
148             Pid_t pid;
149 2           const char *argv[] = { name, flags, cmd, NULL };
150 2           pid = do_posix_spawn(path, (char **)argv);
151 2           return pid;
152             }
153              
154              
155             /* borrowed from Perl's doio.c: Perl_do_exec3 */
156             Pid_t
157 3           do_posix_spawn1 (pTHX_ const char *incmd)
158             {
159 3           Pid_t pid = 0;
160             const char **argv, **a;
161             char *s;
162             char *buf;
163             char *cmd;
164             /* Make a copy so we can change it */
165 3           const Size_t cmdlen = strlen(incmd) + 1;
166              
167             /* PERL_ARGS_ASSERT_DO_EXEC3; */
168             assert(incmd);
169              
170 3           ENTER;
171 3           Newx(buf, cmdlen, char);
172 3           SAVEFREEPV(buf);
173 3           cmd = buf;
174 3           memcpy(cmd, incmd, cmdlen);
175              
176 3 50         while (*cmd && isSPACE(*cmd))
    50          
177 0           cmd++;
178              
179             /* save an extra exec if possible */
180              
181             #ifdef CSH
182             {
183             #define PERL_FLAGS_MAX 10
184             char flags[PERL_FLAGS_MAX];
185             if (strnEQ(cmd,PL_cshname,PL_cshlen) &&
186             strBEGINs(cmd+PL_cshlen," -c")) {
187             my_strlcpy(flags, "-c", PERL_FLAGS_MAX);
188             s = cmd+PL_cshlen+3;
189             if (*s == 'f') {
190             s++;
191             my_strlcat(flags, "f", PERL_FLAGS_MAX - 2);
192             }
193             if (*s == ' ')
194             s++;
195             if (*s++ == '\'') {
196             char * const ncmd = s;
197              
198             while (*s)
199             s++;
200             if (s[-1] == '\n')
201             *--s = '\0';
202             if (s[-1] == '\'') {
203             *--s = '\0';
204             PERL_FPU_PRE_EXEC
205             pid = do_posix_spawn_shell(PL_cshname, "csh", flags, ncmd);
206             PERL_FPU_POST_EXEC
207             if (pid) return pid;
208             *s = '\'';
209             S_posix_spawn_failed(aTHX_ PL_cshname);
210             goto leave;
211             }
212             }
213             }
214             }
215             #endif /* CSH */
216              
217             /* see if there are shell metacharacters in it */
218              
219 3 50         if (*cmd == '.' && isSPACE(cmd[1]))
    0          
220 0           goto doshell;
221              
222 3 50         if (strBEGINs(cmd,"exec") && isSPACE(cmd[4]))
    0          
223 0           goto doshell;
224              
225 3           s = cmd;
226 26 100         while (isWORDCHAR(*s))
227 23           s++; /* catch VAR=val gizmo */
228 3 50         if (*s == '=')
229 0           goto doshell;
230              
231 50 100         for (s = cmd; *s; s++) {
232 49 100         if (*s != ' ' && !isALPHA(*s) &&
    100          
233 7 100         memCHRs("$&*(){}[]'\";\\|?<>~`\n",*s)) {
234 2 50         if (*s == '\n' && !s[1]) {
    0          
235 0           *s = '\0';
236 0           break;
237             }
238             /* handle the 2>&1 construct at the end */
239 2 50         if (*s == '>' && s[1] == '&' && s[2] == '1'
    0          
    0          
240 0 0         && s > cmd + 1 && s[-1] == '2' && isSPACE(s[-2])
    0          
    0          
241 0 0         && (!s[3] || isSPACE(s[3])))
    0          
242             {
243 0           const char *t = s + 3;
244              
245 0 0         while (*t && isSPACE(*t))
    0          
246 0           ++t;
247 0 0         if (!*t && (PerlLIO_dup2(1,2) != -1)) {
    0          
248 0           s[-2] = '\0';
249 0           break;
250             }
251             }
252 2           doshell:
253 2           PERL_FPU_PRE_EXEC
254 2           pid = do_posix_spawn_shell(PL_sh_path, "sh", "-c", cmd);
255 2           PERL_FPU_POST_EXEC
256 2 50         if (pid) return pid;
257 0           S_posix_spawn_failed(aTHX_ PL_sh_path);
258 0           goto leave;
259             }
260             }
261              
262 1 50         Newx(argv, (s - cmd) / 2 + 2, const char*);
263 1           SAVEFREEPV(argv);
264 1           cmd = savepvn(cmd, s-cmd);
265 1           SAVEFREEPV(cmd);
266 1           a = argv;
267 2 100         for (s = cmd; *s;) {
268 1 50         while (isSPACE(*s))
269 0           s++;
270 1 50         if (*s)
271 1           *(a++) = s;
272 20 100         while (*s && !isSPACE(*s))
    50          
273 19           s++;
274 1 50         if (*s)
275 0           *s++ = '\0';
276             }
277 1           *a = NULL;
278 1 50         if (argv[0]) {
279 1           PERL_FPU_PRE_EXEC
280 1           pid = do_posix_spawn(argv[0],EXEC_ARGV_CAST(argv));
281 1           PERL_FPU_POST_EXEC
282 1 50         if (pid) return pid;
283 1 50         if (errno == ENOEXEC) /* for system V NIH syndrome */
284 0           goto doshell;
285 1           S_posix_spawn_failed(aTHX_ argv[0]);
286             }
287 0           leave:
288 1           LEAVE;
289 1           return FALSE;
290             }
291              
292              
293              
294             /* borrowed from Perl's pp_sys.c: pp_exec */
295             XS(XS_POSIX__RT__Spawn_spawn); /* prototype to pass -Wmissing-prototypes */
296 4           XS(XS_POSIX__RT__Spawn_spawn) {
297 4           dVAR; dSP; dMARK; dORIGMARK; dTARGET;
298             Pid_t pid;
299              
300 4 50         if (PL_tainting) {
301 0 0         TAINT_ENV();
302 0 0         while (++MARK <= SP) {
303             /* stringify for taint check */
304 0           (void)SvPV_nolen_const(*MARK);
305 0 0         if (PL_tainted)
306 0           break;
307             }
308 0           MARK = ORIGMARK;
309 0 0         TAINT_PROPER("spawn");
310             }
311              
312 4           PERL_FLUSHALL_FOR_CHILD;
313              
314             /* indirect object syntax */
315             if (0 && PL_op->op_flags & OPf_STACKED) {
316             SV * const really = *++MARK;
317             pid = do_posix_spawn3(aTHX_ really, MARK, SP);
318             }
319 4 100         else if (SP - MARK != 1)
320 1           pid = do_posix_spawn3(aTHX_ NULL, MARK, SP);
321             else {
322 3           pid = do_posix_spawn1(aTHX_ SvPV_nolen(sv_mortalcopy(*SP)));
323             }
324              
325 4           SP = ORIGMARK;
326 4 100         XPUSHi(pid);
    50          
327 4           PUTBACK;
328 4           return;
329             }
330              
331             MODULE = POSIX::RT::Spawn PACKAGE = POSIX::RT::Spawn
332              
333             PROTOTYPES: DISABLE
334              
335             BOOT:
336 2           newXS("POSIX::RT::Spawn::spawn", XS_POSIX__RT__Spawn_spawn, __FILE__);