|
|
|
|
|
|
#include "global.h" |
#include "fdlib.h" |
#include "pike_netlib.h" |
#include "interpret.h" |
#include "svalue.h" |
#include "stralloc.h" |
#include "array.h" |
#include "mapping.h" |
#include "object.h" |
#include "pike_macros.h" |
#include "backend.h" |
#include "fd_control.h" |
#include "module_support.h" |
#include "operators.h" |
#include "bignum.h" |
#include "builtin_functions.h" |
#include "gc.h" |
#include "time_stuff.h" |
#include "buffer.h" |
#include "file_machine.h" |
#include "file.h" |
#include "pike_error.h" |
#include "signal_handler.h" |
#include "pike_types.h" |
#include "threads.h" |
#include "program_id.h" |
|
#include <sys/stat.h> |
#ifdef HAVE_SYS_PARAM_H |
#include <sys/param.h> |
#endif /* HAVE_SYS_PARAM_H */ |
#include <errno.h> |
|
#include <fcntl.h> |
#include <signal.h> |
|
#ifdef HAVE_SYS_EVENT_H |
#include <sys/event.h> |
#endif /* HAVE_SYS_EVENT_H */ |
|
#ifdef HAVE_SYS_FILE_H |
#include <sys/file.h> |
#endif /* HAVE_SYS_FILE_H */ |
|
#ifdef HAVE_SYS_SOCKET_H |
# include <sys/socket.h> |
#endif |
|
#ifdef HAVE_SYS_IOCTL_H |
#include <sys/ioctl.h> |
#endif |
|
#ifdef HAVE_LINUX_IF_H |
#include <linux/if.h> |
#endif |
|
#ifdef HAVE_SYS_UIO_H |
#include <sys/uio.h> |
#endif /* HAVE_SYS_UIO_H */ |
|
#ifdef HAVE_SYS_XATTR_H |
#include <sys/xattr.h> |
#endif /* HAVE_SYS_XATTR_H */ |
|
#ifdef HAVE_AVAILABILITYMACROS_H |
#include <AvailabilityMacros.h> |
#endif |
|
#if defined(HAVE_WINSOCK_H) || defined(HAVE_WINSOCK2_H) |
#ifndef EWOULDBLOCK |
#define EWOULDBLOCK WSAEWOULDBLOCK |
#endif |
#ifndef EADDRINUSE |
#define EADDRINUSE WSAEADDRINUSE |
#endif |
#endif |
|
#ifdef HAVE_SYS_STREAM_H |
#include <sys/stream.h> |
|
|
#ifdef u |
#undef u |
#endif |
|
#endif |
|
#ifdef HAVE_SYS_PROTOSW_H |
#include <sys/protosw.h> |
#endif |
|
#ifdef HAVE_SYS_SOCKETVAR_H |
#include <sys/socketvar.h> |
#endif |
|
|
|
|
|
|
#ifdef NOERROR |
#undef NOERROR |
#endif /* NOERROR */ |
|
#ifdef HAVE_NETDB_H |
#include <netdb.h> |
#endif |
#ifdef HAVE_NET_NETDB_H |
#include <net/netdb.h> |
#endif /* HAVE_NET_NETDB_H */ |
|
#ifdef HAVE_NETINET_TCP_H |
#include <netinet/tcp.h> |
#endif |
|
|
#undef THIS |
#define THIS ((struct my_file *)(Pike_fp->current_storage)) |
#define FD (THIS->box.fd) |
#define ERRNO (THIS->my_errno) |
|
#define READ_BUFFER 8192 |
#define DIRECT_BUFSIZE (64*1024) |
#define SMALL_NETBUF 2048 |
#define INUSE_BUSYWAIT_DELAY 0.01 |
#define INUSE_TIMEOUT 0.1 |
|
|
#ifdef __amigaos__ |
#undef HAVE_SOCKETPAIR |
#endif |
|
#ifdef UNIX_SOCKETS_WORK_WITH_SHUTDOWN |
#undef UNIX_SOCKET_CAPABILITIES |
#define UNIX_SOCKET_CAPABILITIES (fd_INTERPROCESSABLE | fd_BIDIRECTIONAL | fd_CAN_NONBLOCK | fd_CAN_SHUTDOWN | fd_SEND_FD) |
#endif |
|
#ifndef HAVE_DIRFD |
#ifdef HAVE_DIR_DD_FD |
#define dirfd(dir__) (((DIR*)dir__)->dd_fd) |
#define HAVE_DIRFD |
#elif defined(HAVE_DIR_D_FD) |
#define dirfd(dir__) (((DIR*)dir__)->d_fd) |
#define HAVE_DIRFD |
#endif |
#endif |
|
|
|
|
|
#ifdef __GNUC__ |
#pragma GCC optimize "-Os" |
#endif |
|
|
|
struct program *file_program; |
struct program *file_ref_program; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int fd_fd_factory_fun_num = -1; |
static void fd_fd_factory(INT32 args) |
{ |
pop_n_elems(args); |
push_object_inherit(clone_object_from_object(Pike_fp->current_object, 0), |
Pike_fp->context - Pike_fp->current_program->inherits); |
} |
|
|
|
|
|
static void fd_backtick__fd(INT32 args) |
{ |
pop_n_elems(args); |
ref_push_object_inherit(Pike_fp->current_object, |
Pike_fp->context - |
Pike_fp->current_program->inherits); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static struct my_file *get_file_storage(struct object *o) |
{ |
struct my_file *f; |
struct svalue *sval; |
struct object *ob; |
if(o->prog == file_program) |
return ((struct my_file *) |
(o->storage + file_program->inherits->storage_offset)); |
|
if((f=get_storage(o,file_program))) |
return f; |
|
if((sval=get_storage(o,file_ref_program))) { |
if (TYPEOF(*sval) == PIKE_T_OBJECT) { |
ob = sval->u.object; |
|
if(ob && (f=get_storage(ob, file_program))) |
return f; |
} |
} |
|
return 0; |
} |
|
#ifdef PIKE_DEBUG |
static void debug_check_internals (struct my_file *f) |
{ |
size_t ev; |
|
if (f->box.ref_obj->prog && file_program && |
!get_storage(f->box.ref_obj,file_program) ) |
Pike_fatal ("ref_obj is not a file object.\n"); |
|
for (ev = 0; ev < NELEM (f->event_cbs); ev++) |
if (TYPEOF(f->event_cbs[ev]) == PIKE_T_INT && |
f->box.backend && f->box.events & (1 << ev)) |
Pike_fatal ("Got event flag but no callback for event %"PRINTSIZET"d.\n", ev); |
} |
#else |
#define debug_check_internals(f) do {} while (0) |
#endif |
|
#define ADD_FD_EVENTS(F, EVENTS) do { \ |
struct my_file *f_ = (F); \ |
if (!f_->box.backend) \ |
INIT_FD_CALLBACK_BOX (&f_->box, default_backend, f_->box.ref_obj, \ |
f_->box.fd, (EVENTS), got_fd_event, 0); \ |
else \ |
set_fd_callback_events (&f_->box, f_->box.events | (EVENTS), 0); \ |
} while (0) |
|
#define ADD_FD_EVENTS2(F, EVENTS, FFLAGS) do { \ |
struct my_file *f_ = (F); \ |
if (!f_->box.backend) \ |
INIT_FD_CALLBACK_BOX (&f_->box, default_backend, f_->box.ref_obj, \ |
f_->box.fd, (EVENTS), got_fd_event, FFLAGS); \ |
else \ |
set_fd_callback_events (&f_->box, f_->box.events | (EVENTS), FFLAGS); \ |
} while (0) |
|
|
#define SUB_FD_EVENTS(F, EVENTS) do { \ |
struct my_file *f_ = (F); \ |
if (f_->box.backend) \ |
set_fd_callback_events (&f_->box, f_->box.events & ~(EVENTS), 0); \ |
} while (0) |
|
#define SUB_FD_EVENTS2(F, EVENTS, FLAGS) do { \ |
struct my_file *f_ = (F); \ |
if (f_->box.backend) \ |
set_fd_callback_events (&f_->box, f_->box.events & ~(EVENTS), FLAGS); \ |
} while (0) |
|
static int got_fd_event (struct fd_callback_box *box, int event) |
{ |
struct my_file *f = (struct my_file *) box; |
struct svalue *cb = &f->event_cbs[event]; |
|
f->my_errno = errno; |
|
if(event != PIKE_FD_FS_EVENT) |
SUB_FD_EVENTS (f, 1 << event); |
|
check_destructed (cb); |
if (!UNSAFE_IS_ZERO (cb)) { |
if(event == PIKE_FD_FS_EVENT) |
push_int(box->rflags); |
apply_svalue (cb, event == PIKE_FD_FS_EVENT); |
if (TYPEOF(Pike_sp[-1]) == PIKE_T_INT && Pike_sp[-1].u.integer == -1) { |
pop_stack(); |
return -1; |
} |
pop_stack(); |
} |
return 0; |
} |
|
static int fd_receive_fd_fun_num; |
|
static void init_fd(int fd, int open_mode, int flags) |
{ |
size_t ev; |
struct identifier *i; |
ERRNO=0; |
|
i = ID_FROM_INT(Pike_fp->current_object->prog, |
fd_receive_fd_fun_num + Pike_fp->context->identifier_level); |
if (((i->identifier_flags & IDENTIFIER_TYPE_MASK) == |
IDENTIFIER_PIKE_FUNCTION) && (i->func.offset != -1)) { |
|
flags |= FILE_HAVE_RECV_FD; |
} |
|
THIS->flags=flags; |
THIS->open_mode=open_mode; |
for (ev = 0; ev < NELEM (THIS->event_cbs); ev++) { |
SET_SVAL(THIS->event_cbs[ev], PIKE_T_INT, NUMBER_NUMBER, integer, 0); |
} |
#ifdef HAVE_PIKE_SEND_FD |
THIS->fd_info = NULL; |
#endif |
#if defined(HAVE_FD_FLOCK) || defined(HAVE_FD_LOCKF) |
THIS->key=0; |
#endif |
#ifdef PIKE_DEBUG |
|
* if the fd belongs to a backend... */ |
if ((fd >= 0) && !(flags & FILE_NOT_OPENED)) |
debug_check_fd_not_in_use (fd); |
#endif |
change_fd_for_box(&THIS->box, fd); |
} |
|
|
|
static void do_close_fd(ptrdiff_t fd) |
{ |
int ret; |
if (fd < 0) return; |
do { |
ret = fd_close(fd); |
} while ((ret == -1) && (errno == EINTR)); |
} |
|
#ifdef HAVE_PIKE_SEND_FD |
|
* sent, or due to the connection being closed. */ |
static void do_close_fd_info(int *fd_info) |
{ |
int num_fds = fd_info[1]; |
int *fds = fd_info + 2; |
while (num_fds) { |
do_close_fd(fds[--num_fds]); |
#ifdef PIKE_DEBUG |
fds[num_fds] = -1; |
#endif |
} |
fd_info[1] = 0; |
} |
|
|
static void restore_fd_info(int *fd_info) |
{ |
int *other_fd_info; |
if ((other_fd_info = THIS->fd_info)) { |
|
int i, j, cnt; |
int num_fds = fd_info[1] + other_fd_info[1] + 2; |
|
#ifdef PIKE_DEBUG |
if (other_fd_info == fd_info) { |
Pike_fatal("restore_fd_info: Double restore!\n"); |
} |
#endif |
|
if (num_fds <= fd_info[0]) { |
cnt = other_fd_info[1] + 2; |
for (i = 2; i < cnt; i++) { |
fd_info[fd_info[1] + i] = other_fd_info[i]; |
} |
fd_info[1] += cnt-2; |
free(other_fd_info); |
} else if (num_fds <= other_fd_info[0]) { |
|
cnt = fd_info[1] + 2; |
for(i = other_fd_info[1] + 1, j = num_fds-1; j >= cnt; i--, j--) { |
other_fd_info[j] = other_fd_info[i]; |
} |
for(i = 2; i < cnt; i++) { |
other_fd_info[i] = fd_info[i]; |
} |
other_fd_info[1] = num_fds; |
free(fd_info); |
fd_info = other_fd_info; |
} else { |
int *new_fd_info = calloc(num_fds, sizeof(int)); |
if (!new_fd_info) { |
|
Pike_fatal("Out of memory in send_fd().\n"); |
} else { |
new_fd_info[0] = num_fds; |
new_fd_info[1] = num_fds - 2; |
cnt = fd_info[1] + 2; |
for (i = 2; i < cnt; i++) { |
new_fd_info[i] = fd_info[i]; |
} |
free(fd_info); |
fd_info = new_fd_info; |
new_fd_info += i; |
cnt = other_fd_info[1] + 2; |
for (i = 2; i < cnt; i++) { |
new_fd_info[i] = other_fd_info[i]; |
} |
free(other_fd_info); |
} |
} |
} |
THIS->fd_info = fd_info; |
} |
#endif |
|
static void free_fd_stuff(void) |
{ |
size_t ev; |
|
#if defined(HAVE_FD_FLOCK) || defined(HAVE_FD_LOCKF) |
if(THIS->key) |
{ |
destruct(THIS->key); |
THIS->key=0; |
} |
#endif |
|
#ifdef HAVE_PIKE_SEND_FD |
if (THIS->fd_info) do_close_fd_info(THIS->fd_info); |
#endif |
|
for (ev = 0; ev < NELEM (THIS->event_cbs); ev++) { |
free_svalue(& THIS->event_cbs[ev]); |
SET_SVAL(THIS->event_cbs[ev], PIKE_T_INT, NUMBER_NUMBER, integer, 0); |
} |
} |
|
static void close_fd_quietly(void) |
{ |
int fd=FD; |
if(fd<0) return; |
|
free_fd_stuff(); |
SUB_FD_EVENTS (THIS, ~0); |
change_fd_for_box (&THIS->box, -1); |
|
while(1) |
{ |
int i, e; |
THREADS_ALLOW_UID(); |
i=fd_close(fd); |
e=errno; |
THREADS_DISALLOW_UID(); |
|
check_threads_etc(); |
|
if(i < 0) |
{ |
switch(e) |
{ |
default: { |
JMP_BUF jmp; |
if (SETJMP (jmp)) |
call_handle_error(); |
else { |
ERRNO=errno=e; |
change_fd_for_box (&THIS->box, fd); |
push_int(e); |
f_strerror(1); |
Pike_error("Failed to close file: %S\n", Pike_sp[-1].u.string); |
} |
UNSETJMP (jmp); |
break; |
} |
|
case EBADF: |
break; |
|
#ifdef SOLARIS |
|
case EAGAIN: |
break; |
#endif |
|
case EINTR: |
continue; |
} |
} |
break; |
} |
} |
|
static void close_fd(void) |
{ |
int fd=FD; |
if(fd<0) return; |
|
free_fd_stuff(); |
SUB_FD_EVENTS (THIS, ~0); |
change_fd_for_box (&THIS->box, -1); |
|
if ( (THIS->flags & FILE_NOT_OPENED) ) |
return; |
|
while(1) |
{ |
int i, e; |
THREADS_ALLOW_UID(); |
i=fd_close(fd); |
e=errno; |
THREADS_DISALLOW_UID(); |
|
check_threads_etc(); |
|
if(i < 0) |
{ |
switch(e) |
{ |
default: |
ERRNO=errno=e; |
change_fd_for_box (&THIS->box, fd); |
push_int(e); |
f_strerror(1); |
Pike_error("Failed to close file: %S\n", Pike_sp[-1].u.string); |
break; |
|
case EBADF: |
Pike_error("Internal error: Closing a non-active file descriptor %d.\n",fd); |
break; |
|
#ifdef SOLARIS |
|
case EAGAIN: |
break; |
#endif |
|
case EINTR: |
continue; |
} |
} |
break; |
} |
} |
|
void my_set_close_on_exec(int fd, int to) |
{ |
set_close_on_exec(fd, to); |
} |
|
void do_set_close_on_exec(void) |
{ |
} |
|
|
static int parse(char *a) |
{ |
int ret; |
ret=0; |
while(1) |
{ |
switch(*(a++)) |
{ |
case 0: return ret; |
|
case 'r': |
case 'R': |
ret|=FILE_READ; |
break; |
|
case 'w': |
case 'W': |
ret|=FILE_WRITE; |
break; |
|
case 'a': |
case 'A': |
ret|=FILE_APPEND; |
break; |
|
case 'c': |
case 'C': |
ret|=FILE_CREATE; |
break; |
|
case 't': |
case 'T': |
ret|=FILE_TRUNC; |
break; |
|
case 'x': |
case 'X': |
ret|=FILE_EXCLUSIVE; |
break; |
} |
} |
} |
|
|
static int map(int flags) |
{ |
int ret; |
ret=0; |
switch(flags & (FILE_READ|FILE_WRITE)) |
{ |
case FILE_READ: ret=fd_RDONLY; break; |
case FILE_WRITE: ret=fd_WRONLY; break; |
case FILE_READ | FILE_WRITE: ret=fd_RDWR; break; |
} |
if(flags & FILE_APPEND) ret|=fd_APPEND; |
if(flags & FILE_CREATE) ret|=fd_CREAT; |
if(flags & FILE_TRUNC) ret|=fd_TRUNC; |
if(flags & FILE_EXCLUSIVE) ret|=fd_EXCL; |
ret |= fd_LARGEFILE; |
return ret; |
} |
|
static struct pike_string *do_read(int fd, |
INT32 r, |
int all, |
INT_TYPE *err) |
{ |
size_t bytes = r; |
struct byte_buffer buf = BUFFER_INIT(); |
int e = 0; |
|
buffer_set_flags(&buf, BUFFER_GROW_EXACT); |
|
THREADS_ALLOW(); |
|
while (bytes) { |
size_t len = MINIMUM(DIRECT_BUFSIZE, bytes); |
ptrdiff_t i; |
|
|
if (UNLIKELY(!buffer_ensure_space_nothrow(&buf, len+1))) { |
buffer_free(&buf); |
e = ENOMEM; |
break; |
} |
|
i = fd_read(fd, buffer_alloc_unsafe(&buf, len), len); |
|
if (LIKELY(i >= 0)) { |
if ((size_t)i < len) buffer_remove(&buf, len - i); |
bytes -= i; |
if (!i || !all) break; |
} else { |
e=errno; |
if (e == EINTR) { |
e = 0; |
buffer_remove(&buf, len); |
continue; |
} |
|
buffer_free(&buf); |
break; |
} |
} |
|
THREADS_DISALLOW(); |
|
check_threads_etc(); |
|
if (e) { |
*err = e; |
return NULL; |
} |
|
if(!SAFE_IS_ZERO(& THIS->event_cbs[PIKE_FD_READ])) |
ADD_FD_EVENTS (THIS, PIKE_BIT_FD_READ); |
|
return buffer_finish_pike_string(&buf); |
} |
|
|
|
static int low_fd_query_properties(int fd) |
{ |
struct stat st; |
PIKE_SOCKADDR addr; |
ACCEPT_SIZE_T len; |
int i; |
int orig_errno = errno; |
int open_mode = 0; |
|
#ifndef __NT__ |
do { |
i = fcntl(fd, F_GETFL); |
} while ((i == -1) && (errno == EINTR)); |
|
if (i == -1) { |
|
errno = orig_errno; |
return 0; |
} |
|
switch(i & fd_ACCMODE) { |
case fd_RDONLY: |
open_mode = FILE_READ; |
break; |
case fd_WRONLY: |
open_mode = FILE_WRITE; |
break; |
case fd_RDWR: |
open_mode = FILE_READ | FILE_WRITE; |
break; |
} |
if (i & fd_APPEND) open_mode |= FILE_APPEND; |
if (i & fd_CREAT) open_mode |= FILE_CREATE; |
if (i & fd_TRUNC) open_mode |= FILE_TRUNC; |
if (i & fd_EXCL) open_mode |= FILE_EXCLUSIVE; |
#ifdef O_NONBLOCK |
if (i & O_NONBLOCK) open_mode |= FILE_NONBLOCKING; |
#elif defined(O_NDELAY) |
if (i & O_NDELAY) open_mode |= FILE_NONBLOCKING; |
#endif |
|
#endif /* !__NT__ */ |
|
do { |
i = fd_fstat(fd, &st); |
} while ((i < 0) && (errno == EINTR)); |
|
errno = orig_errno; |
if (i < 0) return open_mode|FILE_CAPABILITIES; |
|
switch(st.st_mode & S_IFMT) { |
default: |
return open_mode | FILE_CAPABILITIES; |
#ifdef PIPE_CAPABILITIES |
case S_IFIFO: |
return open_mode | PIPE_CAPABILITIES; |
#endif |
case S_IFSOCK: |
break; |
} |
|
addr.sa.sa_family = AF_INET; |
|
do { |
len = sizeof(addr); |
i = fd_getsockname(fd, &addr.sa, &len); |
} while ((i < 0) && (errno == EINTR)); |
|
errno = orig_errno; |
|
if (i < 0) { |
|
return open_mode | FILE_CAPABILITIES; |
} else { |
switch (addr.sa.sa_family) { |
|
#if defined(AF_UNIX) && (AF_UNIX != AF_INET) && !defined(__NT__) |
case AF_UNIX: |
return open_mode | UNIX_SOCKET_CAPABILITIES; |
#endif |
default: |
|
return open_mode | SOCKET_CAPABILITIES; |
} |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_PIKE_SEND_FD |
static void receive_fds(int *fds, size_t num_fds) |
{ |
volatile size_t i; |
|
for (i = 0; i < num_fds; i++) { |
int fd = fds[i]; |
if (fd >= 0) { |
JMP_BUF jmp; |
if (SETJMP(jmp)) |
call_handle_error(); |
else { |
push_new_fd_object(fd_fd_factory_fun_num, fd, |
low_fd_query_properties(fd), 0); |
apply_current(fd_receive_fd_fun_num, 1); |
pop_stack(); |
} |
UNSETJMP(jmp); |
} |
} |
} |
|
static void check_message(struct msghdr *msg) |
{ |
#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL |
struct cmsghdr *cmsg; |
|
#if defined(MAC_OS_X_VERSION_10_0) && !defined(MAC_OS_X_VERSION_10_6) |
|
* and earlier for 64-bit ABI, where it adds extra padding between |
* the struct cmsghdr and the data, while the kernel doesn't... |
* |
* The macros seem to be fixed in the 10.6 header files. |
* |
* /grubba 2011-09-19. |
*/ |
#undef CMSG_DATA |
#define CMSG_DATA(X) ((void *)((X) + 1)) |
#undef CMSG_LEN |
#define CMSG_LEN(X) (sizeof(struct cmsghdr) + (X)) |
#endif |
|
for (cmsg = CMSG_FIRSTHDR(msg); cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { |
if ((cmsg->cmsg_level == SOL_SOCKET) && (cmsg->cmsg_type == SCM_RIGHTS)) { |
receive_fds((int *)CMSG_DATA(cmsg), |
(cmsg->cmsg_len - CMSG_LEN(0))/sizeof(int)); |
} |
} |
#else |
receive_fds((int *)msg->msg_accrights, msg->msg_accrightslen/sizeof(int)); |
#endif |
} |
|
#ifndef HAVE_STRUCT_MSGHDR_MSG_CONTROL |
|
#define CMSG_LEN(x) (x) |
#endif |
|
static struct pike_string *do_recvmsg(INT32 r, int all) |
{ |
ONERROR ebuf; |
ptrdiff_t bytes_read = 0, i; |
int fd = FD; |
struct { |
struct msghdr msg; |
struct iovec iov; |
char cmsgbuf[CMSG_LEN(sizeof(int)*128)]; |
} message; |
|
ERRNO=0; |
|
message.msg.msg_name = NULL; |
message.msg.msg_namelen = 0; |
message.msg.msg_iov = &message.iov; |
message.msg.msg_iovlen = 1; |
#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL |
|
message.msg.msg_flags = 0; |
#endif |
|
{ |
|
|
|
|
struct byte_buffer b = BUFFER_INIT(); |
|
buffer_set_flags(&b, BUFFER_GROW_EXACT); |
|
|
|
|
|
if (r < 65*1024) buffer_ensure_space(&b, r+1); |
|
SET_ONERROR(ebuf, buffer_free, &b); |
do{ |
int e; |
const INT32 CHUNK = 1024 * 8; |
INT32 try_read=MINIMUM(CHUNK,r); |
|
|
buffer_ensure_space(&b, try_read+1); |
|
#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL |
message.msg.msg_control = &message.cmsgbuf; |
message.msg.msg_controllen = sizeof(message.cmsgbuf); |
#else |
message.msg.msg_accrights = (void *)&message.cmsgbuf; |
message.msg.msg_accrightslen = sizeof(message.cmsgbuf); |
#endif |
message.iov.iov_base = buffer_alloc(&b, try_read); |
message.iov.iov_len = try_read; |
|
THREADS_ALLOW(); |
i = recvmsg(fd, &message.msg, 0); |
e=errno; |
THREADS_DISALLOW(); |
|
check_threads_etc(); |
|
if(i>0) |
{ |
bytes_read+=i; |
r-=i; |
if ( |
#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL |
message.msg.msg_controllen |
#else |
message.msg.msg_accrightslen |
#endif |
) { |
check_message(&message.msg); |
} |
if (i != try_read) { |
buffer_remove(&b, try_read - i); |
} |
if(!all) break; |
} |
else if(i==0) |
{ |
buffer_remove(&b, try_read); |
break; |
} |
else |
{ |
buffer_remove(&b, try_read); |
if(e != EINTR) |
{ |
ERRNO=e; |
if(!bytes_read) |
{ |
buffer_free(&b); |
UNSET_ONERROR(ebuf); |
return 0; |
} |
break; |
} |
} |
}while(r); |
|
UNSET_ONERROR(ebuf); |
|
if(!SAFE_IS_ZERO(& THIS->event_cbs[PIKE_FD_READ])) |
ADD_FD_EVENTS (THIS, PIKE_BIT_FD_READ); |
|
return buffer_finish_pike_string(&b); |
} |
} |
|
|
static int writev_fds(int fd, struct iovec *iov, int iovcnt, |
int *fds, int num_fds) |
{ |
int retval, e; |
struct msghdr msg; |
#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL |
|
|
#ifndef CMSG_SPACE |
#define CMSG_SPACE(X) CMSG_LEN(X) |
#endif |
|
struct cmsghdr *cmsg = malloc(CMSG_SPACE(num_fds * sizeof(int))); |
|
#if 0 |
fprintf(stderr, "writev_fds(%d, %p, %d, %p, %d)\n", fd, iov, iovcnt, fds, num_fds); |
|
for (e = 0; e < num_fds; e++) { |
fprintf(stderr, " fd #%d: %d\n", e, fds[e]); |
} |
#endif /* 0 */ |
|
if (!cmsg) { |
errno = ENOMEM; |
return -1; |
} |
|
msg.msg_control = cmsg; |
msg.msg_controllen = cmsg->cmsg_len = CMSG_LEN(num_fds * sizeof(int)); |
cmsg->cmsg_level = SOL_SOCKET; |
cmsg->cmsg_type = SCM_RIGHTS; |
|
memcpy(CMSG_DATA(cmsg), fds, num_fds * sizeof(int)); |
msg.msg_flags = 0; |
|
#else |
|
msg.msg_accrights = (void *)fds; |
msg.msg_accrightslen = num_fds * sizeof(int); |
#endif |
msg.msg_name = NULL; |
msg.msg_namelen = 0; |
msg.msg_iov = iov; |
msg.msg_iovlen = iovcnt; |
retval = sendmsg(fd, &msg, 0); |
#ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL |
e = errno; |
free(cmsg); |
errno = e; |
#endif |
return retval; |
} |
|
#endif /* HAVE_PIKE_SEND_FD */ |
|
static struct pike_string *do_read_oob(int UNUSED(fd), |
INT32 r, |
int all, |
INT_TYPE *err) |
{ |
ONERROR ebuf; |
INT32 bytes_read,i; |
struct pike_string *str; |
|
bytes_read=0; |
*err=0; |
|
str=begin_shared_string(r); |
|
SET_ONERROR(ebuf, do_free_unlinked_pike_string, str); |
|
do{ |
int e; |
int fd=FD; |
THREADS_ALLOW(); |
i=fd_recv(fd, str->str+bytes_read, r, MSG_OOB); |
e=errno; |
THREADS_DISALLOW(); |
|
check_threads_etc(); |
|
if(i>0) |
{ |
r-=i; |
bytes_read+=i; |
if(!all) break; |
} |
else if ((i==0) || (e == EINVAL) |
#ifdef ECONNRESET |
|| (e == ECONNRESET) |
#endif |
#ifdef EOPNOTSUPP |
|| (e == EOPNOTSUPP) |
#endif |
) |
{ |
|
break; |
} |
else if(e != EINTR) |
{ |
*err=e; |
if(!bytes_read) |
{ |
do_free_unlinked_pike_string(str); |
UNSET_ONERROR(ebuf); |
return 0; |
} |
break; |
} |
}while(r); |
|
UNSET_ONERROR(ebuf); |
|
if(!SAFE_IS_ZERO(& THIS->event_cbs[PIKE_FD_READ_OOB])) |
ADD_FD_EVENTS (THIS, PIKE_BIT_FD_READ_OOB); |
|
if(bytes_read == str->len) |
{ |
return end_shared_string(str); |
}else{ |
return end_and_resize_shared_string(str, bytes_read); |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_read(INT32 args) |
{ |
struct pike_string *tmp; |
INT32 all, len; |
|
if(FD < 0) |
Pike_error("File not open.\n"); |
|
if(!args) |
{ |
len=0x7fffffff; |
} |
else |
{ |
if(TYPEOF(Pike_sp[-args]) != PIKE_T_INT) |
SIMPLE_ARG_TYPE_ERROR("read", 1, "int"); |
len=Pike_sp[-args].u.integer; |
if(len<0) |
Pike_error("Cannot read negative number of characters.\n"); |
if (!len && SUBTYPEOF(Pike_sp[-args])) { |
len = 0x7fffffff; |
} |
} |
|
if(args > 1 && !UNSAFE_IS_ZERO(Pike_sp+1-args)) |
{ |
all=0; |
}else{ |
all=1; |
} |
|
pop_n_elems(args); |
|
#ifdef HAVE_PIKE_SEND_FD |
|
if ((THIS->open_mode & fd_SEND_FD) && |
(THIS->flags & FILE_HAVE_RECV_FD)) { |
if ((tmp = do_recvmsg(len, all))) |
push_string(tmp); |
else { |
errno = ERRNO; |
push_int(0); |
} |
} else |
#endif /* HAVE_PIKE_SEND_FD */ |
if((tmp=do_read(FD, len, all, & ERRNO))) |
push_string(tmp); |
else { |
errno = ERRNO; |
push_int(0); |
} |
|
if (!(THIS->open_mode & FILE_NONBLOCKING)) |
INVALIDATE_CURRENT_TIME(); |
|
|
|
|
THIS->box.revents &= ~(PIKE_BIT_FD_READ|PIKE_BIT_FD_READ_OOB); |
} |
|
#ifdef HAVE_AND_USE_POLL |
#ifdef HAVE_POLL_H |
#include <poll.h> |
#else /* !HAVE_POLL_H */ |
#ifdef HAVE_SYS_POLL_H |
#include <sys/poll.h> |
#else /* !HAVE_SYS_POLL_H */ |
#undef HAVE_AND_USE_POLL |
#endif /* HAVE_SYS_POLL_H */ |
#endif /* HAVE_POLL_H */ |
#else /* HAVE_AND_USE_POLL */ |
|
#ifdef HAVE_SYS_SELECT_H |
#include <sys/select.h> |
#endif |
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_peek(INT32 args) |
{ |
int ret; |
int not_eof = 0; |
FLOAT_TYPE tf = 0.0; |
|
get_all_args("peek",args,".%F%d",&tf,¬_eof); |
|
{ |
#ifdef HAVE_AND_USE_POLL |
struct pollfd fds; |
int timeout = 0; |
if (args && !IS_UNDEFINED(Pike_sp - args)) { |
timeout = (int)(tf*1000); |
} |
fds.fd=FD; |
fds.events=POLLIN; |
fds.revents=0; |
|
if (timeout) { |
THREADS_ALLOW(); |
ret=poll(&fds, 1, timeout); |
THREADS_DISALLOW(); |
} else { |
ret=poll(&fds, 1, 0); |
} |
|
if(ret < 0) |
{ |
ERRNO=errno; |
ret=-1; |
} else if (fds.revents & POLLERR) { |
int err = EPIPE; |
ACCEPT_SIZE_T len = sizeof(err); |
ret = -1; |
getsockopt(FD, SOL_SOCKET, SO_ERROR, (void *)&err, &len); |
ERRNO = err; |
} else if (fds.revents & POLLNVAL) { |
ret = -1; |
errno = EINVAL; |
} else if (not_eof && (fds.revents & POLLHUP)) { |
ret = -1; |
ERRNO = EPIPE; |
}else{ |
ret = (ret > 0) && (fds.revents & POLLIN); |
} |
#else |
fd_set tmp; |
struct timeval tv; |
|
tv.tv_usec=0; |
tv.tv_sec=0; |
fd_FD_ZERO(&tmp); |
fd_FD_SET(FD, &tmp); |
ret = FD; |
|
if (args && !IS_UNDEFINED(Pike_sp - args)) { |
tv.tv_sec=(int)tf; |
tv.tv_usec=(int)(1000000*(tf-tv.tv_sec)); |
} |
|
|
|
if(tv.tv_sec || tv.tv_usec) { |
THREADS_ALLOW(); |
ret = fd_select(ret+1,&tmp,0,0,&tv); |
THREADS_DISALLOW(); |
} |
else |
ret = fd_select(ret+1,&tmp,0,0,&tv); |
|
if(ret < 0) |
{ |
ERRNO=errno; |
ret=-1; |
}else{ |
ret = (ret > 0) && fd_FD_ISSET(FD, &tmp); |
} |
#endif |
} |
pop_n_elems(args); |
push_int(ret); |
} |
|
|
|
|
|
|
#undef events |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_read_oob(INT32 args) |
{ |
struct pike_string *tmp; |
INT32 all, len; |
|
if(FD < 0) |
Pike_error("File not open.\n"); |
|
if(!args) |
{ |
len=0x7fffffff; |
} |
else |
{ |
if(TYPEOF(Pike_sp[-args]) != PIKE_T_INT) |
SIMPLE_ARG_TYPE_ERROR("read_oob", 1, "int"); |
len=Pike_sp[-args].u.integer; |
if(len<0) |
Pike_error("Cannot read negative number of characters.\n"); |
} |
|
if(args > 1 && !UNSAFE_IS_ZERO(Pike_sp+1-args)) |
{ |
all=0; |
}else{ |
all=1; |
} |
|
pop_n_elems(args); |
|
if((tmp=do_read_oob(FD, len, all, & ERRNO))) |
push_string(tmp); |
else { |
errno = ERRNO; |
push_int(0); |
} |
|
if (!(THIS->open_mode & FILE_NONBLOCKING)) |
INVALIDATE_CURRENT_TIME(); |
|
|
|
|
THIS->box.revents &= ~(PIKE_BIT_FD_READ|PIKE_BIT_FD_READ_OOB); |
} |
|
static short get_fd_event_flags(struct my_file *f) |
{ |
if(f->box.backend) |
{ |
return f->box.flags; |
} |
else return 0; |
} |
|
static void set_fd_event_cb (struct my_file *f, struct svalue *cb, int event, int flags) |
{ |
if (UNSAFE_IS_ZERO (cb)) { |
free_svalue (&f->event_cbs[event]); |
SET_SVAL(f->event_cbs[event], PIKE_T_INT, NUMBER_NUMBER, integer, 0); |
SUB_FD_EVENTS2 (f, 1 << event, flags); |
} |
else { |
#ifdef __NT__ |
if (!(fd_query_properties(f->box.fd, fd_CAN_NONBLOCK) & fd_CAN_NONBLOCK)) { |
Pike_error("Setting backend callback on a non-socket!\n"); |
} |
#endif /* __NT__ */ |
assign_svalue (&f->event_cbs[event], cb); |
ADD_FD_EVENTS2 (f, 1 << event, flags); |
} |
} |
|
#undef CBFUNCS |
|
#define CBFUNCS(CB, EVENT) \ |
static void PIKE_CONCAT(file_set_,CB) (INT32 args) \ |
{ \ |
if(!args) \ |
SIMPLE_WRONG_NUM_ARGS_ERROR("set_" #CB, 1); \ |
set_fd_event_cb (THIS, Pike_sp-args, EVENT, 0); \ |
} |
|
#define CBFUNCS2(CB, EVENT) \ |
static void PIKE_CONCAT(file_set_,CB) (INT32 args) \ |
{ \ |
if(args<2) \ |
SIMPLE_WRONG_NUM_ARGS_ERROR("set_" #CB, 2); \ |
if (TYPEOF(Pike_sp[1-args]) != PIKE_T_INT) \ |
SIMPLE_ARG_TYPE_ERROR("set_" #CB, 2, "int"); \ |
set_fd_event_cb (THIS, Pike_sp-args, EVENT, \ |
Pike_sp[1-args].u.integer); \ |
} |
|
CBFUNCS(read_callback, PIKE_FD_READ) |
CBFUNCS(write_callback, PIKE_FD_WRITE) |
CBFUNCS(read_oob_callback, PIKE_FD_READ_OOB) |
CBFUNCS(write_oob_callback, PIKE_FD_WRITE_OOB) |
CBFUNCS2(fs_event_callback, PIKE_FD_FS_EVENT) |
|
|
static void file_query_fs_event_flags(INT32 args) |
{ |
short flags; |
pop_n_elems(args); |
|
flags = get_fd_event_flags(THIS); |
push_int(flags); |
} |
|
|
static void file__enable_callbacks(INT32 UNUSED(args)) |
{ |
struct my_file *f = THIS; |
size_t ev; |
int cb_events = 0; |
|
#if 0 |
if(FD<0) |
Pike_error("File is not open.\n"); |
#endif |
|
debug_check_internals (f); |
|
for (ev = 0; ev < NELEM (f->event_cbs); ev++) |
if (!UNSAFE_IS_ZERO (&f->event_cbs[ev])) |
cb_events |= 1 << ev; |
|
if (cb_events) { |
if (FD >= 0 && |
!(fd_query_properties(FD, fd_CAN_NONBLOCK) & fd_CAN_NONBLOCK)) |
Pike_error("Callback operations not supported for this file.\n"); |
|
ADD_FD_EVENTS (f, cb_events); |
} |
} |
|
static void file__disable_callbacks(INT32 UNUSED(args)) |
{ |
struct my_file *f = THIS; |
|
#if 0 |
if(FD<0) |
Pike_error("File is not open.\n"); |
#endif |
|
SUB_FD_EVENTS (f, ~0); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_write(INT32 args) |
{ |
ptrdiff_t written, i; |
struct pike_string *str; |
|
if(args<1 || ((TYPEOF(Pike_sp[-args]) != PIKE_T_STRING) && |
(TYPEOF(Pike_sp[-args]) != PIKE_T_ARRAY))) |
SIMPLE_ARG_TYPE_ERROR("write", 1, "string|array(string)"); |
|
if(FD < 0) |
Pike_error("File not open for write.\n"); |
|
if (TYPEOF(Pike_sp[-args]) == PIKE_T_ARRAY) { |
struct array *a = Pike_sp[-args].u.array; |
|
if( (a->type_field & ~BIT_STRING) && |
(array_fix_type_field(a) & ~BIT_STRING) ) |
SIMPLE_ARG_TYPE_ERROR("write", 1, "string|array(string)"); |
|
i = a->size; |
while(i--) |
if (a->item[i].u.string->size_shift) |
Pike_error("Bad argument 1 to file->write().\n" |
"Element %ld is a wide string.\n", |
(long)i); |
|
#ifdef HAVE_WRITEV |
if (args > 1) { |
#endif /* HAVE_WRITEV */ |
ref_push_array(a); |
push_empty_string(); |
o_multiply(); |
Pike_sp--; |
dmalloc_touch_svalue(Pike_sp); |
Pike_sp[-args] = *Pike_sp; |
free_array(a); |
|
#ifdef PIKE_DEBUG |
if (TYPEOF(Pike_sp[-args]) != PIKE_T_STRING) { |
Pike_error("Bad return value from string multiplication.\n"); |
} |
#endif /* PIKE_DEBUG */ |
#ifdef HAVE_WRITEV |
} else if (!a->size) { |
|
ERRNO = 0; |
pop_stack(); |
push_int(0); |
return; |
} else { |
struct iovec *iovbase = xalloc(sizeof(struct iovec)*a->size); |
struct iovec *iov = iovbase; |
int iovcnt = a->size; |
|
if (!(THIS->open_mode & FILE_NONBLOCKING)) |
INVALIDATE_CURRENT_TIME(); |
|
i = a->size; |
while(i--) { |
if (a->item[i].u.string->len) { |
iov[i].iov_base = a->item[i].u.string->str; |
iov[i].iov_len = a->item[i].u.string->len; |
} else { |
iov++; |
iovcnt--; |
} |
} |
|
for(written = 0; iovcnt; check_signals(0,0,0)) { |
int fd = FD; |
int e; |
int cnt = iovcnt; |
#ifdef HAVE_PIKE_SEND_FD |
int *fd_info = NULL; |
int num_fds = 0; |
if (THIS->fd_info && (num_fds = THIS->fd_info[1])) { |
fd_info = THIS->fd_info; |
THIS->fd_info = NULL; |
} |
#endif |
THREADS_ALLOW(); |
|
#ifdef IOV_MAX |
if (cnt > IOV_MAX) cnt = IOV_MAX; |
#endif |
|
#ifdef MAX_IOVEC |
if (cnt > MAX_IOVEC) cnt = MAX_IOVEC; |
#endif |
#ifdef HAVE_PIKE_SEND_FD |
if (fd_info) { |
i = writev_fds(fd, iov, cnt, fd_info + 2, num_fds); |
} else |
#endif |
i = writev(fd, iov, cnt); |
THREADS_DISALLOW(); |
|
|
|
|
e=errno; |
check_threads_etc(); |
|
if(i<0) |
{ |
#ifdef HAVE_PIKE_SEND_FD |
if (fd_info) { |
restore_fd_info(fd_info); |
} |
#endif |
switch(e) |
{ |
default: |
free(iovbase); |
ERRNO=errno=e; |
pop_n_elems(args); |
if (!written) { |
push_int(-1); |
} else { |
push_int(written); |
} |
|
THIS->box.revents &= ~(PIKE_BIT_FD_WRITE|PIKE_BIT_FD_WRITE_OOB); |
return; |
|
case EINTR: continue; |
case EWOULDBLOCK: break; |
|
} |
break; |
}else{ |
written += i; |
|
#ifdef HAVE_PIKE_SEND_FD |
if (fd_info) { |
THIS->fd_info = fd_info; |
if (i) { |
do_close_fd_info(THIS->fd_info = fd_info); |
} |
} |
#endif |
|
|
if(THIS->open_mode & FILE_NONBLOCKING) |
break; |
|
while(i) { |
if ((ptrdiff_t)iov->iov_len <= i) { |
i -= iov->iov_len; |
iov++; |
iovcnt--; |
} else { |
|
iov->iov_base = ((char *) iov->iov_base) + i; |
iov->iov_len -= i; |
i = 0; |
} |
} |
} |
#ifdef _REENTRANT |
if (FD<0) { |
free(iovbase); |
Pike_error("File closed while in file->write.\n"); |
} |
#endif |
} |
|
free(iovbase); |
|
|
THIS->box.revents &= ~(PIKE_BIT_FD_WRITE|PIKE_BIT_FD_WRITE_OOB); |
|
if(!SAFE_IS_ZERO(& THIS->event_cbs[PIKE_FD_WRITE])) |
ADD_FD_EVENTS (THIS, PIKE_BIT_FD_WRITE); |
ERRNO=0; |
|
pop_stack(); |
push_int(written); |
return; |
} |
#endif /* HAVE_WRITEV */ |
} |
|
|
|
if(args > 1) |
{ |
f_sprintf(args); |
args=1; |
} |
|
str=Pike_sp[-args].u.string; |
if(str->size_shift) |
Pike_error("Stdio.File->write(): cannot output wide strings.\n"); |
|
for(written=0;written < str->len;check_signals(0,0,0)) |
{ |
int fd=FD; |
int e; |
#ifdef HAVE_PIKE_SEND_FD |
int *fd_info = NULL; |
int num_fds = 0; |
|
if (THIS->fd_info && (num_fds = THIS->fd_info[1])) { |
fd_info = THIS->fd_info; |
THIS->fd_info = NULL; |
} |
#endif |
THREADS_ALLOW(); |
#ifdef HAVE_PIKE_SEND_FD |
if (fd_info) { |
struct iovec iov; |
iov.iov_base = str->str + written; |
iov.iov_len = str->len - written; |
i = writev_fds(fd, &iov, 1, fd_info + 2, num_fds); |
} else |
#endif |
i=fd_write(fd, str->str + written, str->len - written); |
e=errno; |
THREADS_DISALLOW(); |
|
check_threads_etc(); |
|
if (!(THIS->open_mode & FILE_NONBLOCKING)) |
INVALIDATE_CURRENT_TIME(); |
|
if(i<0) |
{ |
#ifdef HAVE_PIKE_SEND_FD |
if (fd_info) { |
restore_fd_info(fd_info); |
} |
#endif |
switch(e) |
{ |
default: |
ERRNO=errno=e; |
pop_n_elems(args); |
if (!written) { |
push_int(-1); |
} else { |
push_int64(written); |
} |
|
THIS->box.revents &= ~(PIKE_BIT_FD_WRITE|PIKE_BIT_FD_WRITE_OOB); |
return; |
|
case EINTR: continue; |
case EWOULDBLOCK: break; |
|
} |
break; |
}else{ |
written+=i; |
|
#ifdef HAVE_PIKE_SEND_FD |
if (i && fd_info) { |
do_close_fd_info(THIS->fd_info = fd_info); |
} |
#endif |
|
if(THIS->open_mode & FILE_NONBLOCKING) |
break; |
} |
#ifdef _REENTRANT |
if(FD<0) Pike_error("File closed while in file->write.\n"); |
#endif |
} |
|
#ifdef _REENTRANT |
|
if(FD<0) Pike_error("File closed while in file->write.\n"); |
#endif |
|
* again for buffer space available after the write above. Not that |
* bad - it will get through in a later backend round. */ |
THIS->box.revents &= ~(PIKE_BIT_FD_WRITE|PIKE_BIT_FD_WRITE_OOB); |
|
if(!SAFE_IS_ZERO(& THIS->event_cbs[PIKE_FD_WRITE])) |
ADD_FD_EVENTS (THIS, PIKE_BIT_FD_WRITE); |
ERRNO=0; |
|
pop_n_elems(args); |
push_int64(written); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_write_oob(INT32 args) |
{ |
ptrdiff_t written, i; |
struct pike_string *str; |
|
if(args<1 || TYPEOF(Pike_sp[-args]) != PIKE_T_STRING) |
SIMPLE_ARG_TYPE_ERROR("write_oob",1,"string"); |
|
if(args > 1) |
{ |
f_sprintf(args); |
args=1; |
} |
|
if(FD < 0) |
Pike_error("File not open for write_oob.\n"); |
|
written=0; |
str=Pike_sp[-args].u.string; |
if(str->size_shift) |
Pike_error("Stdio.File->write_oob(): cannot output wide strings.\n"); |
|
while(written < str->len) |
{ |
int fd=FD; |
int e; |
THREADS_ALLOW(); |
i = fd_send(fd, str->str + written, str->len - written, MSG_OOB); |
e=errno; |
THREADS_DISALLOW(); |
|
check_threads_etc(); |
|
#ifdef _REENTRANT |
if(FD<0) Pike_error("File closed while in file->write_oob.\n"); |
#endif |
|
if(i<0) |
{ |
switch(e) |
{ |
default: |
ERRNO=errno=e; |
pop_n_elems(args); |
if (!written) { |
push_int(-1); |
} else { |
push_int64(written); |
} |
|
THIS->box.revents &= ~(PIKE_BIT_FD_WRITE|PIKE_BIT_FD_WRITE_OOB); |
return; |
|
case EINTR: continue; |
case EWOULDBLOCK: break; |
} |
break; |
}else{ |
written+=i; |
|
|
if(THIS->open_mode & FILE_NONBLOCKING) |
break; |
} |
} |
|
|
|
|
THIS->box.revents &= ~(PIKE_BIT_FD_WRITE|PIKE_BIT_FD_WRITE_OOB); |
|
if(!SAFE_IS_ZERO(& THIS->event_cbs[PIKE_FD_WRITE_OOB])) |
ADD_FD_EVENTS (THIS, PIKE_BIT_FD_WRITE_OOB); |
ERRNO=0; |
|
if (!(THIS->open_mode & FILE_NONBLOCKING)) |
INVALIDATE_CURRENT_TIME(); |
|
pop_n_elems(args); |
push_int64(written); |
} |
|
#ifdef HAVE_PIKE_SEND_FD |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_send_fd(INT32 args) |
{ |
int other_fd; |
struct object *o = NULL; |
struct my_file *f = NULL; |
int *fd_info = NULL; |
|
if(args<1 || (TYPEOF(Pike_sp[-args]) != PIKE_T_OBJECT) || |
!(o = Pike_sp[-args].u.object)->prog || |
(o->prog->inherits[SUBTYPEOF(Pike_sp[-args])].prog != file_program)) |
SIMPLE_ARG_TYPE_ERROR("send_fd", 1, "Stdio.Fd"); |
|
f = (struct my_file *) |
(o->storage + o->prog->inherits[SUBTYPEOF(Pike_sp[-args])].storage_offset); |
|
if (f->box.fd == -1) { |
Pike_error("Bad argument 1 to Stdio.File->send_fd(): " |
"File descriptor not open.\n"); |
} |
|
if(FD < 0) |
Pike_error("File not open for write.\n"); |
|
if (!(THIS->open_mode & fd_SEND_FD)) |
Pike_error("send_fd() not supported on this file.\n"); |
|
if (!(fd_info = THIS->fd_info) || (fd_info[0] == fd_info[1]+2)) { |
int *fds; |
if (fd_info) { |
fds = realloc(fd_info, fd_info[0]*2*sizeof(int)); |
if (fds) fds[0] *= 2; |
} else { |
fds = malloc(8*sizeof(int)); |
if (fds) { |
fds[0] = 8; |
fds[1] = 0; |
} |
} |
if (!fds) |
Pike_error("Out of memory.\n"); |
THIS->fd_info = fd_info = fds; |
#ifdef PIKE_DEBUG |
|
{ |
int e; |
for (e = fds[0]-2; e > fds[1]; e--) { |
fds[e + 1] = -1; |
} |
} |
#endif |
} |
|
do { |
other_fd = dup(f->box.fd); |
} while((other_fd < 0) && (errno == EINTR)); |
|
if (other_fd < 0) { |
Pike_error("Out of file descriptors.\n"); |
} |
fd_info[2 + fd_info[1]++] = other_fd; |
} |
|
#endif |
|
#ifdef SO_LINGER |
|
*! |
*! Set the socket linger behaviour on @[close()]. |
*! |
*! @param seconds |
*! @int |
*! @value -1 |
*! Reset to default behaviour. This typically means that |
*! @[close()] will return immediately, but any buffered |
*! data will still be sent if possible. |
*! @value 0 |
*! Terminate the connection immediately on @[close()], |
*! and discard any buffered data. |
*! @value 1..65535 |
*! Have @[close()] wait for at most @[seconds] seconds |
*! for any buffered data to be sent after which the |
*! connection is terminated. |
*! @endint |
*! |
*! @returns |
*! Returns @expr{1@} on success, and @expr{0@} (zero) on failure. |
*! |
*! @note |
*! This operation is only valid on sockets. |
*! |
*! @note |
*! This function was not available in Pike 7.8.775 and earlier. |
*! |
*! @seealso |
*! @[close()] |
*/ |
static void file_linger(INT32 args) |
{ |
int fd = FD; |
int linger = -1; |
struct linger li; |
|
if(fd < 0) |
Pike_error("File not open.\n"); |
|
get_all_args("linger", args, ".%d", &linger); |
|
if ((linger < -1) || (linger > 0xffff)) { |
SIMPLE_ARG_TYPE_ERROR("linger", 1, "int(-1..65535)"); |
} |
|
if (linger == -1) { |
li.l_onoff = 0; |
li.l_linger = 15; |
} else { |
li.l_onoff = 1; |
li.l_linger = linger; |
} |
|
while ((fd_setsockopt(fd, SOL_SOCKET, SO_LINGER, |
(char *)&li, sizeof(li)) < 0) && |
(errno == EINTR)) { |
errno = 0; |
} |
if (errno) { |
ERRNO = errno; |
push_int(0); |
} else { |
push_int(1); |
} |
} |
#endif |
|
#ifdef TCP_NODELAY |
|
*! |
*! Control Nagle's Algorithm (RFC 896) |
*! |
*! @param state |
*! @int |
*! @value 0 |
*! Return to the normal state of using Nagle's Algorithm |
*! @value 1 |
*! (default) Disable Nagling - small writes will not be queued. |
*! @endint |
*! |
*! @returns |
*! Returns @expr{1@} on success, and @expr{0@} (zero) on failure. |
*! |
*! @note |
*! This operation is only valid on sockets. |
*! |
*! @seealso |
*! setsockopt() |
*/ |
static void file_nodelay(INT32 args) |
{ |
int fd = FD; |
int state = 1; |
|
if(fd < 0) |
Pike_error("File not open.\n"); |
|
get_all_args("set_nodelay", args, ".%d", &state); |
|
if (state && state != 1) { |
SIMPLE_BAD_ARG_ERROR("set_nodelay()", 1, "int(0..1)"); |
} |
|
errno = 0; |
while ((fd_setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, |
&state, sizeof(state)) < 0) && |
(errno == EINTR)) { |
errno = 0; |
} |
if (errno) { |
ERRNO = errno; |
push_int(0); |
} else { |
push_int(1); |
} |
} |
#endif |
|
static int do_close(int flags) |
{ |
struct my_file *f = THIS; |
if(FD == -1) return 1; |
ERRNO=0; |
|
flags &= f->open_mode; |
|
switch(flags & (FILE_READ | FILE_WRITE)) |
{ |
case 0: |
return 0; |
|
case FILE_READ: |
if(f->open_mode & FILE_WRITE) |
{ |
SUB_FD_EVENTS (f, PIKE_BIT_FD_READ|PIKE_BIT_FD_READ_OOB|PIKE_BIT_FD_FS_EVENT); |
fd_shutdown(FD, 0); |
f->open_mode &=~ FILE_READ; |
return 0; |
}else{ |
f->flags&=~FILE_NOT_OPENED; |
close_fd(); |
return 1; |
} |
|
case FILE_WRITE: |
if(f->open_mode & FILE_READ) |
{ |
SUB_FD_EVENTS (f, PIKE_BIT_FD_WRITE|PIKE_BIT_FD_WRITE_OOB|PIKE_BIT_FD_FS_EVENT); |
fd_shutdown(FD, 1); |
f->open_mode &=~ FILE_WRITE; |
#ifdef HAVE_PIKE_SEND_FD |
if (f->fd_info) do_close_fd_info(f->fd_info); |
#endif |
return 0; |
}else{ |
f->flags&=~FILE_NOT_OPENED; |
close_fd(); |
return 1; |
} |
|
case FILE_READ | FILE_WRITE: |
f->flags&=~FILE_NOT_OPENED; |
close_fd(); |
return 1; |
|
default: |
Pike_fatal("Bug in switch implementation!\n"); |
UNREACHABLE(return 0); |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
#if defined(HAVE_GRANTPT) || defined(USE_PT_CHMOD) || defined(USE_CHGPT) |
static void file_grantpt( INT32 args ) |
{ |
pop_n_elems(args); |
#if defined(USE_PT_CHMOD) || defined(USE_CHGPT) |
push_static_text("Process.Process"); |
APPLY_MASTER("resolv", 1); |
|
#ifdef USE_PT_CHMOD |
|
push_text(USE_PT_CHMOD); |
push_static_text("4"); |
f_aggregate(2); |
|
|
push_static_text("fds"); |
ref_push_object(Pike_fp->current_object); |
ref_push_object(Pike_fp->current_object); |
f_aggregate(2); |
f_aggregate_mapping(2); |
#else /* USE_CHGPT */ |
|
push_text(USE_CHGPT); |
f_aggregate(1); |
|
|
push_static_text("stdin"); |
ref_push_object(Pike_fp->current_object); |
f_aggregate_mapping(2); |
#endif /* USE_PT_CHMOD */ |
|
apply_svalue(Pike_sp-3, 2); |
apply(Pike_sp[-1].u.object, "wait", 0); |
if(!UNSAFE_IS_ZERO(Pike_sp-1)) { |
Pike_error( |
#ifdef USE_PT_CHMOD |
USE_PT_CHMOD |
#else /* USE_CHGPT */ |
USE_CHGPT |
#endif /* USE_PT_CHMOD */ |
" returned error %d.\n", Pike_sp[-1].u.integer); |
} |
pop_n_elems(3); |
#else /* HAVE_GRANTPT */ |
|
* to the subprocess (aka /usr/lib/pt_chmod). |
*/ |
set_close_on_exec(FD, 0); |
if( grantpt( FD ) ) |
Pike_error("grantpt failed: %s\n", strerror(errno)); |
set_close_on_exec(FD, 1); |
#endif /* USE_PT_CHMOD || USE_CHGPT */ |
push_text( ptsname( FD ) ); |
#ifdef HAVE_UNLOCKPT |
if( unlockpt( FD ) ) |
Pike_error("unlockpt failed: %s\n", strerror(errno)); |
#endif |
} |
#endif /* HAVE_GRANTPT || USE_PT_CHMOD || USE_CHGPT */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_close(INT32 args) |
{ |
int flags; |
if(args) |
{ |
if(TYPEOF(Pike_sp[-args]) != PIKE_T_STRING) |
Pike_error("Bad argument 1 to file->close()\n"); |
flags=parse(Pike_sp[-args].u.string->str); |
}else{ |
flags=FILE_READ | FILE_WRITE; |
} |
|
if (THIS->flags & FILE_LOCK_FD) { |
Pike_error("close() has been temporarily disabled on this file.\n"); |
} |
|
if((THIS->open_mode & ~flags & (FILE_READ|FILE_WRITE)) && flags) |
{ |
if(!(THIS->open_mode & fd_CAN_SHUTDOWN)) |
{ |
Pike_error("Cannot close one direction on this file.\n"); |
} |
} |
|
flags=do_close(flags); |
pop_n_elems(args); |
push_int(flags); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_open(INT32 args) |
{ |
int flags,fd; |
int access; |
int err; |
struct pike_string *str, *flag_str; |
close_fd(); |
|
if(args < 2) |
SIMPLE_WRONG_NUM_ARGS_ERROR("open", 2); |
|
if(TYPEOF(Pike_sp[-args]) != PIKE_T_STRING && |
TYPEOF(Pike_sp[-args]) != PIKE_T_INT) |
SIMPLE_ARG_TYPE_ERROR("open", 1, "string|int"); |
|
if(TYPEOF(Pike_sp[1-args]) != PIKE_T_STRING) |
SIMPLE_ARG_TYPE_ERROR("open", 2, "string"); |
|
if (args > 2) |
{ |
if (TYPEOF(Pike_sp[2-args]) != PIKE_T_INT) |
SIMPLE_ARG_TYPE_ERROR("open", 3, "int"); |
access = Pike_sp[2-args].u.integer; |
} else |
access = 00666; |
|
flags = parse((flag_str = Pike_sp[1-args].u.string)->str); |
|
if (TYPEOF(Pike_sp[-args]) == PIKE_T_STRING) |
{ |
str=Pike_sp[-args].u.string; |
|
if (string_has_null(str)) { |
|
ERRNO = errno = ENOENT; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
|
if(!( flags & (FILE_READ | FILE_WRITE))) |
Pike_error("Must open file for at least one of read and write.\n"); |
|
do { |
THREADS_ALLOW_UID(); |
fd=fd_open(str->str,map(flags), access); |
err = errno; |
THREADS_DISALLOW_UID(); |
if ((fd < 0) && (err == EINTR)) |
check_threads_etc(); |
} while(fd < 0 && err == EINTR); |
|
if(!Pike_fp->current_object->prog) |
{ |
if (fd >= 0) |
while (fd_close(fd) && errno == EINTR) {} |
Pike_error("Object destructed in Stdio.File->open()\n"); |
} |
|
if(fd < 0) |
{ |
ERRNO=errno=err; |
} |
else |
{ |
init_fd(fd, flags | fd_query_properties(fd, FILE_CAPABILITIES), 0); |
set_close_on_exec(fd,1); |
} |
} |
else |
{ |
fd=Pike_sp[-args].u.integer; |
if (fd<0) |
Pike_error("Not a valid FD.\n"); |
|
|
|
|
init_fd(fd, flags | low_fd_query_properties(fd), FILE_NOT_OPENED); |
} |
|
pop_n_elems(args); |
push_int(fd>=0); |
} |
|
#ifdef HAVE_OPENAT |
|
*! @decl Stdio.File openat(string filename, string mode, int access) |
*! |
*! Open a file relative to an opened directory. |
*! |
*! @returns |
*! Returns a new file object on success, and @expr{0@} (zero) on failure. |
*! |
*! @note |
*! Not available on all architectures. |
*! |
*! @seealso |
*! @[open()], @[statat()], @[unlinkat()] |
*/ |
static void file_openat(INT32 args) |
{ |
int flags, fd, dir_fd; |
int access = 00666; |
int err; |
struct pike_string *str, *flag_str; |
|
if((dir_fd = FD) < 0) |
Pike_error("File not open.\n"); |
|
get_all_args("openat", args, "%S%S.%d", &str, &flag_str, &access); |
|
flags = parse(flag_str->str); |
|
if (string_has_null(str)) { |
|
ERRNO = errno = ENOENT; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
|
if(!(flags & (FILE_READ | FILE_WRITE))) |
Pike_error("Must open file for at least one of read and write.\n"); |
|
do { |
THREADS_ALLOW_UID(); |
fd = openat(dir_fd, str->str, map(flags), access); |
err = errno; |
THREADS_DISALLOW_UID(); |
if ((fd < 0) && (err == EINTR)) |
check_threads_etc(); |
} while(fd < 0 && err == EINTR); |
|
if(fd < 0) |
{ |
ERRNO=errno=err; |
pop_n_elems(args); |
push_int(0); |
} |
else |
{ |
push_new_fd_object(fd_fd_factory_fun_num, fd, flags, FILE_CAPABILITIES); |
set_close_on_exec(fd, 1); |
stack_pop_n_elems_keep_top(args); |
} |
} |
#endif /* HAVE_OPENAT */ |
|
#if !defined(__NT__) && (defined(HAVE_POSIX_OPENPT) || defined(PTY_MASTER_PATHNAME)) |
|
*! |
*! Open the master end of a pseudo-terminal pair. |
*! |
*! @returns |
*! This function returns @expr{1@} for success, @expr{0@} otherwise. |
*! |
*! @seealso |
*! @[grantpt()] |
*/ |
static void file_openpt(INT32 args) |
{ |
int flags,fd; |
#ifdef HAVE_POSIX_OPENPT |
struct pike_string *flag_str; |
#endif |
close_fd(); |
|
if(args < 1) |
SIMPLE_WRONG_NUM_ARGS_ERROR("openpt", 1); |
|
if(TYPEOF(Pike_sp[-args]) != PIKE_T_STRING) |
SIMPLE_ARG_TYPE_ERROR("openpt", 1, "string"); |
|
#ifdef HAVE_POSIX_OPENPT |
flags = parse((flag_str = Pike_sp[-args].u.string)->str); |
|
if(!( flags & (FILE_READ | FILE_WRITE))) |
Pike_error("Must open file for at least one of read and write.\n"); |
|
do { |
THREADS_ALLOW_UID(); |
fd=posix_openpt(map(flags)); |
THREADS_DISALLOW_UID(); |
if ((fd < 0) && (errno == EINTR)) |
check_threads_etc(); |
} while(fd < 0 && errno == EINTR); |
|
if(!Pike_fp->current_object->prog) |
{ |
if (fd >= 0) |
while (fd_close(fd) && errno == EINTR) {} |
Pike_error("Object destructed in Stdio.File->openpt()\n"); |
} |
|
if(fd < 0) |
{ |
ERRNO=errno; |
} |
else |
{ |
init_fd(fd, flags | fd_query_properties(fd, FILE_CAPABILITIES), 0); |
set_close_on_exec(fd,1); |
} |
pop_n_elems(args); |
push_int(fd>=0); |
#else |
if(args > 1) |
pop_n_elems(args - 1); |
push_text(PTY_MASTER_PATHNAME); |
stack_swap(); |
file_open(2); |
#endif |
} |
#endif |
|
#ifdef HAVE_FSYNC |
|
*! |
*! Flush buffers to disk. |
*! |
*! @returns |
*! |
*! Returns @expr{0@} (zero) and sets errno on failure. |
*! |
*! Returns @expr{1@} on success. |
*/ |
void file_sync(INT32 args) |
{ |
int ret; |
int fd = FD; |
int e; |
|
if(fd < 0) |
Pike_error("File not open.\n"); |
|
pop_n_elems(args); |
|
do { |
THREADS_ALLOW(); |
ret = fsync(fd); |
e = errno; |
THREADS_DISALLOW(); |
check_threads_etc(); |
} while ((ret < 0) && (e == EINTR)); |
|
if (ret < 0) { |
ERRNO = errno = e; |
push_int(0); |
} else { |
push_int(1); |
} |
} |
#endif /* HAVE_FSYNC */ |
|
#if (defined(HAVE_LSEEK64) || defined(__NT__)) |
#define SEEK64 |
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_seek(INT32 args) |
{ |
#ifdef SEEK64 |
INT64 to = 0; |
#else |
off_t to = 0; |
#endif |
int how = SEEK_SET; |
|
if( args < 1) |
SIMPLE_WRONG_NUM_ARGS_ERROR("seek", 1); |
|
#if defined (SEEK64) |
if(is_bignum_object_in_svalue(&Pike_sp[-args])) { |
if (!int64_from_bignum(&to, Pike_sp[-args].u.object)) |
Pike_error ("Bad argument 1 to Stdio.File->seek(). Offset too large.\n"); |
} |
else |
#endif |
if(TYPEOF(Pike_sp[-args]) != PIKE_T_INT) |
SIMPLE_ARG_TYPE_ERROR("seek", 1, "int"); |
else |
to=Pike_sp[-args].u.integer; |
|
if(FD < 0) |
Pike_error("File not open.\n"); |
|
|
if( args == 2 && TYPEOF(Pike_sp[-args+1]) == PIKE_T_STRING ) |
{ |
how = Pike_sp[-args+1].u.string->str[0]; |
} |
else |
{ |
if(args>1) |
{ |
if(TYPEOF(Pike_sp[-args+1]) != PIKE_T_INT) |
SIMPLE_ARG_TYPE_ERROR("seek", 2, "int"); |
to *= Pike_sp[-args+1].u.integer; |
} |
if(args>2) |
{ |
if(TYPEOF(Pike_sp[-args+2]) != PIKE_T_INT) |
SIMPLE_ARG_TYPE_ERROR("seek", 3, "int"); |
to += Pike_sp[-args+2].u.integer; |
} |
|
if( to < 0 ) |
how = SEEK_END; |
} |
ERRNO=0; |
|
#if defined(HAVE_LSEEK64) && !defined(__NT__) |
to = lseek64(FD,to,how); |
#else |
to = fd_lseek(FD,to,how); |
#endif |
if(to<0) ERRNO=errno; |
|
pop_n_elems(args); |
push_int64(to); |
} |
|
#if (defined(HAVE_LSEEK64) || defined(__NT__)) |
#define TELL64 |
#endif |
|
|
|
|
|
|
|
|
static void file_tell(INT32 args) |
{ |
#ifdef TELL64 |
INT64 to; |
#else |
off_t to; |
#endif |
|
if(FD < 0) |
Pike_error("File not open.\n"); |
|
ERRNO=0; |
|
#if defined(HAVE_LSEEK64) && !defined(__NT__) |
to = lseek64(FD, 0L, SEEK_CUR); |
#else |
to = fd_lseek(FD, 0L, SEEK_CUR); |
#endif |
|
if(to<0) ERRNO=errno; |
|
pop_n_elems(args); |
push_int64(to); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_truncate(INT32 args) |
{ |
INT64 len = 0; |
int res; |
|
if(args!=1) |
SIMPLE_WRONG_NUM_ARGS_ERROR("truncate", 1); |
|
#if defined (HAVE_FTRUNCATE64) || SIZEOF_OFF_T > SIZEOF_INT_TYPE |
if(is_bignum_object_in_svalue(&Pike_sp[-args])) { |
if (!int64_from_bignum(&len, Pike_sp[-args].u.object)) |
Pike_error ("Bad argument 1 to Stdio.File->truncate(). Length too large.\n"); |
} |
else |
#endif |
if(TYPEOF(Pike_sp[-args]) != PIKE_T_INT) |
SIMPLE_ARG_TYPE_ERROR("truncate", 1, "int"); |
else |
len = Pike_sp[-args].u.integer; |
|
if(FD < 0) |
Pike_error("File not open.\n"); |
|
ERRNO=0; |
#ifdef HAVE_FTRUNCATE64 |
res = ftruncate64 (FD, len); |
#else |
res=fd_ftruncate(FD, len); |
#endif |
|
pop_n_elems(args); |
|
if(res<0) |
ERRNO=errno; |
|
push_int(!res); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_stat(INT32 args) |
{ |
int fd; |
PIKE_STAT_T s; |
int tmp; |
|
if(FD < 0) |
Pike_error("File not open.\n"); |
|
pop_n_elems(args); |
|
fd=FD; |
|
retry: |
THREADS_ALLOW(); |
tmp=fd_fstat(fd, &s); |
THREADS_DISALLOW(); |
|
if(tmp < 0) |
{ |
if(errno == EINTR) { |
check_threads_etc(); |
goto retry; |
} |
ERRNO=errno; |
push_int(0); |
}else{ |
ERRNO=0; |
push_stat(&s); |
} |
} |
|
|
#ifdef HAVE_FSTATAT |
|
*! |
*! Get status for a file relative an open directory. |
*! |
*! This function returns the same information as the function |
*! @[file_stat()], but relative to the file it is called in. If file is not |
*! an open file, @expr{0@} (zero) is returned. Zero is also returned |
*! if file is a pipe or socket. |
*! |
*! @returns |
*! See @[file_stat()] for a description of the return value. |
*! |
*! @note |
*! Not available on all architectures. |
*! |
*! @seealso |
*! @[file_stat()], @[stat()], @[openat()], @[unlinkat()] |
*/ |
static void file_statat(INT32 args) |
{ |
int fd; |
PIKE_STAT_T s; |
int tmp; |
struct pike_string *path; |
int nofollow = 0; |
|
if(FD < 0) |
Pike_error("File not open.\n"); |
|
get_all_args("statat", args, "%S.%d", &path, &nofollow); |
|
if (string_has_null(path)) { |
|
ERRNO = errno = ENOENT; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
|
fd=FD; |
|
retry: |
THREADS_ALLOW(); |
tmp = fstatat(fd, path->str, &s, nofollow?AT_SYMLINK_NOFOLLOW:0); |
THREADS_DISALLOW(); |
|
if(tmp < 0) |
{ |
if(errno == EINTR) { |
check_threads_etc(); |
goto retry; |
} |
ERRNO=errno; |
push_int(0); |
}else{ |
ERRNO=0; |
push_stat(&s); |
} |
stack_pop_n_elems_keep_top(args); |
} |
|
#ifdef HAVE_UNLINKAT |
|
*! |
*! Remove a file or directory relative to an open file. |
*! |
*! @returns |
*! Returns @expr{0@} (zero) on failure, @expr{1@} otherwise. |
*! |
*! @seealso |
*! @[rm()], @[openat()], @[statat()] |
*/ |
static void file_unlinkat(INT32 args) |
{ |
int dir_fd; |
PIKE_STAT_T st; |
struct pike_string *str; |
INT32 i; |
|
destruct_objects_to_destruct(); |
|
if((dir_fd = FD) < 0) |
Pike_error("File not open.\n"); |
|
get_all_args("unlinkat", args, "%S", &str); |
|
if (string_has_null(str)) { |
|
ERRNO = ENOENT; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
|
THREADS_ALLOW_UID(); |
do { |
i = fstatat(dir_fd, str->str, &st, AT_SYMLINK_NOFOLLOW); |
} while ((i < 0) && (errno == EINTR)); |
if (i >= 0) { |
int flag = 0; |
if ((st.st_mode & S_IFMT) == S_IFDIR) { |
flag = AT_REMOVEDIR; |
} |
do { |
i = unlinkat(dir_fd, str->str, flag); |
} while ((i < 0) && (errno == EINTR)); |
} |
THREADS_DISALLOW_UID(); |
pop_n_elems(args); |
if (i < 0) { |
ERRNO = errno; |
push_int(0); |
} else { |
push_int(1); |
} |
} |
#endif /* HAVE_UNLINKAT */ |
#endif /* HAVE_FSTATAT */ |
|
#if defined(HAVE_FDOPENDIR) && defined(HAVE_OPENAT) |
|
*! |
*! Get directory contents relative to an open directory. |
*! |
*! @param path |
*! Path relative to the open directory. Defaults to the |
*! directory itself. |
*! |
*! @returns |
*! Returns an array of filenames. |
*! |
*! @note |
*! Not available on all architectures. |
*! |
*! @seealso |
*! @[predef::get_dir()], @[statat()], @[openat()], @[unlinkat()] |
*/ |
static void file_get_dir(INT32 args) |
{ |
int fd; |
int dfd; |
struct pike_string *path = NULL; |
ptrdiff_t name_max = -1; |
DIR *dir = NULL; |
|
if(FD < 0) |
Pike_error("File not open.\n"); |
|
get_all_args("get_dir", args, ".%S", &path); |
|
if (path && string_has_null(path)) { |
fprintf(stderr, "NULL\n"); |
|
ERRNO = errno = ENOENT; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
|
fd = FD; |
dfd = -1; |
|
while(1) { |
THREADS_ALLOW_UID(); |
if (!path) { |
dfd = dup(fd); |
} else { |
dfd = openat(fd, path->str, O_RDONLY); |
} |
THREADS_DISALLOW_UID(); |
|
if ((dfd == -1) && (errno == EINTR)) { |
check_threads_etc(); |
continue; |
} |
break; |
} |
|
if (dfd == -1) { |
ERRNO = errno; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
|
#ifdef HAVE_FPATHCONF |
name_max = fpathconf(dfd, _PC_NAME_MAX); |
#endif /* HAVE_FPATHCONF */ |
|
if (!(dir = fdopendir(dfd))) { |
ERRNO = errno; |
close(dfd); |
pop_n_elems(args); |
push_int(0); |
return; |
} |
|
|
|
|
|
low_get_dir(dir, name_max); |
} |
#endif /* HAVE_FDOPENDIR && HAVE_OPENAT */ |
|
#if defined(HAVE_FSETXATTR) && defined(HAVE_FGETXATTR) && defined(HAVE_FLISTXATTR) |
|
|
|
|
|
|
static void file_listxattr(INT32 args) |
{ |
char buffer[1024]; |
char *ptr = buffer; |
int mfd = FD, do_free = 0; |
ssize_t res; |
|
pop_n_elems( args ); |
|
THREADS_ALLOW(); |
do { |
#ifdef HAVE_DARWIN_XATTR |
res = flistxattr( mfd, buffer, sizeof(buffer), 0 ); |
#else |
res = flistxattr( mfd, buffer, sizeof(buffer) ); |
#endif /* HAVE_DARWIN_XATTR */ |
} while( res < 0 && errno == EINTR ); |
THREADS_DISALLOW(); |
|
if( res<0 && errno==ERANGE ) |
{ |
|
int blen = DIRECT_BUFSIZE; |
do_free = 1; |
ptr = xalloc( 1 ); |
do { |
char *tmp = realloc( ptr, blen ); |
if( !tmp ) |
break; |
ptr = tmp; |
THREADS_ALLOW(); |
do { |
#ifdef HAVE_DARWIN_XATTR |
res = flistxattr( mfd, ptr, blen, 0 ); |
#else |
res = flistxattr( mfd, ptr, blen ); |
#endif /* HAVE_DARWIN_XATTR */ |
} while( res < 0 && errno == EINTR ); |
THREADS_DISALLOW(); |
blen *= 2; |
} |
while( (res < 0) && (errno == ERANGE) ); |
} |
|
if( res < 0 ) |
{ |
if( do_free ) |
free(ptr); |
push_int(0); |
ERRNO=errno; |
return; |
} |
|
push_string( make_shared_binary_string( ptr, res ) ); |
ptr[0]=0; |
push_string( make_shared_binary_string( ptr, 1 ) ); |
o_divide(); |
push_empty_string(); |
f_aggregate(1); |
o_subtract(); |
|
if( do_free ) |
free( ptr ); |
} |
|
|
|
|
|
static void file_getxattr(INT32 args) |
{ |
char buffer[1024]; |
char *ptr = buffer; |
int mfd = FD, do_free = 0; |
ssize_t res; |
char *name; |
|
get_all_args( "getxattr", args, "%s", &name ); |
|
THREADS_ALLOW(); |
do { |
#ifdef HAVE_DARWIN_XATTR |
res = fgetxattr( mfd, name, buffer, sizeof(buffer), 0, 0 ); |
#else |
res = fgetxattr( mfd, name, buffer, sizeof(buffer) ); |
#endif /* HAVE_DARWIN_XATTR */ |
} while( res < 0 && errno == EINTR ); |
THREADS_DISALLOW(); |
|
if( res<0 && errno==ERANGE ) |
{ |
|
int blen = DIRECT_BUFSIZE; |
do_free = 1; |
ptr = xalloc( 1 ); |
do { |
char *tmp = realloc( ptr, blen ); |
if( !tmp ) |
break; |
ptr = tmp; |
THREADS_ALLOW(); |
do { |
#ifdef HAVE_DARWIN_XATTR |
res = fgetxattr( mfd, name, ptr, blen, 0, 0 ); |
#else |
res = fgetxattr( mfd, name, ptr, blen ); |
#endif /* HAVE_DARWIN_XATTR */ |
} while( res < 0 && errno == EINTR ); |
THREADS_DISALLOW(); |
blen *= 2; |
} |
while( (res < 0) && (errno == ERANGE) ); |
} |
|
if( res < 0 ) |
{ |
if( do_free && ptr ) |
free(ptr); |
push_int(0); |
ERRNO=errno; |
return; |
} |
|
push_string( make_shared_binary_string( ptr, res ) ); |
if( do_free && ptr ) |
free( ptr ); |
} |
|
|
|
|
|
static void file_removexattr( INT32 args ) |
{ |
char *name; |
int mfd = FD; |
int rv; |
get_all_args( "removexattr", args, "%s", &name ); |
THREADS_ALLOW(); |
#ifdef HAVE_DARWIN_XATTR |
while( ((rv=fremovexattr( mfd, name, 0 )) < 0) && (errno == EINTR)) |
; |
#else |
while( ((rv=fremovexattr( mfd, name )) < 0) && (errno == EINTR)) |
; |
#endif /* HAVE_DARWIN_XATTR */ |
THREADS_DISALLOW(); |
|
pop_n_elems(args); |
if( rv < 0 ) |
{ |
ERRNO=errno; |
push_int(0); |
} |
else |
{ |
push_int(1); |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_setxattr( INT32 args ) |
{ |
char *ind; |
struct pike_string *val; |
int flags; |
int rv; |
int mfd = FD; |
get_all_args( "setxattr", args, "%s%S%d", &ind, &val, &flags ); |
THREADS_ALLOW(); |
#ifdef HAVE_DARWIN_XATTR |
while( ((rv=fsetxattr( mfd, ind, val->str, |
(val->len<<val->size_shift), 0, flags )) < 0) && |
(errno == EINTR)) |
; |
#else |
while( ((rv=fsetxattr( mfd, ind, val->str, |
(val->len<<val->size_shift), flags )) < 0) && |
(errno == EINTR)) |
; |
#endif /* HAVE_DARWIN_XATTR */ |
THREADS_DISALLOW(); |
pop_n_elems(args); |
if( rv < 0 ) |
{ |
ERRNO=errno; |
push_int(0); |
} |
else |
push_int(1); |
} |
#endif |
|
|
|
|
|
static void file_errno(INT32 args) |
{ |
pop_n_elems(args); |
push_int(ERRNO); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_mode(INT32 args) |
{ |
pop_n_elems(args); |
push_int(THIS->open_mode); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_set_backend (INT32 args) |
{ |
struct my_file *f = THIS; |
struct Backend_struct *backend; |
|
if (args!=1) |
SIMPLE_WRONG_NUM_ARGS_ERROR ("set_backend", 1); |
if (TYPEOF(Pike_sp[-args]) != PIKE_T_OBJECT) |
SIMPLE_ARG_TYPE_ERROR ("set_backend", 1, "Pike.Backend"); |
backend = get_storage (Pike_sp[-args].u.object, Backend_program); |
if (!backend) |
SIMPLE_ARG_TYPE_ERROR ("set_backend", 1, "Pike.Backend"); |
|
|
|
#ifdef __NT__ |
if ((THIS->box.fd >= 0) && |
!(fd_query_properties(THIS->box.fd, fd_CAN_NONBLOCK) & fd_CAN_NONBLOCK)) { |
Pike_error("set_backend() on non-socket!\n"); |
} |
#endif /* __NT__ */ |
|
if (f->box.backend) |
change_backend_for_box (&f->box, backend); |
else |
INIT_FD_CALLBACK_BOX (&f->box, backend, f->box.ref_obj, |
f->box.fd, 0, got_fd_event, f->box.flags); |
} |
|
|
|
|
|
|
|
|
static void file_query_backend (INT32 args) |
{ |
pop_n_elems (args); |
ref_push_object (get_backend_obj (THIS->box.backend ? THIS->box.backend : |
default_backend)); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_set_nonblocking(INT32 UNUSED(args)) |
{ |
if(FD < 0) Pike_error("File not open.\n"); |
|
if(!(THIS->open_mode & fd_CAN_NONBLOCK)) |
Pike_error("This file does not support nonblocking operation.\n"); |
|
if(set_nonblocking(FD,1)) |
{ |
ERRNO=errno; |
push_int (ERRNO); |
f_strerror (1); |
Pike_error("Stdio.File->set_nonblocking() failed: %S\n", |
Pike_sp[-1].u.string); |
} |
|
THIS->open_mode |= FILE_NONBLOCKING; |
} |
|
|
|
|
|
|
|
|
|
|
static void file_set_blocking(INT32 UNUSED(args)) |
{ |
if(FD >= 0) |
{ |
set_nonblocking(FD,0); |
THIS->open_mode &=~ FILE_NONBLOCKING; |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_set_close_on_exec(INT32 args) |
{ |
if(args != 1) |
SIMPLE_WRONG_NUM_ARGS_ERROR("set_close_on_exec", 1); |
if(FD <0) |
Pike_error("File not open.\n"); |
|
if(UNSAFE_IS_ZERO(Pike_sp-args)) |
{ |
my_set_close_on_exec(FD,0); |
}else{ |
my_set_close_on_exec(FD,1); |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_is_open (INT32 args) |
{ |
|
|
pop_n_elems (args); |
push_int (FD >= 0); |
} |
|
|
|
|
|
static void file_query_fd(INT32 args) |
{ |
if(FD < 0) |
Pike_error("File not open.\n"); |
|
pop_n_elems(args); |
push_int(FD); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
static void file_release_fd(INT32 args) |
{ |
file_query_fd(args); |
change_fd_for_box(&THIS->box, -1); |
} |
|
|
|
|
|
|
|
|
|
|
|
static void file_take_fd(INT32 args) |
{ |
if (args != 1) |
SIMPLE_WRONG_NUM_ARGS_ERROR ("take_fd", 1); |
if (TYPEOF(Pike_sp[-args]) != PIKE_T_INT) |
SIMPLE_ARG_TYPE_ERROR ("take_fd", 0, "int"); |
change_fd_for_box(&THIS->box, Pike_sp[-args].u.integer); |
} |
|
PMOD_EXPORT struct object *file_make_object_from_fd(int fd, int mode, int guess) |
{ |
struct object *o; |
struct my_file *f; |
if (Pike_fp->context->prog == file_program) { |
|
|
|
ONERROR err; |
SET_ONERROR(err, do_close_fd, (ptrdiff_t) fd); |
o = clone_object_from_object(Pike_fp->current_object, 0); |
UNSET_ONERROR(err); |
if (!o->prog) return NULL; |
f = (struct my_file *)(o->storage + Pike_fp->context->storage_offset); |
f->flags |= (THIS->flags & FILE_HAVE_RECV_FD); |
} else { |
|
o = low_clone(file_program); |
f = (struct my_file *) o->storage + file_program->inherits->storage_offset; |
call_c_initializers(o); |
} |
change_fd_for_box(&f->box, fd); |
if (fd >= 0) { |
f->open_mode=mode | fd_query_properties(fd, guess); |
#ifdef PIKE_DEBUG |
debug_check_fd_not_in_use (fd); |
#endif |
} else { |
f->open_mode = 0; |
} |
return o; |
} |
|
PMOD_EXPORT void push_new_fd_object(int factory_fun_num, |
int fd, int mode, int guess) |
{ |
struct object *o = NULL; |
struct my_file *f; |
ONERROR err; |
struct inherit *inh; |
struct identifier *i; |
|
SET_ONERROR(err, do_close_fd, (ptrdiff_t) fd); |
apply_current(factory_fun_num, 0); |
if ((TYPEOF(Pike_sp[-1]) != PIKE_T_OBJECT) || |
!(o = Pike_sp[-1].u.object)->prog || |
((inh = &o->prog->inherits[SUBTYPEOF(Pike_sp[-1])])->prog != file_program)) { |
Pike_error("Invalid return value from fd_factory(). " |
"Expected object(is Stdio.Fd).\n"); |
} |
f = (struct my_file *)(o->storage + inh->storage_offset); |
if (f->box.fd != -1) { |
Pike_error("Invalid return value from fd_factory(). " |
"Expected unopened object(is Stdio.Fd). fd:%d\n", |
f->box.fd); |
} |
UNSET_ONERROR(err); |
change_fd_for_box(&f->box, fd); |
if (fd >= 0) { |
f->open_mode=mode | fd_query_properties(fd, guess); |
#ifdef PIKE_DEBUG |
debug_check_fd_not_in_use (fd); |
#endif |
} else { |
f->open_mode = 0; |
} |
|
i = ID_FROM_INT(o->prog, fd_receive_fd_fun_num + inh->identifier_level); |
if (((i->identifier_flags & IDENTIFIER_TYPE_MASK) == |
IDENTIFIER_PIKE_FUNCTION) && (i->func.offset != -1)) { |
|
f->flags |= FILE_HAVE_RECV_FD; |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_set_buffer(INT32 args) |
{ |
INT32 bufsize; |
int flags; |
|
if(FD==-1) |
Pike_error("Stdio.File->set_buffer() on closed file.\n"); |
if(args<1) |
SIMPLE_WRONG_NUM_ARGS_ERROR("set_buffer", 1); |
if(args>2) |
SIMPLE_WRONG_NUM_ARGS_ERROR("set_buffer", 2); |
if(TYPEOF(Pike_sp[-args]) != PIKE_T_INT) |
SIMPLE_ARG_TYPE_ERROR("set_buffer", 1, "int"); |
|
bufsize=Pike_sp[-args].u.integer; |
if(bufsize < 0) |
Pike_error("Bufsize must be larger than zero.\n"); |
|
if(args>1) |
{ |
if(TYPEOF(Pike_sp[1-args]) != PIKE_T_STRING) |
SIMPLE_ARG_TYPE_ERROR("set_buffer", 2, "string"); |
flags=parse(Pike_sp[1-args].u.string->str); |
}else{ |
flags=FILE_READ | FILE_WRITE; |
} |
|
#ifdef SOCKET_BUFFER_MAX |
#if SOCKET_BUFFER_MAX |
if(bufsize>SOCKET_BUFFER_MAX) bufsize=SOCKET_BUFFER_MAX; |
#endif |
flags &= THIS->open_mode; |
#ifdef SO_RCVBUF |
if(flags & FILE_READ) |
{ |
int tmp=bufsize; |
fd_setsockopt(FD,SOL_SOCKET, SO_RCVBUF, (char *)&tmp, sizeof(tmp)); |
} |
#endif /* SO_RCVBUF */ |
|
#ifdef SO_SNDBUF |
if(flags & FILE_WRITE) |
{ |
int tmp=bufsize; |
fd_setsockopt(FD,SOL_SOCKET, SO_SNDBUF, (char *)&tmp, sizeof(tmp)); |
} |
#endif /* SO_SNDBUF */ |
#endif |
} |
|
#if !defined(HAVE_SOCKETPAIR) || !defined(UNIX_SOCKETS_WORKS_WITH_SHUTDOWN) |
|
|
#undef UNIX_SOCKETS_WORKS_WITH_SHUTDOWN |
|
#ifndef AF_UNIX |
#define AF_UNIX 4711 |
#endif /* AF_UNIX */ |
|
#ifdef SOCKETPAIR_DEBUG |
#define SP_DEBUG(X) fprintf X |
#else /* !SOCKETPAIR_DEBUG */ |
#define SP_DEBUG(X) |
#endif /* SOCKETPAIR_DEBUG */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef errno |
extern int errno; |
#endif /* !errno */ |
|
static int socketpair_fd = -1; |
int my_socketpair(int family, int type, int protocol, int sv[2]) |
{ |
static struct sockaddr_in my_addr; |
struct sockaddr_in addr,addr2; |
int retries=0; |
|
|
|
ACCEPT_SIZE_T len; |
|
memset(&addr,0,sizeof(struct sockaddr_in)); |
|
|
if(family != AF_UNIX || type != SOCK_STREAM) |
{ |
errno=EINVAL; |
return -1; |
} |
|
sv[0] = -1; |
|
if((sv[1]=fd_socket(AF_INET, SOCK_STREAM, 0)) <0) { |
SP_DEBUG((stderr, "my_socketpair:fd_socket() failed, errno:%d (2)\n", |
errno)); |
return -1; |
} |
|
|
|
if(socketpair_fd==-1) |
{ |
if((socketpair_fd=fd_socket(AF_INET, SOCK_STREAM, 0)) < 0) { |
SP_DEBUG((stderr, "my_socketpair:fd_socket() failed, errno:%d\n", |
errno)); |
return -1; |
} |
|
|
|
|
|
|
memset(&my_addr,0,sizeof(struct sockaddr_in)); |
my_addr.sin_family=AF_INET; |
my_addr.sin_addr.s_addr=htonl(INADDR_ANY); |
my_addr.sin_port=htons(0); |
|
|
|
if(fd_bind(socketpair_fd, (struct sockaddr *)&my_addr, sizeof(addr)) < 0) |
{ |
SP_DEBUG((stderr, "my_socketpair:fd_bind() failed, errno:%d\n", |
errno)); |
while (fd_close(socketpair_fd) && errno == EINTR) {} |
socketpair_fd=-1; |
return -1; |
} |
|
|
len = sizeof(my_addr); |
if(fd_getsockname(socketpair_fd,(struct sockaddr *)&my_addr,&len) < 0) |
{ |
SP_DEBUG((stderr, "my_socketpair:fd_getsockname() failed, errno:%d\n", |
errno)); |
while (fd_close(socketpair_fd) && errno == EINTR) {} |
socketpair_fd=-1; |
return -1; |
} |
|
|
if(fd_listen(socketpair_fd, 5) < 0) |
{ |
SP_DEBUG((stderr, "my_socketpair:fd_listen() failed, errno:%d\n", |
errno)); |
while (fd_close(socketpair_fd) && errno == EINTR) {} |
socketpair_fd=-1; |
return -1; |
} |
|
set_close_on_exec(socketpair_fd, 1); |
|
set_nonblocking(socketpair_fd, 1); |
|
my_addr.sin_addr.s_addr=inet_addr("127.0.0.1"); |
} |
|
|
|
retry_connect: |
retries++; |
if(fd_connect(sv[1], (struct sockaddr *)&my_addr, sizeof(addr)) < 0) |
{ |
|
SP_DEBUG((stderr, "my_socketpair:fd_connect() failed, errno:%d (%d)\n", |
errno, EWOULDBLOCK)); |
if((errno != EWOULDBLOCK) |
#ifdef WSAEWOULDBLOCK |
&& (errno != WSAEWOULDBLOCK) |
#endif /* WSAEWOULDBLOCK */ |
) |
{ |
int tmp2; |
for(tmp2=0;tmp2<20;tmp2++) |
{ |
int tmp; |
ACCEPT_SIZE_T len2; |
|
len2=sizeof(addr); |
tmp=fd_accept(socketpair_fd,(struct sockaddr *)&addr,&len2); |
|
if(tmp!=-1) { |
SP_DEBUG((stderr, "my_socketpair:fd_accept() failed, errno:%d\n", |
errno)); |
while (fd_close(tmp) && errno == EINTR) {} |
} |
else |
break; |
} |
if(retries > 20) return -1; |
goto retry_connect; |
} |
} |
|
|
|
|
|
|
|
|
|
do |
{ |
ACCEPT_SIZE_T len3; |
|
if (sv[0] >= 0) { |
|
while (fd_close(sv[0]) && errno == EINTR) |
; |
} |
|
len3=sizeof(addr); |
retry_accept: |
retries++; |
{ |
#ifdef HAVE_AND_USE_POLL |
struct pollfd fds; |
int timeout = 1; |
|
fds.fd = socketpair_fd; |
fds.events = POLLIN; |
fds.revents = 0; |
|
poll(&fds, 1, timeout); |
#else |
fd_set fds; |
|
struct timeval tv; |
tv.tv_usec=5; |
tv.tv_sec=0; |
|
fd_FD_ZERO(&fds); |
fd_FD_SET(socketpair_fd, &fds); |
|
fd_select(socketpair_fd + 1, &fds, 0, 0, &tv); |
#endif |
} |
|
sv[0]=fd_accept(socketpair_fd,(struct sockaddr *)&addr,&len3); |
|
if(sv[0] < 0) { |
SP_DEBUG((stderr, "my_socketpair:fd_accept() failed, errno:%d (2)\n", |
errno)); |
if(retries <= 20) goto retry_accept; |
while (fd_close(sv[1]) && errno == EINTR) {} |
return -1; |
} |
|
set_nonblocking(sv[0],0); |
|
|
len=sizeof(addr); |
if(fd_getpeername(sv[0], (struct sockaddr *)&addr,&len)) { |
SP_DEBUG((stderr, "my_socketpair:fd_getpeername() failed, errno:%d\n", |
errno)); |
return -1; |
} |
len=sizeof(addr); |
if(fd_getsockname(sv[1],(struct sockaddr *)&addr2,&len) < 0) { |
SP_DEBUG((stderr, "my_socketpair:fd_getsockname() failed, errno:%d\n", |
errno)); |
return -1; |
} |
}while(len < (int)sizeof(addr) || |
addr2.sin_addr.s_addr != addr.sin_addr.s_addr || |
addr2.sin_port != addr.sin_port); |
|
|
|
SP_DEBUG((stderr, "my_socketpair: succeeded\n", |
errno)); |
|
return 0; |
} |
|
int socketpair_ultra(int family, int type, int protocol, int sv[2]) |
{ |
int retries=0; |
|
while(1) |
{ |
int ret=my_socketpair(family, type, protocol, sv); |
if(ret>=0) return ret; |
|
switch(errno) |
{ |
case EAGAIN: break; |
|
case EADDRINUSE: |
if(retries++ > 10) return ret; |
break; |
|
default: |
return ret; |
} |
} |
} |
|
#ifndef HAVE_SOCKETPAIR |
#define socketpair socketpair_ultra |
#endif |
#endif /* !HAVE_SOCKETPAIR || !UNIX_SOCKETS_WORKS_WITH_SHUTDOWN */ |
|
|
|
|
static void file_pipe(INT32 args) |
{ |
int inout[2] = { -1, -1 }; |
int i = 0; |
|
int type=fd_CAN_NONBLOCK | fd_BIDIRECTIONAL; |
int reverse; |
|
check_all_args("file->pipe",args, BIT_INT | BIT_VOID, 0); |
if(args && !SUBTYPEOF(Pike_sp[-1])) type = Pike_sp[-args].u.integer; |
|
reverse = type & fd_REVERSE; |
type &= ~fd_REVERSE; |
|
close_fd(); |
pop_n_elems(args); |
ERRNO=0; |
|
do |
{ |
#ifdef PIPE_CAPABILITIES |
if(!(type & ~(PIPE_CAPABILITIES))) |
{ |
i=fd_pipe(&inout[0]); |
if (i >= 0) { |
type=PIPE_CAPABILITIES; |
break; |
} |
} |
#endif |
|
#ifdef UNIX_SOCKETS_WORKS_WITH_SHUTDOWN |
#undef UNIX_SOCKET_CAPABILITIES |
#define UNIX_SOCKET_CAPABILITIES (fd_INTERPROCESSABLE | fd_BIDIRECTIONAL | fd_CAN_NONBLOCK | fd_CAN_SHUTDOWN | fd_SEND_FD) |
#endif |
|
#if defined(HAVE_SOCKETPAIR) |
if(!(type & ~(UNIX_SOCKET_CAPABILITIES))) |
{ |
i=fd_socketpair(AF_UNIX, SOCK_STREAM, 0, inout); |
if (i >= 0) { |
type=UNIX_SOCKET_CAPABILITIES; |
break; |
} |
} |
#endif |
|
#ifndef UNIX_SOCKETS_WORKS_WITH_SHUTDOWN |
if(!(type & ~(SOCKET_CAPABILITIES))) |
{ |
i=socketpair_ultra(AF_UNIX, SOCK_STREAM, 0, inout); |
if (i >= 0) { |
type=SOCKET_CAPABILITIES; |
break; |
} |
} |
#endif |
|
if (!i) { |
Pike_error("Cannot create a pipe matching those parameters.\n"); |
} |
}while(0); |
|
if ((i<0) || (inout[0] < 0) || (inout[1] < 0)) |
{ |
ERRNO=errno; |
if (inout[0] >= 0) { |
while (fd_close(inout[0]) && errno == EINTR) {} |
} |
if (inout[1] >= 0) { |
while (fd_close(inout[1]) && errno == EINTR) {} |
} |
errno = ERRNO; |
push_int(0); |
} |
else if (reverse) |
{ |
init_fd(inout[1], FILE_WRITE | (type&fd_BIDIRECTIONAL?FILE_READ:0) | |
fd_query_properties(inout[1], type), 0); |
|
my_set_close_on_exec(inout[1],1); |
my_set_close_on_exec(inout[0],1); |
change_fd_for_box (&THIS->box, inout[1]); |
|
ERRNO=0; |
push_new_fd_object(fd_fd_factory_fun_num, inout[0], |
(type&fd_BIDIRECTIONAL?FILE_WRITE:0)| FILE_READ, type); |
} else { |
init_fd(inout[0], FILE_READ | (type&fd_BIDIRECTIONAL?FILE_WRITE:0) | |
fd_query_properties(inout[0], type), 0); |
|
my_set_close_on_exec(inout[0],1); |
my_set_close_on_exec(inout[1],1); |
change_fd_for_box (&THIS->box, inout[0]); |
|
ERRNO=0; |
push_new_fd_object(fd_fd_factory_fun_num, inout[1], |
(type&fd_BIDIRECTIONAL?FILE_READ:0)| FILE_WRITE, type); |
} |
} |
|
static void file_handle_events(int event) |
{ |
struct object *o=Pike_fp->current_object; |
struct my_file *f = THIS; |
struct identifier *i; |
|
switch(event) |
{ |
case PROG_EVENT_INIT: |
f->box.backend = NULL; |
init_fd (-1, 0, 0); |
INIT_FD_CALLBACK_BOX(&f->box, NULL, o, f->box.fd, 0, got_fd_event, f->box.flags); |
|
i = ID_FROM_INT(o->prog, fd_receive_fd_fun_num + |
Pike_fp->context->identifier_level); |
if (((i->identifier_flags & IDENTIFIER_TYPE_MASK) == |
IDENTIFIER_PIKE_FUNCTION) && (i->func.offset != -1)) { |
|
f->flags |= FILE_HAVE_RECV_FD; |
} |
break; |
|
case PROG_EVENT_EXIT: |
if(!(f->flags & (FILE_NO_CLOSE_ON_DESTRUCT | |
FILE_LOCK_FD | |
FILE_NOT_OPENED))) |
close_fd_quietly(); |
else |
free_fd_stuff(); |
#ifdef HAVE_PIKE_SEND_FD |
if (f->fd_info) { |
free(f->fd_info); |
f->fd_info = NULL; |
} |
#endif |
unhook_fd_callback_box (&f->box); |
break; |
|
case PROG_EVENT_GC_RECURSE: |
if (f->box.backend) { |
|
|
int cb_events = 0; |
size_t ev; |
for (ev = 0; ev < NELEM (f->event_cbs); ev++) |
if (TYPEOF(f->event_cbs[ev]) != PIKE_T_INT) |
cb_events |= 1 << ev; |
SUB_FD_EVENTS (f, ~cb_events); |
} |
break; |
} |
} |
|
|
static void low_dup(struct object *UNUSED(toob), |
struct my_file *to, |
struct my_file *from) |
{ |
size_t ev; |
|
debug_check_internals (from); |
|
my_set_close_on_exec(to->box.fd, to->box.fd > 2); |
|
to->open_mode=from->open_mode; |
to->flags = from->flags & ~(FILE_NO_CLOSE_ON_DESTRUCT | |
FILE_LOCK_FD | |
FILE_NOT_OPENED); |
|
|
|
|
|
if (to->box.fd <= 2) { |
to->flags |= FILE_NO_CLOSE_ON_DESTRUCT; |
dmalloc_accept_leak_fd(to->box.fd); |
} |
|
|
|
|
|
unhook_fd_callback_box (&to->box); |
if (from->box.backend) |
INIT_FD_CALLBACK_BOX (&to->box, from->box.backend, to->box.ref_obj, |
to->box.fd, from->box.events, got_fd_event, from->box.flags); |
|
for (ev = 0; ev < NELEM (to->event_cbs); ev++) |
assign_svalue (&to->event_cbs[ev], &from->event_cbs[ev]); |
|
debug_check_internals (to); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_dup2(INT32 args) |
{ |
struct object *o; |
struct my_file *fd; |
|
if(args != 1) |
SIMPLE_WRONG_NUM_ARGS_ERROR("dup2", 1); |
|
if(FD < 0) |
Pike_error("File not open.\n"); |
|
if(TYPEOF(Pike_sp[-args]) != PIKE_T_OBJECT) |
SIMPLE_ARG_TYPE_ERROR("dup2", 1, "Stdio.File"); |
|
o=Pike_sp[-args].u.object; |
|
fd=get_file_storage(o); |
|
if(!fd) |
SIMPLE_ARG_TYPE_ERROR("dup2", 1, "Stdio.File"); |
|
if(fd->box.fd < 0) { |
int new_fd; |
if((new_fd = fd_dup(FD)) < 0) |
{ |
ERRNO = errno; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
fd->box.revents = 0; |
change_fd_for_box (&fd->box, new_fd); |
} else { |
if (fd->flags & FILE_LOCK_FD) { |
Pike_error("File has been temporarily locked from closing.\n"); |
} |
|
if(fd_dup2(FD, fd->box.fd) < 0) |
{ |
ERRNO = errno; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
THIS->box.revents = 0; |
} |
ERRNO=0; |
low_dup(o, fd, THIS); |
|
pop_n_elems(args); |
push_int(1); |
} |
|
|
|
|
|
|
|
|
static void file_dup(INT32 args) |
{ |
int fd; |
struct object *o; |
struct my_file *f; |
|
pop_n_elems(args); |
|
if(FD < 0) |
Pike_error("File not open.\n"); |
|
if((fd=fd_dup(FD)) < 0) |
{ |
ERRNO=errno; |
push_int(0); |
return; |
} |
push_new_fd_object(fd_fd_factory_fun_num, |
fd, THIS->open_mode, THIS->open_mode); |
o = Pike_sp[-1].u.object; |
f = ((struct my_file *) |
(o->storage + o->prog->inherits[SUBTYPEOF(Pike_sp[-1])].storage_offset)); |
ERRNO=0; |
low_dup(o, f, THIS); |
} |
|
|
|
|
static void file_open_socket(INT32 args) |
{ |
int fd; |
int family=-1; |
|
close_fd(); |
|
if (args > 2 && TYPEOF(Pike_sp[2-args]) == PIKE_T_INT && |
Pike_sp[2-args].u.integer != 0) |
family = Pike_sp[2-args].u.integer; |
else if (args > 2 && TYPEOF(Pike_sp[2-args]) == PIKE_T_STRING && |
!Pike_sp[2-args].u.string->size_shift) { |
PIKE_SOCKADDR addr; |
get_inet_addr(&addr, (char *) STR0(Pike_sp[2-args].u.string), |
NULL, -1, 0); |
family = SOCKADDR_FAMILY(addr); |
INVALIDATE_CURRENT_TIME(); |
} |
|
if (args && TYPEOF(Pike_sp[-args]) == PIKE_T_INT && |
Pike_sp[-args].u.integer < 0) { |
pop_n_elems(args); |
args = 0; |
} |
|
|
|
if (args) { |
PIKE_SOCKADDR addr; |
int addr_len; |
char *name; |
int o; |
|
if (TYPEOF(Pike_sp[-args]) != PIKE_T_INT && |
(TYPEOF(Pike_sp[-args]) != PIKE_T_STRING || |
Pike_sp[-args].u.string->size_shift)) { |
SIMPLE_ARG_TYPE_ERROR("open_socket", 1, "int|string(8bit)"); |
} |
if (args > 1 && !UNSAFE_IS_ZERO(&Pike_sp[1-args])) { |
if (TYPEOF(Pike_sp[1-args]) != PIKE_T_STRING) { |
SIMPLE_ARG_TYPE_ERROR("open_socket", 2, "string"); |
} |
|
name = Pike_sp[1-args].u.string->str; |
} else { |
name = NULL; |
} |
addr_len = get_inet_addr(&addr, name, |
(TYPEOF(Pike_sp[-args]) == PIKE_T_STRING? |
Pike_sp[-args].u.string->str : NULL), |
(TYPEOF(Pike_sp[-args]) == PIKE_T_INT? |
Pike_sp[-args].u.integer : -1), 0); |
INVALIDATE_CURRENT_TIME(); |
|
fd=fd_socket((family<0? SOCKADDR_FAMILY(addr):family), SOCK_STREAM, 0); |
if(fd < 0) |
{ |
ERRNO=errno; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
|
o=1; |
if(fd_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&o, sizeof(int)) < 0) { |
ERRNO=errno; |
while (fd_close(fd) && errno == EINTR) {} |
errno = ERRNO; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
|
#if defined(IPV6_V6ONLY) && defined(IPPROTO_IPV6) |
if ((family<0? SOCKADDR_FAMILY(addr):family) == AF_INET6) { |
|
|
|
|
o = 0; |
fd_setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&o, sizeof(int)); |
} |
#endif |
|
#ifdef SO_REUSEPORT |
|
* Linux 2.6.x seems to have reserved a slot for the option, but not |
* enabled it. Survive libc's with the option on kernels without. |
* |
* The emulated Linux runtime on MS Windows 10 fails this with EINVAL. |
*/ |
o=1; |
if((fd_setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *)&o, sizeof(int)) < 0) |
#ifdef ENOPROTOOPT |
&& (errno != ENOPROTOOPT) |
#endif |
#ifdef EINVAL |
&& (errno != EINVAL) |
#endif |
#ifdef WSAENOPROTOOPT |
&& (errno != WSAENOPROTOOPT) |
#endif |
){ |
ERRNO=errno; |
while (fd_close(fd) && errno == EINTR) {} |
errno = ERRNO; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
#endif /* SO_REUSEPORT */ |
if (fd_bind(fd, (struct sockaddr *)&addr, addr_len) < 0) { |
ERRNO=errno; |
while (fd_close(fd) && errno == EINTR) {} |
errno = ERRNO; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
} else { |
int o; |
fd=fd_socket((family<0? AF_INET:family), SOCK_STREAM, 0); |
if(fd < 0) |
{ |
ERRNO=errno; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
o=1; |
if(fd_setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&o, sizeof(int)) < 0) { |
ERRNO=errno; |
while (fd_close(fd) && errno == EINTR) {} |
errno = ERRNO; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
|
#if defined(IPV6_V6ONLY) && defined(IPPROTO_IPV6) |
if ((family<0? AF_INET:family) == AF_INET6) { |
|
|
|
|
o = 0; |
fd_setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&o, sizeof(int)); |
} |
#endif |
|
#ifdef SO_REUSEPORT |
|
* Linux 2.6.x seems to have reserved a slot for the option, but not |
* enabled it. Survive libc's with the option on kernels without. |
* |
* The emulated Linux runtime on MS Windows 10 fails this with EINVAL. |
*/ |
o=1; |
if((fd_setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *)&o, sizeof(int)) < 0) |
#ifdef ENOPROTOOPT |
&& (errno != ENOPROTOOPT) |
#endif |
#ifdef EINVAL |
&& (errno != EINVAL) |
#endif |
#ifdef WSAENOPROTOOPT |
&& (errno != WSAENOPROTOOPT) |
#endif |
){ |
ERRNO=errno; |
while (fd_close(fd) && errno == EINTR) {} |
errno = ERRNO; |
pop_n_elems(args); |
push_int(0); |
return; |
} |
#endif /* SO_REUSEPORT */ |
} |
|
init_fd(fd, FILE_READ | FILE_WRITE | |
fd_query_properties(fd, SOCKET_CAPABILITIES), 0); |
my_set_close_on_exec(fd,1); |
change_fd_for_box (&THIS->box, FD); |
ERRNO=0; |
|
pop_n_elems(args); |
push_int(1); |
} |
|
|
|
|
|
|
|
static void file_set_keepalive(INT32 args) |
{ |
int tmp, i; |
INT_TYPE t; |
|
get_all_args("set_keepalive", args, "%i", &t); |
|
|
tmp = t; |
|
#ifdef SO_KEEPALIVE |
i = fd_setsockopt(FD,SOL_SOCKET, SO_KEEPALIVE, (char *)&tmp, sizeof(tmp)); |
if(i) |
{ |
ERRNO=errno; |
}else{ |
ERRNO=0; |
} |
#else /* !SO_KEEPALIVE */ |
#ifdef ENOTSUP |
ERRNO = errno = ENOTSUP; |
#else /* !ENOTSUP */ |
#ifdef ENOTTY |
ERRNO = errno = ENOTTY; |
#else /* !ENOTTY */ |
ERRNO = errno = EIO; |
#endif /* ENOTTY */ |
#endif /* ENOTSUP */ |
#endif /* SO_KEEPALIVE */ |
pop_n_elems(args); |
push_int(!i); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_setsockopt(INT32 args) |
{ |
int tmp, i, opt, level; |
INT_TYPE o, t, l; |
|
get_all_args("setsockopt", args, "%i%i%i", &l, &o, &t); |
|
|
tmp = t; opt = o; level = l; |
|
i = fd_setsockopt(FD, level, opt, (char *)&tmp, sizeof(tmp)); |
if(i) |
{ |
ERRNO=errno; |
}else{ |
ERRNO=0; |
} |
pop_n_elems(args); |
push_int(!i); |
} |
|
#ifdef HAVE_SYS_UN_H |
#include <sys/un.h> |
|
#ifndef PATH_MAX |
#ifdef _POSIX_PATH_MAX |
#define PATH_MAX _POSIX_PATH_MAX |
#else /* !_POSIX_PATH_MAX */ |
#define PATH_MAX 255 /* Minimum according to POSIX. */ |
#endif /* _POSIX_PATH_MAX */ |
#endif /* !PATH_MAX */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_connect_unix( INT32 args ) |
{ |
struct sockaddr_un *name; |
int addr_len; |
int tmp; |
|
if( args != 1 ) |
SIMPLE_WRONG_NUM_ARGS_ERROR("connect_unix", 1); |
if( (TYPEOF(Pike_sp[-args]) != PIKE_T_STRING) || |
(Pike_sp[-args].u.string->size_shift) ) |
Pike_error("Illegal argument. Expected string(8bit)\n"); |
|
|
|
|
addr_len = sizeof(struct sockaddr_un) + Pike_sp[-args].u.string->len + 1 - |
sizeof(name->sun_path); |
name = xalloc(addr_len); |
|
name->sun_family=AF_UNIX; |
strcpy( name->sun_path, Pike_sp[-args].u.string->str ); |
#ifdef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN |
|
name->sun_len = Pike_sp[-args].u.string->len + 1; |
#endif |
pop_n_elems(args); |
|
close_fd(); |
change_fd_for_box (&THIS->box, socket(AF_UNIX,SOCK_STREAM,0)); |
|
if( FD < 0 ) |
{ |
free(name); |
ERRNO = errno; |
push_int(0); |
return; |
} |
|
init_fd(FD, FILE_READ | FILE_WRITE |
| fd_query_properties(FD, UNIX_SOCKET_CAPABILITIES), 0); |
my_set_close_on_exec(FD, 1); |
|
do { |
tmp=connect(FD,(void *)name, addr_len); |
} while ((tmp < 0) && (errno == EINTR)); |
free(name); |
if (tmp == -1) { |
ERRNO = errno; |
push_int(0); |
} else { |
push_int(1); |
} |
} |
#endif /* HAVE_SYS_UN_H */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_connect(INT32 args) |
{ |
PIKE_SOCKADDR addr; |
int addr_len; |
struct pike_string *dest_addr = NULL; |
struct pike_string *src_addr = NULL; |
struct pike_string *data = NULL; |
struct svalue *dest_port = NULL; |
struct svalue *src_port = NULL; |
|
int tmp, was_closed = FD < 0; |
int fd, sent = 0; |
int nb_mode; |
int old_events; |
int e; |
|
if (args < 4) |
{ |
get_all_args("connect", args, "%S%*", &dest_addr, &dest_port); |
} |
else if( args == 5 ) |
{ |
struct svalue *src_sv; |
get_all_args("connect", args, "%S%*%*%*%S", |
&dest_addr, &dest_port, &src_sv, &src_port, &data); |
if(TYPEOF(*src_sv) != PIKE_T_INT ) |
{ |
if (TYPEOF(*src_sv) != PIKE_T_STRING || src_sv->u.string->size_shift) |
SIMPLE_ARG_TYPE_ERROR("connect", 3, "int|string(8bit)"); |
src_addr = src_sv->u.string; |
} |
} else { |
get_all_args("connect", args, "%S%*%S%*", |
&dest_addr, &dest_port, &src_addr, &src_port); |
} |
|
if(TYPEOF(*dest_port) != PIKE_T_INT && |
(TYPEOF(*dest_port) != PIKE_T_STRING || dest_port->u.string->size_shift)) |
SIMPLE_ARG_TYPE_ERROR("connect", 2, "int|string(8bit)"); |
|
if(src_port && TYPEOF(*src_port) != PIKE_T_INT && |
(TYPEOF(*src_port) != PIKE_T_STRING || src_port->u.string->size_shift)) |
SIMPLE_ARG_TYPE_ERROR("connect", 4, "int|string(8bit)"); |
|
|
|
addr_len = get_inet_addr(&addr, dest_addr->str, |
(TYPEOF(*dest_port) == PIKE_T_STRING? |
dest_port->u.string->str : NULL), |
(TYPEOF(*dest_port) == PIKE_T_INT? |
dest_port->u.integer : -1), 0); |
INVALIDATE_CURRENT_TIME(); |
|
if(was_closed) |
{ |
if (!src_addr) { |
push_int(-1); |
push_int(0); |
push_int(SOCKADDR_FAMILY(addr)); |
file_open_socket(3); |
} else { |
push_svalue(src_port); |
ref_push_string(src_addr); |
file_open_socket(2); |
} |
if(UNSAFE_IS_ZERO(Pike_sp-1) || FD < 0) |
Pike_error("Stdio.File->connect(): Failed to open socket.\n"); |
pop_stack(); |
} |
|
nb_mode = !!(THIS->open_mode & FILE_NONBLOCKING); |
|
|
if ((old_events = THIS->box.events)) { |
set_fd_callback_events(&(THIS->box), 0, THIS->box.flags); |
} |
|
fd = FD; |
THREADS_ALLOW(); |
for(;;) |
{ |
#ifdef MSG_FASTOPEN |
if( data ) |
{ |
tmp = sendto(fd, data->str, data->len, MSG_FASTOPEN, |
(struct sockaddr *)&addr, addr_len ); |
} |
else |
#endif |
{ |
tmp=fd_connect(fd, (struct sockaddr *)&addr, addr_len); |
} |
if( tmp<0 && (errno==EINTR)) |
continue; |
break; |
} |
THREADS_DISALLOW(); |
|
e = errno; |
|
if (old_events) { |
|
set_fd_callback_events(&(THIS->box), old_events, THIS->box.flags); |
} |
|
errno = e; |
|
|
|
|
|
|
|
|
if(tmp < 0 |
#ifdef EINPROGRESS |
&& !(errno == EINPROGRESS && nb_mode) |
#endif |
#ifdef WSAEWOULDBLOCK |
&& !(errno == WSAEWOULDBLOCK && nb_mode) |
#endif |
#ifdef EWOULDBLOCK |
&& !(errno == EWOULDBLOCK && nb_mode) |
#endif |
) |
{ |
|
ERRNO=errno; |
if (was_closed) { |
while (fd_close (FD) && errno == EINTR) {} |
change_fd_for_box (&THIS->box, -1); |
errno = ERRNO; |
} |
pop_n_elems(args); |
push_int(0); |
}else{ |
ERRNO=0; |
if( data ) |
{ |
push_string( make_shared_binary_string( data->str + tmp, data->len-tmp ) ); |
stack_pop_n_elems_keep_top( args ); |
} |
else |
{ |
pop_n_elems(args); |
push_int(1); |
} |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void file_query_address(INT32 args) |
{ |
PIKE_SOCKADDR addr; |
int i; |
char buffer[496]; |
|
|
|
ACCEPT_SIZE_T len; |
|
if(FD <0) |
Pike_error("Stdio.File->query_address(): Connection not open.\n"); |
|
len=sizeof(addr); |
if(args > 0 && !UNSAFE_IS_ZERO(Pike_sp-args)) |
{ |
i=fd_getsockname(FD,(struct sockaddr *)&addr,&len); |
}else{ |
i=fd_getpeername(FD,(struct sockaddr *)&addr,&len); |
} |
pop_n_elems(args); |
if(i < 0) |
{ |
ERRNO=errno; |
push_int(0); |
return; |
} |
|
#ifdef fd_inet_ntop |
if(!fd_inet_ntop(SOCKADDR_FAMILY(addr), SOCKADDR_IN_ADDR(addr), |
buffer, sizeof(buffer)-20)) |
{ |
ERRNO=errno; |
push_int(0); |
return; |
} |
#else |
if(SOCKADDR_FAMILY(addr) == AF_INET) |
{ |
char *q = inet_ntoa(*SOCKADDR_IN_ADDR(addr)); |
strncpy(buffer,q,sizeof(buffer)-20); |
buffer[sizeof(buffer)-20]=0; |
}else{ |
#ifdef EAFNOSUPPORT |
ERRNO=EAFNOSUPPORT; |
#else |
ERRNO=EINVAL; |
#endif |
push_int(0); |
return; |
} |
#endif |
sprintf(buffer+strlen(buffer)," %d",(int)(ntohs(addr.ipv4.sin_port))); |
|
|
|
|
|
|
if ((!strncmp(buffer, "::FFFF:", 7) || !strncmp(buffer, "::ffff:", 7)) && |
!strchr(buffer + 7, ':')) { |
push_text(buffer+7); |
} else { |
push_text(buffer); |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
static void file_create(INT32 args) |
{ |
if(!args) return; |
if(TYPEOF(Pike_sp[-args]) != PIKE_T_STRING && |
TYPEOF(Pike_sp[-args]) != PIKE_T_INT) |
SIMPLE_ARG_TYPE_ERROR("create", 1, "int|string"); |
|
close_fd(); |
file_open(args); |
} |
|
#ifdef _REENTRANT |
|
struct new_thread_data |
{ |
INT32 from, to; |
char buffer[READ_BUFFER]; |
}; |
|
static TH_RETURN_TYPE proxy_thread(void * data) |
{ |
struct new_thread_data *p=(struct new_thread_data *)data; |
|
while(1) |
{ |
ptrdiff_t len, w; |
len = fd_read(p->from, p->buffer, READ_BUFFER); |
if(len==0) break; |
if(len<0) |
{ |
if(errno==EINTR) continue; |
|
break; |
} |
|
w=0; |
while(w<len) |
{ |
ptrdiff_t wl = fd_write(p->to, p->buffer+w, len-w); |
if (!wl) { |
goto close_and_exit; |
} |
if(wl<0) |
{ |
if(errno==EINTR) continue; |
|
break; |
} |
w+=wl; |
} |
} |
|
close_and_exit: |
|
|
while (fd_close(p->to) && errno == EINTR) {} |
while (fd_close(p->from) && errno == EINTR) {} |
low_mt_lock_interpreter(); |
num_threads--; |
mt_unlock_interpreter(); |
free(p); |
th_exit(0); |
return 0; |
} |
|
|
|
|
|
|
|
|
|
void file_proxy(INT32 args) |
{ |
struct my_file *f; |
struct new_thread_data *p; |
int from, to; |
|
THREAD_T id; |
check_all_args("Stdio.File->proxy",args, BIT_OBJECT,0); |
f=get_file_storage(Pike_sp[-args].u.object); |
if(!f) |
SIMPLE_ARG_TYPE_ERROR("proxy", 1, "Stdio.File"); |
|
from=fd_dup(f->box.fd); |
if(from<0) |
{ |
ERRNO=errno; |
Pike_error("Failed to dup proxy fd. (errno=%d)\n",errno); |
} |
to=fd_dup(FD); |
if(to<0) |
{ |
ERRNO=errno; |
while (fd_close(from) && errno == EINTR) {} |
errno = ERRNO; |
Pike_error("Failed to dup proxy fd.\n"); |
} |
|
p=ALLOC_STRUCT(new_thread_data); |
p->from=from; |
p->to=to; |
|
num_threads++; |
if(th_create_small(&id,proxy_thread,p)) |
{ |
free(p); |
while (fd_close(from) && errno == EINTR) {} |
while (fd_close(to) && errno == EINTR) {} |
Pike_error("Failed to create thread.\n"); |
} |
|
th_destroy(& id); |
} |
|
PMOD_EXPORT void create_proxy_pipe(struct object *o, int for_reading) |
{ |
struct object *n,*n2; |
push_object(n=clone_object(file_program,0)); |
push_int(fd_INTERPROCESSABLE); |
apply(n,"pipe",1); |
if(TYPEOF(Pike_sp[-1]) != PIKE_T_OBJECT) |
Pike_error("Failed to create proxy pipe (errno=%d)!\n",get_file_storage(n)->my_errno); |
n2=Pike_sp[-1].u.object; |
|
if(for_reading) |
{ |
ref_push_object(o); |
apply(n2,"proxy",1); |
pop_n_elems(2); |
}else{ |
|
Pike_sp[-2].u.object=n2; |
Pike_sp[-1].u.object=n; |
apply(o,"proxy",1); |
pop_stack(); |
} |
} |
|
#endif |
|
#if defined(HAVE_FD_FLOCK) || defined(HAVE_FD_LOCKF) |
|
static struct program * file_lock_key_program; |
|
struct file_lock_key_storage |
{ |
struct my_file *f; |
struct object *file; |
#ifdef _REENTRANT |
struct thread_state *owner; |
struct object *owner_obj; |
#endif |
}; |
|
|
#define OB2KEY(O) ((struct file_lock_key_storage *)((O)->storage)) |
|
static void low_file_lock(INT32 args, int flags) |
{ |
int ret,fd=FD; |
struct object *o; |
|
destruct_objects_to_destruct(); |
|
if(FD < 0) |
Pike_error("Stdio.File->lock(): File is not open.\n"); |
|
if(!args || UNSAFE_IS_ZERO(Pike_sp-args)) |
{ |
if(THIS->key |
#ifdef _REENTRANT |
&& OB2KEY(THIS->key)->owner == Pike_interpreter.thread_state |
#endif |
) |
{ |
if (flags & fd_LOCK_NB) { |
#ifdef EWOULDBLOCK |
ERRNO = errno = EWOULDBLOCK; |
#else /* !EWOULDBLOCK */ |
ERRNO = errno = EAGAIN; |
#endif /* EWOULDBLOCK */ |
pop_n_elems(args); |
push_int(0); |
return; |
} else { |
Pike_error("Recursive file locks!\n"); |
} |
} |
} |
|
o=clone_object(file_lock_key_program,0); |
|
THREADS_ALLOW(); |
#ifdef HAVE_FD_FLOCK |
ret=fd_flock(fd, flags); |
#else |
ret=fd_lockf(fd, flags); |
#endif |
THREADS_DISALLOW(); |
|
if(ret<0) |
{ |
free_object(o); |
ERRNO=errno; |
pop_n_elems(args); |
push_int(0); |
}else{ |
THIS->key = o; |
OB2KEY(o)->f=THIS; |
add_ref(OB2KEY(o)->file = Pike_fp->current_object); |
pop_n_elems(args); |
push_object(o); |
} |
} |
|
|
|
|
|
|
|
|
|
static void file_lock(INT32 args) |
{ |
low_file_lock(args, fd_LOCK_EX); |
} |
|
|
|
|
|
|
|
|
|
|
|