1 /* amigaio.c mixes amigaos and perl APIs, 2 * as opposed to amigaos.c which is pure amigaos */ 3 4 #include "EXTERN.h" 5 #include "perl.h" 6 7 #include "amigaos4/amigaio.h" 8 #include "amigaos.h" 9 10 #ifdef WORD 11 # undef WORD 12 # define WORD int16 13 #endif 14 15 #include <stdio.h> 16 17 #include <exec/semaphores.h> 18 #include <exec/exectags.h> 19 #include <proto/exec.h> 20 #include <proto/dos.h> 21 #include <proto/utility.h> 22 #include <dos/dos.h> 23 24 extern struct SignalSemaphore popen_sema; 25 extern unsigned int pipenum; 26 27 extern int32 myruncommand(BPTR seglist, int stack, char *command, int length, char **envp); 28 29 void amigaos_stdio_get(pTHX_ StdioStore *store) 30 { 31 store->astdin = 32 amigaos_get_file(PerlIO_fileno(IoIFP(GvIO(PL_stdingv)))); 33 store->astderr = 34 amigaos_get_file(PerlIO_fileno(IoIFP(GvIO(PL_stderrgv)))); 35 store->astdout = amigaos_get_file( 36 PerlIO_fileno(IoIFP(GvIO(gv_fetchpv("STDOUT", TRUE, SVt_PVIO))))); 37 } 38 39 void amigaos_stdio_save(pTHX_ StdioStore *store) 40 { 41 amigaos_stdio_get(aTHX_ store); 42 store->oldstdin = IDOS->SelectInput(store->astdin); 43 store->oldstderr = IDOS->SelectErrorOutput(store->astderr); 44 store->oldstdout = IDOS->SelectOutput(store->astdout); 45 } 46 47 void amigaos_stdio_restore(pTHX_ const StdioStore *store) 48 { 49 IDOS->SelectInput(store->oldstdin); 50 IDOS->SelectErrorOutput(store->oldstderr); 51 IDOS->SelectOutput(store->oldstdout); 52 } 53 54 void amigaos_post_exec(int fd, int do_report) 55 { 56 /* We *must* write something to our pipe or else 57 * the other end hangs */ 58 if (do_report) 59 { 60 int e = errno; 61 PerlLIO_write(fd, (void *)&e, sizeof(e)); 62 PerlLIO_close(fd); 63 } 64 } 65 66 67 struct popen_data 68 { 69 struct Task *parent; 70 STRPTR command; 71 }; 72 73 static int popen_result = 0; 74 75 int popen_child() 76 { 77 struct Task *thisTask = IExec->FindTask(0); 78 struct popen_data *pd = (struct popen_data *)thisTask->tc_UserData; 79 const char *argv[4]; 80 81 argv[0] = "sh"; 82 argv[1] = "-c"; 83 argv[2] = pd->command ? pd->command : NULL; 84 argv[3] = NULL; 85 86 // adebug("%s %ld %s\n",__FUNCTION__,__LINE__,command?command:"NULL"); 87 88 /* We need to give this to sh via execvp, execvp expects filename, 89 * argv[] 90 */ 91 IExec->ObtainSemaphore(&popen_sema); 92 93 IExec->Signal(pd->parent,SIGBREAKF_CTRL_F); 94 95 popen_result = myexecvp(FALSE, argv[0], (char **)argv); 96 if (pd->command) 97 IExec->FreeVec(pd->command); 98 IExec->FreeVec(pd); 99 100 IExec->ReleaseSemaphore(&popen_sema); 101 IExec->Forbid(); 102 return 0; 103 } 104 105 106 PerlIO *Perl_my_popen(pTHX_ const char *cmd, const char *mode) 107 { 108 109 PERL_FLUSHALL_FOR_CHILD; 110 PerlIO *result = NULL; 111 char pipe_name[50]; 112 char unix_pipe[50]; 113 char ami_pipe[50]; 114 BPTR input = 0; 115 BPTR output = 0; 116 struct Process *proc = NULL; 117 struct Task *thisTask = IExec->FindTask(0); 118 struct popen_data * pd = NULL; 119 120 /* First we need to check the mode 121 * We can only have unidirectional pipes 122 */ 123 // adebug("%s %ld cmd %s mode %s \n",__FUNCTION__,__LINE__,cmd, 124 // mode); 125 126 switch (mode[0]) 127 { 128 case 'r': 129 case 'w': 130 break; 131 132 default: 133 134 errno = EINVAL; 135 return result; 136 } 137 138 /* Make a unique pipe name 139 * we need a unix one and an amigaos version (of the same pipe!) 140 * as were linking with libunix. 141 */ 142 143 sprintf(pipe_name, "%x%08lx/4096/0", pipenum++, 144 IUtility->GetUniqueID()); 145 sprintf(unix_pipe, "/PIPE/%s", pipe_name); 146 sprintf(ami_pipe, "PIPE:%s", pipe_name); 147 148 /* Now we open the AmigaOs Filehandles That we wil pass to our 149 * Sub process 150 */ 151 152 if (mode[0] == 'r') 153 { 154 /* A read mode pipe: Output from pipe input from Output() or NIL:*/ 155 /* First attempt to DUP Output() */ 156 input = IDOS->DupFileHandle(IDOS->Input()); 157 if(input == 0) 158 { 159 input = IDOS->Open("NIL:", MODE_READWRITE); 160 } 161 if (input != 0) 162 { 163 output = IDOS->Open(ami_pipe, MODE_NEWFILE); 164 } 165 result = PerlIO_open(unix_pipe, mode); 166 } 167 else 168 { 169 /* Open the write end first! */ 170 171 result = PerlIO_open(unix_pipe, mode); 172 173 input = IDOS->Open(ami_pipe, MODE_OLDFILE); 174 if (input != 0) 175 { 176 output = IDOS->DupFileHandle(IDOS->Output()); 177 if(output == 0) 178 { 179 output = IDOS->Open("NIL:", MODE_READWRITE); 180 } 181 } 182 } 183 if ((input == 0) || (output == 0) || (result == NULL)) 184 { 185 /* Ouch stream opening failed */ 186 /* Close and bail */ 187 if (input) 188 IDOS->Close(input); 189 if (output) 190 IDOS->Close(output); 191 if(result) 192 { 193 PerlIO_close(result); 194 result = NULL; 195 } 196 return result; 197 } 198 199 /* We have our streams now start our new process 200 * We're using a new process so that execve can modify the environment 201 * with messing things up for the shell that launched perl 202 * Copy cmd before we launch the subprocess as perl seems to waste 203 * no time in overwriting it! The subprocess will free the copy. 204 */ 205 206 if((pd = (struct popen_data*)IExec->AllocVecTags(sizeof(struct popen_data),AVT_Type,MEMF_SHARED,TAG_DONE))) 207 { 208 pd->parent = thisTask; 209 if ((pd->command = mystrdup(cmd))) 210 { 211 // adebug("%s %ld 212 // %s\n",__FUNCTION__,__LINE__,cmd_copy?cmd_copy:"NULL"); 213 proc = IDOS->CreateNewProcTags( 214 NP_Entry, popen_child, NP_Child, TRUE, NP_StackSize, 215 ((struct Process *)thisTask)->pr_StackSize, NP_Input, input, 216 NP_Output, output, NP_Error, IDOS->ErrorOutput(), 217 NP_CloseError, FALSE, NP_Cli, TRUE, NP_Name, 218 "Perl: popen process", NP_UserData, (int)pd, 219 TAG_DONE); 220 } 221 } 222 if(proc) 223 { 224 /* wait for the child be setup right */ 225 IExec->Wait(SIGBREAKF_CTRL_F); 226 } 227 if (!proc) 228 { 229 /* New Process Failed to start 230 * Close and bail out 231 */ 232 if(pd) 233 { 234 if(pd->command) 235 { 236 IExec->FreeVec(pd->command); 237 } 238 IExec->FreeVec(pd); 239 } 240 if (input) 241 IDOS->Close(input); 242 if (output) 243 IDOS->Close(output); 244 if(result) 245 { 246 PerlIO_close(result); 247 result = NULL; 248 } 249 } 250 251 /* Our new process is running and will close it streams etc 252 * once its done. All we need to is open the pipe via stdio 253 */ 254 255 return result; 256 } 257 258 I32 259 Perl_my_pclose(pTHX_ PerlIO *ptr) 260 { 261 int result = -1; 262 /* close the file before obtaining the semaphore else we might end up 263 hanging waiting for the child to read the last bit from the pipe */ 264 PerlIO_close(ptr); 265 IExec->ObtainSemaphore(&popen_sema); 266 result = popen_result; 267 IExec->ReleaseSemaphore(&popen_sema); 268 return result; 269 } 270 271 272 #ifdef USE_ITHREADS 273 274 /* An arbitrary number to start with, should work out what the real max should 275 * be */ 276 277 #ifndef MAX_THREADS 278 # define MAX_THREADS 64 279 #endif 280 281 #define REAPED 0 282 #define ACTIVE 1 283 #define EXITED -1 284 285 struct thread_info 286 { 287 pthread_t ti_pid; 288 int ti_children; 289 pthread_t ti_parent; 290 struct MsgPort *ti_port; 291 struct Process *ti_Process; 292 }; 293 294 static struct thread_info pseudo_children[MAX_THREADS]; 295 static int num_pseudo_children = 0; 296 static struct SignalSemaphore fork_array_sema; 297 298 void amigaos4_init_fork_array() 299 { 300 IExec->InitSemaphore(&fork_array_sema); 301 pseudo_children[0].ti_pid = (pthread_t)IExec->FindTask(0); 302 pseudo_children[0].ti_parent = -1; 303 pseudo_children[0].ti_port = 304 (struct MsgPort *)IExec->AllocSysObjectTags(ASOT_PORT, TAG_DONE); 305 } 306 307 void amigaos4_dispose_fork_array() 308 { 309 while (pseudo_children[0].ti_children > 0) 310 { 311 void *msg; 312 IExec->WaitPort(pseudo_children[0].ti_port); 313 msg = IExec->GetMsg(pseudo_children[0].ti_port); 314 if (msg) 315 IExec->FreeSysObject(ASOT_MESSAGE, msg); 316 pseudo_children[0].ti_children--; 317 } 318 IExec->FreeSysObject(ASOT_PORT, pseudo_children[0].ti_port); 319 } 320 321 struct thread_exit_message 322 { 323 struct Message tem_Message; 324 pthread_t tem_pid; 325 int tem_status; 326 }; 327 328 int getnextchild() 329 { 330 int i; 331 for (i = 0; i < MAX_THREADS; i++) 332 { 333 if (pseudo_children[i].ti_pid == 0) 334 return i; 335 } 336 return -1; 337 } 338 339 int findparent(pthread_t pid) 340 { 341 int i; 342 for (i = 0; i < MAX_THREADS; i++) 343 { 344 if (pseudo_children[i].ti_pid == pid) 345 return i; 346 } 347 return -1; 348 } 349 350 struct child_arg 351 { 352 struct Task *ca_parent_task; 353 pthread_t ca_parent; 354 PerlInterpreter *ca_interp; 355 }; 356 357 #undef kill 358 359 /* FIXME: Is here's a chance, albeit it small of a clash between our pseudo pid */ 360 /* derived from the pthread API and the dos.library pid that newlib kill uses? */ 361 /* clib2 used the Process address so there was no issue */ 362 363 int amigaos_kill(Pid_t pid, int signal) 364 { 365 int i; 366 BOOL thistask = FALSE; 367 Pid_t realpid = pid; // Perhaps we have a real pid from else where? 368 /* Look for our DOS pid */ 369 IExec->ObtainSemaphore(&fork_array_sema); 370 for (i = 0; i < MAX_THREADS; i++) 371 { 372 if (pseudo_children[i].ti_pid == pid) 373 { 374 realpid = (Pid_t)IDOS->GetPID(pseudo_children[i].ti_Process,GPID_PROCESS); 375 if(pseudo_children[i].ti_Process == (struct Process *)IExec->FindTask(NULL)) 376 { 377 thistask = TRUE; 378 } 379 break; 380 } 381 } 382 IExec->ReleaseSemaphore(&fork_array_sema); 383 /* Allow the C library to work out which signals are realy valid */ 384 if(thistask) 385 { 386 /* A quirk in newlib kill handling means it's better to call raise() rather than kill on out own task. */ 387 return raise(signal); 388 } 389 else 390 { 391 return kill(realpid,signal); 392 } 393 } 394 395 static THREAD_RET_TYPE amigaos4_start_child(void *arg) 396 { 397 398 PerlInterpreter *my_perl = 399 (PerlInterpreter *)((struct child_arg *)arg)->ca_interp; 400 ; 401 402 GV *tmpgv; 403 int status; 404 int parent; 405 int nextchild; 406 pthread_t pseudo_id = pthread_self(); 407 408 #ifdef PERL_SYNC_FORK 409 static long sync_fork_id = 0; 410 long id = ++sync_fork_id; 411 #endif 412 413 /* before we do anything set up our process semaphore and add 414 a new entry to the pseudochildren */ 415 416 /* get next available slot */ 417 /* should not fail here! */ 418 419 IExec->ObtainSemaphore(&fork_array_sema); 420 421 nextchild = getnextchild(); 422 423 pseudo_children[nextchild].ti_pid = pseudo_id; 424 pseudo_children[nextchild].ti_Process = (struct Process *)IExec->FindTask(NULL); 425 pseudo_children[nextchild].ti_parent = 426 ((struct child_arg *)arg)->ca_parent; 427 pseudo_children[nextchild].ti_port = 428 (struct MsgPort *)IExec->AllocSysObjectTags(ASOT_PORT, TAG_DONE); 429 430 num_pseudo_children++; 431 IExec->ReleaseSemaphore(&fork_array_sema); 432 433 /* We're set up let the parent continue */ 434 435 IExec->Signal(((struct child_arg *)arg)->ca_parent_task, 436 SIGBREAKF_CTRL_F); 437 438 PERL_SET_THX(my_perl); 439 if ((tmpgv = gv_fetchpv("$", TRUE, SVt_PV))) 440 { 441 SV *sv = GvSV(tmpgv); 442 SvREADONLY_off(sv); 443 sv_setiv(sv, (IV)pseudo_id); 444 SvREADONLY_on(sv); 445 } 446 hv_clear(PL_pidstatus); 447 448 /* push a zero on the stack (we are the child) */ 449 { 450 dSP; 451 dTARGET; 452 PUSHi(0); 453 PUTBACK; 454 } 455 456 /* continue from next op */ 457 PL_op = PL_op->op_next; 458 459 { 460 dJMPENV; 461 volatile int oldscope = PL_scopestack_ix; 462 463 restart: 464 JMPENV_PUSH(status); 465 switch (status) 466 { 467 case 0: 468 CALLRUNOPS(aTHX); 469 status = 0; 470 break; 471 case 2: 472 while (PL_scopestack_ix > oldscope) 473 { 474 LEAVE; 475 } 476 FREETMPS; 477 PL_curstash = PL_defstash; 478 if (PL_endav && !PL_minus_c) 479 call_list(oldscope, PL_endav); 480 status = STATUS_EXIT; 481 break; 482 case 3: 483 if (PL_restartop) 484 { 485 POPSTACK_TO(PL_mainstack); 486 PL_op = PL_restartop; 487 PL_restartop = (OP *)NULL; 488 ; 489 goto restart; 490 } 491 PerlIO_printf(Perl_error_log, "panic: restartop\n"); 492 FREETMPS; 493 status = 1; 494 break; 495 } 496 JMPENV_POP; 497 498 /* XXX hack to avoid perl_destruct() freeing optree */ 499 PL_main_root = (OP *)NULL; 500 } 501 502 { 503 do_close(PL_stdingv, FALSE); 504 do_close(gv_fetchpv("STDOUT", TRUE, SVt_PVIO), 505 FALSE); /* PL_stdoutgv - ISAGN */ 506 do_close(PL_stderrgv, FALSE); 507 } 508 509 /* destroy everything (waits for any pseudo-forked children) */ 510 511 /* wait for any remaining children */ 512 513 while (pseudo_children[nextchild].ti_children > 0) 514 { 515 if (IExec->WaitPort(pseudo_children[nextchild].ti_port)) 516 { 517 void *msg = 518 IExec->GetMsg(pseudo_children[nextchild].ti_port); 519 IExec->FreeSysObject(ASOT_MESSAGE, msg); 520 pseudo_children[nextchild].ti_children--; 521 } 522 } 523 if (PL_scopestack_ix <= 1) 524 { 525 perl_destruct(my_perl); 526 } 527 perl_free(my_perl); 528 529 IExec->ObtainSemaphore(&fork_array_sema); 530 parent = findparent(pseudo_children[nextchild].ti_parent); 531 pseudo_children[nextchild].ti_pid = 0; 532 pseudo_children[nextchild].ti_parent = 0; 533 IExec->FreeSysObject(ASOT_PORT, pseudo_children[nextchild].ti_port); 534 pseudo_children[nextchild].ti_port = NULL; 535 536 IExec->ReleaseSemaphore(&fork_array_sema); 537 538 { 539 if (parent >= 0) 540 { 541 struct thread_exit_message *tem = 542 (struct thread_exit_message *) 543 IExec->AllocSysObjectTags( 544 ASOT_MESSAGE, ASOMSG_Size, 545 sizeof(struct thread_exit_message), 546 ASOMSG_Length, 547 sizeof(struct thread_exit_message)); 548 if (tem) 549 { 550 tem->tem_pid = pseudo_id; 551 tem->tem_status = status; 552 IExec->PutMsg(pseudo_children[parent].ti_port, 553 (struct Message *)tem); 554 } 555 } 556 } 557 #ifdef PERL_SYNC_FORK 558 return id; 559 #else 560 return (void *)status; 561 #endif 562 } 563 564 #endif /* USE_ITHREADS */ 565 566 Pid_t amigaos_fork() 567 { 568 dTHX; 569 pthread_t id; 570 int handle; 571 struct child_arg arg; 572 if (num_pseudo_children >= MAX_THREADS) 573 { 574 errno = EAGAIN; 575 return -1; 576 } 577 arg.ca_interp = perl_clone((PerlInterpreter *)aTHX, CLONEf_COPY_STACKS); 578 arg.ca_parent_task = IExec->FindTask(NULL); 579 arg.ca_parent = 580 pthread_self() ? pthread_self() : (pthread_t)IExec->FindTask(0); 581 582 handle = pthread_create(&id, NULL, amigaos4_start_child, (void *)&arg); 583 pseudo_children[findparent(arg.ca_parent)].ti_children++; 584 585 IExec->Wait(SIGBREAKF_CTRL_F); 586 587 PERL_SET_THX(aTHX); /* XXX perl_clone*() set TLS */ 588 if (handle) 589 { 590 errno = EAGAIN; 591 return -1; 592 } 593 return id; 594 } 595 596 Pid_t amigaos_waitpid(pTHX_ int optype, Pid_t pid, void *argflags) 597 { 598 int result; 599 if (PL_signals & PERL_SIGNALS_UNSAFE_FLAG) 600 { 601 result = pthread_join(pid, (void **)argflags); 602 } 603 else 604 { 605 while ((result = pthread_join(pid, (void **)argflags)) == -1 && 606 errno == EINTR) 607 { 608 // PERL_ASYNC_CHECK(); 609 } 610 } 611 return result; 612 } 613 614 void amigaos_fork_set_userdata( 615 pTHX_ struct UserData *userdata, I32 did_pipes, int pp, SV **sp, SV **mark) 616 { 617 userdata->parent = IExec->FindTask(0); 618 userdata->did_pipes = did_pipes; 619 userdata->pp = pp; 620 userdata->sp = sp; 621 userdata->mark = mark; 622 userdata->my_perl = aTHX; 623 } 624 625 /* AmigaOS specific versions of #?exec#? solely for use in amigaos_system_child 626 */ 627 628 static void S_exec_failed(pTHX_ const char *cmd, int fd, int do_report) 629 { 630 const int e = errno; 631 // PERL_ARGS_ASSERT_EXEC_FAILED; 632 if (e) 633 { 634 if (ckWARN(WARN_EXEC)) 635 Perl_warner(aTHX_ packWARN(WARN_EXEC), 636 "Can't exec \"%s\": %s", cmd, Strerror(e)); 637 } 638 if (do_report) 639 { 640 /* XXX silently ignore failures */ 641 PERL_UNUSED_RESULT(PerlLIO_write(fd, (void *)&e, sizeof(int))); 642 PerlLIO_close(fd); 643 } 644 } 645 646 static I32 S_do_amigaos_exec3(pTHX_ const char *incmd, int fd, int do_report) 647 { 648 const char **argv, **a; 649 char *s; 650 char *buf; 651 char *cmd; 652 /* Make a copy so we can change it */ 653 const Size_t cmdlen = strlen(incmd) + 1; 654 I32 result = -1; 655 656 PERL_ARGS_ASSERT_DO_EXEC3; 657 658 ENTER; 659 Newx(buf, cmdlen, char); 660 SAVEFREEPV(buf); 661 cmd = buf; 662 memcpy(cmd, incmd, cmdlen); 663 664 while (*cmd && isSPACE(*cmd)) 665 cmd++; 666 667 /* see if there are shell metacharacters in it */ 668 669 if (*cmd == '.' && isSPACE(cmd[1])) 670 goto doshell; 671 672 if (strBEGINs(cmd, "exec") && isSPACE(cmd[4])) 673 goto doshell; 674 675 s = cmd; 676 while (isWORDCHAR(*s)) 677 s++; /* catch VAR=val gizmo */ 678 if (*s == '=') 679 goto doshell; 680 681 for (s = cmd; *s; s++) 682 { 683 if (*s != ' ' && !isALPHA(*s) && 684 memCHRs("$&*(){}[]'\";\\|?<>~`\n", *s)) 685 { 686 if (*s == '\n' && !s[1]) 687 { 688 *s = '\0'; 689 break; 690 } 691 /* handle the 2>&1 construct at the end */ 692 if (*s == '>' && s[1] == '&' && s[2] == '1' && 693 s > cmd + 1 && s[-1] == '2' && isSPACE(s[-2]) && 694 (!s[3] || isSPACE(s[3]))) 695 { 696 const char *t = s + 3; 697 698 while (*t && isSPACE(*t)) 699 ++t; 700 if (!*t && (PerlLIO_dup2(1, 2) != -1)) 701 { 702 s[-2] = '\0'; 703 break; 704 } 705 } 706 doshell: 707 PERL_FPU_PRE_EXEC 708 result = myexecl(FALSE, PL_sh_path, "sh", "-c", cmd, 709 (char *)NULL); 710 PERL_FPU_POST_EXEC 711 S_exec_failed(aTHX_ PL_sh_path, fd, do_report); 712 amigaos_post_exec(fd, do_report); 713 goto leave; 714 } 715 } 716 717 Newx(argv, (s - cmd) / 2 + 2, const char *); 718 SAVEFREEPV(argv); 719 cmd = savepvn(cmd, s - cmd); 720 SAVEFREEPV(cmd); 721 a = argv; 722 for (s = cmd; *s;) 723 { 724 while (isSPACE(*s)) 725 s++; 726 if (*s) 727 *(a++) = s; 728 while (*s && !isSPACE(*s)) 729 s++; 730 if (*s) 731 *s++ = '\0'; 732 } 733 *a = NULL; 734 if (argv[0]) 735 { 736 PERL_FPU_PRE_EXEC 737 result = myexecvp(FALSE, argv[0], EXEC_ARGV_CAST(argv)); 738 PERL_FPU_POST_EXEC 739 if (errno == ENOEXEC) /* for system V NIH syndrome */ 740 goto doshell; 741 S_exec_failed(aTHX_ argv[0], fd, do_report); 742 amigaos_post_exec(fd, do_report); 743 } 744 leave: 745 LEAVE; 746 return result; 747 } 748 749 I32 S_do_amigaos_aexec5( 750 pTHX_ SV *really, SV **mark, SV **sp, int fd, int do_report) 751 { 752 I32 result = -1; 753 PERL_ARGS_ASSERT_DO_AEXEC5; 754 ENTER; 755 if (sp > mark) 756 { 757 const char **argv, **a; 758 const char *tmps = NULL; 759 Newx(argv, sp - mark + 1, const char *); 760 SAVEFREEPV(argv); 761 a = argv; 762 763 while (++mark <= sp) 764 { 765 if (*mark) { 766 char *arg = savepv(SvPV_nolen_const(*mark)); 767 SAVEFREEPV(arg); 768 *a++ = arg; 769 } else 770 *a++ = ""; 771 } 772 *a = NULL; 773 if (really) { 774 tmps = savepv(SvPV_nolen_const(really)); 775 SAVEFREEPV(tmps); 776 } 777 if ((!really && *argv[0] != '/') || 778 (really && *tmps != '/')) /* will execvp use PATH? */ 779 TAINT_ENV(); /* testing IFS here is overkill, probably 780 */ 781 PERL_FPU_PRE_EXEC 782 if (really && *tmps) 783 { 784 result = myexecvp(FALSE, tmps, EXEC_ARGV_CAST(argv)); 785 } 786 else 787 { 788 result = myexecvp(FALSE, argv[0], EXEC_ARGV_CAST(argv)); 789 } 790 PERL_FPU_POST_EXEC 791 S_exec_failed(aTHX_(really ? tmps : argv[0]), fd, do_report); 792 } 793 amigaos_post_exec(fd, do_report); 794 LEAVE; 795 return result; 796 } 797 798 void *amigaos_system_child(void *userdata) 799 { 800 struct Task *parent; 801 I32 did_pipes; 802 int pp; 803 I32 value; 804 STRLEN n_a; 805 /* these next are declared by macros else where but I may be 806 * passing modified values here so declare them explicitly but 807 * still referred to by macro below */ 808 809 register SV **sp; 810 register SV **mark; 811 register PerlInterpreter *my_perl; 812 813 StdioStore store; 814 815 struct UserData *ud = (struct UserData *)userdata; 816 817 did_pipes = ud->did_pipes; 818 parent = ud->parent; 819 pp = ud->pp; 820 SP = ud->sp; 821 MARK = ud->mark; 822 my_perl = ud->my_perl; 823 PERL_SET_THX(my_perl); 824 825 amigaos_stdio_save(aTHX_ & store); 826 827 if (did_pipes) 828 { 829 // PerlLIO_close(pp[0]); 830 } 831 if (PL_op->op_flags & OPf_STACKED) 832 { 833 SV *really = *++MARK; 834 value = (I32)S_do_amigaos_aexec5(aTHX_ really, MARK, SP, pp, 835 did_pipes); 836 } 837 else if (SP - MARK != 1) 838 { 839 value = (I32)S_do_amigaos_aexec5(aTHX_ NULL, MARK, SP, pp, 840 did_pipes); 841 } 842 else 843 { 844 value = (I32)S_do_amigaos_exec3( 845 aTHX_ SvPVx(sv_mortalcopy(*SP), n_a), pp, did_pipes); 846 } 847 848 // Forbid(); 849 // Signal(parent, SIGBREAKF_CTRL_F); 850 851 amigaos_stdio_restore(aTHX_ & store); 852 853 return (void *)value; 854 } 855 856 static BOOL contains_whitespace(char *string) 857 { 858 859 if (string) 860 { 861 862 if (strchr(string, ' ')) 863 return TRUE; 864 if (strchr(string, '\t')) 865 return TRUE; 866 if (strchr(string, '\n')) 867 return TRUE; 868 if (strchr(string, 0xA0)) 869 return TRUE; 870 if (strchr(string, '"')) 871 return TRUE; 872 } 873 return FALSE; 874 } 875 876 static int no_of_escapes(char *string) 877 { 878 int cnt = 0; 879 char *p; 880 for (p = string; p < string + strlen(string); p++) 881 { 882 if (*p == '"') 883 cnt++; 884 if (*p == '*') 885 cnt++; 886 if (*p == '\n') 887 cnt++; 888 if (*p == '\t') 889 cnt++; 890 } 891 return cnt; 892 } 893 894 struct command_data 895 { 896 STRPTR args; 897 BPTR seglist; 898 struct Task *parent; 899 }; 900 901 #undef fopen 902 #undef fgetc 903 #undef fgets 904 #undef fclose 905 906 #define __USE_RUNCOMMAND__ 907 908 int myexecve(bool isperlthread, 909 const char *filename, 910 char *argv[], 911 char *envp[]) 912 { 913 FILE *fh; 914 char buffer[1000]; 915 int size = 0; 916 char **cur; 917 char *interpreter = 0; 918 char *interpreter_args = 0; 919 char *full = 0; 920 char *filename_conv = 0; 921 char *interpreter_conv = 0; 922 // char *tmp = 0; 923 char *fname; 924 // int tmpint; 925 // struct Task *thisTask = IExec->FindTask(0); 926 int result = -1; 927 928 StdioStore store; 929 930 pTHX = NULL; 931 932 if (isperlthread) 933 { 934 aTHX = PERL_GET_THX; 935 /* Save away our stdio */ 936 amigaos_stdio_save(aTHX_ & store); 937 } 938 939 // adebug("%s %ld %s\n",__FUNCTION__,__LINE__,filename?filename:"NULL"); 940 941 /* Calculate the size of filename and all args, including spaces and 942 * quotes */ 943 size = 0; // strlen(filename) + 1; 944 for (cur = (char **)argv /* +1 */; *cur; cur++) 945 { 946 size += 947 strlen(*cur) + 1 + 948 (contains_whitespace(*cur) ? (2 + no_of_escapes(*cur)) : 0); 949 } 950 /* Check if it's a script file */ 951 IExec->DebugPrintF("%s %ld %08lx %c %c\n",__FILE__,__LINE__,filename,filename[0],filename[1]); 952 fh = fopen(filename, "r"); 953 if (fh) 954 { 955 if (fgetc(fh) == '#' && fgetc(fh) == '!') 956 { 957 char *p; 958 char *q; 959 fgets(buffer, 999, fh); 960 p = buffer; 961 while (*p == ' ' || *p == '\t') 962 p++; 963 if (buffer[strlen(buffer) - 1] == '\n') 964 buffer[strlen(buffer) - 1] = '\0'; 965 if ((q = strchr(p, ' '))) 966 { 967 *q++ = '\0'; 968 if (*q != '\0') 969 { 970 interpreter_args = mystrdup(q); 971 } 972 } 973 else 974 interpreter_args = mystrdup(""); 975 976 interpreter = mystrdup(p); 977 size += strlen(interpreter) + 1; 978 size += strlen(interpreter_args) + 1; 979 } 980 981 fclose(fh); 982 } 983 else 984 { 985 /* We couldn't open this why not? */ 986 if (errno == ENOENT) 987 { 988 /* file didn't exist! */ 989 goto out; 990 } 991 } 992 993 /* Allocate the command line */ 994 filename_conv = convert_path_u2a(filename); 995 996 if (filename_conv) 997 size += strlen(filename_conv); 998 size += 1; 999 full = (char *)IExec->AllocVecTags(size + 10, AVT_ClearWithValue, 0 ,TAG_DONE); 1000 if (full) 1001 { 1002 if (interpreter) 1003 { 1004 interpreter_conv = convert_path_u2a(interpreter); 1005 #if !defined(__USE_RUNCOMMAND__) 1006 #warning(using system!) 1007 sprintf(full, "%s %s %s ", interpreter_conv, 1008 interpreter_args, filename_conv); 1009 #else 1010 sprintf(full, "%s %s ", interpreter_args, 1011 filename_conv); 1012 #endif 1013 IExec->FreeVec(interpreter); 1014 IExec->FreeVec(interpreter_args); 1015 1016 if (filename_conv) 1017 IExec->FreeVec(filename_conv); 1018 fname = mystrdup(interpreter_conv); 1019 1020 if (interpreter_conv) 1021 IExec->FreeVec(interpreter_conv); 1022 } 1023 else 1024 { 1025 #ifndef __USE_RUNCOMMAND__ 1026 sprintf(full, "%s ", filename_conv); 1027 #else 1028 sprintf(full, ""); 1029 #endif 1030 fname = mystrdup(filename_conv); 1031 if (filename_conv) 1032 IExec->FreeVec(filename_conv); 1033 } 1034 1035 for (cur = (char **)(argv + 1); *cur != 0; cur++) 1036 { 1037 if (contains_whitespace(*cur)) 1038 { 1039 int esc = no_of_escapes(*cur); 1040 1041 if (esc > 0) 1042 { 1043 char *buff = (char *)IExec->AllocVecTags( 1044 strlen(*cur) + 4 + esc, 1045 AVT_ClearWithValue,0, 1046 TAG_DONE); 1047 char *p = *cur; 1048 char *q = buff; 1049 1050 *q++ = '"'; 1051 while (*p != '\0') 1052 { 1053 1054 if (*p == '\n') 1055 { 1056 *q++ = '*'; 1057 *q++ = 'N'; 1058 p++; 1059 continue; 1060 } 1061 else if (*p == '"') 1062 { 1063 *q++ = '*'; 1064 *q++ = '"'; 1065 p++; 1066 continue; 1067 } 1068 else if (*p == '*') 1069 { 1070 *q++ = '*'; 1071 } 1072 *q++ = *p++; 1073 } 1074 *q++ = '"'; 1075 *q++ = ' '; 1076 *q = '\0'; 1077 strcat(full, buff); 1078 IExec->FreeVec(buff); 1079 } 1080 else 1081 { 1082 strcat(full, "\""); 1083 strcat(full, *cur); 1084 strcat(full, "\" "); 1085 } 1086 } 1087 else 1088 { 1089 strcat(full, *cur); 1090 strcat(full, " "); 1091 } 1092 } 1093 strcat(full, "\n"); 1094 1095 // if(envp) 1096 // createvars(envp); 1097 1098 #ifndef __USE_RUNCOMMAND__ 1099 result = IDOS->SystemTags( 1100 full, SYS_UserShell, TRUE, NP_StackSize, 1101 ((struct Process *)thisTask)->pr_StackSize, SYS_Input, 1102 ((struct Process *)thisTask)->pr_CIS, SYS_Output, 1103 ((struct Process *)thisTask)->pr_COS, SYS_Error, 1104 ((struct Process *)thisTask)->pr_CES, TAG_DONE); 1105 #else 1106 1107 if (fname) 1108 { 1109 BPTR seglist = IDOS->LoadSeg(fname); 1110 if (seglist) 1111 { 1112 /* check if we have an executable! */ 1113 struct PseudoSegList *ps = NULL; 1114 if (!IDOS->GetSegListInfoTags( 1115 seglist, GSLI_Native, &ps, TAG_DONE)) 1116 { 1117 IDOS->GetSegListInfoTags( 1118 seglist, GSLI_68KPS, &ps, TAG_DONE); 1119 } 1120 if (ps != NULL) 1121 { 1122 // adebug("%s %ld %s 1123 // %s\n",__FUNCTION__,__LINE__,fname,full); 1124 IDOS->SetCliProgramName(fname); 1125 // result=RunCommand(seglist,8*1024,full,strlen(full)); 1126 // result=myruncommand(seglist,8*1024,full,strlen(full),envp); 1127 result = myruncommand(seglist, 8 * 1024, 1128 full, -1, envp); 1129 errno = 0; 1130 } 1131 else 1132 { 1133 errno = ENOEXEC; 1134 } 1135 IDOS->UnLoadSeg(seglist); 1136 } 1137 else 1138 { 1139 errno = ENOEXEC; 1140 } 1141 IExec->FreeVec(fname); 1142 } 1143 1144 #endif /* USE_RUNCOMMAND */ 1145 1146 IExec->FreeVec(full); 1147 if (errno == ENOEXEC) 1148 { 1149 result = -1; 1150 } 1151 goto out; 1152 } 1153 1154 if (interpreter) 1155 IExec->FreeVec(interpreter); 1156 if (filename_conv) 1157 IExec->FreeVec(filename_conv); 1158 1159 errno = ENOMEM; 1160 1161 out: 1162 if (isperlthread) 1163 { 1164 amigaos_stdio_restore(aTHX_ & store); 1165 STATUS_NATIVE_CHILD_SET(result); 1166 PL_exit_flags |= PERL_EXIT_EXPECTED; 1167 if (result != -1) 1168 my_exit(result); 1169 } 1170 return (result); 1171 } 1172