1 /* run.c --- routines for executing subprocesses under OS/2.
2
3 This file is part of GNU CVS.
4
5 GNU CVS is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by the
7 Free Software Foundation; either version 2, or (at your option) any
8 later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details. */
14
15 #include "cvs.h"
16
17 #include "os2inc.h"
18
19 #include <process.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <string.h>
26 #include <fcntl.h>
27 #include <errno.h>
28 #include <io.h>
29
30 #define STDIN 0
31 #define STDOUT 1
32 #define STDERR 2
33
34 static void run_add_arg PROTO((const char *s));
35 static void run_init_prog PROTO((void));
36
37 extern char *strtok ();
38
39 /*
40 * To exec a program under CVS, first call run_setup() to setup any initial
41 * arguments. The options to run_setup are essentially like printf(). The
42 * arguments will be parsed into whitespace separated words and added to the
43 * global run_argv list.
44 *
45 * Then, optionally call run_arg() for each additional argument that you'd like
46 * to pass to the executed program.
47 *
48 * Finally, call run_exec() to execute the program with the specified
49 * arguments.
50 * The execvp() syscall will be used, so that the PATH is searched correctly.
51 * File redirections can be performed in the call to run_exec().
52 */
53 static char **run_argv;
54 static int run_argc;
55 static int run_argc_allocated;
56
57 void
run_setup(const char * prog)58 run_setup (const char *prog)
59 {
60 char *cp;
61 int i;
62
63 char *run_prog;
64
65 /* clean out any malloc'ed values from run_argv */
66 for (i = 0; i < run_argc; i++)
67 {
68 if (run_argv[i])
69 {
70 free (run_argv[i]);
71 run_argv[i] = (char *) 0;
72 }
73 }
74 run_argc = 0;
75
76 run_prog = xstrdup (prog);
77
78 /* put each word into run_argv, allocating it as we go */
79 for (cp = strtok (run_prog, " \t"); cp; cp = strtok ((char *) NULL, " \t"))
80 run_add_arg (cp);
81
82 free (run_prog)
83 }
84
85 void
run_arg(s)86 run_arg (s)
87 const char *s;
88 {
89 run_add_arg (s);
90 }
91
92 /* Return a malloc'd copy of s, with double quotes around it. */
93 static char *
quote(const char * s)94 quote (const char *s)
95 {
96 size_t s_len = strlen (s);
97 char *copy = xmalloc (s_len + 3);
98 char *scan = copy;
99
100 *scan++ = '"';
101 strcpy (scan, s);
102 scan += s_len;
103 *scan++ = '"';
104 *scan++ = '\0';
105
106 return copy;
107 }
108
109 static void
run_add_arg(s)110 run_add_arg (s)
111 const char *s;
112 {
113 /* allocate more argv entries if we've run out */
114 if (run_argc >= run_argc_allocated)
115 {
116 run_argc_allocated += 50;
117 run_argv = (char **) xrealloc ((char *) run_argv,
118 run_argc_allocated * sizeof (char **));
119 }
120
121 if (s)
122 {
123 run_argv[run_argc] = (run_argc ? quote (s) : xstrdup (s));
124 run_argc++;
125 }
126 else
127 /* not post-incremented on purpose! */
128 run_argv[run_argc] = (char *) 0;
129 }
130
131 int
run_exec(stin,stout,sterr,flags)132 run_exec (stin, stout, sterr, flags)
133 char *stin;
134 char *stout;
135 char *sterr;
136 int flags;
137 {
138 int shin, shout, sherr;
139 int sain, saout, saerr; /* saved handles */
140 int mode_out, mode_err;
141 int status = -1;
142 int rerrno = 0;
143 int rval = -1;
144 void (*old_sigint) (int);
145
146 if (trace) /* if in trace mode */
147 {
148 (void) fprintf (stderr, "-> system(");
149 run_print (stderr);
150 (void) fprintf (stderr, ")\n");
151 }
152 if (noexec && (flags & RUN_REALLY) == 0) /* if in noexec mode */
153 return (0);
154
155 /*
156 * start the engine and take off
157 */
158
159 /* make sure that we are null terminated, since we didn't calloc */
160 run_add_arg ((char *) 0);
161
162 /* setup default file descriptor numbers */
163 shin = 0;
164 shout = 1;
165 sherr = 2;
166
167 /* set the file modes for stdout and stderr */
168 mode_out = mode_err = O_WRONLY | O_CREAT;
169 mode_out |= ((flags & RUN_STDOUT_APPEND) ? O_APPEND : O_TRUNC);
170 mode_err |= ((flags & RUN_STDERR_APPEND) ? O_APPEND : O_TRUNC);
171
172 /* open the files as required, shXX are shadows of stdin... */
173 if (stin && (shin = open (stin, O_RDONLY)) == -1)
174 {
175 rerrno = errno;
176 error (0, errno, "cannot open %s for reading (prog %s)",
177 stin, run_argv[0]);
178 goto out0;
179 }
180 if (stout && (shout = open (stout, mode_out, 0666)) == -1)
181 {
182 rerrno = errno;
183 error (0, errno, "cannot open %s for writing (prog %s)",
184 stout, run_argv[0]);
185 goto out1;
186 }
187 if (sterr && (flags & RUN_COMBINED) == 0)
188 {
189 if ((sherr = open (sterr, mode_err, 0666)) == -1)
190 {
191 rerrno = errno;
192 error (0, errno, "cannot open %s for writing (prog %s)",
193 sterr, run_argv[0]);
194 goto out2;
195 }
196 }
197 /* now save the standard handles */
198 sain = saout = saerr = -1;
199 sain = dup( 0); /* dup stdin */
200 saout = dup( 1); /* dup stdout */
201 saerr = dup( 2); /* dup stderr */
202
203 /* the new handles will be dup'd to the standard handles
204 * for the spawn.
205 */
206
207 if (shin != 0)
208 {
209 (void) dup2 (shin, 0);
210 (void) close (shin);
211 }
212 if (shout != 1)
213 {
214 (void) dup2 (shout, 1);
215 (void) close (shout);
216 }
217 if (flags & RUN_COMBINED)
218 (void) dup2 (1, 2);
219 else if (sherr != 2)
220 {
221 (void) dup2 (sherr, 2);
222 (void) close (sherr);
223 }
224
225 /* Ignore signals while we're running this. */
226 old_sigint = signal (SIGINT, SIG_IGN);
227
228 /* dup'ing is done. try to run it now */
229 rval = spawnvp ( P_WAIT, run_argv[0], run_argv);
230
231 /* Restore signal handling. */
232 signal (SIGINT, old_sigint);
233
234 /* restore the original file handles */
235 if (sain != -1) {
236 (void) dup2( sain, 0); /* re-connect stdin */
237 (void) close( sain);
238 }
239 if (saout != -1) {
240 (void) dup2( saout, 1); /* re-connect stdout */
241 (void) close( saout);
242 }
243 if (saerr != -1) {
244 (void) dup2( saerr, 2); /* re-connect stderr */
245 (void) close( saerr);
246 }
247
248 /* Recognize the return code for a failed subprocess. */
249 if (rval == -1)
250 return 2;
251 else
252 return rval; /* return child's exit status */
253
254 /* error cases */
255 /* cleanup the open file descriptors */
256 out2:
257 if (stout)
258 (void) close (shout);
259 out1:
260 if (stin)
261 (void) close (shin);
262
263 out0:
264 if (rerrno)
265 errno = rerrno;
266 return (status);
267 }
268
269
270 void
run_print(fp)271 run_print (fp)
272 FILE *fp;
273 {
274 int i;
275
276 for (i = 0; i < run_argc; i++)
277 {
278 (void) fprintf (fp, "'%s'", run_argv[i]);
279 if (i != run_argc - 1)
280 (void) fprintf (fp, " ");
281 }
282 }
283
284 static char *
requote(const char * cmd)285 requote (const char *cmd)
286 {
287 char *requoted = xmalloc (strlen (cmd) + 1);
288 char *p = requoted;
289
290 strcpy (requoted, cmd);
291 while ((p = strchr (p, '\'')) != NULL)
292 {
293 *p++ = '"';
294 }
295
296 return requoted;
297 }
298
299 FILE *
run_popen(cmd,mode)300 run_popen (cmd, mode)
301 const char *cmd;
302 const char *mode;
303 {
304 if (trace)
305 #ifdef SERVER_SUPPORT
306 (void) fprintf (stderr, "%c-> run_popen(%s,%s)\n",
307 (server_active) ? 'S' : ' ', cmd, mode);
308 #else
309 (void) fprintf (stderr, "-> run_popen(%s,%s)\n", cmd, mode);
310 #endif
311
312 if (noexec)
313 return (NULL);
314
315 /* If the command string uses single quotes, turn them into
316 double quotes. */
317 {
318 char *requoted = requote (cmd);
319 FILE *result = popen (requoted, mode);
320 free (requoted);
321 return result;
322 }
323 }
324
325
326 /* Running children with pipes connected to them. */
327
328 /* Create a pipe. Set READWRITE[0] to its reading end, and
329 READWRITE[1] to its writing end. */
330
331 static int
my_pipe(int * readwrite)332 my_pipe (int *readwrite)
333 {
334 fprintf (stderr,
335 "Error: my_pipe() is unimplemented.\n");
336 exit (1);
337 }
338
339
340 /* Create a child process running COMMAND with IN as its standard input,
341 and OUT as its standard output. Return a handle to the child, or
342 INVALID_HANDLE_VALUE. */
343 static int
start_child(char * command,int in,int out)344 start_child (char *command, int in, int out)
345 {
346 fprintf (stderr,
347 "Error: start_child() is unimplemented.\n");
348 exit (1);
349 }
350
351
352 /* Given an array of arguments that one might pass to spawnv,
353 construct a command line that one might pass to CreateProcess.
354 Try to quote things appropriately. */
355 static char *
build_command(char ** argv)356 build_command (char **argv)
357 {
358 int len;
359
360 /* Compute the total length the command will have. */
361 {
362 int i;
363
364 len = 0;
365 for (i = 0; argv[i]; i++)
366 {
367 char *p;
368
369 len += 2; /* for the double quotes */
370
371 for (p = argv[i]; *p; p++)
372 {
373 if (*p == '"')
374 len += 2;
375 else
376 len++;
377 }
378 }
379 len++; /* for the space or the '\0' */
380 }
381
382 {
383 char *command = (char *) malloc (len);
384 int i;
385 char *p;
386
387 if (! command)
388 {
389 errno = ENOMEM;
390 return command;
391 }
392
393 p = command;
394 /* copy each element of argv to command, putting each command
395 in double quotes, and backslashing any quotes that appear
396 within an argument. */
397 for (i = 0; argv[i]; i++)
398 {
399 char *a;
400 *p++ = '"';
401 for (a = argv[i]; *a; a++)
402 {
403 if (*a == '"')
404 *p++ = '\\', *p++ = '"';
405 else
406 *p++ = *a;
407 }
408 *p++ = '"';
409 *p++ = ' ';
410 }
411 p[-1] = '\0';
412
413 return command;
414 }
415 }
416
417
418 /* Create an asynchronous child process executing ARGV,
419 with its standard input and output connected to the
420 parent with pipes. Set *TO to the file descriptor on
421 which one writes data for the child; set *FROM to
422 the file descriptor from which one reads data from the child.
423 Return the handle of the child process (this is what
424 _cwait and waitpid expect). */
425 int
piped_child(char ** argv,int * to,int * from)426 piped_child (char **argv, int *to, int *from)
427 {
428 fprintf (stderr,
429 "Error: piped_child() is unimplemented.\n");
430 exit (1);
431 }
432
433 /*
434 * dir = 0 : main proc writes to new proc, which writes to oldfd
435 * dir = 1 : main proc reads from new proc, which reads from oldfd
436 *
437 * If this returns at all, then it was successful and the return value
438 * is a file descriptor; else it errors and exits.
439 */
440 int
filter_stream_through_program(int oldfd,int dir,char ** prog,int * pidp)441 filter_stream_through_program (int oldfd, int dir,
442 char **prog, int *pidp)
443 {
444 int newfd; /* Gets set to one end of the pipe and returned. */
445 HFILE from, to;
446 HFILE Old0 = -1, Old1 = -1, Old2 = -1, Tmp;
447
448 if (DosCreatePipe (&from, &to, 4096))
449 return FALSE;
450
451 /* Save std{in,out,err} */
452 DosDupHandle (STDIN, &Old0);
453 DosSetFHState (Old1, OPEN_FLAGS_NOINHERIT);
454 DosDupHandle (STDOUT, &Old1);
455 DosSetFHState (Old2, OPEN_FLAGS_NOINHERIT);
456 DosDupHandle (STDERR, &Old2);
457 DosSetFHState (Old2, OPEN_FLAGS_NOINHERIT);
458
459 /* Redirect std{in,out,err} */
460 if (dir) /* Who goes where? */
461 {
462 Tmp = STDIN;
463 DosDupHandle (oldfd, &Tmp);
464 Tmp = STDOUT;
465 DosDupHandle (to, &Tmp);
466 Tmp = STDERR;
467 DosDupHandle (to, &Tmp);
468
469 newfd = from;
470 _setmode (newfd, O_BINARY);
471
472 DosClose (oldfd);
473 DosClose (to);
474 DosSetFHState (from, OPEN_FLAGS_NOINHERIT);
475 }
476 else
477 {
478 Tmp = STDIN;
479 DosDupHandle (from, &Tmp);
480 Tmp = STDOUT;
481 DosDupHandle (oldfd, &Tmp);
482 Tmp = STDERR;
483 DosDupHandle (oldfd, &Tmp);
484
485 newfd = to;
486 _setmode (newfd, O_BINARY);
487
488 DosClose (oldfd);
489 DosClose (from);
490 DosSetFHState (to, OPEN_FLAGS_NOINHERIT);
491 }
492
493 /* Spawn we now our hoary brood. */
494 *pidp = spawnvp (P_NOWAIT, prog[0], prog);
495
496 /* Restore std{in,out,err} */
497 Tmp = STDIN;
498 DosDupHandle (Old0, &Tmp);
499 DosClose (Old0);
500 Tmp = STDOUT;
501 DosDupHandle (Old1, &Tmp);
502 DosClose (Old1);
503 Tmp = STDERR;
504 DosDupHandle (Old2, &Tmp);
505 DosClose (Old2);
506
507 if(*pidp < 0)
508 {
509 DosClose (from);
510 DosClose (to);
511 error (1, 0, "error spawning %s", prog[0]);
512 }
513
514 return newfd;
515 }
516
517
518 int
pipe(int * filedesc)519 pipe (int *filedesc)
520 {
521 /* todo: actually, we can use DosCreatePipe(). Fix this. */
522 fprintf (stderr,
523 "Error: pipe() should not have been called in client.\n");
524 exit (1);
525 }
526
527
528 void
close_on_exec(int fd)529 close_on_exec (int fd)
530 {
531 /* Just does nothing for now... */
532
533 /* Actually, we probably *can* implement this one. Let's see... */
534 /* Nope. OS/2 has <fcntl.h>, but no fcntl() ! Wow. */
535 /* Well, I'll leave this stuff in for future reference. */
536 }
537
538
539 /* Actually, we #define sleep() in config.h now. */
540 #ifndef sleep
541 unsigned int
sleep(unsigned int seconds)542 sleep (unsigned int seconds)
543 {
544 /* I don't want to interfere with alarm signals, so I'm going to do
545 this the nasty way. */
546
547 time_t base;
548 time_t tick;
549 int i;
550
551 /* Init. */
552 time (&base);
553 time (&tick);
554
555 /* Loop until time has passed. */
556 while (difftime (tick, base) < seconds)
557 {
558 /* This might be more civilized than calling time over and over
559 again. */
560 for (i = 0; i < 10000; i++)
561 ;
562 time (&tick);
563 }
564
565 return 0;
566 }
567 #endif /* sleep */
568