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