1324 lines
31 KiB
C
1324 lines
31 KiB
C
#ifdef __VMS
|
|
|
|
#include "links.h"
|
|
|
|
#define __NEW_STARLET 1
|
|
#include <starlet.h>
|
|
#include <iodef.h>
|
|
#include <ssdef.h>
|
|
#include <stsdef.h>
|
|
#include <descrip.h>
|
|
#include <dvidef.h>
|
|
#include <ttdef.h>
|
|
#include <tt2def.h>
|
|
#include <lib$routines.h>
|
|
#include <libdef.h>
|
|
#include <libclidef.h>
|
|
|
|
#if defined(HAVE_OPENSSL) && !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_SHA1)
|
|
#include <openssl/sha.h>
|
|
#define USE_SHA
|
|
#endif
|
|
|
|
#ifdef __VAX
|
|
struct _iosb {
|
|
unsigned short iosb$w_status;
|
|
unsigned short iosb$w_bcnt;
|
|
unsigned iosb$l_dev_depend;
|
|
};
|
|
struct _generic_64 {
|
|
unsigned gen64$l_longword[2];
|
|
};
|
|
struct _va_range {
|
|
void *va_range$ps_start_va;
|
|
void *va_range$ps_end_va;
|
|
};
|
|
#else
|
|
#include <iosbdef.h>
|
|
#include <gen64def.h>
|
|
#include <vadef.h>
|
|
#include <va_rangedef.h>
|
|
#endif
|
|
|
|
#include <pthread.h>
|
|
#include <tis.h>
|
|
#ifdef HAVE_SYS_IOCTL_H
|
|
#include <sys/ioctl.h>
|
|
#endif
|
|
|
|
struct tt_mode {
|
|
unsigned char class;
|
|
unsigned char mode;
|
|
unsigned short width;
|
|
unsigned basic;
|
|
unsigned extended;
|
|
};
|
|
|
|
#ifndef OPENVMS_64BIT
|
|
|
|
#define va_range _va_range
|
|
#define links_expreg sys$expreg
|
|
#define links_cretva sys$cretva
|
|
#define links_deltva sys$deltva
|
|
|
|
#else
|
|
|
|
struct va_range {
|
|
void *va_range$ps_start_va;
|
|
void *va_range$ps_end_va;
|
|
};
|
|
|
|
static int links_expreg(size_t pagelets, struct va_range *retadr, unsigned ignore_mode, char ignore_reg)
|
|
{
|
|
__int64 region_int = VA$C_P2;
|
|
struct _generic_64 region;
|
|
unsigned __int64 len;
|
|
int vms_ret;
|
|
|
|
memcpy(®ion, ®ion_int, 8);
|
|
|
|
vms_ret = sys$expreg_64(®ion, pagelets << 9, 0, 0, &retadr->va_range$ps_start_va, &len);
|
|
retadr->va_range$ps_end_va = (unsigned char *)retadr->va_range$ps_start_va + len - 1;
|
|
|
|
return vms_ret;
|
|
}
|
|
|
|
static int links_cretva(struct va_range *creadr, struct va_range *retadr, unsigned int ignore_mode)
|
|
{
|
|
__int64 region_int = VA$C_P2;
|
|
struct _generic_64 region;
|
|
unsigned __int64 len;
|
|
int vms_ret;
|
|
|
|
memcpy(®ion, ®ion_int, 8);
|
|
|
|
vms_ret = sys$cretva_64(®ion, creadr->va_range$ps_start_va, (unsigned char *)creadr->va_range$ps_end_va - (unsigned char *)creadr->va_range$ps_start_va + 1, 0, 0, &retadr->va_range$ps_start_va, &len);
|
|
retadr->va_range$ps_end_va = (unsigned char *)retadr->va_range$ps_start_va + len - 1;
|
|
|
|
return vms_ret;
|
|
}
|
|
|
|
static int links_deltva(struct va_range *deladr, struct va_range *retadr, unsigned int ignore_mode)
|
|
{
|
|
__int64 region_int = VA$C_P2;
|
|
struct _generic_64 region;
|
|
unsigned __int64 len;
|
|
int vms_ret;
|
|
|
|
memcpy(®ion, ®ion_int, 8);
|
|
|
|
vms_ret = sys$deltva_64(®ion, deladr->va_range$ps_start_va, (unsigned char *)deladr->va_range$ps_end_va - (unsigned char *)deladr->va_range$ps_start_va + 1, 0, &retadr->va_range$ps_start_va, &len);
|
|
retadr->va_range$ps_end_va = (unsigned char *)retadr->va_range$ps_start_va + len - 1;
|
|
|
|
return vms_ret;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* reimplement pipes because sockets + select have too big latency */
|
|
#define VMS_VIRTUAL_PIPE
|
|
|
|
/* use a special thread for select processing (if not defined, sockets
|
|
are polled with a dynamic timer) */
|
|
#define VMS_SELECT_THREAD
|
|
|
|
#define VIRTUAL_PIPE_SIZE 512
|
|
|
|
#define GETTIMEOFDAY_POOL 256
|
|
|
|
/*#define TEST_WAKE_BUG*/
|
|
/*#define TRACE_PIPES*/
|
|
|
|
#undef read
|
|
#undef write
|
|
#undef pipe
|
|
#undef close
|
|
#undef select
|
|
|
|
#if !defined(VMS_VIRTUAL_PIPE) && defined(VMS_SELECT_THREAD)
|
|
#undef VMS_SELECT_THREAD
|
|
#endif
|
|
|
|
static void vms_fatal_exit(char *m, ...)
|
|
{
|
|
va_list l;
|
|
va_start(l, m);
|
|
fprintf(stderr, "\n");
|
|
vfprintf(stderr, cast_const_char m, l);
|
|
fprintf(stderr, "%c\n", (unsigned char)7);
|
|
fflush(stderr);
|
|
va_end(l);
|
|
exit(RET_FATAL);
|
|
}
|
|
|
|
void portable_sleep(unsigned msec)
|
|
{
|
|
struct timespec tv;
|
|
int rs;
|
|
tv.tv_sec = msec / 1000;
|
|
tv.tv_nsec = msec % 1000 * 1000000;
|
|
if (!msec) tv.tv_nsec = 1;
|
|
rs = pthread_delay_np(&tv);
|
|
if (rs && rs != EINTR)
|
|
vms_fatal_exit("pthread_delay_np failed: %d", rs);
|
|
}
|
|
|
|
static $DESCRIPTOR (output_channel_desc, "SYS$OUTPUT:");
|
|
|
|
void get_terminal_size(int *x, int *y)
|
|
{
|
|
int ret;
|
|
int x_code = DVI$_DEVBUFSIZ;
|
|
int y_code = DVI$_TT_PAGE;
|
|
unsigned long result;
|
|
*x = 80;
|
|
*y = 24;
|
|
ret = lib$getdvi(&x_code, 0, &output_channel_desc, &result, 0, 0);
|
|
if ($VMS_STATUS_SUCCESS(ret) && result) *x = result;
|
|
ret = lib$getdvi(&y_code, 0, &output_channel_desc, &result, 0, 0);
|
|
if ($VMS_STATUS_SUCCESS(ret) && result) *y = result;
|
|
}
|
|
|
|
static $DESCRIPTOR (display_desc, "DECW$DISPLAY");
|
|
|
|
int is_xterm(void)
|
|
{
|
|
static int xt = -1;
|
|
if (xt == -1) {
|
|
xt = $VMS_STATUS_SUCCESS(lib$get_logical(&display_desc, NULL, NULL, NULL, NULL, NULL, NULL, NULL));
|
|
}
|
|
return xt;
|
|
}
|
|
|
|
void get_path_to_exe(void)
|
|
{
|
|
path_to_exe = cast_uchar "links";
|
|
}
|
|
|
|
static int io_raw = -1;
|
|
static int input_handle = -1;
|
|
|
|
static $DESCRIPTOR (input_channel_desc, "SYS$INPUT:");
|
|
static unsigned short input_channel;
|
|
|
|
static pthread_mutex_t io_mutex;
|
|
static pthread_mutex_t cancel_mutex;
|
|
|
|
static volatile struct timeval gettimeofday_pool[GETTIMEOFDAY_POOL];
|
|
static volatile unsigned gettimeofday_clock = 0;
|
|
|
|
#define INPUT_BUFFER_SIZE 16
|
|
|
|
static void vms_input_thread(void *n, int h)
|
|
{
|
|
unsigned char buffer[INPUT_BUFFER_SIZE];
|
|
int buffer_start = 0;
|
|
int buffer_end = 0;
|
|
unsigned ef;
|
|
int vms_ret;
|
|
#ifndef VMS_VIRTUAL_PIPE
|
|
/* VMS is buggy - if high priority thread blocks in write,
|
|
no low priority threads have a chance to run.
|
|
So, as a workaround, we set the socket nonblocking. */
|
|
set_nonblock(h);
|
|
#endif
|
|
vms_ret = lib$get_ef(&ef);
|
|
if (!$VMS_STATUS_SUCCESS(vms_ret))
|
|
vms_fatal_exit("lib$get_ef failed: %d", vms_ret);
|
|
while (1) {
|
|
int ret;
|
|
int count;
|
|
struct _iosb iosb;
|
|
ret = pthread_mutex_lock(&cancel_mutex);
|
|
if (ret)
|
|
vms_fatal_exit("pthread_mutex_lock failed: %d", ret);
|
|
ret = pthread_mutex_unlock(&cancel_mutex);
|
|
if (ret)
|
|
vms_fatal_exit("pthread_mutex_unlock failed: %d", ret);
|
|
ret = pthread_mutex_lock(&io_mutex);
|
|
if (ret)
|
|
vms_fatal_exit("pthread_mutex_lock failed: %d", ret);
|
|
vms_ret = sys$clref(ef);
|
|
if (!$VMS_STATUS_SUCCESS(vms_ret))
|
|
vms_fatal_exit("sys$clref failed: %d", vms_ret);
|
|
vms_ret = sys$qio(ef, input_channel, IO$_READVBLK | IO$M_NOECHO | IO$M_NOFILTR, &iosb, tis_io_complete, 0, buffer + buffer_end, 1, 0, 0, 0, 0);
|
|
if ($VMS_STATUS_SUCCESS(vms_ret)) {
|
|
if (!iosb.iosb$w_status) {
|
|
if (buffer_start != buffer_end) {
|
|
hard_write(h, buffer + buffer_start, buffer_end - buffer_start);
|
|
buffer_start = buffer_end;
|
|
}
|
|
}
|
|
vms_ret = tis_synch(ef, &iosb);
|
|
}
|
|
ret = pthread_mutex_unlock(&io_mutex);
|
|
if (ret)
|
|
vms_fatal_exit("pthread_mutex_unlock failed: %d", ret);
|
|
if (vms_ret == SS$_CANCEL ||
|
|
vms_ret == SS$_ABORT)
|
|
continue;
|
|
if (!$VMS_STATUS_SUCCESS(vms_ret))
|
|
vms_fatal_exit("sys$qio failed: %d", vms_ret);
|
|
if (!$VMS_STATUS_SUCCESS(iosb.iosb$w_status) &&
|
|
iosb.iosb$w_status != SS$_PARTESCAPE &&
|
|
iosb.iosb$w_status != SS$_DATAOVERUN &&
|
|
iosb.iosb$w_status != SS$_CANCEL &&
|
|
iosb.iosb$w_status != SS$_ABORT)
|
|
vms_fatal_exit("IO$_READVBLK failed: %d, %d", (int)iosb.iosb$w_status, (int)iosb.iosb$w_bcnt);
|
|
count = iosb.iosb$w_bcnt + (iosb.iosb$l_dev_depend >> 16);
|
|
if (count == 1) {
|
|
buffer_end++;
|
|
if (buffer_start) {
|
|
memmove(buffer, buffer + buffer_start, buffer_end - buffer_start);
|
|
buffer_end -= buffer_start;
|
|
buffer_start = 0;
|
|
}
|
|
if (buffer_end == INPUT_BUFFER_SIZE) {
|
|
hard_write(h, buffer + buffer_start, buffer_end - buffer_start);
|
|
buffer_start = buffer_end = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void tty_passthru_mode(int enable)
|
|
{
|
|
struct _iosb iosb;
|
|
struct tt_mode mode;
|
|
int ret;
|
|
|
|
ret = sys$qiow(0, input_channel, IO$_SENSEMODE, &iosb, 0, 0, &mode, sizeof(mode), 0, 0, 0, 0);
|
|
if (!$VMS_STATUS_SUCCESS(ret)) {
|
|
error("IO$_SENSEMODE failed: %d", ret);
|
|
return;
|
|
}
|
|
|
|
if (enable) mode.extended |= TT2$M_PASTHRU;
|
|
else mode.extended &= ~TT2$M_PASTHRU;
|
|
|
|
ret = sys$qiow(0, input_channel, IO$_SETMODE, &iosb, 0, 0, &mode, sizeof(mode), 0, 0, 0, 0);
|
|
if (!$VMS_STATUS_SUCCESS(ret)) {
|
|
error("IO$_SETMODE failed: %d", ret);
|
|
return;
|
|
}
|
|
}
|
|
|
|
int setraw(int ctl, int save)
|
|
{
|
|
int ret;
|
|
if (io_raw > 0)
|
|
return 0;
|
|
|
|
do_signal(SIGINT, SIG_IGN);
|
|
|
|
ret = sys$assign(&input_channel_desc, &input_channel, 0, NULL);
|
|
if (!$VMS_STATUS_SUCCESS(ret))
|
|
vms_fatal_exit("sys$assign failed: %d", ret);
|
|
io_raw = 1;
|
|
|
|
tty_passthru_mode(1);
|
|
|
|
ret = pthread_mutex_unlock(&io_mutex);
|
|
if (ret)
|
|
vms_fatal_exit("pthread_mutex_unlock failed: %d", ret);
|
|
return 0;
|
|
}
|
|
|
|
void setcooked(int ctl)
|
|
{
|
|
int ret;
|
|
if (io_raw <= 0)
|
|
return;
|
|
ret = pthread_mutex_lock(&cancel_mutex);
|
|
if (ret)
|
|
vms_fatal_exit("pthread_mutex_lock failed: %d", ret);
|
|
clear_events(input_handle, 1);
|
|
while ((ret = pthread_mutex_trylock(&io_mutex))) {
|
|
if (ret != EBUSY)
|
|
vms_fatal_exit("pthread_mutex_trylock failed: %d", ret);
|
|
ret = sys$cancel(input_channel);
|
|
if (!$VMS_STATUS_SUCCESS(ret))
|
|
vms_fatal_exit("sys$cancel failed: %d", ret);
|
|
clear_events(input_handle, 1);
|
|
portable_sleep(0);
|
|
}
|
|
ret = pthread_mutex_unlock(&cancel_mutex);
|
|
if (ret)
|
|
vms_fatal_exit("pthread_mutex_unlock failed: %d", ret);
|
|
|
|
tty_passthru_mode(0);
|
|
|
|
io_raw = 0;
|
|
ret = sys$dassgn(input_channel);
|
|
if (!$VMS_STATUS_SUCCESS(ret))
|
|
vms_fatal_exit("sys$dassgn failed: %d", ret);
|
|
}
|
|
|
|
int get_input_handle(void)
|
|
{
|
|
int ret;
|
|
if (input_handle >= 0)
|
|
return input_handle;
|
|
ret = pthread_mutex_init(&io_mutex, NULL);
|
|
if (ret)
|
|
vms_fatal_exit("pthread_mutex_init failed: %d", ret);
|
|
ret = pthread_mutex_init(&cancel_mutex, NULL);
|
|
if (ret)
|
|
vms_fatal_exit("pthread_mutex_init failed: %d", ret);
|
|
ret = pthread_mutex_lock(&io_mutex);
|
|
if (ret)
|
|
vms_fatal_exit("pthread_mutex_lock failed: %d", ret);
|
|
vms_thread_high_priority = 1;
|
|
input_handle = start_thread(vms_input_thread, NULL, 0, 0);
|
|
if (input_handle == -1)
|
|
vms_fatal_exit("unable to start keyboard thread");
|
|
vms_thread_high_priority = 0;
|
|
return input_handle;
|
|
}
|
|
|
|
#if defined(GRDRV_X)
|
|
|
|
static int x11_ef = -1;
|
|
static int x11_pipe = -1;
|
|
|
|
static void vms_x11_thread(void *n, int h)
|
|
{
|
|
int ret;
|
|
set_nonblock(h);
|
|
while (1) {
|
|
ret = sys$clref(x11_ef);
|
|
if (!$VMS_STATUS_SUCCESS(ret))
|
|
vms_fatal_exit("sys$clref failed: %d", ret);
|
|
EINTRLOOP(ret, (int)vms_write(h, "", 1));
|
|
ret = sys$waitfr(x11_ef);
|
|
if (!$VMS_STATUS_SUCCESS(ret))
|
|
vms_fatal_exit("sys$waitfr failed: %d", ret);
|
|
}
|
|
}
|
|
|
|
int vms_x11_fd(int ef)
|
|
{
|
|
if (x11_pipe >= 0) {
|
|
if (ef != x11_ef) vms_fatal_exit("requesting multiple event flags: %d, %d", x11_ef, ef);
|
|
return x11_pipe;
|
|
}
|
|
x11_ef = ef;
|
|
x11_pipe = start_thread(vms_x11_thread, NULL, 0, 0);
|
|
if (x11_pipe == -1)
|
|
vms_fatal_exit("unable to start Xwindow event thread");
|
|
set_nonblock(x11_pipe);
|
|
return x11_pipe;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if !defined(VMS_VIRTUAL_PIPE) || defined(VMS_SELECT_THREAD)
|
|
|
|
static int vms_socketpair(int *fd)
|
|
{
|
|
#if defined(HAVE_SOCKETPAIR)
|
|
int r;
|
|
EINTRLOOP(r, socketpair(AF_INET, SOCK_STREAM, 0, fd));
|
|
return r;
|
|
#else
|
|
#define PIPE_RETRIES 10
|
|
int rs;
|
|
int s1, s2, s3;
|
|
socklen_t l;
|
|
struct sockaddr_in sa1, sa2;
|
|
int retry_count = 0;
|
|
again:
|
|
s1 = c_socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
if (s1 < 0)
|
|
goto err0;
|
|
s2 = c_socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
if (s2 < 0)
|
|
goto err1;
|
|
memset(&sa1, 0, sizeof(sa1));
|
|
sa1.sin_family = AF_INET;
|
|
sa1.sin_port = htons(0);
|
|
sa1.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
EINTRLOOP(rs, bind(s1, (struct sockaddr *)&sa1, sizeof(sa1)));
|
|
if (rs)
|
|
goto err2;
|
|
l = sizeof(sa2);
|
|
EINTRLOOP(rs, getsockname(s1, (struct sockaddr *)&sa1, &l));
|
|
if (rs)
|
|
goto err2;
|
|
EINTRLOOP(rs, listen(s1, 1));
|
|
if (rs)
|
|
goto err2;
|
|
EINTRLOOP(rs, connect(s2, (struct sockaddr *)&sa1, sizeof(sa1)));
|
|
if (rs)
|
|
goto err2;
|
|
l = sizeof(sa1);
|
|
EINTRLOOP(rs, getsockname(s2, (struct sockaddr *)&sa1, &l));
|
|
if (rs)
|
|
goto err2;
|
|
l = sizeof(sa2);
|
|
s3 = c_accept(s1, (struct sockaddr *)&sa2, &l);
|
|
if (s3 < 0)
|
|
goto err2;
|
|
if (sa1.sin_addr.s_addr != sa2.sin_addr.s_addr ||
|
|
sa1.sin_port != sa2.sin_port) {
|
|
errno = EINVAL;
|
|
goto err2;
|
|
}
|
|
EINTRLOOP(rs, close(s1));
|
|
fd[0] = s2;
|
|
fd[1] = s3;
|
|
return 0;
|
|
err2:
|
|
EINTRLOOP(rs, close(s2));
|
|
err1:
|
|
EINTRLOOP(rs, close(s1));
|
|
err0:
|
|
if (++retry_count > PIPE_RETRIES) return -1;
|
|
portable_sleep(100);
|
|
goto again;
|
|
#endif
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef VMS_VIRTUAL_PIPE
|
|
|
|
static pthread_mutex_t pipe_mutex;
|
|
static pthread_cond_t pipe_cond;
|
|
|
|
static void pipe_lock(void)
|
|
{
|
|
int ret;
|
|
ret = pthread_mutex_lock(&pipe_mutex);
|
|
if (ret)
|
|
vms_fatal_exit("pthread_mutex_lock failed: %d", ret);
|
|
}
|
|
|
|
static void pipe_unlock(void)
|
|
{
|
|
int ret;
|
|
ret = pthread_mutex_unlock(&pipe_mutex);
|
|
if (ret)
|
|
vms_fatal_exit("pthread_unmutex_lock failed: %d", ret);
|
|
}
|
|
|
|
static void pipe_unlock_wait_cond(pthread_cond_t *cond)
|
|
{
|
|
int ret;
|
|
ret = pthread_cond_wait(cond, &pipe_mutex);
|
|
if (ret && ret != EINTR)
|
|
vms_fatal_exit("pthread_cond_wait failed: %d", ret);
|
|
}
|
|
|
|
static void pipe_unlock_wait(void)
|
|
{
|
|
pipe_unlock_wait_cond(&pipe_cond);
|
|
}
|
|
|
|
static int pipe_unlock_wait_time(struct timespec *ts)
|
|
{
|
|
int ret;
|
|
ret = pthread_cond_timedwait(&pipe_cond, &pipe_mutex, ts);
|
|
if (ret && ret != EINTR && ret != ETIMEDOUT)
|
|
vms_fatal_exit("pthread_cond_timedwait failed: %d", ret);
|
|
return ret == ETIMEDOUT;
|
|
}
|
|
|
|
static void pipe_wake_cond(pthread_cond_t *cond)
|
|
{
|
|
int ret;
|
|
ret = pthread_cond_broadcast(cond);
|
|
if (ret)
|
|
vms_fatal_exit("pthread_cond_broadcast failed: %d", ret);
|
|
}
|
|
|
|
static void pipe_wake(void)
|
|
{
|
|
pipe_wake_cond(&pipe_cond);
|
|
}
|
|
|
|
#include "vpipe.inc"
|
|
|
|
static void get_abstime(time_t sec, unsigned usec, struct timespec *ts)
|
|
{
|
|
#if 0
|
|
int ret;
|
|
struct timespec exp;
|
|
exp.tv_sec = sec;
|
|
exp.tv_nsec = usec * 1000;
|
|
ret = pthread_get_expiration_np(&exp, ts);
|
|
if (ret)
|
|
vms_fatal_exit("pthread_get_expiration_np failed: %d", ret);
|
|
#else
|
|
int ret;
|
|
struct timeval tv;
|
|
ret = gettimeofday(&tv, NULL);
|
|
if (ret)
|
|
vms_fatal_exit("gettimeofday failed: %d", errno);
|
|
tv.tv_sec += sec;
|
|
tv.tv_usec += usec;
|
|
if (tv.tv_usec >= 1000000) {
|
|
tv.tv_usec -= 1000000;
|
|
tv.tv_sec++;
|
|
}
|
|
{
|
|
unsigned c = gettimeofday_clock;
|
|
if (c >= GETTIMEOFDAY_POOL) c = 0;
|
|
gettimeofday_clock = c;
|
|
gettimeofday_pool[c].tv_sec = (unsigned long)gettimeofday_pool[c].tv_sec * 11 + (unsigned long)tv.tv_sec;
|
|
gettimeofday_pool[c].tv_usec = (unsigned long)gettimeofday_pool[c].tv_usec * 11 + (unsigned long)tv.tv_usec;
|
|
}
|
|
ts->tv_sec = tv.tv_sec;
|
|
ts->tv_nsec = tv.tv_usec * 1000;
|
|
#endif
|
|
}
|
|
|
|
static int check_fd(int fd, int what)
|
|
{
|
|
fd_set fds;
|
|
struct timeval tv = {0, 0};
|
|
int rs;
|
|
FD_ZERO(&fds);
|
|
FD_SET(fd, &fds);
|
|
EINTRLOOP(rs, select(fd + 1,
|
|
what == 0 ? &fds : NULL,
|
|
what == 1 ? &fds : NULL,
|
|
what == 2 ? &fds : NULL,
|
|
&tv));
|
|
if (rs < 0) vms_fatal_exit("select for %d/%d write failed: %d", fd, what, errno);
|
|
return rs;
|
|
}
|
|
|
|
#ifdef VMS_SELECT_THREAD
|
|
|
|
static pthread_cond_t select_cond;
|
|
|
|
static int select_thread_signal[2];
|
|
|
|
static int select_set_max;
|
|
|
|
static fd_set select_set_read;
|
|
static fd_set select_set_write;
|
|
static fd_set select_set_exception;
|
|
|
|
static fd_set add_set_read;
|
|
static fd_set add_set_write;
|
|
static fd_set add_set_exception;
|
|
|
|
static fd_set remove_set;
|
|
|
|
static fd_set rs, ws, es;
|
|
|
|
static void add_to_thread_set(int fd, fd_set *s)
|
|
{
|
|
FD_SET(fd, s);
|
|
if (fd >= select_set_max)
|
|
select_set_max = fd + 1;
|
|
}
|
|
|
|
static void vms_select_thread(void *p, int n)
|
|
{
|
|
int should_wake = 0;
|
|
pipe_lock();
|
|
FD_ZERO(&rs);
|
|
FD_ZERO(&ws);
|
|
FD_ZERO(&es);
|
|
while (1) {
|
|
int i;
|
|
int r;
|
|
int need_select = 0;
|
|
int new_max = 0;
|
|
for (i = 0; i < select_set_max; i++) {
|
|
int set;
|
|
if (FD_ISSET(i, &add_set_read)) {
|
|
FD_CLR(i, &add_set_read);
|
|
FD_SET(i, &select_set_read);
|
|
}
|
|
if (FD_ISSET(i, &add_set_write)) {
|
|
FD_CLR(i, &add_set_write);
|
|
FD_SET(i, &select_set_write);
|
|
}
|
|
if (FD_ISSET(i, &add_set_exception)) {
|
|
FD_CLR(i, &add_set_exception);
|
|
FD_SET(i, &select_set_exception);
|
|
}
|
|
if (FD_ISSET(i, &remove_set)) {
|
|
FD_CLR(i, &remove_set);
|
|
#ifndef TEST_WAKE_BUG
|
|
FD_CLR(i, &select_set_read);
|
|
FD_CLR(i, &select_set_write);
|
|
FD_CLR(i, &select_set_exception);
|
|
#endif
|
|
should_wake = 1;
|
|
}
|
|
set = 0;
|
|
if (FD_ISSET(i, &select_set_read)) {
|
|
set = 1;
|
|
FD_SET(i, &rs);
|
|
}
|
|
if (FD_ISSET(i, &select_set_write)) {
|
|
set = 1;
|
|
FD_SET(i, &ws);
|
|
}
|
|
if (FD_ISSET(i, &select_set_exception)) {
|
|
set = 1;
|
|
FD_SET(i, &es);
|
|
}
|
|
if (set && i >= new_max)
|
|
new_max = i + 1;
|
|
if (set && i != select_thread_signal[0])
|
|
need_select = 1;
|
|
}
|
|
select_set_max = new_max;
|
|
if (!need_select) {
|
|
if (should_wake) {
|
|
pipe_wake();
|
|
should_wake = 0;
|
|
}
|
|
pipe_unlock_wait_cond(&select_cond);
|
|
clear_events(select_thread_signal[0], 0);
|
|
} else {
|
|
pipe_unlock();
|
|
if (should_wake) {
|
|
#ifdef TEST_WAKE_BUG
|
|
fprintf(stderr, "wake 1. ");
|
|
#endif
|
|
pipe_wake();
|
|
#ifdef TEST_WAKE_BUG
|
|
fprintf(stderr, "wake 2. ");
|
|
#endif
|
|
should_wake = 0;
|
|
}
|
|
/* It is needed to sleep here with pthread_yield_np() or
|
|
portable_sleep(0), otherwise some bug in decthreads
|
|
is triggered and the main thread isn't woken up.
|
|
tis_io_complete apparently seems to fix the bug too,
|
|
so we do a dummy syscall that calls tis_io_complete
|
|
as an AST */
|
|
{
|
|
struct _generic_64 priv;
|
|
memset(&priv, 0, sizeof priv);
|
|
sys$check_privilegew(0, &priv, NULL, 0, NULL, NULL, tis_io_complete, 0);
|
|
}
|
|
/*tis_io_complete();*/
|
|
/*fprintf(stderr, "time1 %d. ", get_time());
|
|
pthread_yield_np();
|
|
portable_sleep(0);
|
|
fprintf(stderr, "time2 %d. ", get_time());*/
|
|
#ifdef TEST_WAKE_BUG
|
|
fprintf(stderr, "wake 3. ");
|
|
#endif
|
|
EINTRLOOP(r, select(select_set_max, &rs, &ws, &es, NULL));
|
|
#ifdef TEST_WAKE_BUG
|
|
fprintf(stderr, "wake 4. ");
|
|
#endif
|
|
if (r < 0)
|
|
vms_fatal_exit("select in thread failed: %d", errno);
|
|
pipe_lock();
|
|
for (i = 0; i < select_set_max; i++) {
|
|
if (i == select_thread_signal[0]) {
|
|
if (FD_ISSET(i, &rs))
|
|
clear_events(i, 0);
|
|
continue;
|
|
}
|
|
if (FD_ISSET(i, &rs)) {
|
|
FD_CLR(i, &rs);
|
|
FD_CLR(i, &select_set_read);
|
|
should_wake = 1;
|
|
}
|
|
if (FD_ISSET(i, &ws)) {
|
|
FD_CLR(i, &ws);
|
|
FD_CLR(i, &select_set_write);
|
|
should_wake = 1;
|
|
}
|
|
if (FD_ISSET(i, &es)) {
|
|
FD_CLR(i, &es);
|
|
FD_CLR(i, &select_set_exception);
|
|
should_wake = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void select_thread_wake(void)
|
|
{
|
|
int r;
|
|
EINTRLOOP(r, (int)write(select_thread_signal[1], "", 1));
|
|
pipe_wake_cond(&select_cond);
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifndef VMS_SELECT_THREAD
|
|
|
|
#ifdef __VAX
|
|
#define MIN_WAIT_TIME 10000
|
|
#else
|
|
#define MIN_WAIT_TIME 900
|
|
#endif
|
|
#define MAX_WAIT_TIME 999999
|
|
|
|
static unsigned wait_time = MIN_WAIT_TIME;
|
|
|
|
static void increase_dynamic_time(void)
|
|
{
|
|
wait_time += (wait_time / 2);
|
|
if (wait_time > MAX_WAIT_TIME) wait_time = MAX_WAIT_TIME;
|
|
}
|
|
|
|
static void decrease_dynamic_time(void)
|
|
{
|
|
wait_time /= 2;
|
|
if (wait_time > MIN_WAIT_TIME) wait_time = MIN_WAIT_TIME;
|
|
}
|
|
|
|
#endif
|
|
|
|
int vms_select(int n, fd_set *rs, fd_set *ws, fd_set *es, struct timeval *t)
|
|
{
|
|
struct timespec ts;
|
|
int i;
|
|
int last_pass = 0;
|
|
int ret_cnt = 0;
|
|
int dynamic_wait;
|
|
#ifndef VMS_SELECT_THREAD
|
|
int dynamically_waited = 0;
|
|
#endif
|
|
/*if (!rs && !ws && !es)
|
|
return select(0, NULL, NULL, NULL, t);*/
|
|
if (t) {
|
|
if (!t->tv_sec && !t->tv_usec) {
|
|
last_pass = 1;
|
|
} else {
|
|
get_abstime(t->tv_sec, t->tv_usec, &ts);
|
|
}
|
|
}
|
|
pipe_lock();
|
|
test_again:
|
|
dynamic_wait = 0;
|
|
for (i = 0; i < n; i++) {
|
|
int ts = 0;
|
|
if (rs && FD_ISSET(i, rs)) {
|
|
int signaled = 0;
|
|
if (pipe_desc[i]) {
|
|
signaled |= vpipe_may_read(i);
|
|
} else {
|
|
#ifdef VMS_SELECT_THREAD
|
|
if (FD_ISSET(i, &select_set_read)) dynamic_wait |= 1;
|
|
else
|
|
#endif
|
|
if (check_fd(i, 0)) signaled = 1;
|
|
else {
|
|
#ifdef VMS_SELECT_THREAD
|
|
add_to_thread_set(i, &add_set_read);
|
|
#endif
|
|
dynamic_wait |= 3;
|
|
}
|
|
}
|
|
if (!last_pass) {
|
|
if (signaled) {
|
|
clear_inactive(rs, i);
|
|
clear_inactive(ws, i);
|
|
clear_inactive(es, i);
|
|
last_pass = 1;
|
|
}
|
|
} else {
|
|
if (!signaled) FD_CLR(i, rs);
|
|
}
|
|
ts |= signaled;
|
|
}
|
|
if (ws && FD_ISSET(i, ws)) {
|
|
int signaled = 0;
|
|
if (pipe_desc[i]) {
|
|
signaled |= vpipe_may_write(i);
|
|
} else {
|
|
#ifdef VMS_SELECT_THREAD
|
|
if (FD_ISSET(i, &select_set_write)) dynamic_wait |= 1;
|
|
else
|
|
#endif
|
|
if (check_fd(i, 1)) signaled = 1;
|
|
else {
|
|
#ifdef VMS_SELECT_THREAD
|
|
add_to_thread_set(i, &add_set_write);
|
|
#endif
|
|
dynamic_wait |= 3;
|
|
}
|
|
}
|
|
if (!last_pass) {
|
|
if (signaled) {
|
|
clear_inactive(rs, i + 1);
|
|
clear_inactive(ws, i);
|
|
clear_inactive(es, i);
|
|
last_pass = 1;
|
|
}
|
|
} else {
|
|
if (!signaled) FD_CLR(i, ws);
|
|
}
|
|
ts |= signaled;
|
|
}
|
|
if (es && FD_ISSET(i, es)) {
|
|
int signaled = 0;
|
|
if (pipe_desc[i]) {
|
|
} else {
|
|
#ifdef VMS_SELECT_THREAD
|
|
if (FD_ISSET(i, &select_set_exception)) dynamic_wait |= 1;
|
|
else
|
|
#endif
|
|
if (check_fd(i, 2)) signaled = 1;
|
|
else {
|
|
#ifdef VMS_SELECT_THREAD
|
|
add_to_thread_set(i, &add_set_exception);
|
|
dynamic_wait |= 3;
|
|
#endif
|
|
}
|
|
}
|
|
if (!last_pass) {
|
|
if (signaled) {
|
|
clear_inactive(rs, i + 1);
|
|
clear_inactive(ws, i + 1);
|
|
clear_inactive(es, i);
|
|
last_pass = 1;
|
|
}
|
|
} else {
|
|
if (!signaled) FD_CLR(i, es);
|
|
}
|
|
ts |= signaled;
|
|
}
|
|
if (last_pass) ret_cnt += ts;
|
|
}
|
|
if (!last_pass) {
|
|
#ifndef VMS_SELECT_THREAD
|
|
if (dynamic_wait) {
|
|
struct timespec ts2;
|
|
if (dynamically_waited)
|
|
increase_dynamic_time();
|
|
/*fprintf(stderr, "wait time: %d\n", wait_time);*/
|
|
get_abstime(0, wait_time, &ts2);
|
|
if (t &&
|
|
(ts2.tv_sec > ts.tv_sec ||
|
|
(ts2.tv_sec == ts.tv_sec && ts2.tv_nsec > ts.tv_nsec)))
|
|
goto full_wait;
|
|
dynamically_waited = pipe_unlock_wait_time(&ts2);
|
|
goto test_again;
|
|
}
|
|
#else
|
|
if (dynamic_wait & 2)
|
|
select_thread_wake();
|
|
#endif
|
|
#ifndef VMS_SELECT_THREAD
|
|
dynamically_waited = 0;
|
|
#endif
|
|
if (!t) {
|
|
pipe_unlock_wait();
|
|
goto test_again;
|
|
} else {
|
|
full_wait:
|
|
if (pipe_unlock_wait_time(&ts))
|
|
last_pass = 1;
|
|
goto test_again;
|
|
}
|
|
}
|
|
#ifndef VMS_SELECT_THREAD
|
|
if (dynamically_waited)
|
|
decrease_dynamic_time();
|
|
#endif
|
|
pipe_unlock();
|
|
return ret_cnt;
|
|
}
|
|
|
|
#else
|
|
|
|
int vms_select(int n, fd_set *rs, fd_set *ws, fd_set *es, struct timeval *t)
|
|
{
|
|
return select(n, rs, ws, es, t);
|
|
}
|
|
|
|
#endif
|
|
|
|
int vms_close(int fd)
|
|
{
|
|
#ifdef VMS_VIRTUAL_PIPE
|
|
int r = vpipe_close(fd);
|
|
if (r != -2) return r;
|
|
#ifdef VMS_SELECT_THREAD
|
|
pipe_lock();
|
|
while (1) {
|
|
FD_CLR(fd, &add_set_read);
|
|
FD_CLR(fd, &add_set_write);
|
|
FD_CLR(fd, &add_set_exception);
|
|
if (!(FD_ISSET(fd, &select_set_read) ||
|
|
FD_ISSET(fd, &select_set_write) ||
|
|
FD_ISSET(fd, &select_set_exception)))
|
|
break;
|
|
add_to_thread_set(fd, &remove_set);
|
|
#ifdef TEST_WAKE_BUG
|
|
fprintf(stderr, "close 1. ");
|
|
#endif
|
|
select_thread_wake();
|
|
#ifdef TEST_WAKE_BUG
|
|
fprintf(stderr, "close 2. ");
|
|
#endif
|
|
pipe_unlock_wait();
|
|
#ifdef TEST_WAKE_BUG
|
|
fprintf(stderr, "close 3. ");
|
|
#endif
|
|
}
|
|
pipe_unlock();
|
|
#endif
|
|
#endif
|
|
return close(fd);
|
|
}
|
|
|
|
int vms_pipe(int fd[2])
|
|
{
|
|
#ifdef VMS_VIRTUAL_PIPE
|
|
return vpipe_create(fd);
|
|
#else
|
|
return vms_socketpair(fd);
|
|
#endif
|
|
}
|
|
|
|
void set_nonblock(int fd)
|
|
{
|
|
#ifdef VMS_VIRTUAL_PIPE
|
|
if (get_virtual_pipe(fd)) {
|
|
pipe_flags[fd] |= VIRTUAL_PIPE_FLAG_NONBLOCK;
|
|
pipe_unlock();
|
|
pipe_wake();
|
|
} else
|
|
#endif
|
|
{
|
|
int rs;
|
|
int on = 1;
|
|
EINTRLOOP(rs, ioctl(fd, FIONBIO, &on));
|
|
}
|
|
}
|
|
|
|
#define BOUNCE_BUFFER_SIZE 64
|
|
|
|
int vms_read(int fd, void *buf, size_t size)
|
|
{
|
|
#ifdef VMS_VIRTUAL_PIPE
|
|
int r = vpipe_read(fd, buf, size);
|
|
if (r != -2) return r;
|
|
#endif
|
|
#ifdef OPENVMS_64BIT
|
|
if ((my_uintptr_t)buf + size >= 0x80000000U) {
|
|
int r;
|
|
unsigned char static_buffer[BOUNCE_BUFFER_SIZE];
|
|
unsigned char *bounce_buffer = NULL;
|
|
if (size > BOUNCE_BUFFER_SIZE)
|
|
bounce_buffer = _malloc32(size);
|
|
if (!bounce_buffer) {
|
|
bounce_buffer = static_buffer;
|
|
if (size > BOUNCE_BUFFER_SIZE) size = BOUNCE_BUFFER_SIZE;
|
|
}
|
|
r = read(fd, bounce_buffer, size);
|
|
if (r > 0) memcpy(buf, bounce_buffer, r);
|
|
if (bounce_buffer != static_buffer)
|
|
free(bounce_buffer);
|
|
return r;
|
|
}
|
|
#endif
|
|
return read(fd, buf, size);
|
|
}
|
|
|
|
int vms_write(int fd, const void *buf, size_t size)
|
|
{
|
|
#ifdef VMS_VIRTUAL_PIPE
|
|
int r = vpipe_write(fd, buf, size);
|
|
if (r != -2) return r;
|
|
#endif
|
|
#ifdef OPENVMS_64BIT
|
|
if ((my_uintptr_t)buf + size >= 0x80000000U) {
|
|
int r;
|
|
unsigned char static_buffer[BOUNCE_BUFFER_SIZE];
|
|
unsigned char *bounce_buffer = NULL;
|
|
if (size > BOUNCE_BUFFER_SIZE)
|
|
bounce_buffer = _malloc32(size);
|
|
if (!bounce_buffer) {
|
|
bounce_buffer = static_buffer;
|
|
if (size > BOUNCE_BUFFER_SIZE) size = BOUNCE_BUFFER_SIZE;
|
|
}
|
|
memcpy(bounce_buffer, buf, size);
|
|
r = write(fd, bounce_buffer, size);
|
|
if (bounce_buffer != static_buffer)
|
|
free(bounce_buffer);
|
|
return r;
|
|
}
|
|
#endif
|
|
return write(fd, buf, size);
|
|
}
|
|
|
|
#ifdef VMS_ADVANCED_HEAP
|
|
|
|
struct region {
|
|
unsigned char *start;
|
|
size_t len;
|
|
int allocated;
|
|
};
|
|
|
|
static struct region *regions = NULL;
|
|
static int n_used_regions = 0;
|
|
static int n_allocated_regions = 0;
|
|
static unsigned char *max_expreg = NULL;
|
|
|
|
static int expand_regions(void)
|
|
{
|
|
struct region *regs;
|
|
if (n_used_regions < n_allocated_regions)
|
|
return 0;
|
|
if (n_allocated_regions >= MAXINT / 2 / sizeof(struct region))
|
|
return -1;
|
|
regs = realloc(regions, (n_allocated_regions + 1) * sizeof(struct region));
|
|
if (!regs)
|
|
return -1;
|
|
regions = regs;
|
|
n_allocated_regions++;
|
|
return 0;
|
|
}
|
|
|
|
void *virtual_alloc(size_t len)
|
|
{
|
|
int i;
|
|
struct va_range expr, cre_range;
|
|
int vms_ret;
|
|
|
|
search_again:
|
|
for (i = 0; i < n_used_regions; i++) {
|
|
if (regions[i].allocated)
|
|
continue;
|
|
if (regions[i].len < len)
|
|
continue;
|
|
if (regions[i].len == len) {
|
|
regions[i].allocated = 1;
|
|
goto ret_st;
|
|
}
|
|
if (expand_regions())
|
|
return NULL;
|
|
memmove(®ions[i + 1], ®ions[i], (n_used_regions - i) * sizeof(struct region));
|
|
n_used_regions++;
|
|
regions[i].len = len;
|
|
regions[i].allocated = 1;
|
|
regions[i + 1].start = regions[i].start + len;
|
|
regions[i + 1].len -= len;
|
|
|
|
ret_st:
|
|
cre_range.va_range$ps_start_va = regions[i].start;
|
|
cre_range.va_range$ps_end_va = regions[i].start + len - 1;
|
|
vms_ret = links_cretva(&cre_range, &expr, 0);
|
|
if (vms_ret != SS$_NORMAL) {
|
|
/*fprintf(stderr, "cretva failed\n");*/
|
|
regions[i].allocated = 0;
|
|
return NULL;
|
|
}
|
|
|
|
/*fprintf(stderr, "allocated: %p,%p ; %p,%p\n", cre_range.va_range$ps_start_va, cre_range.va_range$ps_end_va, expr.va_range$ps_start_va, expr.va_range$ps_end_va);*/
|
|
|
|
/*fprintf(stderr, "found existing (%d)\n", n_used_regions);*/
|
|
/*memset(regions[i].start, 0, regions[i].len);*/
|
|
|
|
return regions[i].start;
|
|
}
|
|
if (expand_regions())
|
|
return NULL;
|
|
vms_ret = links_expreg(len >> 9, &expr, 0, 0);
|
|
if (vms_ret != SS$_NORMAL) {
|
|
return NULL;
|
|
}
|
|
/*fprintf(stderr, "expreg (%p,%p %p) (%d)\n", expr.va_range$ps_start_va, expr.va_range$ps_end_va, (void *)len, n_used_regions);*/
|
|
max_expreg = expr.va_range$ps_end_va;
|
|
regions[n_used_regions].start = expr.va_range$ps_start_va;
|
|
regions[n_used_regions].len = (unsigned char *)expr.va_range$ps_end_va - (unsigned char *)expr.va_range$ps_start_va + 1;
|
|
regions[n_used_regions].allocated = 1;
|
|
n_used_regions++;
|
|
if (len != regions[n_used_regions - 1].len) {
|
|
/*fprintf(stderr, "size mismatch\n");*/
|
|
regions[n_used_regions - 1].allocated = 0;
|
|
return NULL;
|
|
}
|
|
return expr.va_range$ps_start_va;
|
|
}
|
|
|
|
void virtual_free(void *ptr, size_t len)
|
|
{
|
|
int i;
|
|
struct va_range del_range, del_ret;
|
|
int vms_ret;
|
|
|
|
for (i = 0; i < n_used_regions; i++) {
|
|
if (!regions[i].allocated)
|
|
continue;
|
|
if (regions[i].start == ptr && regions[i].len == len)
|
|
goto found;
|
|
}
|
|
vms_fatal_exit("alloaction %p, %p not found", ptr, (void *)len);
|
|
found:
|
|
/*fprintf(stderr, "dealloc (%d - %p,%p) (%d)\n", i, ptr, (void *)len, n_used_regions);*/
|
|
regions[i].allocated = 0;
|
|
if (i < n_used_regions - 1 && !regions[i + 1].allocated && regions[i].start + regions[i].len == regions[i + 1].start) {
|
|
regions[i].len += regions[i + 1].len;
|
|
memmove(®ions[i + 1], ®ions[i + 2], (n_used_regions - i - 2) * sizeof(struct region));
|
|
n_used_regions--;
|
|
/*fprintf(stderr, "front merge\n");*/
|
|
}
|
|
if (i && !regions[i - 1].allocated && regions[i - 1].start + regions[i - 1].len == regions[i].start) {
|
|
regions[i - 1].len += regions[i].len;
|
|
memmove(®ions[i], ®ions[i + 1], (n_used_regions - 1 - i) * sizeof(struct region));
|
|
n_used_regions--;
|
|
/*fprintf(stderr, "back merge\n");*/
|
|
}
|
|
|
|
del_range.va_range$ps_start_va = ptr;
|
|
del_range.va_range$ps_end_va = (unsigned char *)ptr + (len - 1);
|
|
if (del_range.va_range$ps_end_va == max_expreg) {
|
|
del_range.va_range$ps_end_va = (unsigned char *)del_range.va_range$ps_end_va - page_size;
|
|
memset((unsigned char *)del_range.va_range$ps_end_va + 1, 0, page_size);
|
|
}
|
|
if (del_range.va_range$ps_start_va <= del_range.va_range$ps_end_va) {
|
|
vms_ret = links_deltva(&del_range, &del_ret, 0);
|
|
if (vms_ret != SS$_NORMAL)
|
|
vms_fatal_exit("deltva failed: %d", vms_ret);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
void init_os(void)
|
|
{
|
|
#if !defined(__VAX) && defined(__FEATURE_MODE_INIT_STATE) && defined(__FEATURE_MODE_CURVAL)
|
|
if (decc$feature_get("DECC$EFS_CHARSET", __FEATURE_MODE_INIT_STATE) < 1)
|
|
decc$feature_set("DECC$EFS_CHARSET", __FEATURE_MODE_CURVAL, 1);
|
|
#endif
|
|
#if defined(OPENVMS_64BIT)
|
|
{
|
|
int i;
|
|
char **new_argv = malloc((g_argc + 1) * sizeof(char *));
|
|
if (!new_argv) vms_fatal_exit("can't allocate argv");
|
|
for (i = 0; i <= g_argc; i++)
|
|
new_argv[i] = (char *)(my_intptr_t)(((unsigned *)g_argv)[i]);
|
|
g_argv = new_argv;
|
|
}
|
|
#endif
|
|
#ifdef VMS_VIRTUAL_PIPE
|
|
{
|
|
int ret;
|
|
ret = pthread_mutex_init(&pipe_mutex, NULL);
|
|
if (ret)
|
|
vms_fatal_exit("pthread_mutex_init failed: %d", ret);
|
|
ret = pthread_cond_init(&pipe_cond, NULL);
|
|
if (ret)
|
|
vms_fatal_exit("pthread_cond_init failed: %d", ret);
|
|
#ifdef VMS_SELECT_THREAD
|
|
ret = pthread_cond_init(&select_cond, NULL);
|
|
if (ret)
|
|
vms_fatal_exit("pthread_cond_init failed: %d", ret);
|
|
ret = vms_socketpair(select_thread_signal);
|
|
if (ret)
|
|
vms_fatal_exit("can't create select thread socket");
|
|
set_nonblock(select_thread_signal[0]);
|
|
set_nonblock(select_thread_signal[1]);
|
|
select_set_max = 0;
|
|
FD_ZERO(&select_set_read);
|
|
FD_ZERO(&select_set_write);
|
|
FD_ZERO(&select_set_exception);
|
|
FD_ZERO(&add_set_read);
|
|
FD_ZERO(&add_set_write);
|
|
FD_ZERO(&add_set_exception);
|
|
FD_ZERO(&remove_set);
|
|
add_to_thread_set(select_thread_signal[0], &select_set_read);
|
|
select_set_max = select_thread_signal[0] + 1;
|
|
vms_thread_high_priority = -1;
|
|
ret = start_thread(vms_select_thread, NULL, 0, 0);
|
|
if (ret == -1)
|
|
vms_fatal_exit("unable to start select thread");
|
|
vms_thread_high_priority = 0;
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void os_seed_random(unsigned char **pool, int *pool_size)
|
|
{
|
|
struct history_item *hi;
|
|
struct list_head *lhi;
|
|
DIR *dir;
|
|
int n, h;
|
|
|
|
*pool = init_str();
|
|
*pool_size = 0;
|
|
|
|
/*
|
|
* This is not very secure, but I don't know a better way.
|
|
*/
|
|
add_bytes_to_str(pool, pool_size, (unsigned char *)(void *)&gettimeofday_pool, sizeof gettimeofday_pool);
|
|
|
|
#ifdef USE_SHA
|
|
/*
|
|
* Make sure that even if the transformation is reversible (due to
|
|
* poor randomness on OpenVMS), the adversary won't be able to find
|
|
* any URLs in the history.
|
|
*/
|
|
n = 0;
|
|
foreach(struct history_item, hi, lhi, goto_url_history.items) {
|
|
SHA_CTX ctx;
|
|
unsigned char result[SHA_DIGEST_LENGTH];
|
|
unsigned char sum;
|
|
int i;
|
|
|
|
SHA1_Init(&ctx);
|
|
SHA1_Update(&ctx, hi->str, strlen(cast_const_char hi->str));
|
|
SHA1_Final(result, &ctx);
|
|
|
|
sum = 0;
|
|
for (i = 0; i < SHA_DIGEST_LENGTH; i++) {
|
|
sum += result[i] + (result[i] >> 4);
|
|
}
|
|
add_chr_to_str(pool, pool_size, sum & 0xf);
|
|
if (++n >= 64)
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
dir = c_opendir(cast_uchar "/SYS$LOGIN");
|
|
if (dir) {
|
|
for (n = 0; n < 256; n++) {
|
|
struct dirent *de;
|
|
unsigned char *path;
|
|
struct stat st;
|
|
ENULLLOOP(de, (void *)readdir(dir));
|
|
if (!de)
|
|
break;
|
|
path = stracpy(cast_uchar "/SYS$LOGIN/");
|
|
add_to_strn(&path, cast_uchar de->d_name);
|
|
if (!stat(cast_const_char path, &st)) {
|
|
add_bytes_to_str(pool, pool_size, (unsigned char *)&st.st_ctime, (int)sizeof st.st_ctime);
|
|
}
|
|
mem_free(path);
|
|
}
|
|
closedir(dir);
|
|
}
|
|
|
|
h = c_open(cast_uchar "/SYS$LOGIN/SSH2/RANDOM_SEED", O_RDONLY);
|
|
if (h == -1)
|
|
h = c_open(cast_uchar "/SYS$LOGIN/SSH/RANDOM_SEED", O_RDONLY);
|
|
if (h != -1) {
|
|
unsigned char buffer[512];
|
|
int r;
|
|
r = hard_read(h, buffer, (int)sizeof buffer);
|
|
if (r >= 0)
|
|
add_bytes_to_str(pool, pool_size, buffer, r);
|
|
EINTRLOOP(r, close(h));
|
|
}
|
|
}
|
|
|
|
void terminate_osdep(void)
|
|
{
|
|
}
|
|
|
|
#else
|
|
|
|
typedef int vms_c_no_empty_unit;
|
|
|
|
#endif
|