#ifdef __VMS #include "links.h" #define __NEW_STARLET 1 #include #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_OPENSSL) && !defined(OPENSSL_NO_SHA) && !defined(OPENSSL_NO_SHA1) #include #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 #include #include #include #endif #include #include #ifdef HAVE_SYS_IOCTL_H #include #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