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