From: John E. Malmberg Date: Sun, 16 Oct 2005 02:30:43 +0000 (-0400) Subject: [patch@25763] Fix VMS error/exit handling, update kill function X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=7a7fd8e0ed51785cbfb9fc040ff670a40911ca48;p=p5sagit%2Fp5-mst-13.2.git [patch@25763] Fix VMS error/exit handling, update kill function From: "John E. Malmberg" Message-id: <4351F393.8030809@qsl.net> Date: Sun, 16 Oct 2005 02:30:43 -0400 p4raw-id: //depot/perl@25772 --- diff --git a/perl.c b/perl.c index cab20e3..2f8dbf4 100644 --- a/perl.c +++ b/perl.c @@ -5155,7 +5155,7 @@ Perl_my_exit(pTHX_ U32 status) STATUS_ALL_FAILURE; break; default: - STATUS_UNIX_SET(status); + STATUS_UNIX_EXIT_SET(status); break; } my_exit_jump(); diff --git a/perl.h b/perl.h index e8bf99f..f613aac 100644 --- a/perl.h +++ b/perl.h @@ -2545,49 +2545,133 @@ typedef pthread_key_t perl_key; #define STATUS_UNIX PL_statusvalue #ifdef VMS # define STATUS_NATIVE PL_statusvalue_vms +/* + * vaxc$errno is only guaranteed to be valid if errno == EVMSERR, otherwise + * it's contents can not be trusted. Unfortunately, Perl seems to check + * it on exit, so it when PL_statusvalue_vms is updated, vaxc$errno should + * be updated also. + */ +# include +# include +/* Presume this because if VMS changes it, it will require a new + * set of APIs for waiting on children for binary compatibility. + */ +# define child_offset_bits (8) +# ifndef C_FAC_POSIX +# define C_FAC_POSIX 0x35A000 +# endif + +/* STATUS_EXIT - validates and returns a NATIVE exit status code for the + * platform from the existing UNIX or Native status values. + */ + # define STATUS_EXIT \ - (((I32)PL_statusvalue_vms == -1 ? 44 : PL_statusvalue_vms) | (VMSISH_HUSHED ? 0x10000000 : 0)) + (((I32)PL_statusvalue_vms == -1 ? SS$_ABORT : PL_statusvalue_vms) | \ + (VMSISH_HUSHED ? STS$M_INHIB_MSG : 0)) + +/* STATUS_NATIVE_SET - takes a NATIVE status code and converts it to a + * UNIX/POSIX status value and updates both the native and PL_statusvalue + * as needed. This currently seems only exist for VMS and is used in the exit + * handling. + */ + # define STATUS_NATIVE_SET(n) STATUS_NATIVE_SET_PORC(n, 0) + +/* STATUS_NATIVE_CHILD_SET - same as STATUS_NATIVE_SET, but shifts the UNIX + * value over the correct number of bits to be a child status. Usually + * the number of bits is 8, but that could be platform dependent. The NATIVE + * status code is presumed to have either from a child process. + */ + # define STATUS_NATIVE_CHILD_SET(n) STATUS_NATIVE_SET_PORC(n, 1) + + /* internal convert VMS status codes to UNIX error or status codes */ # define STATUS_NATIVE_SET_PORC(n, _x) \ STMT_START { \ I32 evalue = (I32)n; \ if (evalue == EVMSERR) { \ PL_statusvalue_vms = vaxc$errno; \ PL_statusvalue = evalue; \ - } \ - else { \ + } else { \ PL_statusvalue_vms = evalue; \ - if ((I32)PL_statusvalue_vms == -1) \ + if ((I32)PL_statusvalue_vms == -1) { \ PL_statusvalue = -1; \ - else \ - PL_statusvalue = vms_status_to_unix(evalue); \ + PL_statusvalue_vms = SS$_ABORT; /* Should not happen */ \ + } else \ + PL_statusvalue = Perl_vms_status_to_unix(evalue, _x); \ set_vaxc_errno(evalue); \ set_errno(PL_statusvalue); \ - if (_x) PL_statusvalue = PL_statusvalue << 8; \ + if (_x) PL_statusvalue = PL_statusvalue << child_offset_bits; \ } \ } STMT_END + # ifdef VMSISH_STATUS # define STATUS_CURRENT (VMSISH_STATUS ? STATUS_NATIVE : STATUS_UNIX) # else # define STATUS_CURRENT STATUS_UNIX # endif + + /* STATUS_UNIX_SET - takes a UNIX/POSIX errno value and attempts to update + * the NATIVE status to an equivalent value. Can not be used to translate + * exit code values as exit code values are not guaranteed to have any + * relationship at all to errno values. + * This is used when Perl is forcing errno to have a specific value. + */ # define STATUS_UNIX_SET(n) \ STMT_START { \ - PL_statusvalue = (n); \ + I32 evalue = (I32)n; \ + PL_statusvalue = evalue; \ if (PL_statusvalue != -1) { \ if (PL_statusvalue != EVMSERR) { \ PL_statusvalue &= 0xFFFF; \ - PL_statusvalue_vms = PL_statusvalue ? 44 : 1; \ + PL_statusvalue_vms = Perl_unix_status_to_vms(evalue); \ } \ else { \ PL_statusvalue_vms = vaxc$errno; \ } \ } \ - else PL_statusvalue_vms = -1; \ + else PL_statusvalue_vms = SS$_ABORT; \ + set_vaxc_errno(evalue); \ + } STMT_END + + /* STATUS_UNIX_EXIT_SET - Takes a UNIX/POSIX exit code and sets + * the NATIVE error status based on it. It does not assume that + * the UNIX/POSIX exit codes have any relationship to errno + * values and are only being encoded into the NATIVE form so + * that they can be properly passed through to the calling + * program or shell. + */ + +# define STATUS_UNIX_EXIT_SET(n) \ + STMT_START { \ + I32 evalue = (I32)n; \ + PL_statusvalue = evalue; \ + if (PL_statusvalue != -1) { \ + if (PL_statusvalue != EVMSERR) { \ + if (PL_statusvalue < 256) { \ + if (PL_statusvalue == 0) \ + PL_statusvalue_vms == SS$_NORMAL; \ + else \ + PL_statusvalue_vms = MY_POSIX_EXIT ? \ + (C_FAC_POSIX | (evalue << 3 ) | (evalue == 1)? \ + (STS$K_ERROR | STS$M_INHIB_MSG) : 0) : evalue; \ + } else { /* forgive them Perl, for they have sinned */ \ + PL_statusvalue_vms = evalue; \ + } /* And obviously used a VMS status value instead of UNIX */ \ + PL_statusvalue = EVMSERR; \ + } \ + else { \ + PL_statusvalue_vms = vaxc$errno; \ + } \ + } \ + else PL_statusvalue_vms = SS$_ABORT; \ + set_vaxc_errno(PL_statusvalue_vms); \ } STMT_END -# define STATUS_ALL_SUCCESS (PL_statusvalue = 0, PL_statusvalue_vms = 1) -# define STATUS_ALL_FAILURE (PL_statusvalue = 1, PL_statusvalue_vms = 44) +# define STATUS_ALL_SUCCESS \ + (PL_statusvalue = 0, PL_statusvalue_vms = SS$_NORMAL) +# define STATUS_ALL_FAILURE (PL_statusvalue = 1, \ + vaxc$errno = PL_statusvalue_vms = MY_POSIX_EXIT ? \ + (C_FAC_POSIX | (1 << 3) | STS$K_ERROR | STS$M_INHIB_MSG) : SS$_ABORT) #else # define STATUS_NATIVE PL_statusvalue_posix # if defined(WCOREDUMP) @@ -2633,6 +2717,7 @@ typedef pthread_key_t perl_key; if (PL_statusvalue != -1) \ PL_statusvalue &= 0xFFFF; \ } STMT_END +# define STATUS_UNIX_EXIT_SET(n) STATUS_UNIX_SET(n) # define STATUS_CURRENT STATUS_UNIX # define STATUS_EXIT STATUS_UNIX # define STATUS_ALL_SUCCESS (PL_statusvalue = 0, PL_statusvalue_posix = 0) @@ -3478,6 +3563,8 @@ char *getlogin (void); #endif #endif /* !__cplusplus */ +/* Fixme on VMS. This needs to be a run-time, not build time options */ +/* Also rename() is affected by this */ #ifdef UNLINK_ALL_VERSIONS /* Currently only makes sense for VMS */ #define UNLINK unlnk I32 unlnk (const char*); diff --git a/t/run/exit.t b/t/run/exit.t index 90eeafc..8302ae8 100644 --- a/t/run/exit.t +++ b/t/run/exit.t @@ -17,7 +17,7 @@ sub run { BEGIN { # MacOS system() doesn't have good return value - $numtests = ($^O eq 'VMS') ? 14 : ($^O eq 'MacOS') ? 0 : 17; + $numtests = ($^O eq 'VMS') ? 16 : ($^O eq 'MacOS') ? 0 : 17; } require "test.pl"; @@ -95,24 +95,30 @@ if ($^O ne 'VMS') { # Double quotes are needed to pass these commands through DCL to PERL $exit = run("exit 268632065"); # %CLI-S-NORMAL - is( $exit, 0, 'PERL success exit' ); + is( $exit >> 8, 0, 'PERL success exit' ); is( ${^CHILD_ERROR_NATIVE} & 7, 1, 'VMS success exit' ); $exit = run("exit 268632067"); # %CLI-I-NORMAL - is( $exit, 0, 'PERL informational exit' ); + is( $exit >> 8, 0, 'PERL informational exit' ); is( ${^CHILD_ERROR_NATIVE} & 7, 3, 'VMS informational exit' ); $exit = run("exit 268632064"); # %CLI-W-NORMAL - is( $exit != 0, 1, 'Perl warning exit' ); + is( $exit >> 8, 1, 'Perl warning exit' ); is( ${^CHILD_ERROR_NATIVE} & 7, 0, 'VMS warning exit' ); $exit = run("exit 268632066"); # %CLI-E-NORMAL - is( $exit != 0, 1, 'Perl error exit' ); + is( $exit >> 8, 2, 'Perl error exit' ); is( ${^CHILD_ERROR_NATIVE} & 7, 2, 'VMS error exit' ); $exit = run("exit 268632068"); # %CLI-F-NORMAL - is( $exit != 0, 1, 'Perl fatal error exit' ); + is( $exit >> 8, 4, 'Perl fatal error exit' ); is( ${^CHILD_ERROR_NATIVE} & 7, 4, 'VMS fatal exit' ); + + $exit = run("exit 02015320012"); # POSIX exit code 1 + is( $exit >> 8, 1, 'Posix exit code 1' ); + + $exit = run("exit 02015323771"); # POSIX exit code 255 + is( $exit >> 8 , 255, 'Posix exit code 255' ); } $exit_arg = 42; @@ -132,7 +138,7 @@ $exit = run("END { \$? = $exit_arg }"); # status codes to SS$_ABORT on exit, but passes through unmodified UNIX # status codes that exit() is called with by scripts. -$exit_arg = 4 if $^O eq 'VMS'; +$exit_arg = (44 & 7) if $^O eq 'VMS'; is( $exit >> 8, $exit_arg, 'Changing $? in END block' ); } diff --git a/vms/vms.c b/vms/vms.c index ad14ddc..b2c47d9 100644 --- a/vms/vms.c +++ b/vms/vms.c @@ -76,8 +76,7 @@ int decc$feature_set_value(int index, int mode, int value); #include #endif -#ifndef __VAX -#if __CRTL_VER >= 70300000 +#if __CRTL_VER >= 70300000 && !defined(__VAX) static int set_feature_default(const char *name, int value) { @@ -99,7 +98,6 @@ static int set_feature_default(const char *name, int value) return 0; } #endif -#endif /* Older versions of ssdef.h don't have these */ #ifndef SS$_INVFILFOROP @@ -1477,9 +1475,48 @@ Perl_my_kill(int pid, int sig) struct dsc$descriptor_s *prcname, unsigned int code); + /* sig 0 means validate the PID */ + /*------------------------------*/ + if (sig == 0) { + const unsigned long int jpicode = JPI$_PID; + pid_t ret_pid; + int status; + status = lib$getjpi(&jpicode, &pid, NULL, &ret_pid, NULL, NULL); + if ($VMS_STATUS_SUCCESS(status)) + return 0; + switch (status) { + case SS$_NOSUCHNODE: + case SS$_UNREACHABLE: + case SS$_NONEXPR: + errno = ESRCH; + break; + case SS$_NOPRIV: + errno = EPERM; + break; + default: + errno = EVMSERR; + } + vaxc$errno=status; + return -1; + } + code = Perl_sig_to_vmscondition(sig); - if (!pid || !code) { + if (!code) { + SETERRNO(EINVAL, SS$_BADPARAM); + return -1; + } + + /* Fixme: Per official UNIX specification: If pid = 0, or negative then + * signals are to be sent to multiple processes. + * pid = 0 - all processes in group except ones that the system exempts + * pid = -1 - all processes except ones that the system exempts + * pid = -n - all processes in group (abs(n)) except ... + * For now, just report as not supported. + */ + + if (pid <= 0) { + SETERRNO(ENOTSUP, SS$_UNSUPPORTED); return -1; } @@ -1526,7 +1563,7 @@ Perl_my_kill(int pid, int sig) #define DCL_IVVERB 0x38090 #endif -int vms_status_to_unix(int vms_status) +int Perl_vms_status_to_unix(int vms_status, int child_flag) { int facility; int fac_sp; @@ -1546,7 +1583,7 @@ int unix_status; fac_sp = vms_status & STS$M_FAC_SP; msg_no = vms_status & (STS$M_MSG_NO | STS$M_SEVERITY); - if ((facility == 0) || (fac_sp == 0)) { + if ((facility == 0) || (fac_sp == 0) && (child_flag == 0)) { switch(msg_no) { case SS$_NORMAL: unix_status = 0; @@ -1554,6 +1591,13 @@ int unix_status; case SS$_ACCVIO: unix_status = EFAULT; break; + case SS$_DEVOFFLINE: + unix_status = EBUSY; + break; + case SS$_CLEARED: + unix_status = ENOTCONN; + break; + case SS$_IVCHAN: case SS$_IVLOGNAM: case SS$_BADPARAM: case SS$_IVLOGTAB: @@ -1565,6 +1609,9 @@ int unix_status; case SS$_IVIDENT: unix_status = EINVAL; break; + case SS$_UNSUPPORTED: + unix_status = ENOTSUP; + break; case SS$_FILACCERR: case SS$_NOGRPPRV: case SS$_NOSYSPRV: @@ -1612,9 +1659,31 @@ int unix_status; else { /* Translate a POSIX exit code to a UNIX exit code */ if ((facility == C_FACILITY_NO) && ((msg_no & 0xA000) == 0xA000)) { - unix_status = (msg_no & 0x0FF0) >> 3; + unix_status = (msg_no & 0x07F8) >> 3; } else { + + /* Documented traditional behavior for handling VMS child exits */ + /*--------------------------------------------------------------*/ + if (child_flag != 0) { + + /* Success / Informational return 0 */ + /*----------------------------------*/ + if (msg_no & STS$K_SUCCESS) + return 0; + + /* Warning returns 1 */ + /*-------------------*/ + if ((msg_no & (STS$K_ERROR | STS$K_SEVERE)) == 0) + return 1; + + /* Everything else pass through the severity bits */ + /*------------------------------------------------*/ + return (msg_no & STS$M_SEVERITY); + } + + /* Normal VMS status to ERRNO mapping attempt */ + /*--------------------------------------------*/ switch(msg_status) { /* case RMS$_EOF: */ /* End of File */ case RMS$_FNF: /* File Not Found */ @@ -1630,6 +1699,14 @@ int unix_status; case RMS$_DEV: unix_status = ENODEV; break; + case RMS$_IFI: + case RMS$_FAC: + case RMS$_ISI: + unix_status = EBADF; + break; + case RMS$_FEX: + unix_status = EEXIST; + break; case RMS$_SYN: case RMS$_FNM: case LIB$_INVSTRDES: @@ -1658,6 +1735,135 @@ int unix_status; return unix_status; } +/* Try to guess at what VMS error status should go with a UNIX errno + * value. This is hard to do as there could be many possible VMS + * error statuses that caused the errno value to be set. + */ + +int Perl_unix_status_to_vms(int unix_status) +{ +int test_unix_status; + + /* Trivial cases first */ + /*---------------------*/ + if (unix_status == EVMSERR) + return vaxc$errno; + + /* Is vaxc$errno sane? */ + /*---------------------*/ + test_unix_status = Perl_vms_status_to_unix(vaxc$errno, 0); + if (test_unix_status == unix_status) + return vaxc$errno; + + /* If way out of range, must be VMS code already */ + /*-----------------------------------------------*/ + if (unix_status > EVMSERR) + return unix_status; + + /* If out of range, punt */ + /*-----------------------*/ + if (unix_status > __ERRNO_MAX) + return SS$_ABORT; + + + /* Ok, now we have to do it the hard way. */ + /*----------------------------------------*/ + switch(unix_status) { + case 0: return SS$_NORMAL; + case EPERM: return SS$_NOPRIV; + case ENOENT: return SS$_NOSUCHOBJECT; + case ESRCH: return SS$_UNREACHABLE; + case EINTR: return SS$_ABORT; + /* case EIO: */ + /* case ENXIO: */ + case E2BIG: return SS$_BUFFEROVF; + /* case ENOEXEC */ + case EBADF: return RMS$_IFI; + case ECHILD: return SS$_NONEXPR; + /* case EAGAIN */ + case ENOMEM: return SS$_INSFMEM; + case EACCES: return SS$_FILACCERR; + case EFAULT: return SS$_ACCVIO; + /* case ENOTBLK */ + case EBUSY: SS$_DEVOFFLINE; + case EEXIST: return RMS$_FEX; + /* case EXDEV */ + case ENODEV: return SS$_NOSUCHDEV; + case ENOTDIR: return RMS$_DIR; + /* case EISDIR */ + case EINVAL: return SS$_INVARG; + /* case ENFILE */ + /* case EMFILE */ + /* case ENOTTY */ + /* case ETXTBSY */ + /* case EFBIG */ + case ENOSPC: return SS$_DEVICEFULL; + case ESPIPE: return LIB$_INVARG; + /* case EROFS: */ + /* case EMLINK: */ + /* case EPIPE: */ + /* case EDOM */ + case ERANGE: return LIB$_INVARG; + /* case EWOULDBLOCK */ + /* case EINPROGRESS */ + /* case EALREADY */ + /* case ENOTSOCK */ + /* case EDESTADDRREQ */ + /* case EMSGSIZE */ + /* case EPROTOTYPE */ + /* case ENOPROTOOPT */ + /* case EPROTONOSUPPORT */ + /* case ESOCKTNOSUPPORT */ + /* case EOPNOTSUPP */ + /* case EPFNOSUPPORT */ + /* case EAFNOSUPPORT */ + /* case EADDRINUSE */ + /* case EADDRNOTAVAIL */ + /* case ENETDOWN */ + /* case ENETUNREACH */ + /* case ENETRESET */ + /* case ECONNABORTED */ + /* case ECONNRESET */ + /* case ENOBUFS */ + /* case EISCONN */ + case ENOTCONN: return SS$_CLEARED; + /* case ESHUTDOWN */ + /* case ETOOMANYREFS */ + /* case ETIMEDOUT */ + /* case ECONNREFUSED */ + /* case ELOOP */ + /* case ENAMETOOLONG */ + /* case EHOSTDOWN */ + /* case EHOSTUNREACH */ + /* case ENOTEMPTY */ + /* case EPROCLIM */ + /* case EUSERS */ + /* case EDQUOT */ + /* case ENOMSG */ + /* case EIDRM */ + /* case EALIGN */ + /* case ESTALE */ + /* case EREMOTE */ + /* case ENOLCK */ + /* case ENOSYS */ + /* case EFTYPE */ + /* case ECANCELED */ + /* case EFAIL */ + /* case EINPROG */ + case ENOTSUP: + return SS$_UNSUPPORTED; + /* case EDEADLK */ + /* case ENWAIT */ + /* case EILSEQ */ + /* case EBADCAT */ + /* case EBADMSG */ + /* case EABANDONED */ + default: + return SS$_ABORT; /* punt */ + } + + return SS$_ABORT; /* Should not get here */ +} /* default piping mailbox size */ @@ -8308,6 +8514,10 @@ Perl_sys_intern_init(pTHX) VMSISH_HUSHED = 0; + /* fix me later to track running under GNV */ + /* this allows some limited testing */ + MY_POSIX_EXIT = decc_filename_unix_report; + x = (float)ix; MY_INV_RAND_MAX = 1./x; } diff --git a/vms/vmsish.h b/vms/vmsish.h index 41b2bb2..2ca6f03 100644 --- a/vms/vmsish.h +++ b/vms/vmsish.h @@ -300,10 +300,12 @@ #define HAVE_INTERP_INTERN struct interp_intern { int hushed; + int posix_exit; double inv_rand_max; }; #define VMSISH_HUSHED (PL_sys_intern.hushed) #define MY_INV_RAND_MAX (PL_sys_intern.inv_rand_max) +#define MY_POSIX_EXIT (PL_sys_intern.posix_exit) /* Flags for vmstrnenv() */ #define PERL__TRNENV_SECURE 0x01 @@ -762,7 +764,8 @@ typedef unsigned myino_t; void prime_env_iter (void); void init_os_extras (void); -int vms_status_to_unix(int vms_status); +int Perl_vms_status_to_unix(int vms_status, int child_flag); +int Perl_unix_status_to_vms(int unix_status); /* prototype section start marker; `typedef' passes through cpp */ typedef char __VMS_PROTOTYPES__; int Perl_vmstrnenv (const char *, char *, unsigned long int, struct dsc$descriptor_s **, unsigned long int);