xref: /openbsd/gnu/usr.bin/cvs/os2/run.c (revision 7b36286a)
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
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
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 *
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
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
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
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 *
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 *
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
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
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 *
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
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
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
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
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
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