1 /* amigaio.c mixes amigaos and perl APIs,
2 * as opposed to amigaos.c which is pure amigaos */
7 #include "amigaos4/amigaio.h"
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>
24 void amigaos_stdio_get(pTHX_ StdioStore *store)
27 amigaos_get_file(PerlIO_fileno(IoIFP(GvIO(PL_stdingv))));
29 amigaos_get_file(PerlIO_fileno(IoIFP(GvIO(PL_stderrgv))));
30 store->astdout = amigaos_get_file(
31 PerlIO_fileno(IoIFP(GvIO(gv_fetchpv("STDOUT", TRUE, SVt_PVIO)))));
34 void amigaos_stdio_save(pTHX_ StdioStore *store)
36 amigaos_stdio_get(aTHX_ store);
37 store->oldstdin = IDOS->SelectInput(store->astdin);
38 store->oldstderr = IDOS->SelectErrorOutput(store->astderr);
39 store->oldstdout = IDOS->SelectOutput(store->astdout);
42 void amigaos_stdio_restore(pTHX_ const StdioStore *store)
44 IDOS->SelectInput(store->oldstdin);
45 IDOS->SelectErrorOutput(store->oldstderr);
46 IDOS->SelectOutput(store->oldstdout);
49 void amigaos_post_exec(int fd, int do_report)
51 /* We *must* write something to our pipe or else
52 * the other end hangs */
56 PerlLIO_write(fd, (void *)&e, sizeof(e));
61 PerlIO *Perl_my_popen(pTHX_ const char *cmd, const char *mode)
63 PERL_FLUSHALL_FOR_CHILD;
64 /* Call system's popen() to get a FILE *, then import it.
65 * used 0 for 2nd parameter to PerlIO_importFILE;
68 // FILE *f=amigaos_popen(cmd,mode);
69 // fprintf(stderr,"popen returned %d\n",f);
70 return PerlIO_importFILE(amigaos_popen(cmd, mode), mode);
71 // return PerlIO_importFILE(f, 0);
74 I32 Perl_my_pclose(pTHX_ PerlIO *ptr)
76 FILE * const f = PerlIO_findFILE(ptr);
77 const I32 result = amigaos_pclose(f);
78 PerlIO_releaseFILE(ptr,f);
84 /* An arbitrary number to start with, should work out what the real max should
88 # define MAX_THREADS 64
100 struct MsgPort *ti_port;
101 struct Process *ti_Process;
104 static struct thread_info pseudo_children[MAX_THREADS];
105 static int num_pseudo_children = 0;
106 static struct SignalSemaphore fork_array_sema;
108 void amigaos4_init_fork_array()
110 IExec->InitSemaphore(&fork_array_sema);
111 pseudo_children[0].ti_pid = (pthread_t)IExec->FindTask(0);
112 pseudo_children[0].ti_parent = -1;
113 pseudo_children[0].ti_port =
114 (struct MsgPort *)IExec->AllocSysObjectTags(ASOT_PORT, TAG_DONE);
117 void amigaos4_dispose_fork_array()
119 while (pseudo_children[0].ti_children > 0)
122 IExec->WaitPort(pseudo_children[0].ti_port);
123 msg = IExec->GetMsg(pseudo_children[0].ti_port);
125 IExec->FreeSysObject(ASOT_MESSAGE, msg);
126 pseudo_children[0].ti_children--;
128 IExec->FreeSysObject(ASOT_PORT, pseudo_children[0].ti_port);
131 struct thread_exit_message
133 struct Message tem_Message;
141 for (i = 0; i < MAX_THREADS; i++)
143 if (pseudo_children[i].ti_pid == 0)
149 int findparent(pthread_t pid)
152 for (i = 0; i < MAX_THREADS; i++)
154 if (pseudo_children[i].ti_pid == pid)
162 struct Task *ca_parent_task;
164 PerlInterpreter *ca_interp;
169 /* FIXME: Is here's a chance, albeit it small of a clash between our pseudo pid */
170 /* derived from the pthread API and the dos.library pid that newlib kill uses? */
171 /* clib2 used the Process address so there was no issue */
173 int amigaos_kill(Pid_t pid, int signal)
176 BOOL thistask = FALSE;
177 Pid_t realpid = pid; // Perhaps we have a real pid from else where?
178 /* Look for our DOS pid */
179 IExec->ObtainSemaphore(&fork_array_sema);
180 for (i = 0; i < MAX_THREADS; i++)
182 if (pseudo_children[i].ti_pid == pid)
184 realpid = (Pid_t)IDOS->GetPID(pseudo_children[i].ti_Process,GPID_PROCESS);
185 if(pseudo_children[i].ti_Process == IExec->FindTask(NULL))
192 IExec->ReleaseSemaphore(&fork_array_sema);
193 /* Allow the C library to work out which signals are realy valid */
196 /* A quirk in newlib kill handling means it's better to call raise() rather than kill on out own task. */
197 return raise(signal);
201 return kill(realpid,signal);
205 static THREAD_RET_TYPE amigaos4_start_child(void *arg)
208 PerlInterpreter *my_perl =
209 (PerlInterpreter *)((struct child_arg *)arg)->ca_interp;
216 pthread_t pseudo_id = pthread_self();
218 #ifdef PERL_SYNC_FORK
219 static long sync_fork_id = 0;
220 long id = ++sync_fork_id;
223 /* before we do anything set up our process semaphore and add
224 a new entry to the pseudochildren */
226 /* get next available slot */
227 /* should not fail here! */
229 IExec->ObtainSemaphore(&fork_array_sema);
231 nextchild = getnextchild();
233 pseudo_children[nextchild].ti_pid = pseudo_id;
234 pseudo_children[nextchild].ti_Process = (struct Process *)IExec->FindTask(NULL);
235 pseudo_children[nextchild].ti_parent =
236 ((struct child_arg *)arg)->ca_parent;
237 pseudo_children[nextchild].ti_port =
238 (struct MsgPort *)IExec->AllocSysObjectTags(ASOT_PORT, TAG_DONE);
240 num_pseudo_children++;
241 IExec->ReleaseSemaphore(&fork_array_sema);
243 /* We're set up let the parent continue */
245 IExec->Signal(((struct child_arg *)arg)->ca_parent_task,
248 PERL_SET_THX(my_perl);
249 if ((tmpgv = gv_fetchpv("$", TRUE, SVt_PV)))
251 SV *sv = GvSV(tmpgv);
253 sv_setiv(sv, (IV)pseudo_id);
256 hv_clear(PL_pidstatus);
258 /* push a zero on the stack (we are the child) */
266 /* continue from next op */
267 PL_op = PL_op->op_next;
271 volatile int oldscope = PL_scopestack_ix;
282 while (PL_scopestack_ix > oldscope)
287 PL_curstash = PL_defstash;
288 if (PL_endav && !PL_minus_c)
289 call_list(oldscope, PL_endav);
290 status = STATUS_EXIT;
295 POPSTACK_TO(PL_mainstack);
296 PL_op = PL_restartop;
297 PL_restartop = (OP *)NULL;
301 PerlIO_printf(Perl_error_log, "panic: restartop\n");
308 /* XXX hack to avoid perl_destruct() freeing optree */
309 PL_main_root = (OP *)NULL;
313 do_close(PL_stdingv, FALSE);
314 do_close(gv_fetchpv("STDOUT", TRUE, SVt_PVIO),
315 FALSE); /* PL_stdoutgv - ISAGN */
316 do_close(PL_stderrgv, FALSE);
319 /* destroy everything (waits for any pseudo-forked children) */
321 /* wait for any remaining children */
323 while (pseudo_children[nextchild].ti_children > 0)
325 if (IExec->WaitPort(pseudo_children[nextchild].ti_port))
328 IExec->GetMsg(pseudo_children[nextchild].ti_port);
329 IExec->FreeSysObject(ASOT_MESSAGE, msg);
330 pseudo_children[nextchild].ti_children--;
333 if (PL_scopestack_ix <= 1)
335 perl_destruct(my_perl);
339 IExec->ObtainSemaphore(&fork_array_sema);
340 parent = findparent(pseudo_children[nextchild].ti_parent);
341 pseudo_children[nextchild].ti_pid = 0;
342 pseudo_children[nextchild].ti_parent = 0;
343 IExec->FreeSysObject(ASOT_PORT, pseudo_children[nextchild].ti_port);
344 pseudo_children[nextchild].ti_port = NULL;
346 IExec->ReleaseSemaphore(&fork_array_sema);
351 struct thread_exit_message *tem =
352 (struct thread_exit_message *)
353 IExec->AllocSysObjectTags(
354 ASOT_MESSAGE, ASOMSG_Size,
355 sizeof(struct thread_exit_message),
357 sizeof(struct thread_exit_message));
360 tem->tem_pid = pseudo_id;
361 tem->tem_status = status;
362 IExec->PutMsg(pseudo_children[parent].ti_port,
363 (struct Message *)tem);
367 #ifdef PERL_SYNC_FORK
370 return (void *)status;
374 #endif /* USE_ITHREADS */
381 struct child_arg arg;
382 if (num_pseudo_children >= MAX_THREADS)
387 arg.ca_interp = perl_clone((PerlInterpreter *)aTHX, CLONEf_COPY_STACKS);
388 arg.ca_parent_task = IExec->FindTask(NULL);
390 pthread_self() ? pthread_self() : (pthread_t)IExec->FindTask(0);
392 handle = pthread_create(&id, NULL, amigaos4_start_child, (void *)&arg);
393 pseudo_children[findparent(arg.ca_parent)].ti_children++;
395 IExec->Wait(SIGBREAKF_CTRL_F);
397 PERL_SET_THX(aTHX); /* XXX perl_clone*() set TLS */
406 Pid_t amigaos_waitpid(pTHX_ int optype, Pid_t pid, void *argflags)
409 if (PL_signals & PERL_SIGNALS_UNSAFE_FLAG)
411 result = pthread_join(pid, argflags);
415 while ((result = pthread_join(pid, argflags)) == -1 &&
418 // PERL_ASYNC_CHECK();
424 void amigaos_fork_set_userdata(
425 pTHX_ struct UserData *userdata, I32 did_pipes, int pp, SV **sp, SV **mark)
427 userdata->parent = IExec->FindTask(0);
428 userdata->did_pipes = did_pipes;
431 userdata->mark = mark;
432 userdata->my_perl = aTHX;
435 /* AmigaOS specific versions of #?exec#? solely for use in amigaos_system_child
438 static void S_exec_failed(pTHX_ const char *cmd, int fd, int do_report)
441 // PERL_ARGS_ASSERT_EXEC_FAILED;
444 if (ckWARN(WARN_EXEC))
445 Perl_warner(aTHX_ packWARN(WARN_EXEC),
446 "Can't exec \"%s\": %s", cmd, Strerror(e));
450 /* XXX silently ignore failures */
451 PERL_UNUSED_RESULT(PerlLIO_write(fd, (void *)&e, sizeof(int)));
456 static I32 S_do_amigaos_exec3(pTHX_ const char *incmd, int fd, int do_report)
463 /* Make a copy so we can change it */
464 const Size_t cmdlen = strlen(incmd) + 1;
467 PERL_ARGS_ASSERT_DO_EXEC3;
469 Newx(buf, cmdlen, char);
471 memcpy(cmd, incmd, cmdlen);
473 while (*cmd && isSPACE(*cmd))
476 /* see if there are shell metacharacters in it */
478 if (*cmd == '.' && isSPACE(cmd[1]))
481 if (strnEQ(cmd, "exec", 4) && isSPACE(cmd[4]))
485 while (isWORDCHAR(*s))
486 s++; /* catch VAR=val gizmo */
490 for (s = cmd; *s; s++)
492 if (*s != ' ' && !isALPHA(*s) &&
493 strchr("$&*(){}[]'\";\\|?<>~`\n", *s))
495 if (*s == '\n' && !s[1])
500 /* handle the 2>&1 construct at the end */
501 if (*s == '>' && s[1] == '&' && s[2] == '1' &&
502 s > cmd + 1 && s[-1] == '2' && isSPACE(s[-2]) &&
503 (!s[3] || isSPACE(s[3])))
505 const char *t = s + 3;
507 while (*t && isSPACE(*t))
509 if (!*t && (PerlLIO_dup2(1, 2) != -1))
517 result = myexecl(FALSE, PL_sh_path, "sh", "-c", cmd,
520 S_exec_failed(aTHX_ PL_sh_path, fd, do_report);
521 amigaos_post_exec(fd, do_report);
527 Newx(PL_Argv, (s - cmd) / 2 + 2, const char *);
528 PL_Cmd = savepvn(cmd, s - cmd);
530 for (s = PL_Cmd; *s;)
536 while (*s && !isSPACE(*s))
545 result = myexecvp(FALSE, PL_Argv[0], EXEC_ARGV_CAST(PL_Argv));
547 if (errno == ENOEXEC)
549 /* for system V NIH syndrome */
553 S_exec_failed(aTHX_ PL_Argv[0], fd, do_report);
554 amigaos_post_exec(fd, do_report);
561 I32 S_do_amigaos_aexec5(
562 pTHX_ SV *really, SV **mark, SV **sp, int fd, int do_report)
566 PERL_ARGS_ASSERT_DO_AEXEC5;
570 const char *tmps = NULL;
571 Newx(PL_Argv, sp - mark + 1, const char *);
577 *a++ = SvPV_nolen_const(*mark);
583 tmps = SvPV_nolen_const(really);
584 if ((!really && *PL_Argv[0] != '/') ||
585 (really && *tmps != '/')) /* will execvp use PATH? */
586 TAINT_ENV(); /* testing IFS here is overkill, probably
591 result = myexecvp(FALSE, tmps, EXEC_ARGV_CAST(PL_Argv));
595 result = myexecvp(FALSE, PL_Argv[0],
596 EXEC_ARGV_CAST(PL_Argv));
599 S_exec_failed(aTHX_(really ? tmps : PL_Argv[0]), fd, do_report);
601 amigaos_post_exec(fd, do_report);
606 void *amigaos_system_child(void *userdata)
613 /* these next are declared by macros else where but I may be
614 * passing modified values here so declare them explictly but
615 * still referred to by macro below */
619 register PerlInterpreter *my_perl;
623 struct UserData *ud = (struct UserData *)userdata;
625 did_pipes = ud->did_pipes;
630 my_perl = ud->my_perl;
631 PERL_SET_THX(my_perl);
633 amigaos_stdio_save(aTHX_ & store);
637 // PerlLIO_close(pp[0]);
639 if (PL_op->op_flags & OPf_STACKED)
641 SV *really = *++MARK;
642 value = (I32)S_do_amigaos_aexec5(aTHX_ really, MARK, SP, pp,
645 else if (SP - MARK != 1)
647 value = (I32)S_do_amigaos_aexec5(aTHX_ NULL, MARK, SP, pp,
652 value = (I32)S_do_amigaos_exec3(
653 aTHX_ SvPVx(sv_mortalcopy(*SP), n_a), pp, did_pipes);
657 // Signal(parent, SIGBREAKF_CTRL_F);
659 amigaos_stdio_restore(aTHX_ & store);
664 static BOOL contains_whitespace(char *string)
670 if (strchr(string, ' '))
672 if (strchr(string, '\t'))
674 if (strchr(string, '\n'))
676 if (strchr(string, 0xA0))
678 if (strchr(string, '"'))
684 static int no_of_escapes(char *string)
688 for (p = string; p < string + strlen(string); p++)
714 #define __USE_RUNCOMMAND__
716 int myexecve(bool isperlthread,
717 const char *filename,
725 char *interpreter = 0;
726 char *interpreter_args = 0;
728 char *filename_conv = 0;
729 char *interpreter_conv = 0;
733 // struct Task *thisTask = IExec->FindTask(0);
743 /* Save away our stdio */
744 amigaos_stdio_save(aTHX_ & store);
747 // adebug("%s %ld %s\n",__FUNCTION__,__LINE__,filename?filename:"NULL");
749 /* Calculate the size of filename and all args, including spaces and
751 size = 0; // strlen(filename) + 1;
752 for (cur = (char **)argv /* +1 */; *cur; cur++)
756 (contains_whitespace(*cur) ? (2 + no_of_escapes(*cur)) : 0);
758 /* Check if it's a script file */
759 IExec->DebugPrintF("%s %ld %08lx %c %c\n",__FILE__,__LINE__,filename,filename[0],filename[1]);
760 fh = fopen(filename, "r");
763 if (fgetc(fh) == '#' && fgetc(fh) == '!')
767 fgets(buffer, 999, fh);
769 while (*p == ' ' || *p == '\t')
771 if (buffer[strlen(buffer) - 1] == '\n')
772 buffer[strlen(buffer) - 1] = '\0';
773 if ((q = strchr(p, ' ')))
778 interpreter_args = mystrdup(q);
782 interpreter_args = mystrdup("");
784 interpreter = mystrdup(p);
785 size += strlen(interpreter) + 1;
786 size += strlen(interpreter_args) + 1;
793 /* We couldn't open this why not? */
796 /* file didn't exist! */
801 /* Allocate the command line */
802 filename_conv = convert_path_u2a(filename);
805 size += strlen(filename_conv);
807 full = (char *)IExec->AllocVec(size + 10, MEMF_ANY | MEMF_CLEAR);
812 interpreter_conv = convert_path_u2a(interpreter);
813 #if !defined(__USE_RUNCOMMAND__)
814 #warning(using system!)
815 sprintf(full, "%s %s %s ", interpreter_conv,
816 interpreter_args, filename_conv);
818 sprintf(full, "%s %s ", interpreter_args,
821 IExec->FreeVec(interpreter);
822 IExec->FreeVec(interpreter_args);
825 IExec->FreeVec(filename_conv);
826 fname = mystrdup(interpreter_conv);
828 if (interpreter_conv)
829 IExec->FreeVec(interpreter_conv);
833 #ifndef __USE_RUNCOMMAND__
834 sprintf(full, "%s ", filename_conv);
838 fname = mystrdup(filename_conv);
840 IExec->FreeVec(filename_conv);
843 for (cur = (char **)(argv + 1); *cur != 0; cur++)
845 if (contains_whitespace(*cur))
847 int esc = no_of_escapes(*cur);
851 char *buff = (char *)IExec->AllocVec(
852 strlen(*cur) + 4 + esc,
853 MEMF_ANY | MEMF_CLEAR);
885 IExec->FreeVec(buff);
905 #ifndef __USE_RUNCOMMAND__
906 result = IDOS->SystemTags(
907 full, SYS_UserShell, TRUE, NP_StackSize,
908 ((struct Process *)thisTask)->pr_StackSize, SYS_Input,
909 ((struct Process *)thisTask)->pr_CIS, SYS_Output,
910 ((struct Process *)thisTask)->pr_COS, SYS_Error,
911 ((struct Process *)thisTask)->pr_CES, TAG_DONE);
916 BPTR seglist = IDOS->LoadSeg(fname);
919 /* check if we have an executable! */
920 struct PseudoSegList *ps = NULL;
921 if (!IDOS->GetSegListInfoTags(
922 seglist, GSLI_Native, &ps, TAG_DONE))
924 IDOS->GetSegListInfoTags(
925 seglist, GSLI_68KPS, &ps, TAG_DONE);
930 // %s\n",__FUNCTION__,__LINE__,fname,full);
931 IDOS->SetCliProgramName(fname);
932 // result=RunCommand(seglist,8*1024,full,strlen(full));
933 // result=myruncommand(seglist,8*1024,full,strlen(full),envp);
934 result = myruncommand(seglist, 8 * 1024,
942 IDOS->UnLoadSeg(seglist);
948 IExec->FreeVec(fname);
951 #endif /* USE_RUNCOMMAND */
953 IExec->FreeVec(full);
954 if (errno == ENOEXEC)
962 IExec->FreeVec(interpreter);
964 IExec->FreeVec(filename_conv);
971 amigaos_stdio_restore(aTHX_ & store);
972 STATUS_NATIVE_CHILD_SET(result);
973 PL_exit_flags |= PERL_EXIT_EXPECTED;