diff options
author | Andy Broad <andy@broad.ology.org.uk> | 2015-09-13 14:37:43 -0400 |
---|---|---|
committer | Jarkko Hietaniemi <jhi@iki.fi> | 2015-09-16 07:44:29 -0400 |
commit | 738ab09f5846887e462080d6443fb8d1e751f247 (patch) | |
tree | 28815d963b6369d436789f18cf4551d662b3e171 /amigaos4 | |
parent | dee43f802e849f37f65552e91b5dda77197dc05b (diff) | |
download | perl-738ab09f5846887e462080d6443fb8d1e751f247.tar.gz |
amigaos4: move the amigaos exec code under amigaos4
Largely reimplements 839a9f02, 54fa14d7, e8432c63, 40262ff4.
The upside is that now doio.c and pp_sys.c have much less AmigaOS
specific ifdefs. As a downside, the exec code is now forked (pun
only partially accidental.)
The earlier story regarding fork+exec, that the AmigaOS creating
thread doesn't terminate but instead continues running is both true
and false. The more detailed story is that the user-observable
behaviour is as with POSIX/UNIX. The thread that created the new
"task" (to use the AmigaOS terms) does hang around -- but all it
does is to wait for the new task to terminate, and more importantly,
it holds on to the resources like filehandles. If the task were to
immediately terminate, the resources would be reclaimed by the kernel.
Diffstat (limited to 'amigaos4')
-rw-r--r-- | amigaos4/amigaio.c | 498 | ||||
-rw-r--r-- | amigaos4/amigaio.h | 2 | ||||
-rw-r--r-- | amigaos4/amigaos.c | 50 | ||||
-rw-r--r-- | amigaos4/amigaos.h | 27 |
4 files changed, 548 insertions, 29 deletions
diff --git a/amigaos4/amigaio.c b/amigaos4/amigaio.c index b13ac29fa0..53f059ff81 100644 --- a/amigaos4/amigaio.c +++ b/amigaos4/amigaio.c @@ -12,6 +12,8 @@ # define WORD int16 #endif +#include <stdio.h> + #include <exec/semaphores.h> #include <exec/exectags.h> #include <proto/exec.h> @@ -381,6 +383,176 @@ void amigaos_fork_set_userdata( userdata->my_perl = aTHX; } +/* AmigaOS specific versions of #?exec#? solely for use in amigaos_system_child + */ + +static void S_exec_failed(pTHX_ const char *cmd, int fd, int do_report) +{ + const int e = errno; +// PERL_ARGS_ASSERT_EXEC_FAILED; + if (e) + { + if (ckWARN(WARN_EXEC)) + Perl_warner(aTHX_ packWARN(WARN_EXEC), + "Can't exec \"%s\": %s", cmd, Strerror(e)); + } + if (do_report) + { + /* XXX silently ignore failures */ + PERL_UNUSED_RESULT(PerlLIO_write(fd, (void *)&e, sizeof(int))); + PerlLIO_close(fd); + } +} + +static I32 S_do_amigaos_exec3(pTHX_ const char *incmd, int fd, int do_report) +{ + dVAR; + const char **a; + char *s; + char *buf; + char *cmd; + /* Make a copy so we can change it */ + const Size_t cmdlen = strlen(incmd) + 1; + I32 result = -1; + + PERL_ARGS_ASSERT_DO_EXEC3; + + Newx(buf, cmdlen, char); + cmd = buf; + memcpy(cmd, incmd, cmdlen); + + while (*cmd && isSPACE(*cmd)) + cmd++; + + /* see if there are shell metacharacters in it */ + + if (*cmd == '.' && isSPACE(cmd[1])) + goto doshell; + + if (strnEQ(cmd, "exec", 4) && isSPACE(cmd[4])) + goto doshell; + + s = cmd; + while (isWORDCHAR(*s)) + s++; /* catch VAR=val gizmo */ + if (*s == '=') + goto doshell; + + for (s = cmd; *s; s++) + { + if (*s != ' ' && !isALPHA(*s) && + strchr("$&*(){}[]'\";\\|?<>~`\n", *s)) + { + if (*s == '\n' && !s[1]) + { + *s = '\0'; + break; + } + /* handle the 2>&1 construct at the end */ + if (*s == '>' && s[1] == '&' && s[2] == '1' && + s > cmd + 1 && s[-1] == '2' && isSPACE(s[-2]) && + (!s[3] || isSPACE(s[3]))) + { + const char *t = s + 3; + + while (*t && isSPACE(*t)) + ++t; + if (!*t && (PerlLIO_dup2(1, 2) != -1)) + { + s[-2] = '\0'; + break; + } + } + doshell: + PERL_FPU_PRE_EXEC + result = myexecl(FALSE, PL_sh_path, "sh", "-c", cmd, + (char *)NULL); + PERL_FPU_POST_EXEC + S_exec_failed(aTHX_ PL_sh_path, fd, do_report); + amigaos_post_exec(fd, do_report); + Safefree(buf); + return result; + } + } + + Newx(PL_Argv, (s - cmd) / 2 + 2, const char *); + PL_Cmd = savepvn(cmd, s - cmd); + a = PL_Argv; + for (s = PL_Cmd; *s;) + { + while (isSPACE(*s)) + s++; + if (*s) + *(a++) = s; + while (*s && !isSPACE(*s)) + s++; + if (*s) + *s++ = '\0'; + } + *a = NULL; + if (PL_Argv[0]) + { + PERL_FPU_PRE_EXEC + result = myexecvp(FALSE, PL_Argv[0], EXEC_ARGV_CAST(PL_Argv)); + PERL_FPU_POST_EXEC + if (errno == ENOEXEC) + { /* for system V NIH syndrome */ + do_execfree(); + goto doshell; + } + S_exec_failed(aTHX_ PL_Argv[0], fd, do_report); + amigaos_post_exec(fd, do_report); + } + do_execfree(); + Safefree(buf); + return result; +} + +I32 S_do_amigaos_aexec5( + pTHX_ SV *really, SV **mark, SV **sp, int fd, int do_report) +{ + dVAR; + I32 result = -1; + PERL_ARGS_ASSERT_DO_AEXEC5; + if (sp > mark) + { + const char **a; + const char *tmps = NULL; + Newx(PL_Argv, sp - mark + 1, const char *); + a = PL_Argv; + + while (++mark <= sp) + { + if (*mark) + *a++ = SvPV_nolen_const(*mark); + else + *a++ = ""; + } + *a = NULL; + if (really) + tmps = SvPV_nolen_const(really); + if ((!really && *PL_Argv[0] != '/') || + (really && *tmps != '/')) /* will execvp use PATH? */ + TAINT_ENV(); /* testing IFS here is overkill, probably + */ + PERL_FPU_PRE_EXEC + if (really && *tmps) + { + result = myexecvp(FALSE, tmps, EXEC_ARGV_CAST(PL_Argv)); + } + else + { + result = myexecvp(FALSE, PL_Argv[0], + EXEC_ARGV_CAST(PL_Argv)); + } + PERL_FPU_POST_EXEC + S_exec_failed(aTHX_(really ? tmps : PL_Argv[0]), fd, do_report); + } + amigaos_post_exec(fd, do_report); + do_execfree(); + return result; +} + void *amigaos_system_child(void *userdata) { struct Task *parent; @@ -417,16 +589,18 @@ void *amigaos_system_child(void *userdata) if (PL_op->op_flags & OPf_STACKED) { SV *really = *++MARK; - value = (I32)do_aexec5(really, MARK, SP, pp, did_pipes); + value = (I32)S_do_amigaos_aexec5(aTHX_ really, MARK, SP, pp, + did_pipes); } else if (SP - MARK != 1) { - value = (I32)do_aexec5(NULL, MARK, SP, pp, did_pipes); + value = (I32)S_do_amigaos_aexec5(aTHX_ NULL, MARK, SP, pp, + did_pipes); } else { - value = (I32)do_exec3(SvPVx(sv_mortalcopy(*SP), n_a), pp, - did_pipes); + value = (I32)S_do_amigaos_exec3( + aTHX_ SvPVx(sv_mortalcopy(*SP), n_a), pp, did_pipes); } // Forbid(); @@ -436,3 +610,319 @@ void *amigaos_system_child(void *userdata) return value; } + +static BOOL contains_whitespace(char *string) +{ + + if (string) + { + + if (strchr(string, ' ')) + return TRUE; + if (strchr(string, '\t')) + return TRUE; + if (strchr(string, '\n')) + return TRUE; + if (strchr(string, 0xA0)) + return TRUE; + if (strchr(string, '"')) + return TRUE; + } + return FALSE; +} + +static int no_of_escapes(char *string) +{ + int cnt = 0; + char *p; + for (p = string; p < string + strlen(string); p++) + { + if (*p == '"') + cnt++; + if (*p == '*') + cnt++; + if (*p == '\n') + cnt++; + if (*p == '\t') + cnt++; + } + return cnt; +} + +struct command_data +{ + STRPTR args; + BPTR seglist; + struct Task *parent; +}; + +#undef fopen +#undef fgetc +#undef fgets +#undef fclose + +#define __USE_RUNCOMMAND__ + +int myexecve(bool isperlthread, + const char *filename, + char *argv[], + char *envp[]) +{ + FILE *fh; + char buffer[1000]; + int size = 0; + char **cur; + char *interpreter = 0; + char *interpreter_args = 0; + char *full = 0; + char *filename_conv = 0; + char *interpreter_conv = 0; + // char *tmp = 0; + char *fname; + // int tmpint; + // struct Task *thisTask = IExec->FindTask(0); + int result = -1; + + StdioStore store; + + pTHX = NULL; + + if (isperlthread) + { + aTHX = PERL_GET_THX; + /* Save away our stdio */ + amigaos_stdio_save(aTHX_ & store); + } + + // adebug("%s %ld %s\n",__FUNCTION__,__LINE__,filename?filename:"NULL"); + + /* Calculate the size of filename and all args, including spaces and + * quotes */ + size = 0; // strlen(filename) + 1; + for (cur = (char **)argv /* +1 */; *cur; cur++) + { + size += + strlen(*cur) + 1 + + (contains_whitespace(*cur) ? (2 + no_of_escapes(*cur)) : 0); + } + /* Check if it's a script file */ + + fh = fopen(filename, "r"); + if (fh) + { + if (fgetc(fh) == '#' && fgetc(fh) == '!') + { + char *p; + char *q; + fgets(buffer, 999, fh); + p = buffer; + while (*p == ' ' || *p == '\t') + p++; + if (buffer[strlen(buffer) - 1] == '\n') + buffer[strlen(buffer) - 1] = '\0'; + if ((q = strchr(p, ' '))) + { + *q++ = '\0'; + if (*q != '\0') + { + interpreter_args = mystrdup(q); + } + } + else + interpreter_args = mystrdup(""); + + interpreter = mystrdup(p); + size += strlen(interpreter) + 1; + size += strlen(interpreter_args) + 1; + } + + fclose(fh); + } + else + { + /* We couldn't open this why not? */ + if (errno == ENOENT) + { + /* file didn't exist! */ + goto out; + } + } + + /* Allocate the command line */ + filename_conv = convert_path_u2a(filename); + + if (filename_conv) + size += strlen(filename_conv); + size += 1; + full = (char *)IExec->AllocVec(size + 10, MEMF_ANY | MEMF_CLEAR); + if (full) + { + if (interpreter) + { + interpreter_conv = convert_path_u2a(interpreter); +#if !defined(__USE_RUNCOMMAND__) +#warning(using system!) + sprintf(full, "%s %s %s ", interpreter_conv, + interpreter_args, filename_conv); +#else + sprintf(full, "%s %s ", interpreter_args, + filename_conv); +#endif + IExec->FreeVec(interpreter); + IExec->FreeVec(interpreter_args); + + if (filename_conv) + IExec->FreeVec(filename_conv); + fname = mystrdup(interpreter_conv); + + if (interpreter_conv) + IExec->FreeVec(interpreter_conv); + } + else + { +#ifndef __USE_RUNCOMMAND__ + sprintf(full, "%s ", filename_conv); +#else + sprintf(full, ""); +#endif + fname = mystrdup(filename_conv); + if (filename_conv) + IExec->FreeVec(filename_conv); + } + + for (cur = (char **)(argv + 1); *cur != 0; cur++) + { + if (contains_whitespace(*cur)) + { + int esc = no_of_escapes(*cur); + + if (esc > 0) + { + char *buff = IExec->AllocVec( + strlen(*cur) + 4 + esc, + MEMF_ANY | MEMF_CLEAR); + char *p = *cur; + char *q = buff; + + *q++ = '"'; + while (*p != '\0') + { + + if (*p == '\n') + { + *q++ = '*'; + *q++ = 'N'; + p++; + continue; + } + else if (*p == '"') + { + *q++ = '*'; + *q++ = '"'; + p++; + continue; + } + else if (*p == '*') + { + *q++ = '*'; + } + *q++ = *p++; + } + *q++ = '"'; + *q++ = ' '; + *q = '\0'; + strcat(full, buff); + IExec->FreeVec(buff); + } + else + { + strcat(full, "\""); + strcat(full, *cur); + strcat(full, "\" "); + } + } + else + { + strcat(full, *cur); + strcat(full, " "); + } + } + strcat(full, "\n"); + +// if(envp) +// createvars(envp); + +#ifndef __USE_RUNCOMMAND__ + result = IDOS->SystemTags( + full, SYS_UserShell, TRUE, NP_StackSize, + ((struct Process *)thisTask)->pr_StackSize, SYS_Input, + ((struct Process *)thisTask)->pr_CIS, SYS_Output, + ((struct Process *)thisTask)->pr_COS, SYS_Error, + ((struct Process *)thisTask)->pr_CES, TAG_DONE); +#else + + if (fname) + { + BPTR seglist = IDOS->LoadSeg(fname); + if (seglist) + { + /* check if we have an executable! */ + struct PseudoSegList *ps = NULL; + if (!IDOS->GetSegListInfoTags( + seglist, GSLI_Native, &ps, TAG_DONE)) + { + IDOS->GetSegListInfoTags( + seglist, GSLI_68KPS, &ps, TAG_DONE); + } + if (ps != NULL) + { + // adebug("%s %ld %s + // %s\n",__FUNCTION__,__LINE__,fname,full); + IDOS->SetCliProgramName(fname); + // result=RunCommand(seglist,8*1024,full,strlen(full)); + // result=myruncommand(seglist,8*1024,full,strlen(full),envp); + result = myruncommand(seglist, 8 * 1024, + full, -1, envp); + errno = 0; + } + else + { + errno = ENOEXEC; + } + IDOS->UnLoadSeg(seglist); + } + else + { + errno = ENOEXEC; + } + IExec->FreeVec(fname); + } + +#endif /* USE_RUNCOMMAND */ + + IExec->FreeVec(full); + if (errno == ENOEXEC) + { + result = -1; + } + goto out; + } + + if (interpreter) + IExec->FreeVec(interpreter); + if (filename_conv) + IExec->FreeVec(filename_conv); + + errno = ENOMEM; + +out: + if (isperlthread) + { + amigaos_stdio_restore(aTHX_ & store); + STATUS_NATIVE_CHILD_SET(result); + PL_exit_flags |= PERL_EXIT_EXPECTED; + if (result != -1) + my_exit(result); + } + return (result); +} diff --git a/amigaos4/amigaio.h b/amigaos4/amigaio.h index 03a64d8888..0385ce14bd 100644 --- a/amigaos4/amigaio.h +++ b/amigaos4/amigaio.h @@ -1,7 +1,9 @@ #ifndef _AMIGAIO_H #define _AMIGAIO_H +#ifndef H_PERL #include "../perl.h" +#endif struct StdioStore { diff --git a/amigaos4/amigaos.c b/amigaos4/amigaos.c index 12fb577b70..8e26064f08 100644 --- a/amigaos4/amigaos.c +++ b/amigaos4/amigaos.c @@ -19,6 +19,9 @@ #include <fcntl.h> #include <ctype.h> #include <stdarg.h> +#include <stdbool.h> +#undef WORD +#define WORD int16 #include <dos/dos.h> #include <proto/dos.h> @@ -87,7 +90,6 @@ int VARARGS68K adebug(UBYTE *fmt, ...); char **myenviron = NULL; char **origenviron = NULL; -int myexecve(const char *path, char *argv[], char *envp[]); static void createvars(char **envp); struct args @@ -145,7 +147,7 @@ int32 myruncommand( return myargs.result; } -static char *mystrdup(const char *s) +char *mystrdup(const char *s) { char *result = NULL; size_t size; @@ -338,7 +340,7 @@ struct command_data struct Task *parent; }; -int myexecvp(const char *filename, char *argv[]) +int myexecvp(bool isperlthread, const char *filename, char *argv[]) { // adebug("%s %ld //%s\n",__FUNCTION__,__LINE__,filename?filename:"NULL"); @@ -391,16 +393,16 @@ int myexecvp(const char *filename, char *argv[]) } while (*p++ != '\0'); } - res = myexecve(filename, argv, myenviron); + res = myexecve(isperlthread, filename, argv, myenviron); return res; } -int myexecv(const char *path, char *argv[]) +int myexecv(bool isperlthread, const char *path, char *argv[]) { - return myexecve(path, argv, myenviron); + return myexecve(isperlthread, path, argv, myenviron); } -int myexecl(const char *path, ...) +int myexecl(bool isperlthread, const char *path, ...) { va_list va; char *argv[1024]; /* 1024 enough? let's hope so! */ @@ -416,9 +418,11 @@ int myexecl(const char *path, ...) } while (argv[i++] != NULL); va_end(va); - return myexecve(path, argv, myenviron); + return myexecve(isperlthread, path, argv, myenviron); } +#if 0 + int myexecve(const char *filename, char *argv[], char *envp[]) { FILE *fh; @@ -436,6 +440,15 @@ int myexecve(const char *filename, char *argv[], char *envp[]) // struct Task *thisTask = IExec->FindTask(0); int result = -1; + StdioStore store; + + dTHX; + if(aTHX) // I hope this is NULL when not on a interpreteer thread nor to level. + { + /* Save away our stdio */ + amigaos_stdio_save(aTHX_ & store); + } + // adebug("%s %ld %s\n",__FUNCTION__,__LINE__,filename?filename:"NULL"); /* Calculate the size of filename and all args, including spaces and @@ -486,7 +499,7 @@ int myexecve(const char *filename, char *argv[], char *envp[]) if (errno == ENOENT) { /* file didn't exist! */ - return -1; + goto out; } } @@ -644,8 +657,10 @@ int myexecve(const char *filename, char *argv[], char *envp[]) IExec->FreeVec(full); if (errno == ENOEXEC) - return -1; - return result; + { + result = -1; + } + goto out; } if (interpreter) @@ -655,9 +670,18 @@ int myexecve(const char *filename, char *argv[], char *envp[]) errno = ENOMEM; - return -1; +out: + + amigaos_stdio_restore(aTHX_ &store); + STATUS_NATIVE_CHILD_SET(result); + PL_exit_flags |= PERL_EXIT_EXPECTED; + if (result != -1) my_exit(result); + + return(result); } +#endif + int pause(void) { fprintf(stderr, "Pause not implemented\n"); @@ -804,7 +828,7 @@ int popen_child() * argv[] */ - myexecvp(argv[0], argv); + myexecvp(FALSE, argv[0], argv); if (command) IExec->FreeVec(command); diff --git a/amigaos4/amigaos.h b/amigaos4/amigaos.h index bcbde8c96f..96f521d59b 100644 --- a/amigaos4/amigaos.h +++ b/amigaos4/amigaos.h @@ -12,23 +12,23 @@ #include <stdio.h> #if defined(__CLIB2__) -# include <dos.h> +#include <dos.h> #endif #if defined(__NEWLIB__) -# include <amiga_platform.h> +#include <amiga_platform.h> #endif #if 1 -int myexecve(const char *path, char *argv[], char *env[]); -int myexecvp(const char *filename, char *argv[]); -int myexecv(const char *path, char *argv[]); -int myexecl(const char *path, ...); +int myexecve(bool isperlthread, const char *path, char *argv[], char *env[]); +int myexecvp(bool isperlthread, const char *filename, char *argv[]); +int myexecv(bool isperlthread, const char *path, char *argv[]); +int myexecl(bool isperlthread, const char *path, ...); #endif -#define execve(path, argv, env) myexecve(path, argv, env) -#define execvp(filename, argv) myexecvp(filename, argv) -#define execv(path, argv) myexecv(path, argv) -#define execl(path, ...) myexecl(path, __VA_ARGS__) +#define execve(path, argv, env) myexecve(TRUE, path, argv, env) +#define execvp(filename, argv) myexecvp(TRUE, filename, argv) +#define execv(path, argv) myexecv(TRUE, path, argv) +#define execl(path, ...) myexecl(TRUE, path, __VA_ARGS__) int pipe(int filedes[2]); @@ -36,6 +36,11 @@ FILE *amigaos_popen(const char *cmd, const char *mode); void amigaos4_obtain_environ(); void amigaos4_release_environ(); +char *mystrdup(const char *s); + +char *convert_path_u2a(const char *filename); +char *convert_path_a2u(const char *filename); + /* signal.h */ // #define SIGQUIT SIGABRT @@ -47,6 +52,4 @@ long amigaos_get_file(int fd); // BOOL constructed; - - #endif |