In Perl.h, create a new macro STATUS_UNIX_EXIT_SET for setting the exit
status correctly on VMS, and set that macro to be the same as
STATUS_UNIX_SET for non VMS platforms.
This new macro restores the previous behavior of EXIT handling on VMS.
Also added a latent feature that allows setting the encoding UNIX exit
status codes from 1 to 255 into a VMS status code that can be decoded by
other VMS programs like the BASH shell in GNV.
Fixed STATUS_NATIVE_CHILD_SET / STATUS_NATIVE_SET to not set the VMS
native status value to the illegal -1 status.
In perl.c, use STATUS_UNIX_EXIT_SET for setting the exit status.
In vmsish.h, add a posix_exit status to the interp_intern structure and
prototype for routine that attempts to map UNIX errno values to VMS
status values.
In vms.c:
Add unix_status_to_vms() routine for converting UNIX errno codes to a
possible VMS status code that could have caused it. This can not be
100% accurate unless the UNIX status was EVMSERR. But this puts a rock
on a can of worms for now that will keep most of them in.
Fix some bugs in Perl_my_kill() so that it returns a better error code
for unimplemented multiple processs signaling, and implement so that
signal 0 can be used to validate a process.
Fix mp_to_vmsify() for a bug that Craig Berry noticed caused translated
values to sometimes be truncated because the code was guessing wrong at
the resulting filename size when doing a Newx().
Todo:
A method needs to be created to allow setting the exit code handling on
VMS into the mode that GNV and programs written in C expect and
documentation about it. For early testing it is temporarily tied to the
DECC$FILENAME_REPORT_UNIX setting as that is not fully functional until
Pathtools and Cwd get updated to also use that setting.
-John
[EMAIL PROTECTED]
Personal Opinion Only
--- /rsync_root/perl/perl.c Thu Oct 13 20:01:44 2005
+++ ./perl.c Thu Oct 13 22:55:25 2005
@@ -5155,7 +5155,7 @@
STATUS_ALL_FAILURE;
break;
default:
- STATUS_UNIX_SET(status);
+ STATUS_UNIX_EXIT_SET(status);
break;
}
my_exit_jump();
@@ -5165,6 +5165,15 @@
Perl_my_failure_exit(pTHX)
{
#ifdef VMS
+
+/* Note: Unless other actions are taken to refresh it,
+ vaxc$errno only has meaningful information if the
+ errno == EVMSERR and errno is not stale.
+
+ Hopefully one of the STATUS macros or something
+ fixed it up before this point.
+ */
+
if (vaxc$errno & 1) {
if (STATUS_NATIVE & 1) /* fortuitiously includes "-1" */
STATUS_NATIVE_SET(44);
--- /rsync_root/perl/perl.h Wed Oct 5 12:03:27 2005
+++ ./perl.h Thu Oct 13 22:50:34 2005
@@ -1097,6 +1097,7 @@
# undef SETERRNO /* SOCKS might have defined this */
#endif
+/* Fixme: Should not this be setting the PL_statusvalue* values also? */
#ifdef VMS
# define SETERRNO(errcode,vmserrcode) \
STMT_START { \
@@ -2545,49 +2546,177 @@
#define STATUS_UNIX PL_statusvalue
#ifdef VMS
# define STATUS_NATIVE PL_statusvalue_vms
+/*
+ * Finding this broken again after fixing it, it is clear that some
+ * documentation is needed so that non-VMS (and some VMS) programmers
+ * can figure out what is going on and what should happen.
+ *
+ * VMS native exit codes are actually a structure that is packed into
+ * a longword. It is normally handled as a longword for convenience.
+ * this information is contained in stsdef.h, which will now be included
+ * below to make sure that it's definitions are visible.
+ * Such exit codes are usually generated by the message utility, with
+ * separate message text files that each code can be translated back to
+ * text. Such message text files can be internationallized.
+ *
+ * UNIX/POSIX error codes are currently in a range of 0 to 255.
+ *
+ * Also unlike UNIX where an exit code is just returned to the shell, the
+ * native VMS exit code is passed through a signal handler as the program
+ * is exited.
+ *
+ * This means that problems will occur if the exit code is not a valid VMS
+ * exit code.
+ *
+ * Past versions of Perl on VMS solved that problem by translating UNIX
+ * error status values to 44 when the program exited. This causes a loss of
+ * information.
+ *
+ * There was also an vmsish pragma to cause it to change the Perl exit code
+ * 1 to UNIX code 0, which the C exit code restores back to 1.
+ *
+ * With VMS 7.0, this problem was addressed by encoding the UNIX exit
+ * codes into a range of VMS status codes. Prior to the last change that
+ * broke things on VMS, I fixed the code to translate these codes back to
+ * the proper UNIX codes for processes. For programs ported from UNIX to
+ * VMS, "#define _POSIX_EXIT 1" is needed to take advantage of this.
+ * Perl is too VMSized to use this exit method.
+ *
+ * Unfortunately there is a practice of passing UNIX codes untranslated
+ * back to DCL that has been adopted by people who do not realize what
+ * real and potential problems can come out of that.
+ *
+ * To work correctly with the GNV utilities, Perl must be better behaved
+ * and when the perl exit codes from 1 through 255 are given it should
+ * translate them with:
+ * vms_status = 0x35A000 | ((unix_status & 0xFF) << 3) if unix_status != 0;
+ *
+ * vaxc$errno is only suppose 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 <stsdef.h>
+# include <ssdef.h>
+/* Code to try to protect against if the size of the UNIX parent/child */
+/* ever changes. Nothing guarantees it to be 8 bits in size always. */
+/* Commenting out for now as not likely to change on VMS, and I do not */
+/* feel like debugging it if I did something wrong */
+/* # include <wait.h> */
+/* # ifndef WEXITSTATUS */
+# define child_offset_bits (8)
+/* # define WEXITSTATUS(s) (((s)>>child_offset_bits)&0xFF) */
+/* # else */
+/* # define child_offset_bits (offsetof(wait,_w_t._w_retcode)*8) */
+/* # endif */
+# 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 where it is needed the least.
+ */
+
# 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 = SS$_ABORT; /* Should not happen */ \
+ } else \
PL_statusvalue = vms_status_to_unix(evalue); \
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.
+ */
# define STATUS_UNIX_SET(n) \
STMT_START { \
PL_statusvalue = (n); \
if (PL_statusvalue != -1) { \
if (PL_statusvalue != EVMSERR) { \
PL_statusvalue &= 0xFFFF; \
- PL_statusvalue_vms = PL_statusvalue ? 44 : 1; \
+ PL_statusvalue_vms = unix_status_to_vms(n); \
} \
else { \
PL_statusvalue_vms = vaxc$errno; \
} \
} \
- else PL_statusvalue_vms = -1; \
+ else PL_statusvalue_vms = SS$_ABORT; \
+ set_vaxc_errno(n); \
+ } 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 { \
+ PL_statusvalue = (n); \
+ 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 |
((n) << 3)) : (n); \
+ } else { /* forgive them Perl, for they have sinned */ \
+ PL_statusvalue_vms = (n); \
+ } /* 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)) : SS$_ABORT)
#else
# define STATUS_NATIVE PL_statusvalue_posix
# if defined(WCOREDUMP)
@@ -2633,6 +2762,7 @@
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 +3608,8 @@
#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*);
--- /rsync_root/perl/vms/vms.c Tue Sep 13 02:52:56 2005
+++ vms/vms.c Fri Oct 14 00:57:49 2005
@@ -1477,9 +1477,50 @@
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) {
+ errno = EINVAL;
+ vaxc$errno = 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 lie and say the user has no privileges.
+ */
+
+ if (pid <= 0) {
+ errno = EPERM;
+ vaxc$errno = SS$_NOPRIV;
return -1;
}
@@ -1554,6 +1595,13 @@
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:
@@ -1630,6 +1678,14 @@
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 +1714,135 @@
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 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 = vms_status_to_unix(vaxc$errno);
+ 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 */
+ /* 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 */
@@ -4370,7 +4555,7 @@
if (path == NULL) return NULL;
if (buf) rslt = buf;
- else if (ts) Newx(rslt,strlen(path)+9,char);
+ else if (ts) Newx(rslt,NAM$C_MAXRSS+1,char);
else rslt = __tovmsspec_retbuf;
if (strpbrk(path,"]:>") ||
(dirend = strrchr(path,'/')) == NULL) {
@@ -4402,7 +4587,7 @@
while (*(cp2+1) == '/') cp2++; /* Skip multiple /s */
if (!*(cp2+1)) {
- if (!buf & ts) Renew(rslt,18,char);
+/* if (!buf & ts) Renew(rslt,18,char); */
if (decc_disable_posix_root) {
strcpy(rslt,"sys$disk:[000000]");
}
@@ -4626,6 +4811,8 @@
* which is wrong. UNIX notation should be ".dir. unless
* the DECC$FILENAME_UNIX_NO_VERSION is enabled.
* changing this behavior could break more things at this time.
+ * efs character set effectively does not allow "." to be a version
+ * delimiter.
*/
if (decc_filename_unix_report != 0) {
*(cp1++) = '^';
@@ -8308,6 +8495,10 @@
double x;
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;
--- /rsync_root/perl/vms/vmsish.h Sun Oct 2 18:15:16 2005
+++ vms/vmsish.h Thu Oct 13 23:23:44 2005
@@ -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
@@ -763,6 +765,7 @@
void prime_env_iter (void);
void init_os_extras (void);
int vms_status_to_unix(int vms_status);
+int 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);