/* Stubs for some unix routines used by Hercules which are not found in VMS (c) Copyright Peter Coghlan 2004-2012 */ #include #include #include #include #include #include #include "sched.h" #include "termios.h" #include #include #include #include #include /* The definitions of sched_get_priority_max/min are broken in the pthread.h that comes with VMS 8.2. (The typedef for timespec_t also seems to have fallen out...) */ #undef sched_get_priority_max #undef sched_get_priority_min # define sched_get_priority_max(_pol_) \ (_pol_ == SCHED_OTHER ? PRI_FG_MAX_NP : PRI_FIFO_MAX) # define sched_get_priority_min(_pol_) \ (_pol_ == SCHED_OTHER ? PRI_FG_MIN_NP : PRI_FIFO_MIN) int getpriority(int which, int who) { int sched_policy, min_priority, max_priority, scaling, status; sched_param_t sched_params; /* Only allow the priority of the current thread to be retrieved */ if ((which != PRIO_PROCESS) || ((who != 0) && (who != getpid()))) { errno = ENOTSUP; return -1; } status = pthread_getschedparam(pthread_self(), &sched_policy, &sched_params); if (status != 0) { errno = status; return -1; } /* Map the thread priority value obtained into the "nice" values understood by getpriority(). */ min_priority = sched_get_priority_min(sched_policy); max_priority = sched_get_priority_max(sched_policy); /* Set errno to 0 to indicate no error. Yuk. */ errno = 0; /* We assume that "nice" values are typical unix values which range from -20 to +19 with -20 corresponding to highest priority and calculate a scaling factor to scale thread priorities into "nice" values. */ scaling = (19 - -20 + 1) / (max_priority - min_priority + 1); return 20 - ((sched_params.sched_priority - min_priority) * scaling); } int setpriority(int which, int who, int priority) { /* This routine appears to be intended to set the process priority. However, Hercules appears to be using it to set the thread priority which appears to amount to the same thing under unix. As this is not the case under vms, we will have to specificly address the thread priority. */ int sched_policy, min_priority, max_priority, scaling, status; sched_param_t sched_params, dummy; pthread_t this_thread; /* Only allow the priority of the current thread to be altered */ if ((which != PRIO_PROCESS) || ((who != 0) && (who != getpid()))) { errno = ENOTSUP; return -1; } /* Obtain the current scheduling policy first as it doesn't seem to be possible to alter the thread priority without also altering the scheduling policy. */ this_thread = pthread_self(); status = pthread_getschedparam(this_thread, &sched_policy, &sched_params); if (status != 0) { errno = status; return -1; } /* Now that we have the scheduling policy, we might as well make use of it to map the "nice" values of setpriority to the thread priority values of pthread_setschedparam(). */ min_priority = sched_get_priority_min(sched_policy); max_priority = sched_get_priority_max(sched_policy); /* We assume that "nice" values are typical unix values which range from -20 to +19 with -20 corresponding to highest priority and calculate a scaling factor to scale these values into thread priorities. */ scaling = (19 - -20 + 1) / (max_priority - min_priority + 1); /* A small sleight of hand here (using 20 instead of 19) ensures that the "nice" values 15 and 16 used by hercules both generate different thread priority values. */ sched_params.sched_priority = min_priority + ((20 - priority) / scaling); status = pthread_setschedparam(this_thread, sched_policy, &sched_params); if (status != 0) { errno = status; return -1; } /* At this point, everything worked ok. */ return 0; } int tcgetattr(int _fildes, struct termios *_termios_p) { /* Just get the terminals wrap characteristic. Hide it in _termios_p->c_cc[VMIN] for restoration on exit. */ unsigned long int lib$get_ef(), lib$free_ef(), sys$assign(), sys$dassgn(); unsigned long int lib$getdvi(), sys$qiow(), lib$signal(); unsigned long int status, devclass, efn, channel; struct { unsigned short int status; unsigned short int speeds; unsigned short int fills; unsigned short int parity; } terminal_iosb; struct { unsigned char class; /* device class */ unsigned char type; /* device type */ unsigned short int buffer_size; /* buffer size ( = page width) */ union ttdef basic; /* basic terminal characteristics */ } terminal_characteristics; $DESCRIPTOR(outputdevice, "SYS$COMMAND:"); /* Assign a channel to the output device */ status = sys$assign(&outputdevice, &channel, 0, 0, 0); if (!(status & 1)) lib$signal(status); /* Get its device class */ status = lib$getdvi(&DVI$_DEVCLASS, &channel, 0, &devclass, 0, 0); if (!(status & 1)) lib$signal(status); /* Ensure the device is a terminal */ if (devclass == DC$_TERM) { /* Get an event flag for sys$qiow */ status = lib$get_ef(&efn); if (!(status & 1)) lib$signal(status); /* Read terminal characteristics */ status = sys$qiow(efn, channel, IO$_SENSEMODE, &terminal_iosb, 0, 0, &terminal_characteristics, sizeof(terminal_characteristics), 0, 0, 0, 0); _termios_p->c_cc[VMIN] = terminal_characteristics.basic.tt$v_wrap; if (!(status & 1)) lib$signal(status); if (!(terminal_iosb.status & 1)) lib$signal(terminal_iosb.status); /* Release the event flag */ status = lib$free_ef(&efn); if (!(status & 1)) lib$signal(status); } /* Deassign the channel */ status = sys$dassgn(channel); if (!(status & 1)) lib$signal(status); /* Return 0 to indicate no error */ return 0; } int tcsetattr(int _fildes, int _optional_actions, const struct termios *_termios_p) { /* Use this opportunity to turn off terminal wrap. Otherwise, the panel status line ends up scrolling up the screen. Piggyback the wrap setting on _termios_p->c_cc[VMIN] which hconsole.c sets to 0 on the way in and restores to what it was previously on the way out. */ unsigned long int lib$get_ef(), lib$free_ef(), sys$assign(), sys$dassgn(); unsigned long int lib$getdvi(), sys$qiow(), lib$signal(); unsigned long int status, devclass, efn, channel; struct { unsigned short int status; unsigned short int speeds; unsigned short int fills; unsigned short int parity; } terminal_iosb; struct { unsigned char class; /* device class */ unsigned char type; /* device type */ unsigned short int buffer_size; /* buffer size ( = page width) */ union ttdef basic; /* basic terminal characteristics */ } terminal_characteristics; $DESCRIPTOR(outputdevice, "SYS$COMMAND:"); /* Assign a channel to the output device */ status = sys$assign(&outputdevice, &channel, 0, 0, 0); if (!(status & 1)) lib$signal(status); /* Get its device class */ status = lib$getdvi(&DVI$_DEVCLASS, &channel, 0, &devclass, 0, 0); if (!(status & 1)) lib$signal(status); /* Ensure the device is a terminal */ if (devclass == DC$_TERM) { /* Get an event flag for sys$qiow */ status = lib$get_ef(&efn); if (!(status & 1)) lib$signal(status); /* Read terminal characteristics */ status = sys$qiow(efn, channel, IO$_SENSEMODE, &terminal_iosb, 0, 0, &terminal_characteristics, sizeof(terminal_characteristics), 0, 0, 0, 0); if (!(status & 1)) lib$signal(status); if (!(terminal_iosb.status & 1)) lib$signal(terminal_iosb.status); /* Set the TT$M_WRAP characteristic */ terminal_characteristics.basic.tt$v_wrap = _termios_p->c_cc[VMIN]; /* Write back terminal characteristics */ /* It appears that P4 = 0 will leave fill characteristics as they are and that it is necessary to clear tt$v_crfill and/or tt$v_lffill to turn off fills. We therefore won't bother replacing the fill settings. */ status = sys$qiow(efn, channel, IO$_SETMODE, &terminal_iosb, 0, 0, &terminal_characteristics, sizeof(terminal_characteristics), 0, 0, 0, 0); if (!(status & 1)) lib$signal(status); if (!(terminal_iosb.status & 1)) lib$signal(terminal_iosb.status); /* Release the event flag */ status = lib$free_ef(&efn); if (!(status & 1)) lib$signal(status); } /* Deassign the channel */ status = sys$dassgn(channel); if (!(status & 1)) lib$signal(status); /* Return 0 to indicate no error */ return 0; } int getrusage(int who, struct rusage *rusage) { rusage->ru_utime.tv_sec = 0; rusage->ru_stime.tv_sec = 0; rusage->ru_utime.tv_usec = 0; rusage->ru_stime.tv_usec = 0; return 0; } char *vms_realpath(const char *file_name, char *resolved_name) { int last; strcpy(resolved_name, file_name); /* It appears that realpath removes trailing slashes except where the slash is the only character present. */ last = strlen(resolved_name) - 1; if (last > 0) if (resolved_name[last] == '/') resolved_name[last] = '\0'; return resolved_name; } int pthread_kill(int thread, int signal) { return 0; } /* When sleep() is called by more than one thread concurrently, it appears that one or more threads may not wake up after the required interval. This routine provides a thread safe alternative to sleep() */ int threadsafe_sleep(int seconds) { timespec_t interval; interval.tv_sec = seconds; interval.tv_nsec = 0; return pthread_delay_np(&interval); } /* I have not tested it but I presume the same problem exits with usleep() */ int threadsafe_usleep(int microseconds) { timespec_t interval; interval.tv_sec = 0; interval.tv_nsec = microseconds * 1000; return pthread_delay_np(&interval); } /* Additional routines for 3.04.1 */ unsigned char vms_getch(int efn, short channel) { unsigned long int lib$signal(), sys$qiow(), status; unsigned long int termination_mask[8] = {0, 0, 0, 0, 0, 0, 0, 0}; unsigned long int termination_desc[2]; unsigned char character; struct { unsigned short int status; unsigned short int offset; unsigned short int terminator; unsigned short int size; } iosb; termination_desc[0] = sizeof(termination_mask); termination_desc[1] = (int) &termination_mask; status = sys$qiow(efn, channel, IO$_READVBLK|IO$M_TIMED|IO$M_NOFILTR, &iosb, 0, 0, &character, sizeof(character), 0, &termination_desc, 0, 0); if (!(status & 1)) lib$signal(status); /* If we didn't read anything, return 0 */ if (iosb.offset == 0) character = '\0'; /* Translate CR to LF to help the unixy bits of hercules cope */ if (character == '\r') character = '\n'; return character; } /* In some TCP/IP stacks on VMS, send(), recv() and related functions may not cope correctly with buffer sizes greater than 65535. This is an issue in particular with recv() calls from console.c and read_pipe() calls from logger.c. Hercules currently does not appear to call any other functions with buffers this large. */ ssize_t vms_send(int socket, const void *buffer, size_t size, int flags) { void logmsg(char *, ...); if (size > 65535) { logmsg("HHCVMS001W send() buffer truncated from %d to 65535 bytes\n", size); size = 65535; } return send(socket, buffer, size, flags); } ssize_t vms_recv(int socket, void *buffer, size_t size, int flags) { if (size > 65535) size = 65535; return recv(socket, buffer, size, flags); }