diff options
-rw-r--r-- | winsup/cygwin/ChangeLog | 66 | ||||
-rw-r--r-- | winsup/cygwin/fhandler.h | 1389 | ||||
-rw-r--r-- | winsup/cygwin/fhandler_socket.cc | 690 | ||||
-rw-r--r-- | winsup/cygwin/include/sys/socket.h | 58 | ||||
-rw-r--r-- | winsup/cygwin/libc/inet_addr.c | 1 | ||||
-rw-r--r-- | winsup/cygwin/libc/inet_network.c | 1 | ||||
-rw-r--r-- | winsup/cygwin/net.cc | 67 | ||||
-rw-r--r-- | winsup/cygwin/poll.cc | 146 |
8 files changed, 2003 insertions, 415 deletions
diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index cb7e5ebc695..4990f713e0c 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,69 @@ +2006-07-10 Corinna Vinschen <corinna@vinschen.de> + + * fhandler.h (class fhandler_socket): Add wsock_mtx, wsock_evt + and wsock_events members. Remove closed status flag, add listener + status flag. Accomodate new implementation of socket event handling + methods. Declare recv* and send* functions ssize_t as the POSIX + equivalents. + (fhandler_socket::recv_internal): Declare. + (fhandler_socket::send_internal): Ditto. + * fhandler_socket.cc (EVENT_MASK): Define mask of selected events. + (fhandler_socket::fhandler_socket): Initialize new members. + (fhandler_socket::af_local_setblocking): Don't actually set the + socket to blocking mode. Keep sane event selection. + (fhandler_socket::af_local_unsetblocking): Don't actually set the + socket to previous blocking setting, just remember it. + (struct wsa_event): New structure to keep event data per shared + socket. + (NUM_SOCKS): Define number of shared sockets concurrently handled by + all active Cygwin processes. + (wsa_events): New shared datastructure keeping all wsa_event records. + (socket_serial_number): New shared variable to identify shared sockets. + (wsa_slot_mtx): Global mutex to serialize wsa_events access. + (search_wsa_event_slot): New static function to select a new wsa_event + slot for a new socket. + (fhandler_socket::prepare): Rewrite. Prepare event selection + per new socket. + (fhandler_socket::wait): Rewrite. Wait for socket events in thread + safe and multiple process safe. + (fhandler_socket::release): Rewrite. Close per-socket descriptor + mutex handle and event handle. + (fhandler_socket::dup): Duplicate wsock_mtx and wsock_evt. Fix + copy-paste error in debug output. + (fhandler_socket::connect): Accomodate new event handling. + (fhandler_socket::listen): Set listener flag on successful listen. + (fhandler_socket::accept): Accomodate new event handling. + (fhandler_socket::recv_internal): New inline method centralizing + common recv code. + (fhandler_socket::recvfrom): Call recv_internal now. + (fhandler_socket::recvmsg): Ditto. Streamline copying from iovec + to WSABUF. + (fhandler_socket::send_internal): New inline method centralizing + common send code. + (fhandler_socket::sendto): Call send_internal now. + (fhandler_socket::sendmsg): Ditto. Streamline copying from iovec + to WSABUF. + (fhandler_socket::close): Call release now. + (fhandler_socket::ioctl): Never actually switch to blocking mode. + Just keep track of the setting. + * net.cc (fdsock): Call prepare now. + (cygwin_connect): Revert again to event driven technique. + (cygwin_accept): Ditto. + * poll.cc (poll): Don't call recvfrom on a listening socket. + Remove special case for failing recvfrom. + * include/sys/socket.h: Declare recv* and send* functions ssize_t as + requested by POSIX. + +2006-07-10 Corinna Vinschen <corinna@vinschen.de> + + * libc/inet_addr.c: Define __INSIDE_CYGWIN_NET__. + * libc/inet_network.c: Ditto. + +2006-07-07 Corinna Vinschen <corinna@vinschen.de> + + * fhandler_socket.cc (fhandler_socket::wait): Disable SA_RESTART + handling for now. + 2006-07-07 Corinna Vinschen <corinna@vinschen.de> * net.cc (cygwin_inet_ntop): Fix data type of forth parameter. diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h new file mode 100644 index 00000000000..887b929663c --- /dev/null +++ b/winsup/cygwin/fhandler.h @@ -0,0 +1,1389 @@ +/* fhandler.h + + Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, + 2005, 2006 Red Hat, Inc. + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#ifndef _FHANDLER_H_ +#define _FHANDLER_H_ + +/* fcntl flags used only internaly. */ +#define O_NOSYMLINK 0x080000 +#define O_DIROPEN 0x100000 + +/* newlib used to define O_NDELAY differently from O_NONBLOCK. Now it + properly defines both to be the same. Unfortunately, we have to + behave properly the old version, too, to accommodate older executables. */ +#define OLD_O_NDELAY (CYGWIN_VERSION_CHECK_FOR_OLD_O_NONBLOCK ? 4 : 0) + +/* Care for the old O_NDELAY flag. If one of the flags is set, + both flags are set. */ +#define O_NONBLOCK_MASK (O_NONBLOCK | OLD_O_NDELAY) + +extern const char *windows_device_names[]; +extern struct __cygwin_perfile *perfile_table; +#define __fmode (*(user_data->fmode_ptr)) +extern const char proc[]; +extern const int proc_len; + +class select_record; +class fhandler_disk_file; +typedef struct __DIR DIR; +struct dirent; +struct iovec; +struct __acl32; + +enum dirent_states +{ + dirent_ok = 0x0000, + dirent_saw_dot = 0x0001, + dirent_saw_dot_dot = 0x0002, + dirent_saw_eof = 0x0004, + dirent_isroot = 0x0008, + dirent_set_d_ino = 0x0010, + dirent_get_d_ino = 0x0020 +}; + +enum conn_state +{ + unconnected = 0, + connect_pending = 1, + connected = 2, + connect_failed = 3 +}; + +enum line_edit_status +{ + line_edit_ok = 0, + line_edit_input_done = 1, + line_edit_signalled = 2, + line_edit_error = 3, + line_edit_pipe_full = 4 +}; + +enum bg_check_types +{ + bg_error = -1, + bg_eof = 0, + bg_ok = 1, + bg_signalled = 2 +}; + +enum query_state { + no_query = 0, + query_read_control = 1, + query_stat_control = 2, + query_write_control = 3, + query_write_attributes = 4 +}; + +class fhandler_base +{ + friend class dtable; + friend void close_all_files (bool); + + struct status_flags + { + unsigned rbinary : 1; /* binary read mode */ + unsigned rbinset : 1; /* binary read mode explicitly set */ + unsigned wbinary : 1; /* binary write mode */ + unsigned wbinset : 1; /* binary write mode explicitly set */ + unsigned nohandle : 1; /* No handle associated with fhandler. */ + unsigned uninterruptible_io : 1; /* Set if I/O should be uninterruptible. */ + unsigned append_mode : 1; /* always append */ + unsigned did_lseek : 1; /* set when lseek is called as a flag that + _write should check if we've moved + beyond EOF, zero filling or making + file sparse if so. */ + unsigned query_open : 3; /* open file without requesting either + read or write access */ + unsigned close_on_exec : 1; /* close-on-exec */ + unsigned need_fork_fixup : 1; /* Set if need to fixup after fork. */ + unsigned has_changed : 1; /* Flag used to set ctime on close. */ + + public: + status_flags () : + rbinary (0), rbinset (0), wbinary (0), wbinset (0), nohandle (0), + uninterruptible_io (0), append_mode (0), did_lseek (0), + query_open (no_query), close_on_exec (0), need_fork_fixup (0), + has_changed (0) + {} + } status, open_status; + + private: + int access; + HANDLE io_handle; + + __ino64_t namehash; /* hashed filename, used as inode num */ + + protected: + /* File open flags from open () and fcntl () calls */ + int openflags; + + char *rabuf; /* used for crlf conversion in text files */ + size_t ralen; + size_t raixget; + size_t raixput; + size_t rabuflen; + + DWORD fs_flags; + HANDLE read_state; + path_conv pc; + + public: + class fhandler_base *archetype; + int usecount; + + void set_name (path_conv &pc); + int error () const {return pc.error;} + void set_error (int error) {pc.error = error;} + bool exists () const {return pc.exists ();} + int pc_binmode () const {return pc.binmode ();} + device& dev () {return pc.dev;} + operator DWORD& () {return (DWORD&) pc;} + virtual size_t size () const {return sizeof (*this);} + + virtual fhandler_base& operator =(fhandler_base &x); + fhandler_base (); + virtual ~fhandler_base (); + + /* Non-virtual simple accessor functions. */ + void set_io_handle (HANDLE x) { io_handle = x; } + + DWORD& get_device () { return dev ().devn; } + DWORD get_major () { return dev ().major; } + DWORD get_minor () { return dev ().minor; } + virtual int get_unit () { return dev ().minor; } + + int get_access () const { return access; } + void set_access (int x) { access = x; } + + int get_flags () { return openflags; } + void set_flags (int x, int supplied_bin = 0); + + bool is_nonblocking (); + void set_nonblocking (int yes); + + bool wbinary () const { return status.wbinset ? status.wbinary : 1; } + bool rbinary () const { return status.rbinset ? status.rbinary : 1; } + + void wbinary (bool b) {status.wbinary = b; status.wbinset = 1;} + void rbinary (bool b) {status.rbinary = b; status.rbinset = 1;} + + void set_open_status () {open_status = status;} + void reset_to_open_binmode () + { + set_flags ((get_flags () & ~(O_TEXT | O_BINARY)) + | ((open_status.wbinary || open_status.rbinary) + ? O_BINARY : O_TEXT)); + } + + IMPLEMENT_STATUS_FLAG (bool, wbinset) + IMPLEMENT_STATUS_FLAG (bool, rbinset) + IMPLEMENT_STATUS_FLAG (bool, nohandle) + IMPLEMENT_STATUS_FLAG (bool, uninterruptible_io) + IMPLEMENT_STATUS_FLAG (bool, append_mode) + IMPLEMENT_STATUS_FLAG (bool, did_lseek) + IMPLEMENT_STATUS_FLAG (query_state, query_open) + IMPLEMENT_STATUS_FLAG (bool, close_on_exec) + IMPLEMENT_STATUS_FLAG (bool, need_fork_fixup) + IMPLEMENT_STATUS_FLAG (bool, has_changed) + + int get_default_fmode (int flags); + + virtual void set_close_on_exec (bool val); + + LPSECURITY_ATTRIBUTES get_inheritance (bool all = 0) + { + if (all) + return close_on_exec () ? &sec_all_nih : &sec_all; + else + return close_on_exec () ? &sec_none_nih : &sec_none; + } + + virtual void fixup_before_fork_exec (DWORD) {} + virtual void fixup_after_fork (HANDLE); + virtual void fixup_after_exec (); + void create_read_state (LONG n) + { + read_state = CreateSemaphore (&sec_none_nih, 0, n, NULL); + ProtectHandle (read_state); + } + + void signal_read_state (LONG n) + { + ReleaseSemaphore (read_state, n, NULL); + } + + void set_fs_flags (DWORD flags) { fs_flags = flags; } + bool get_fs_flags (DWORD flagval = UINT32_MAX) + { return (fs_flags & (flagval)); } + + bool get_readahead_valid () { return raixget < ralen; } + int puts_readahead (const char *s, size_t len = (size_t) -1); + int put_readahead (char value); + + int get_readahead (); + int peek_readahead (int queryput = 0); + + int eat_readahead (int n); + + void set_readahead_valid (int val, int ch = -1); + + int get_readahead_into_buffer (char *buf, size_t buflen); + + bool has_acls () const { return pc.has_acls (); } + + bool isremote () { return pc.isremote (); } + + bool has_attribute (DWORD x) const {return pc.has_attribute (x);} + const char *get_name () const { return pc.normalized_path; } + const char *get_win32_name () { return pc.get_win32 (); } + __ino64_t get_namehash () { return namehash ?: namehash = hash_path_name (0, get_win32_name ()); } + /* Returns name used for /proc/<pid>/fd in buf. */ + virtual char *get_proc_fd_name (char *buf); + + virtual void hclose (HANDLE h) {CloseHandle (h);} + virtual void set_no_inheritance (HANDLE &h, int not_inheriting); + + /* fixup fd possibly non-inherited handles after fork */ + bool fork_fixup (HANDLE parent, HANDLE &h, const char *name); + virtual bool need_fixup_before () const {return false;} + + int open_9x (int flags, mode_t mode = 0); + virtual int open (int flags, mode_t mode = 0); + int open_fs (int flags, mode_t mode = 0); + virtual int close (); + int close_fs (); + virtual int __stdcall fstat (struct __stat64 *buf) __attribute__ ((regparm (2))); + int __stdcall fstat_fs (struct __stat64 *buf) __attribute__ ((regparm (2))); + int __stdcall fstat_helper (struct __stat64 *buf, + FILETIME ftChangeTime, + FILETIME ftLastAccessTime, + FILETIME ftLastWriteTime, + DWORD dwVolumeSerialNumber, + ULONGLONG nFileSize, + LONGLONG nAllocSize, + ULONGLONG nFileIndex, + DWORD nNumberOfLinks, + DWORD dwFileAttributes) + __attribute__ ((regparm (3))); + int __stdcall fstat_by_handle (struct __stat64 *buf) __attribute__ ((regparm (2))); + int __stdcall fstat_by_name (struct __stat64 *buf) __attribute__ ((regparm (2))); + int fhandler_base::utimes_fs (const struct timeval *) __attribute__ ((regparm (2))); + virtual int __stdcall fchmod (mode_t mode) __attribute__ ((regparm (1))); + virtual int __stdcall fchown (__uid32_t uid, __gid32_t gid) __attribute__ ((regparm (2))); + virtual int __stdcall facl (int, int, __acl32 *) __attribute__ ((regparm (3))); + virtual int __stdcall ftruncate (_off64_t) __attribute__ ((regparm (2))); + virtual int __stdcall link (const char *) __attribute__ ((regparm (2))); + virtual int __stdcall utimes (const struct timeval *) __attribute__ ((regparm (2))); + virtual int __stdcall fsync () __attribute__ ((regparm (1))); + virtual int ioctl (unsigned int cmd, void *); + virtual int fcntl (int cmd, void *); + virtual char const *ttyname () { return get_name (); } + virtual void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + virtual int write (const void *ptr, size_t len); + virtual ssize_t readv (const struct iovec *, int iovcnt, ssize_t tot = -1); + virtual ssize_t writev (const struct iovec *, int iovcnt, ssize_t tot = -1); + virtual ssize_t __stdcall pread (void *, size_t, _off64_t) __attribute__ ((regparm (3))); + virtual ssize_t __stdcall pwrite (void *, size_t, _off64_t) __attribute__ ((regparm (3))); + virtual _off64_t lseek (_off64_t offset, int whence); + virtual int lock (int, struct __flock64 *); + virtual int dup (fhandler_base *child); + + virtual HANDLE mmap (caddr_t *addr, size_t len, int prot, + int flags, _off64_t off); + virtual int munmap (HANDLE h, caddr_t addr, size_t len); + virtual int msync (HANDLE h, caddr_t addr, size_t len, int flags); + virtual bool fixup_mmap_after_fork (HANDLE h, int prot, int flags, + _off64_t offset, DWORD size, + void *address); + + void *operator new (size_t, void *p) __attribute__ ((nothrow)) {return p;} + + virtual void init (HANDLE, DWORD, mode_t); + + virtual int tcflush (int); + virtual int tcsendbreak (int); + virtual int tcdrain (); + virtual int tcflow (int); + virtual int tcsetattr (int a, const struct termios *t); + virtual int tcgetattr (struct termios *t); + virtual int tcsetpgrp (const pid_t pid); + virtual int tcgetpgrp (); + virtual int is_tty () { return 0; } + virtual bool isdevice () { return true; } + virtual bool isfifo () { return false; } + virtual char *ptsname () { return NULL;} + virtual class fhandler_socket *is_socket () { return NULL; } + virtual class fhandler_console *is_console () { return 0; } + virtual int is_windows () {return 0; } + + virtual void raw_read (void *ptr, size_t& ulen); + virtual int raw_write (const void *ptr, size_t ulen); + + /* Virtual accessor functions to hide the fact + that some fd's have two handles. */ + virtual HANDLE& get_handle () { return io_handle; } + virtual HANDLE& get_io_handle () { return io_handle; } + virtual HANDLE& get_output_handle () { return io_handle; } + virtual bool hit_eof () {return false;} + virtual select_record *select_read (select_record *s); + virtual select_record *select_write (select_record *s); + virtual select_record *select_except (select_record *s); + virtual int ready_for_read (int fd, DWORD howlong); + virtual const char *get_native_name () + { + return dev ().native; + } + virtual bg_check_types bg_check (int) {return bg_ok;} + void clear_readahead () + { + raixput = raixget = ralen = rabuflen = 0; + rabuf = NULL; + } + void operator delete (void *); + virtual HANDLE get_guard () const {return NULL;} + virtual void set_eof () {} + virtual int mkdir (mode_t mode); + virtual int rmdir (); + virtual DIR *opendir (); + virtual int readdir (DIR *, dirent *) __attribute__ ((regparm (3))); + virtual _off64_t telldir (DIR *); + virtual void seekdir (DIR *, _off64_t); + virtual void rewinddir (DIR *); + virtual int closedir (DIR *); + virtual bool is_slow () {return 0;} + bool is_auto_device () {return isdevice () && !dev ().isfs ();} + bool is_fs_special () {return pc.is_fs_special ();} + bool issymlink () {return pc.issymlink ();} + bool device_access_denied (int) __attribute__ ((regparm (2))); + int fhaccess (int flags) __attribute__ ((regparm (2))); + friend class fhandler_fifo; +}; + +class fhandler_mailslot : public fhandler_base +{ + public: + fhandler_mailslot (); + int __stdcall fstat (struct __stat64 *buf) __attribute__ ((regparm (2))); + int open (int flags, mode_t mode = 0); + int write (const void *ptr, size_t len); + int ioctl (unsigned int cmd, void *); + select_record *select_read (select_record *s); +}; + +struct wsa_event; + +class fhandler_socket: public fhandler_base +{ + private: + int addr_family; + int type; + int connect_secret[4]; + + HANDLE wsock_mtx; + HANDLE wsock_evt; + wsa_event *wsock_events; + + pid_t sec_pid; + __uid32_t sec_uid; + __gid32_t sec_gid; + pid_t sec_peer_pid; + __uid32_t sec_peer_uid; + __gid32_t sec_peer_gid; + void af_local_set_secret (char *); + void af_local_setblocking (bool &, bool &); + void af_local_unsetblocking (bool, bool); + void af_local_set_cred (); + void af_local_copy (fhandler_socket *); + bool af_local_recv_secret (); + bool af_local_send_secret (); + bool af_local_recv_cred (); + bool af_local_send_cred (); + int af_local_accept (); + public: + int af_local_connect (); + void af_local_set_sockpair_cred (); + + private: + struct _WSAPROTOCOL_INFOA *prot_info_ptr; + char *sun_path; + struct status_flags + { + unsigned async_io : 1; /* async I/O */ + unsigned saw_shutdown_read : 1; /* Socket saw a SHUT_RD */ + unsigned saw_shutdown_write : 1; /* Socket saw a SHUT_WR */ + unsigned saw_reuseaddr : 1; /* Socket saw SO_REUSEADDR call */ + unsigned owner : 1; /* fcntl(F_SETOWN) called */ + unsigned listener : 1; /* listen called */ + unsigned connect_state : 2; + public: + status_flags () : + async_io (0), saw_shutdown_read (0), saw_shutdown_write (0), + owner (0), listener (0), connect_state (unconnected) + {} + } status; + + public: + bool prepare (); + private: + int wait (long event_mask); + void release (); + + public: + fhandler_socket (); + ~fhandler_socket (); + int get_socket () { return (int) get_handle(); } + fhandler_socket *is_socket () { return this; } + + IMPLEMENT_STATUS_FLAG (bool, async_io) + IMPLEMENT_STATUS_FLAG (bool, saw_shutdown_read) + IMPLEMENT_STATUS_FLAG (bool, saw_shutdown_write) + IMPLEMENT_STATUS_FLAG (bool, saw_reuseaddr) + IMPLEMENT_STATUS_FLAG (bool, owner) + IMPLEMENT_STATUS_FLAG (bool, listener) + IMPLEMENT_STATUS_FLAG (conn_state, connect_state) + + int bind (const struct sockaddr *name, int namelen); + int connect (const struct sockaddr *name, int namelen); + int listen (int backlog); + int accept (struct sockaddr *peer, int *len); + int getsockname (struct sockaddr *name, int *namelen); + int getpeername (struct sockaddr *name, int *namelen); + int getpeereid (pid_t *pid, __uid32_t *euid, __gid32_t *egid); + + int open (int flags, mode_t mode = 0); + ssize_t readv (const struct iovec *, int iovcnt, ssize_t tot = -1); + inline ssize_t recv_internal (struct _WSABUF *wsabuf, DWORD wsacnt, + DWORD flags, + struct sockaddr *from, int *fromlen); + ssize_t recvfrom (void *ptr, size_t len, int flags, + struct sockaddr *from, int *fromlen); + ssize_t recvmsg (struct msghdr *msg, int flags, ssize_t tot = -1); + + ssize_t writev (const struct iovec *, int iovcnt, ssize_t tot = -1); + inline ssize_t send_internal (struct _WSABUF *wsabuf, DWORD wsacnt, int flags, + const struct sockaddr *to, int tolen); + ssize_t sendto (const void *ptr, size_t len, int flags, + const struct sockaddr *to, int tolen); + ssize_t sendmsg (const struct msghdr *msg, int flags, ssize_t tot = -1); + + int ioctl (unsigned int cmd, void *); + int fcntl (int cmd, void *); + _off64_t lseek (_off64_t, int) { return 0; } + int shutdown (int how); + int close (); + void hclose (HANDLE) {close ();} + int dup (fhandler_base *child); + + void set_close_on_exec (bool val); + virtual void fixup_before_fork_exec (DWORD); + void fixup_after_fork (HANDLE); + void fixup_after_exec (); + bool need_fixup_before () const {return true;} + char *get_proc_fd_name (char *buf); + + select_record *select_read (select_record *s); + select_record *select_write (select_record *s); + select_record *select_except (select_record *s); + void set_addr_family (int af) {addr_family = af;} + int get_addr_family () {return addr_family;} + void set_socket_type (int st) { type = st;} + int get_socket_type () {return type;} + void set_sun_path (const char *path); + char *get_sun_path () {return sun_path;} + + int __stdcall fstat (struct __stat64 *buf) __attribute__ ((regparm (2))); + int __stdcall fchmod (mode_t mode) __attribute__ ((regparm (1))); + int __stdcall fchown (__uid32_t uid, __gid32_t gid) __attribute__ ((regparm (2))); + int __stdcall facl (int, int, __acl32 *) __attribute__ ((regparm (3))); + int __stdcall link (const char *) __attribute__ ((regparm (2))); + bool is_slow () {return 1;} +}; + +class fhandler_pipe: public fhandler_base +{ +protected: + HANDLE guard; + bool broken_pipe; + HANDLE writepipe_exists; + DWORD orig_pid; + unsigned id; +public: + fhandler_pipe (); + _off64_t lseek (_off64_t offset, int whence); + select_record *select_read (select_record *s); + select_record *select_write (select_record *s); + select_record *select_except (select_record *s); + char *get_proc_fd_name (char *buf); + void set_close_on_exec (bool val); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + int open (int flags, mode_t mode = 0); + int close (); + void create_guard (SECURITY_ATTRIBUTES *sa) + { + guard = CreateMutex (sa, FALSE, NULL); + ProtectHandleINH (guard); + } + int dup (fhandler_base *child); + int ioctl (unsigned int cmd, void *); + void fixup_in_child (); + virtual void fixup_after_fork (HANDLE); + void fixup_after_exec (); + bool hit_eof (); + void set_eof () {broken_pipe = true;} + HANDLE get_guard () const {return guard;} + int ready_for_read (int fd, DWORD howlong); + static int create (fhandler_pipe *[2], unsigned, int, bool = false); + bool is_slow () {return true;} + static int create_selectable (LPSECURITY_ATTRIBUTES, HANDLE&, HANDLE&, DWORD, bool); + friend class fhandler_fifo; +}; + +class fhandler_fifo: public fhandler_pipe +{ + HANDLE output_handle; + long read_use; + long write_use; + virtual HANDLE& get_io_handle () { return io_handle ?: output_handle; } +public: + fhandler_fifo (); + int open (int flags, mode_t mode = 0); + int open_not_mine (int flags) __attribute__ ((regparm (2))); + int close (); + void set_use (int flags) __attribute__ ((regparm (2))); + bool isfifo () { return true; } + HANDLE& get_output_handle () { return output_handle; } + void set_output_handle (HANDLE h) { output_handle = h; } + void set_use (); + int dup (fhandler_base *child); + bool is_slow () {return true;} + void close_one_end (); +}; + +class fhandler_dev_raw: public fhandler_base +{ + protected: + char *devbuf; + size_t devbufsiz; + size_t devbufstart; + size_t devbufend; + struct status_flags + { + unsigned lastblk_to_read : 1; + public: + status_flags () : lastblk_to_read (0) {} + } status; + + IMPLEMENT_STATUS_FLAG (bool, lastblk_to_read) + + fhandler_dev_raw (); + + public: + ~fhandler_dev_raw (); + + int open (int flags, mode_t mode = 0); + + int __stdcall fstat (struct __stat64 *buf) __attribute__ ((regparm (2))); + + int dup (fhandler_base *child); + int ioctl (unsigned int cmd, void *buf); + + void fixup_after_fork (HANDLE); + void fixup_after_exec (); +}; + +class fhandler_dev_floppy: public fhandler_dev_raw +{ + private: + _off64_t drive_size; + unsigned long bytes_per_sector; + struct status_flags + { + unsigned eom_detected : 1; + public: + status_flags () : eom_detected (0) {} + } status; + + IMPLEMENT_STATUS_FLAG (bool, eom_detected) + + inline _off64_t get_current_position (); + int fhandler_dev_floppy::get_drive_info (struct hd_geometry *geo); + + BOOL write_file (const void *buf, DWORD to_write, DWORD *written, int *err); + BOOL read_file (void *buf, DWORD to_read, DWORD *read, int *err); + + public: + fhandler_dev_floppy (); + + int open (int flags, mode_t mode = 0); + int dup (fhandler_base *child); + void raw_read (void *ptr, size_t& ulen); + int raw_write (const void *ptr, size_t ulen); + _off64_t lseek (_off64_t offset, int whence); + int ioctl (unsigned int cmd, void *buf); +}; + +class fhandler_dev_tape: public fhandler_dev_raw +{ + HANDLE mt_mtx; + HANDLE mt_evt; + + bool is_rewind_device () { return get_minor () < 128; } + unsigned int driveno () { return (unsigned int) get_minor () & 0x7f; } + void drive_init (); + + inline bool _lock (); + inline int unlock (int ret = 0); + + public: + fhandler_dev_tape (); + + virtual int open (int flags, mode_t mode = 0); + virtual int close (); + + void raw_read (void *ptr, size_t& ulen); + int raw_write (const void *ptr, size_t ulen); + + virtual _off64_t lseek (_off64_t offset, int whence); + + virtual int __stdcall fstat (struct __stat64 *buf) __attribute__ ((regparm (2))); + + virtual int dup (fhandler_base *child); + virtual void fixup_after_fork (HANDLE parent); + virtual void set_close_on_exec (bool val); + virtual int ioctl (unsigned int cmd, void *buf); +}; + +/* Standard disk file */ + +class fhandler_disk_file: public fhandler_base +{ + void touch_ctime (); + int readdir_helper (DIR *, dirent *, DWORD, DWORD, char *) __attribute__ ((regparm (3))); + int readdir_9x (DIR *, dirent *) __attribute__ ((regparm (3))); + + public: + fhandler_disk_file (); + fhandler_disk_file (path_conv &pc); + + int open (int flags, mode_t mode); + int close (); + int lock (int, struct __flock64 *); + bool isdevice () { return false; } + int __stdcall fstat (struct __stat64 *buf) __attribute__ ((regparm (2))); + int __stdcall fchmod (mode_t mode) __attribute__ ((regparm (1))); + int __stdcall fchown (__uid32_t uid, __gid32_t gid) __attribute__ ((regparm (2))); + int __stdcall facl (int, int, __acl32 *) __attribute__ ((regparm (3))); + int __stdcall ftruncate (_off64_t) __attribute__ ((regparm (2))); + int __stdcall link (const char *) __attribute__ ((regparm (2))); + int __stdcall utimes (const struct timeval *) __attribute__ ((regparm (2))); + + HANDLE mmap (caddr_t *addr, size_t len, int prot, int flags, _off64_t off); + int munmap (HANDLE h, caddr_t addr, size_t len); + int msync (HANDLE h, caddr_t addr, size_t len, int flags); + bool fixup_mmap_after_fork (HANDLE h, int prot, int flags, + _off64_t offset, DWORD size, void *address); + int mkdir (mode_t mode); + int rmdir (); + DIR *opendir (); + int readdir (DIR *, dirent *) __attribute__ ((regparm (3))); + _off64_t telldir (DIR *); + void seekdir (DIR *, _off64_t); + void rewinddir (DIR *); + int closedir (DIR *); + + ssize_t __stdcall pread (void *, size_t, _off64_t) __attribute__ ((regparm (3))); + ssize_t __stdcall pwrite (void *, size_t, _off64_t) __attribute__ ((regparm (3))); +}; + +class fhandler_cygdrive: public fhandler_disk_file +{ + int ndrives; + const char *pdrive; + void set_drives (); + public: + fhandler_cygdrive (); + int open (int flags, mode_t mode); + int close (); + DIR *opendir (); + int readdir (DIR *, dirent *) __attribute__ ((regparm (3))); + void rewinddir (DIR *); + int closedir (DIR *); + int __stdcall fstat (struct __stat64 *buf) __attribute__ ((regparm (2))); +}; + +class fhandler_serial: public fhandler_base +{ + private: + size_t vmin_; /* from termios */ + unsigned int vtime_; /* from termios */ + pid_t pgrp_; + int rts; /* for Windows 9x purposes only */ + int dtr; /* for Windows 9x purposes only */ + + public: + int overlapped_armed; + OVERLAPPED io_status; + DWORD ev; + + /* Constructor */ + fhandler_serial (); + + int open (int flags, mode_t mode); + int close (); + void init (HANDLE h, DWORD a, mode_t flags); + void overlapped_setup (); + int dup (fhandler_base *child); + void raw_read (void *ptr, size_t& ulen); + int raw_write (const void *ptr, size_t ulen); + int tcsendbreak (int); + int tcdrain (); + int tcflow (int); + int ioctl (unsigned int cmd, void *); + int switch_modem_lines (int set, int clr); + int tcsetattr (int a, const struct termios *t); + int tcgetattr (struct termios *t); + _off64_t lseek (_off64_t, int) { return 0; } + int tcflush (int); + int is_tty () { return 1; } + void fixup_after_fork (HANDLE parent); + void fixup_after_exec (); + + /* We maintain a pgrp so that tcsetpgrp and tcgetpgrp work, but we + don't use it for permissions checking. fhandler_tty_slave does + permission checking on pgrps. */ + virtual int tcgetpgrp () { return pgrp_; } + virtual int tcsetpgrp (const pid_t pid) { pgrp_ = pid; return 0; } + select_record *select_read (select_record *s); + select_record *select_write (select_record *s); + select_record *select_except (select_record *s); + bool is_slow () {return 1;} +}; + +#define acquire_output_mutex(ms) \ + __acquire_output_mutex (__PRETTY_FUNCTION__, __LINE__, ms); + +#define release_output_mutex() \ + __release_output_mutex (__PRETTY_FUNCTION__, __LINE__); + +class tty; +class tty_min; +class fhandler_termios: public fhandler_base +{ + protected: + HANDLE output_handle; + virtual void doecho (const void *, DWORD) {}; + virtual int accept_input () {return 1;}; + public: + tty_min *tc; + fhandler_termios () : + fhandler_base () + { + need_fork_fixup (true); + } + HANDLE& get_output_handle () { return output_handle; } + line_edit_status line_edit (const char *rptr, int nread, termios&); + void set_output_handle (HANDLE h) { output_handle = h; } + void tcinit (tty_min *this_tc, bool force = false); + virtual int is_tty () { return 1; } + int tcgetpgrp (); + int tcsetpgrp (int pid); + bg_check_types bg_check (int sig); + virtual DWORD __acquire_output_mutex (const char *fn, int ln, DWORD ms) {return 1;} + virtual void __release_output_mutex (const char *fn, int ln) {} + void echo_erase (int force = 0); + virtual _off64_t lseek (_off64_t, int); +}; + +enum ansi_intensity +{ + INTENSITY_INVISIBLE, + INTENSITY_DIM, + INTENSITY_NORMAL, + INTENSITY_BOLD +}; + +#define normal 0 +#define gotesc 1 +#define gotsquare 2 +#define gotarg1 3 +#define gotrsquare 4 +#define gotcommand 5 +#define gettitle 6 +#define eattitle 7 +#define MAXARGS 10 + +class dev_console +{ + WORD default_color, underline_color, dim_color; + + /* Used to determine if an input keystroke should be modified with META. */ + int meta_mask; + +/* Output state */ + int state_; + int args_[MAXARGS]; + int nargs_; + unsigned rarg; + bool saw_question_mark; + bool alternate_charset_active; + bool metabit; + + char my_title_buf [TITLESIZE + 1]; + + WORD current_win32_attr; + ansi_intensity intensity; + bool underline, blink, reverse; + WORD fg, bg; + + /* saved cursor coordinates */ + int savex, savey; + + /* saved screen */ + COORD savebufsiz; + PCHAR_INFO savebuf; + + struct + { + short Top, Bottom; + } scroll_region; + struct + { + SHORT winTop; + SHORT winBottom; + COORD dwWinSize; + COORD dwBufferSize; + COORD dwCursorPosition; + WORD wAttributes; + } info; + + COORD dwLastCursorPosition; + DWORD dwLastButtonState; + int nModifiers; + + bool insert_mode; + bool use_mouse; + bool raw_win32_keyboard_mode; + + bool con_to_str (char *d, const char *s, DWORD sz); + bool str_to_con (char *d, const char *s, DWORD sz); + void set_color (HANDLE); + bool fillin_info (HANDLE); + void set_default_attr (); + + friend class fhandler_console; +}; + +/* This is a input and output console handle */ +class fhandler_console: public fhandler_termios +{ + private: + static dev_console *dev_state; + static bool invisible_console; + +/* Output calls */ + void set_default_attr (); + + void clear_screen (int, int, int, int); + void scroll_screen (int, int, int, int, int, int); + void cursor_set (bool, int, int); + void cursor_get (int *, int *); + void cursor_rel (int, int); + const unsigned char *write_normal (unsigned const char*, unsigned const char *); + void char_command (char); + bool set_raw_win32_keyboard_mode (bool); + int output_tcsetattr (int a, const struct termios *t); + +/* Input calls */ + int igncr_enabled (); + int input_tcsetattr (int a, const struct termios *t); + void set_cursor_maybe (); + + public: + fhandler_console (); + + fhandler_console* is_console () { return this; } + + int open (int flags, mode_t mode = 0); + + int write (const void *ptr, size_t len); + void doecho (const void *str, DWORD len) { (void) write (str, len); } + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + int close (); + + int tcflush (int); + int tcsetattr (int a, const struct termios *t); + int tcgetattr (struct termios *t); + + /* Special dup as we must dup two handles */ + int dup (fhandler_base *child); + + int ioctl (unsigned int cmd, void *); + void init (HANDLE, DWORD, mode_t); + bool mouse_aware () {return dev_state->use_mouse;} + + select_record *select_read (select_record *s); + select_record *select_write (select_record *s); + select_record *select_except (select_record *s); + void fixup_after_fork_exec (bool); + void fixup_after_exec () {fixup_after_fork_exec (true);} + void fixup_after_fork (HANDLE) {fixup_after_fork_exec (false);} + void set_close_on_exec (bool val); + void set_input_state (); + void send_winch_maybe (); + static tty_min *get_tty_stuff (int); + bool is_slow () {return 1;} + static bool need_invisible (); + static bool fhandler_console::has_a () {return !invisible_console;} +}; + +class fhandler_tty_common: public fhandler_termios +{ + public: + fhandler_tty_common () + : fhandler_termios (), output_done_event (NULL), + ioctl_request_event (NULL), ioctl_done_event (NULL), output_mutex (NULL), + input_mutex (NULL), input_available_event (NULL) + { + // nothing to do + } + HANDLE output_done_event; // Raised by master when tty's output buffer + // written. Write status in tty::write_retval. + HANDLE ioctl_request_event; // Raised by slave to perform ioctl() request. + // Ioctl() request in tty::cmd/arg. + HANDLE ioctl_done_event; // Raised by master on ioctl() completion. + // Ioctl() status in tty::ioctl_retval. + HANDLE output_mutex, input_mutex; + HANDLE input_available_event; + + DWORD __acquire_output_mutex (const char *fn, int ln, DWORD ms); + void __release_output_mutex (const char *fn, int ln); + + tty *get_ttyp () { return (tty *) tc; } + + int close (); + _off64_t lseek (_off64_t, int); + void set_close_on_exec (bool val); + select_record *select_read (select_record *s); + select_record *select_write (select_record *s); + select_record *select_except (select_record *s); + bool is_slow () {return 1;} +}; + +class fhandler_tty_slave: public fhandler_tty_common +{ + HANDLE inuse; // used to indicate that a tty is in use + public: + /* Constructor */ + fhandler_tty_slave (); + + int open (int flags, mode_t mode = 0); + int write (const void *ptr, size_t len); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + void init (HANDLE, DWORD, mode_t); + + int tcsetattr (int a, const struct termios *t); + int tcgetattr (struct termios *t); + int tcflush (int); + int ioctl (unsigned int cmd, void *); + int close (); + int dup (fhandler_base *child); + void fixup_after_fork (HANDLE parent); + void fixup_after_exec (); + + select_record *select_read (select_record *s); + int cygserver_attach_tty (HANDLE*, HANDLE*); + int get_unit (); + virtual char const *ttyname () { return pc.dev.name; } +}; + +class fhandler_pty_master: public fhandler_tty_common +{ + int pktmode; // non-zero if pty in a packet mode. +public: + int need_nl; // Next read should start with \n + DWORD dwProcessId; // Owner of master handles + + /* Constructor */ + fhandler_pty_master (); + + int process_slave_output (char *buf, size_t len, int pktmode_on); + void doecho (const void *str, DWORD len); + int accept_input (); + int open (int flags, mode_t mode = 0); + int write (const void *ptr, size_t len); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + int close (); + + int tcsetattr (int a, const struct termios *t); + int tcgetattr (struct termios *t); + int tcflush (int); + int ioctl (unsigned int cmd, void *); + + char *ptsname (); + + HANDLE from_master, to_master; + bool hit_eof (); + bool setup (bool); + int dup (fhandler_base *); + void fixup_after_fork (HANDLE parent); + void fixup_after_exec (); +}; + +class fhandler_tty_master: public fhandler_pty_master +{ + public: + /* Constructor */ + fhandler_console *console; // device handler to perform real i/o. + + fhandler_tty_master (); + int init (); + int init_console (); + void set_winsize (bool); + bool is_slow () {return 1;} +}; + +class fhandler_dev_null: public fhandler_base +{ + public: + fhandler_dev_null (); + int open (int, mode_t); + + select_record *select_read (select_record *s); + select_record *select_write (select_record *s); + select_record *select_except (select_record *s); +}; + +class fhandler_dev_zero: public fhandler_base +{ + public: + fhandler_dev_zero (); + int open (int flags, mode_t mode = 0); + int write (const void *ptr, size_t len); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + _off64_t lseek (_off64_t offset, int whence); + + virtual HANDLE mmap (caddr_t *addr, size_t len, int prot, + int flags, _off64_t off); + virtual int munmap (HANDLE h, caddr_t addr, size_t len); + virtual int msync (HANDLE h, caddr_t addr, size_t len, int flags); + virtual bool fixup_mmap_after_fork (HANDLE h, int prot, int flags, + _off64_t offset, DWORD size, + void *address); +}; + +class fhandler_dev_random: public fhandler_base +{ + protected: + HCRYPTPROV crypt_prov; + long pseudo; + + bool crypt_gen_random (void *ptr, size_t len); + int pseudo_write (const void *ptr, size_t len); + int pseudo_read (void *ptr, size_t len); + + public: + fhandler_dev_random (); + int open (int flags, mode_t mode = 0); + int write (const void *ptr, size_t len); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + _off64_t lseek (_off64_t offset, int whence); + int close (); + int dup (fhandler_base *child); +}; + +class fhandler_dev_mem: public fhandler_base +{ + protected: + DWORD mem_size; + _off64_t pos; + + public: + fhandler_dev_mem (); + ~fhandler_dev_mem (); + + int open (int flags, mode_t mode = 0); + int write (const void *ptr, size_t ulen); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + _off64_t lseek (_off64_t offset, int whence); + int __stdcall fstat (struct __stat64 *buf) __attribute__ ((regparm (2))); + int dup (fhandler_base *child); + + HANDLE mmap (caddr_t *addr, size_t len, int prot, int flags, _off64_t off); + int munmap (HANDLE h, caddr_t addr, size_t len); + int msync (HANDLE h, caddr_t addr, size_t len, int flags); + bool fixup_mmap_after_fork (HANDLE h, int prot, int flags, + _off64_t offset, DWORD size, void *address); +} ; + +class fhandler_dev_clipboard: public fhandler_base +{ + _off64_t pos; + void *membuffer; + size_t msize; + bool eof; + public: + fhandler_dev_clipboard (); + int is_windows () { return 1; } + int open (int flags, mode_t mode = 0); + int write (const void *ptr, size_t len); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + _off64_t lseek (_off64_t offset, int whence); + int close (); + + int dup (fhandler_base *child); + void fixup_after_exec (); +}; + +class fhandler_windows: public fhandler_base +{ + private: + HWND hWnd_; // the window whose messages are to be retrieved by read() call + int method_; // write method (Post or Send) + public: + fhandler_windows (); + int is_windows () { return 1; } + int open (int flags, mode_t mode = 0); + int write (const void *ptr, size_t len); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + int ioctl (unsigned int cmd, void *); + _off64_t lseek (_off64_t, int) { return 0; } + int close () { return 0; } + + void set_close_on_exec (bool val); + void fixup_after_fork (HANDLE parent); + select_record *select_read (select_record *s); + select_record *select_write (select_record *s); + select_record *select_except (select_record *s); + bool is_slow () {return 1;} +}; + +class fhandler_dev_dsp: public fhandler_base +{ + public: + class Audio; + class Audio_out; + class Audio_in; + private: + int audioformat_; + int audiofreq_; + int audiobits_; + int audiochannels_; + Audio_out *audio_out_; + Audio_in *audio_in_; + public: + fhandler_dev_dsp (); + + int open (int flags, mode_t mode = 0); + int write (const void *ptr, size_t len); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + int ioctl (unsigned int cmd, void *); + _off64_t lseek (_off64_t, int); + int close (); + int dup (fhandler_base *child); + void fixup_after_fork (HANDLE parent); + void fixup_after_exec (); + private: + void close_audio_in (); + void close_audio_out (bool immediately = false); +}; + +class fhandler_virtual : public fhandler_base +{ + protected: + char *filebuf; + size_t bufalloc; + _off64_t filesize; + _off64_t position; + int fileid; // unique within each class + public: + + fhandler_virtual (); + virtual ~fhandler_virtual(); + + virtual int exists(); + virtual DIR *opendir (); + _off64_t telldir (DIR *); + void seekdir (DIR *, _off64_t); + void rewinddir (DIR *); + int closedir (DIR *); + int write (const void *ptr, size_t len); + void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3))); + _off64_t lseek (_off64_t, int); + int dup (fhandler_base *child); + int open (int flags, mode_t mode = 0); + int close (); + int __stdcall fstat (struct stat *buf) __attribute__ ((regparm (2))); + int __stdcall fchmod (mode_t mode) __attribute__ ((regparm (1))); + int __stdcall fchown (__uid32_t uid, __gid32_t gid) __attribute__ ((regparm (2))); + int __stdcall facl (int, int, __acl32 *) __attribute__ ((regparm (3))); + virtual bool fill_filebuf (); + char *get_filebuf () { return filebuf; } + void fixup_after_exec (); +}; + +class fhandler_proc: public fhandler_virtual +{ + public: + fhandler_proc (); + int exists(); + int readdir (DIR *, dirent *) __attribute__ ((regparm (3))); + static DWORD get_proc_fhandler(const char *path); + + int open (int flags, mode_t mode = 0); + int __stdcall fstat (struct __stat64 *buf) __attribute__ ((regparm (2))); + bool fill_filebuf (); +}; + +class fhandler_netdrive: public fhandler_virtual +{ + public: + fhandler_netdrive (); + int exists(); + int readdir (DIR *, dirent *) __attribute__ ((regparm (3))); + void seekdir (DIR *, _off64_t); + void rewinddir (DIR *); + int closedir (DIR *); + int open (int flags, mode_t mode = 0); + int __stdcall fstat (struct __stat64 *buf) __attribute__ ((regparm (2))); +}; + +class fhandler_registry: public fhandler_proc +{ + private: + char *value_name; + public: + fhandler_registry (); + int exists(); + int readdir (DIR *, dirent *) __attribute__ ((regparm (3))); + _off64_t telldir (DIR *); + void seekdir (DIR *, _off64_t); + void rewinddir (DIR *); + int closedir (DIR *); + + int open (int flags, mode_t mode = 0); + int __stdcall fstat (struct __stat64 *buf) __attribute__ ((regparm (2))); + bool fill_filebuf (); + int close (); +}; + +class pinfo; +class fhandler_process: public fhandler_proc +{ + pid_t pid; + public: + fhandler_process (); + int exists(); + DIR *opendir (); + int readdir (DIR *, dirent *) __attribute__ ((regparm (3))); + int open (int flags, mode_t mode = 0); + int __stdcall fstat (struct __stat64 *buf) __attribute__ ((regparm (2))); + bool fill_filebuf (); +}; + +struct fhandler_nodevice: public fhandler_base +{ + fhandler_nodevice (); + int open (int flags, mode_t mode = 0); + // int __stdcall fstat (struct __stat64 *buf, path_conv *); +}; + +#define report_tty_counts(fh, call, use_op) \ + termios_printf ("%s %s, %susecount %d",\ + fh->ttyname (), call,\ + use_op, ((fhandler_tty_slave *) fh)->archetype->usecount); + +typedef union +{ + char __base[sizeof (fhandler_base)]; + char __console[sizeof (fhandler_console)]; + char __cygdrive[sizeof (fhandler_cygdrive)]; + char __dev_clipboard[sizeof (fhandler_dev_clipboard)]; + char __dev_dsp[sizeof (fhandler_dev_dsp)]; + char __dev_floppy[sizeof (fhandler_dev_floppy)]; + char __dev_mem[sizeof (fhandler_dev_mem)]; + char __dev_null[sizeof (fhandler_dev_null)]; + char __dev_random[sizeof (fhandler_dev_random)]; + char __dev_raw[sizeof (fhandler_dev_raw)]; + char __dev_tape[sizeof (fhandler_dev_tape)]; + char __dev_zero[sizeof (fhandler_dev_zero)]; + char __disk_file[sizeof (fhandler_disk_file)]; + char __fifo[sizeof (fhandler_fifo)]; + char __mailslot[sizeof (fhandler_mailslot)]; + char __netdrive[sizeof (fhandler_netdrive)]; + char __nodevice[sizeof (fhandler_nodevice)]; + char __pipe[sizeof (fhandler_pipe)]; + char __proc[sizeof (fhandler_proc)]; + char __process[sizeof (fhandler_process)]; + char __pty_master[sizeof (fhandler_pty_master)]; + char __registry[sizeof (fhandler_registry)]; + char __serial[sizeof (fhandler_serial)]; + char __socket[sizeof (fhandler_socket)]; + char __termios[sizeof (fhandler_termios)]; + char __tty_common[sizeof (fhandler_tty_common)]; + char __tty_master[sizeof (fhandler_tty_master)]; + char __tty_slave[sizeof (fhandler_tty_slave)]; + char __virtual[sizeof (fhandler_virtual)]; + char __windows[sizeof (fhandler_windows)]; +} fhandler_union; + +struct select_record +{ + int fd; + HANDLE h; + fhandler_base *fh; + int thread_errno; + bool windows_handle; + bool read_ready, write_ready, except_ready; + bool read_selected, write_selected, except_selected; + bool except_on_write; + int (*startup) (select_record *me, class select_stuff *stuff); + int (*peek) (select_record *, bool); + int (*verify) (select_record *me, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds); + void (*cleanup) (select_record *me, class select_stuff *stuff); + struct select_record *next; + void set_select_errno () {__seterrno (); thread_errno = errno;} + int saw_error () {return thread_errno;} + + select_record (fhandler_base *in_fh = NULL) : fd (0), h (NULL), + fh (in_fh), thread_errno (0), windows_handle (false), + read_ready (false), write_ready (false), except_ready (false), + read_selected (false), write_selected (false), + except_selected (false), except_on_write (false), + startup (NULL), peek (NULL), verify (NULL), cleanup (NULL), + next (NULL) {} +}; + +class select_stuff +{ + public: + ~select_stuff (); + bool always_ready, windows_used; + select_record start; + void *device_specific_pipe; + void *device_specific_socket; + void *device_specific_serial; + void *device_specific_mailslot; + + int test_and_set (int i, fd_set *readfds, fd_set *writefds, + fd_set *exceptfds); + int poll (fd_set *readfds, fd_set *writefds, fd_set *exceptfds); + int wait (fd_set *readfds, fd_set *writefds, fd_set *exceptfds, DWORD ms); + void cleanup (); + select_stuff (): always_ready (0), windows_used (0), start (0), + device_specific_pipe (0), + device_specific_socket (0), + device_specific_serial (0), + device_specific_mailslot (0) {} +}; + +void __stdcall set_console_state_for_spawn (bool); +#endif /* _FHANDLER_H_ */ diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc index 51d72fc8f90..b71f8bb31c8 100644 --- a/winsup/cygwin/fhandler_socket.cc +++ b/winsup/cygwin/fhandler_socket.cc @@ -30,6 +30,7 @@ #include "fhandler.h" #include "dtable.h" #include "cygheap.h" +#include "shared_info.h" #include "sigproc.h" #include "cygthread.h" #include "select.h" @@ -40,6 +41,7 @@ #include "cygwin/in6.h" #define ASYNC_MASK (FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT) +#define EVENT_MASK (FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT|FD_CLOSE) extern bool fdsock (cygheap_fdmanip& fd, const device *, SOCKET soc); extern "C" { @@ -128,6 +130,9 @@ get_inet_addr (const struct sockaddr *in, int inlen, fhandler_socket::fhandler_socket () : fhandler_base (), + wsock_mtx (NULL), + wsock_evt (NULL), + wsock_events (NULL), sun_path (NULL), status () { @@ -179,10 +184,11 @@ fhandler_socket::af_local_setblocking (bool &async, bool &nonblocking) { async = async_io (); nonblocking = is_nonblocking (); - if (async || nonblocking) - WSAAsyncSelect (get_socket (), winmsg, 0, 0); - unsigned long p = 0; - ioctlsocket (get_socket (), FIONBIO, &p); + if (async) + { + WSAAsyncSelect (get_socket (), winmsg, 0, 0); + WSAEventSelect (get_socket (), wsock_evt, EVENT_MASK); + } set_nonblocking (false); async_io (false); } @@ -191,11 +197,7 @@ void fhandler_socket::af_local_unsetblocking (bool async, bool nonblocking) { if (nonblocking) - { - unsigned long p = 1; - ioctlsocket (get_socket (), FIONBIO, &p); - set_nonblocking (true); - } + set_nonblocking (true); if (async) { WSAAsyncSelect (get_socket (), winmsg, WM_ASYNCIO, ASYNC_MASK); @@ -393,6 +395,220 @@ fhandler_socket::af_local_set_secret (char *buf) connect_secret [2], connect_secret [3]); } +struct wsa_event +{ + LONG serial_number; + long events; + int connect_errorcode; +}; + +/* Maximum number of concurrently opened sockets from all Cygwin processes + on a machine. Note that shared sockets (through dup/fork/exec) are + counted as one socket. */ +#define NUM_SOCKS (65536 / sizeof (wsa_event)) + +static wsa_event wsa_events[NUM_SOCKS] __attribute__((section (".cygwin_dll_common"), shared)) = { 0 }; + +static LONG socket_serial_number __attribute__((section (".cygwin_dll_common"), shared)) = 0; + +static HANDLE wsa_slot_mtx; + +static wsa_event * +search_wsa_event_slot (LONG new_serial_number) +{ + char name[CYG_MAX_PATH], searchname[CYG_MAX_PATH]; + + if (!wsa_slot_mtx) + { + wsa_slot_mtx = CreateMutex (&sec_all, FALSE, + shared_name (name, "sock", 0)); + if (!wsa_slot_mtx) + api_fatal ("Couldn't create/open shared socket mutex, %E"); + } + switch (WaitForSingleObject (wsa_slot_mtx, INFINITE)) + { + case WAIT_OBJECT_0: + case WAIT_ABANDONED: + break; + default: + api_fatal ("WFSO failed for shared socket mutex, %E"); + break; + } + unsigned int slot = new_serial_number % NUM_SOCKS; + while (wsa_events[slot].serial_number) + { + HANDLE searchmtx = OpenMutex (STANDARD_RIGHTS_READ, FALSE, + shared_name (searchname, "sock", wsa_events[slot].serial_number)); + if (!searchmtx) + break; + /* Mutex still exists, attached socket is active, try next slot. */ + CloseHandle (searchmtx); + slot = (slot + 1) % NUM_SOCKS; + if (slot == (new_serial_number % NUM_SOCKS)) + { + /* Did the whole array once. Too bad. */ + debug_printf ("No free socket slot"); + ReleaseMutex (wsa_slot_mtx); + return NULL; + } + } + wsa_events[slot].serial_number = new_serial_number; + ReleaseMutex (wsa_slot_mtx); + return wsa_events + slot; +} + +bool +fhandler_socket::prepare () +{ + LONG new_serial_number; + char name[CYG_MAX_PATH]; + DWORD err = 0; + + do + { + new_serial_number = InterlockedIncrement (&socket_serial_number); + if (!new_serial_number) /* 0 is reserved for global mutex */ + InterlockedIncrement (&socket_serial_number); + wsock_mtx = CreateMutex (&sec_all, FALSE, + shared_name (name, "sock", new_serial_number)); + if (!wsock_mtx) + { + debug_printf ("CreateMutex, %E"); + set_errno (ENOBUFS); + return false; + } + err = GetLastError (); + if (err == ERROR_ALREADY_EXISTS) + CloseHandle (wsock_mtx); + } + while (err == ERROR_ALREADY_EXISTS); + if ((wsock_evt = CreateEvent (&sec_all, TRUE, FALSE, NULL)) + == WSA_INVALID_EVENT) + { + debug_printf ("WSACreateEvent, %E"); + set_errno (ENOBUFS); + CloseHandle (wsock_mtx); + return false; + } + if (WSAEventSelect (get_socket (), wsock_evt, EVENT_MASK) == SOCKET_ERROR) + { + debug_printf ("WSAEventSelect, %E"); + set_winsock_errno (); + CloseHandle (wsock_evt); + CloseHandle (wsock_mtx); + return false; + } + wsock_events = search_wsa_event_slot (new_serial_number); + memset (wsock_events, 0, sizeof *wsock_events); + return true; +} + +int +fhandler_socket::wait (long event_mask) +{ + int ret = SOCKET_ERROR; + int wsa_err = 0; + DWORD timeout = (is_nonblocking () ? 0 : INFINITE); + long events; + + if (async_io ()) + return 0; + + WaitForSingleObject (wsock_mtx, INFINITE); + WSAEVENT ev[2] = { wsock_evt, signal_arrived }; + +sa_rerun: + + if ((events = (wsock_events->events & event_mask)) != 0) + { + if (events & FD_CONNECT) + { + wsa_err = wsock_events->connect_errorcode; + wsock_events->connect_errorcode = 0; + } + wsock_events->events &= ~(events & ~FD_CLOSE); + if (!wsa_err) + ret = 0; + else + WSASetLastError (wsa_err); + ReleaseMutex (wsock_mtx); + return ret; + } + ReleaseMutex (wsock_mtx); + +/* If WSAWaitForMultipleEvents is interrupted by a signal, and the signal + has the SA_RESTART flag set, return to this label and... restart. */ +sa_restart: + + WSANETWORKEVENTS evts = { 0 }; + switch (WSAWaitForMultipleEvents (2, ev, FALSE, timeout, FALSE)) + { + case WSA_WAIT_TIMEOUT: + WSASetLastError (WSAEINPROGRESS); + break; + case WSA_WAIT_EVENT_0: + WaitForSingleObject (wsock_mtx, INFINITE); + if (!WSAEnumNetworkEvents (get_socket (), wsock_evt, &evts)) + { + if (!evts.lNetworkEvents) + { + if (timeout == INFINITE) + goto sa_rerun; + ReleaseMutex (wsock_mtx); + WSASetLastError (WSAEINPROGRESS); + break; + } + wsock_events->events |= evts.lNetworkEvents; + if (evts.lNetworkEvents & FD_CONNECT) + wsock_events->connect_errorcode = evts.iErrorCode[FD_CONNECT_BIT]; + if ((evts.lNetworkEvents & FD_OOB) + && !evts.iErrorCode[FD_OOB_BIT] + && owner ()) + { + siginfo_t si = {0}; + si.si_signo = SIGURG; + si.si_code = SI_KERNEL; + sig_send (myself_nowait, si); + if (_my_tls.call_signal_handler ()) + { + sig_dispatch_pending (); + goto sa_rerun; + } + if (evts.lNetworkEvents & event_mask) + goto sa_rerun; + WSASetLastError (WSAEINTR); + } + else + { + if (timeout == INFINITE || (evts.lNetworkEvents & event_mask)) + goto sa_rerun; + WSASetLastError (WSAEINPROGRESS); + } + } + ReleaseMutex (wsock_mtx); + break; + case WSA_WAIT_EVENT_0 + 1: + if (_my_tls.call_signal_handler ()) + { + sig_dispatch_pending (); + goto sa_restart; + } + WSASetLastError (WSAEINTR); + break; + default: + WSASetLastError (WSAEFAULT); + break; + } + return ret; +} + +void +fhandler_socket::release () +{ + CloseHandle (wsock_evt); + CloseHandle (wsock_mtx); +} + void fhandler_socket::fixup_before_fork_exec (DWORD win_proc_id) { @@ -445,6 +661,11 @@ fhandler_socket::fixup_after_exec () { if (!close_on_exec ()) fixup_after_fork (NULL); + else + { + CloseHandle (wsock_evt); + CloseHandle (wsock_mtx); + } } int @@ -454,6 +675,24 @@ fhandler_socket::dup (fhandler_base *child) debug_printf ("here"); fhandler_socket *fhs = (fhandler_socket *) child; + + if (!DuplicateHandle (hMainProc, wsock_mtx, hMainProc, &fhs->wsock_mtx, 0, + TRUE, DUPLICATE_SAME_ACCESS)) + { + system_printf ("DuplicateHandle(%x) failed, %E", wsock_mtx); + __seterrno (); + return -1; + } + if (!DuplicateHandle (hMainProc, wsock_evt, hMainProc, &fhs->wsock_evt, 0, + TRUE, DUPLICATE_SAME_ACCESS)) + { + system_printf ("DuplicateHandle(%x) failed, %E", wsock_evt); + __seterrno (); + CloseHandle (fhs->wsock_mtx); + return -1; + } + fhs->wsock_events = wsock_events; + fhs->addr_family = addr_family; fhs->set_socket_type (get_socket_type ()); if (get_addr_family () == AF_LOCAL) @@ -500,8 +739,10 @@ fhandler_socket::dup (fhandler_base *child) if (!DuplicateHandle (hMainProc, get_io_handle (), hMainProc, &nh, 0, FALSE, DUPLICATE_SAME_ACCESS)) { - system_printf ("!DuplicateHandle(%x) failed, %E", get_io_handle ()); + system_printf ("DuplicateHandle(%x) failed, %E", get_io_handle ()); __seterrno (); + CloseHandle (fhs->wsock_evt); + CloseHandle (fhs->wsock_mtx); return -1; } VerifyHandle (nh); @@ -755,6 +996,10 @@ fhandler_socket::connect (const struct sockaddr *name, int namelen) } res = ::connect (get_socket (), (struct sockaddr *) &sst, namelen); + if (!is_nonblocking () + && res == SOCKET_ERROR + && WSAGetLastError () == WSAEWOULDBLOCK) + res = wait (FD_CONNECT | FD_CLOSE); if (!res) err = 0; @@ -840,6 +1085,7 @@ fhandler_socket::listen (int backlog) if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM) af_local_set_cred (); connect_state (connected); + listener (true); } else set_winsock_errno (); @@ -849,8 +1095,6 @@ fhandler_socket::listen (int backlog) int fhandler_socket::accept (struct sockaddr *peer, int *len) { - int res = -1; - /* Allows NULL peer and len parameters. */ struct sockaddr_in peer_dummy; int len_dummy; @@ -869,7 +1113,11 @@ fhandler_socket::accept (struct sockaddr *peer, int *len) if (len && ((unsigned) *len < sizeof (struct sockaddr_in))) *len = sizeof (struct sockaddr_in); - res = ::accept (get_socket (), peer, len); + + int res = 0; + while (!(res = wait (FD_ACCEPT | FD_CLOSE)) + && (res = ::accept (get_socket (), peer, len)) == WSAEWOULDBLOCK) + ; if (res == (int) INVALID_SOCKET) set_winsock_errno (); @@ -961,135 +1209,6 @@ fhandler_socket::getpeername (struct sockaddr *name, int *namelen) return res; } -bool -fhandler_socket::prepare (HANDLE &event, long event_mask) -{ - WSASetLastError (0); - closed (false); - if ((event = WSACreateEvent ()) == WSA_INVALID_EVENT) - { - debug_printf ("WSACreateEvent, %E"); - return false; - } - if (WSAEventSelect (get_socket (), event, event_mask) == SOCKET_ERROR) - { - debug_printf ("WSAEventSelect(evt), %d", WSAGetLastError ()); - return false; - } - return true; -} - -int -fhandler_socket::wait (HANDLE event, int flags, DWORD timeout) -{ - int ret = SOCKET_ERROR; - int wsa_err = 0; - WSAEVENT ev[2] = { event, signal_arrived }; - WSANETWORKEVENTS evts; - -/* If WSAWaitForMultipleEvents is interrupted by a signal, and the signal - has the SA_RESTART flag set, return to this label and... restart. */ -sa_restart: - - switch (WSAWaitForMultipleEvents (2, ev, FALSE, timeout, FALSE)) - { - case WSA_WAIT_TIMEOUT: - ret = 0; - break; - case WSA_WAIT_EVENT_0: - if (!WSAEnumNetworkEvents (get_socket (), event, &evts)) - { - if (!evts.lNetworkEvents) - { - ret = 0; - break; - } - if (evts.lNetworkEvents & FD_OOB) - { - if (evts.iErrorCode[FD_OOB_BIT]) - wsa_err = evts.iErrorCode[FD_OOB_BIT]; - else if (flags & MSG_OOB) - ret = 0; - else - { - raise (SIGURG); - WSASetLastError (WSAEINTR); - break; - } - } - if (evts.lNetworkEvents & FD_ACCEPT) - { - if (evts.iErrorCode[FD_ACCEPT_BIT]) - wsa_err = evts.iErrorCode[FD_ACCEPT_BIT]; - else - ret = 0; - } - if (evts.lNetworkEvents & FD_CONNECT) - { - if (evts.iErrorCode[FD_CONNECT_BIT]) - wsa_err = evts.iErrorCode[FD_CONNECT_BIT]; - else - ret = 0; - } - else if (evts.lNetworkEvents & FD_READ) - { - if (evts.iErrorCode[FD_READ_BIT]) - wsa_err = evts.iErrorCode[FD_READ_BIT]; - else - ret = 0; - } - else if (evts.lNetworkEvents & FD_WRITE) - { - if (evts.iErrorCode[FD_WRITE_BIT]) - wsa_err = evts.iErrorCode[FD_WRITE_BIT]; - else - ret = 0; - } - if (evts.lNetworkEvents & FD_CLOSE) - { - closed (true); - if (!wsa_err) - { - if (evts.iErrorCode[FD_CLOSE_BIT]) - wsa_err = evts.iErrorCode[FD_CLOSE_BIT]; - else - ret = 0; - } - } - if (wsa_err) - WSASetLastError (wsa_err); - } - break; - case WSA_WAIT_EVENT_0 + 1: - if (_my_tls.call_signal_handler ()) - { - sig_dispatch_pending (); - goto sa_restart; - } - WSASetLastError (WSAEINTR); - break; - default: - WSASetLastError (WSAEFAULT); - break; - } - return ret; -} - -void -fhandler_socket::release (HANDLE event) -{ - int last_err = WSAGetLastError (); - /* KB 168349: NT4 fails if the event parameter is not NULL. */ - if (WSAEventSelect (get_socket (), NULL, 0) == SOCKET_ERROR) - debug_printf ("WSAEventSelect(NULL), %d", WSAGetLastError ()); - WSACloseEvent (event); - unsigned long non_block = 0; - if (ioctlsocket (get_socket (), FIONBIO, &non_block)) - debug_printf ("return to blocking failed: %d", WSAGetLastError ()); - else - WSASetLastError (last_err); -} - int fhandler_socket::readv (const struct iovec *const iov, const int iovcnt, ssize_t tot) @@ -1108,38 +1227,26 @@ fhandler_socket::readv (const struct iovec *const iov, const int iovcnt, return recvmsg (&msg, 0, tot); } -int -fhandler_socket::recvfrom (void *ptr, size_t len, int flags, - struct sockaddr *from, int *fromlen) +inline ssize_t +fhandler_socket::recv_internal (WSABUF *wsabuf, DWORD wsacnt, DWORD flags, + struct sockaddr *from, int *fromlen) { - int res = SOCKET_ERROR; + ssize_t res = 0; DWORD ret = 0; - WSABUF wsabuf = { len, (char *) ptr }; - - if (is_nonblocking () || closed () || async_io ()) - { - DWORD lflags = (DWORD) (flags & MSG_WINMASK); - res = WSARecvFrom (get_socket (), &wsabuf, 1, &ret, - &lflags, from, fromlen, NULL, NULL); - } + flags &= MSG_WINMASK; + if (flags & MSG_PEEK) + res = WSARecvFrom (get_socket (), wsabuf, wsacnt, &ret, + &flags, from, fromlen, NULL, NULL); else { - HANDLE evt; - if (prepare (evt, FD_CLOSE | FD_READ | (owner () ? FD_OOB : 0))) - { - do - { - DWORD lflags = (DWORD) (flags & MSG_WINMASK); - res = WSARecvFrom (get_socket (), &wsabuf, 1, &ret, &lflags, - from, fromlen, NULL, NULL); - } - while (res == SOCKET_ERROR - && WSAGetLastError () == WSAEWOULDBLOCK - && !closed () - && !(res = wait (evt, flags))); - release (evt); - } + int evt_mask = FD_READ | FD_CLOSE + | ((flags & MSG_OOB) ? FD_OOB : 0); + while ((res = WSARecvFrom (get_socket (), wsabuf, wsacnt, &ret, + &flags, from, fromlen, NULL, NULL)) == -1 + && WSAGetLastError () == WSAEWOULDBLOCK + && !(res = wait (evt_mask))) + ; } if (res == SOCKET_ERROR) @@ -1147,7 +1254,7 @@ fhandler_socket::recvfrom (void *ptr, size_t len, int flags, /* According to SUSv3, errno isn't set in that case and no error condition is returned. */ if (WSAGetLastError () == WSAEMSGSIZE) - return len; + return ret; /* ESHUTDOWN isn't defined for recv in SUSv3. Simply EOF is returned in this case. */ @@ -1163,6 +1270,14 @@ fhandler_socket::recvfrom (void *ptr, size_t len, int flags, } int +fhandler_socket::recvfrom (void *ptr, size_t len, int flags, + struct sockaddr *from, int *fromlen) +{ + WSABUF wsabuf = { len, (char *) ptr }; + return recv_internal (&wsabuf, 1, flags, from, fromlen); +} + +int fhandler_socket::recvmsg (struct msghdr *msg, int flags, ssize_t tot) { if (CYGWIN_VERSION_CHECK_FOR_USING_ANCIENT_MSGHDR) @@ -1182,73 +1297,19 @@ fhandler_socket::recvmsg (struct msghdr *msg, int flags, ssize_t tot) /*TODO*/ } - struct iovec *const iov = msg->msg_iov; - const int iovcnt = msg->msg_iovlen; - - struct sockaddr *from = (struct sockaddr *) msg->msg_name; - int *fromlen = from ? &msg->msg_namelen : NULL; - - int res = SOCKET_ERROR; - - WSABUF wsabuf[iovcnt]; - unsigned long len = 0L; - - const struct iovec *iovptr = iov + iovcnt; - WSABUF *wsaptr = wsabuf + iovcnt; - do + WSABUF wsabuf[msg->msg_iovlen]; + WSABUF *wsaptr = wsabuf + msg->msg_iovlen; + const struct iovec *iovptr = msg->msg_iov + msg->msg_iovlen; + while (--wsaptr >= wsabuf) { - iovptr -= 1; - wsaptr -= 1; - len += wsaptr->len = iovptr->iov_len; + wsaptr->len = (--iovptr)->iov_len; wsaptr->buf = (char *) iovptr->iov_base; } - while (wsaptr != wsabuf); - - DWORD ret = 0; - - if (is_nonblocking () || closed () || async_io ()) - { - DWORD lflags = (DWORD) (flags & MSG_WINMASK); - res = WSARecvFrom (get_socket (), wsabuf, iovcnt, &ret, - &lflags, from, fromlen, NULL, NULL); - } - else - { - HANDLE evt; - if (prepare (evt, FD_CLOSE | FD_READ | (owner () ? FD_OOB : 0))) - { - do - { - DWORD lflags = (DWORD) (flags & MSG_WINMASK); - res = WSARecvFrom (get_socket (), wsabuf, iovcnt, &ret, - &lflags, from, fromlen, NULL, NULL); - } - while (res == SOCKET_ERROR - && WSAGetLastError () == WSAEWOULDBLOCK - && !closed () - && !(res = wait (evt, flags))); - release (evt); - } - } - - if (res == SOCKET_ERROR) - { - /* According to SUSv3, errno isn't set in that case and no error - condition is returned. */ - if (WSAGetLastError () == WSAEMSGSIZE) - return len; - - /* ESHUTDOWN isn't defined for recv in SUSv3. Simply EOF is returned - in this case. */ - if (WSAGetLastError () == WSAESHUTDOWN) - return 0; - set_winsock_errno (); - } - else - res = ret; + struct sockaddr *from = (struct sockaddr *) msg->msg_name; + int *fromlen = from ? &msg->msg_namelen : NULL; - return res; + return recv_internal (wsabuf, msg->msg_iovlen, flags, from, fromlen); } int @@ -1269,49 +1330,20 @@ fhandler_socket::writev (const struct iovec *const iov, const int iovcnt, return sendmsg (&msg, 0, tot); } -int -fhandler_socket::sendto (const void *ptr, size_t len, int flags, - const struct sockaddr *to, int tolen) +inline ssize_t +fhandler_socket::send_internal (struct _WSABUF *wsabuf, DWORD wsacnt, int flags, + const struct sockaddr *to, int tolen) { - struct sockaddr_storage sst; - - if (to && !get_inet_addr (to, tolen, &sst, &tolen)) - return SOCKET_ERROR; - - int res = SOCKET_ERROR; + int res = 0; DWORD ret = 0; - - WSABUF wsabuf = { len, (char *) ptr }; - - if (is_nonblocking () || closed () || async_io ()) - res = WSASendTo (get_socket (), &wsabuf, 1, &ret, - flags & MSG_WINMASK, - (to ? (const struct sockaddr *) &sst : NULL), tolen, - NULL, NULL); - else - { - HANDLE evt; - if (prepare (evt, FD_CLOSE | FD_WRITE | (owner () ? FD_OOB : 0))) - { - do - { - res = WSASendTo (get_socket (), &wsabuf, 1, &ret, - flags & MSG_WINMASK, - (to ? (const struct sockaddr *) &sst : NULL), - tolen, NULL, NULL); - } - while (res == SOCKET_ERROR - && WSAGetLastError () == WSAEWOULDBLOCK - && !(res = wait (evt, 0)) - && !closed ()); - release (evt); - } - } + while ((res = WSASendTo (get_socket (), wsabuf, wsacnt, &ret, + flags & MSG_WINMASK, to, tolen, NULL, NULL)) == -1 + && WSAGetLastError () == WSAEWOULDBLOCK + && !(res = wait (FD_WRITE | FD_CLOSE))) + ; if (res == SOCKET_ERROR) set_winsock_errno (); - else - res = ret; /* Special handling for EPIPE and SIGPIPE. @@ -1325,10 +1357,26 @@ fhandler_socket::sendto (const void *ptr, size_t len, int flags, if (! (flags & MSG_NOSIGNAL)) raise (SIGPIPE); } + else + res = ret; return res; } +ssize_t +fhandler_socket::sendto (const void *ptr, size_t len, int flags, + const struct sockaddr *to, int tolen) +{ + struct sockaddr_storage sst; + + if (to && !get_inet_addr (to, tolen, &sst, &tolen)) + return SOCKET_ERROR; + + WSABUF wsabuf = { len, (char *) ptr }; + return send_internal (&wsabuf, 1, flags, + (to ? (const struct sockaddr *) &sst : NULL), tolen); +} + int fhandler_socket::sendmsg (const struct msghdr *msg, int flags, ssize_t tot) { @@ -1341,69 +1389,17 @@ fhandler_socket::sendmsg (const struct msghdr *msg, int flags, ssize_t tot) /*TODO*/ } - struct iovec *const iov = msg->msg_iov; - const int iovcnt = msg->msg_iovlen; - - int res = SOCKET_ERROR; - - WSABUF wsabuf[iovcnt]; - - const struct iovec *iovptr = iov + iovcnt; - WSABUF *wsaptr = wsabuf + iovcnt; - do + WSABUF wsabuf[msg->msg_iovlen]; + WSABUF *wsaptr = wsabuf + msg->msg_iovlen; + const struct iovec *iovptr = msg->msg_iov + msg->msg_iovlen; + while (--wsaptr >= wsabuf) { - iovptr -= 1; - wsaptr -= 1; - wsaptr->len = iovptr->iov_len; + wsaptr->len = (--iovptr)->iov_len; wsaptr->buf = (char *) iovptr->iov_base; } - while (wsaptr != wsabuf); - - DWORD ret = 0; - - if (is_nonblocking () || closed () || async_io ()) - res = WSASendTo (get_socket (), wsabuf, iovcnt, &ret, - flags & MSG_WINMASK, (struct sockaddr *) msg->msg_name, - msg->msg_namelen, NULL, NULL); - else - { - HANDLE evt; - if (prepare (evt, FD_CLOSE | FD_WRITE | (owner () ? FD_OOB : 0))) - { - do - { - res = WSASendTo (get_socket (), wsabuf, iovcnt, - &ret, flags & MSG_WINMASK, - (struct sockaddr *) msg->msg_name, - msg->msg_namelen, NULL, NULL); - } - while (res == SOCKET_ERROR - && WSAGetLastError () == WSAEWOULDBLOCK - && !(res = wait (evt, 0)) - && !closed ()); - release (evt); - } - } - - if (res == SOCKET_ERROR) - set_winsock_errno (); - else - res = ret; - - /* Special handling for EPIPE and SIGPIPE. - - EPIPE is generated if the local end has been shut down on a connection - oriented socket. In this case the process will also receive a SIGPIPE - unless MSG_NOSIGNAL is set. */ - if (res == SOCKET_ERROR && get_errno () == ESHUTDOWN - && get_socket_type () == SOCK_STREAM) - { - set_errno (EPIPE); - if (! (flags & MSG_NOSIGNAL)) - raise (SIGPIPE); - } - return res; + return send_internal (wsabuf, msg->msg_iovlen, flags, + (struct sockaddr *) msg->msg_name, msg->msg_namelen); } int @@ -1444,6 +1440,7 @@ fhandler_socket::close () setsockopt (get_socket (), SOL_SOCKET, SO_LINGER, (const char *)&linger, sizeof linger); + release (); while ((res = closesocket (get_socket ())) != 0) { if (WSAGetLastError () != WSAEWOULDBLOCK) @@ -1572,6 +1569,9 @@ fhandler_socket::ioctl (unsigned int cmd, void *p) syscall_printf ("Async I/O on socket %s", *(int *) p ? "started" : "cancelled"); async_io (*(int *) p != 0); + /* If async_io is switched off, revert the event handling. */ + if (*(int *) p == 0) + WSAEventSelect (get_socket (), wsock_evt, EVENT_MASK); break; case FIONREAD: res = ioctlsocket (get_socket (), FIONREAD, (unsigned long *) p); @@ -1579,31 +1579,17 @@ fhandler_socket::ioctl (unsigned int cmd, void *p) set_winsock_errno (); break; default: - /* We must cancel WSAAsyncSelect (if any) before setting socket to - * blocking mode - */ - if (cmd == FIONBIO && *(int *) p == 0) - { - if (async_io ()) - WSAAsyncSelect (get_socket (), winmsg, 0, 0); - if (WSAEventSelect (get_socket (), NULL, 0) == SOCKET_ERROR) - debug_printf ("WSAEventSelect(NULL), %d", WSAGetLastError ()); - } - res = ioctlsocket (get_socket (), cmd, (unsigned long *) p); - if (res == SOCKET_ERROR) - set_winsock_errno (); + /* Sockets are always non-blocking internally. So we just note the + state here. */ if (cmd == FIONBIO) { - if (!res) - { - syscall_printf ("socket is now %sblocking", - *(int *) p ? "non" : ""); - set_nonblocking (*(int *) p); - } - /* Start AsyncSelect if async socket unblocked */ - if (*(int *) p && async_io ()) - WSAAsyncSelect (get_socket (), winmsg, WM_ASYNCIO, ASYNC_MASK); - } + syscall_printf ("socket is now %sblocking", + *(int *) p ? "non" : ""); + set_nonblocking (*(int *) p); + res = 0; + } + else + res = ioctlsocket (get_socket (), cmd, (unsigned long *) p); break; } syscall_printf ("%d = ioctl_socket (%x, %x)", res, cmd, p); diff --git a/winsup/cygwin/include/sys/socket.h b/winsup/cygwin/include/sys/socket.h new file mode 100644 index 00000000000..d4b1227ba31 --- /dev/null +++ b/winsup/cygwin/include/sys/socket.h @@ -0,0 +1,58 @@ +/* sys/socket.h + + Copyright 1996-2001 Red Hat, Inc. + +This file is part of Cygwin. + +This software is a copyrighted work licensed under the terms of the +Cygwin license. Please consult the file "CYGWIN_LICENSE" for +details. */ + +#ifndef _SYS_SOCKET_H +#define _SYS_SOCKET_H + +#include <features.h> +#include <cygwin/socket.h> +#include <sys/time.h> + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* SUS symbolic values for the second parm to shutdown(2) */ +#define SHUT_RD 0 /* == Win32 SD_RECEIVE */ +#define SHUT_WR 1 /* == Win32 SD_SEND */ +#define SHUT_RDWR 2 /* == Win32 SD_BOTH */ + +#ifndef __INSIDE_CYGWIN_NET__ + int accept (int, struct sockaddr *__peer, socklen_t *); + int bind (int, const struct sockaddr *__my_addr, socklen_t __addrlen); + int connect (int, const struct sockaddr *, socklen_t); + int getpeername (int, struct sockaddr *__peer, socklen_t *); + int getsockname (int, struct sockaddr *__addr, socklen_t *); + int listen (int, int __n); + ssize_t recv (int, void *__buff, size_t __len, int __flags); + ssize_t recvfrom (int, void *__buff, size_t __len, int __flags, + struct sockaddr *__from, socklen_t *__fromlen); + ssize_t recvmsg(int s, struct msghdr *msg, int flags); + ssize_t send (int, const void *__buff, size_t __len, int __flags); + ssize_t sendmsg(int s, const struct msghdr *msg, int flags); + ssize_t sendto (int, const void *, size_t __len, int __flags, + const struct sockaddr *__to, socklen_t __tolen); + int setsockopt (int __s, int __level, int __optname, const void *optval, + socklen_t __optlen); + int getsockopt (int __s, int __level, int __optname, void *__optval, + socklen_t *__optlen); + int shutdown (int, int); + int socket (int __family, int __type, int __protocol); + int socketpair (int __domain, int __type, int __protocol, int *__socket_vec); + + struct servent *getservbyname (const char *__name, const char *__proto); +#endif + +#ifdef __cplusplus +}; +#endif + +#endif /* _SYS_SOCKET_H */ diff --git a/winsup/cygwin/libc/inet_addr.c b/winsup/cygwin/libc/inet_addr.c index 2efd76f6e89..5716ee9c379 100644 --- a/winsup/cygwin/libc/inet_addr.c +++ b/winsup/cygwin/libc/inet_addr.c @@ -77,6 +77,7 @@ static const char rcsid[] = "$Id$"; __FBSDID("$FreeBSD$"); #else #define __INSIDE_CYGWIN__ +#define __INSIDE_CYGWIN_NET__ #endif #ifndef __CYGWIN__ diff --git a/winsup/cygwin/libc/inet_network.c b/winsup/cygwin/libc/inet_network.c index d42676680c7..70625fd7f36 100644 --- a/winsup/cygwin/libc/inet_network.c +++ b/winsup/cygwin/libc/inet_network.c @@ -39,6 +39,7 @@ static const char sccsid[] = "@(#)inet_network.c 8.1 (Berkeley) 6/4/93"; __FBSDID("$FreeBSD$"); #else #define __INSIDE_CYGWIN__ +#define __INSIDE_CYGWIN_NET__ #endif #ifndef __CYGWIN__ diff --git a/winsup/cygwin/net.cc b/winsup/cygwin/net.cc index bd70161269a..a6d7ecef2f1 100644 --- a/winsup/cygwin/net.cc +++ b/winsup/cygwin/net.cc @@ -513,6 +513,8 @@ fdsock (cygheap_fdmanip& fd, const device *dev, SOCKET soc) if (!fd.isopen ()) return false; fd->set_io_handle ((HANDLE) soc); + if (!((fhandler_socket *) fd)->prepare ()) + return false; fd->set_flags (O_RDWR | O_BINARY); fd->uninterruptible_io (true); cygheap->fdtab.inc_need_fixup_before (); @@ -776,56 +778,7 @@ cygwin_connect (int fd, const struct sockaddr *name, socklen_t namelen) if (efault.faulted (EFAULT) || !fh) res = -1; else - { - bool was_blocking = false; - if (!fh->is_nonblocking ()) - { - int nonblocking = 1; - fh->ioctl (FIONBIO, &nonblocking); - was_blocking = true; - } - res = fh->connect (name, namelen); - if (was_blocking) - { - if (res == -1 && get_errno () == EINPROGRESS) - { - size_t fds_size = howmany (fd + 1, NFDBITS) * sizeof (fd_mask); - fd_set *write_fds = (fd_set *) alloca (fds_size); - fd_set *except_fds = (fd_set *) alloca (fds_size); - memset (write_fds, 0, fds_size); - memset (except_fds, 0, fds_size); - FD_SET (fd, write_fds); - FD_SET (fd, except_fds); - res = cygwin_select (fd + 1, NULL, write_fds, except_fds, NULL); - if (res > 0 && FD_ISSET (fd, except_fds)) - { - res = -1; - for (;;) - { - int err; - int len = sizeof err; - cygwin_getsockopt (fd, SOL_SOCKET, SO_ERROR, - (void *) &err, &len); - if (err) - { - set_errno (err); - break; - } - low_priority_sleep (0); - } - } - else if (res > 0) - res = 0; - else - { - WSASetLastError (WSAEINPROGRESS); - set_winsock_errno (); - } - } - int nonblocking = 0; - fh->ioctl (FIONBIO, &nonblocking); - } - } + res = fh->connect (name, namelen); syscall_printf ("%d = connect (%d, %p, %d)", res, fd, name, namelen); @@ -959,19 +912,7 @@ cygwin_accept (int fd, struct sockaddr *peer, socklen_t *len) if (efault.faulted (EFAULT) || !fh) res = -1; else - { - if (!fh->is_nonblocking ()) - { - size_t fds_size = howmany (fd + 1, NFDBITS) * sizeof (fd_mask); - fd_set *read_fds = (fd_set *) alloca (fds_size); - memset (read_fds, 0, fds_size); - FD_SET (fd, read_fds); - res = cygwin_select (fd + 1, read_fds, NULL, NULL, NULL); - if (res == -1) - return -1; - } - res = fh->accept (peer, len); - } + res = fh->accept (peer, len); syscall_printf ("%d = accept (%d, %p, %p)", res, fd, peer, len); return res; diff --git a/winsup/cygwin/poll.cc b/winsup/cygwin/poll.cc new file mode 100644 index 00000000000..3ce46fab616 --- /dev/null +++ b/winsup/cygwin/poll.cc @@ -0,0 +1,146 @@ +/* poll.cc. Implements poll(2) via usage of select(2) call. + + Copyright 2000, 2001, 2002, 2003, 2004, 2005 Red Hat, Inc. + + This file is part of Cygwin. + + This software is a copyrighted work licensed under the terms of the + Cygwin license. Please consult the file "CYGWIN_LICENSE" for + details. */ + +#define __INSIDE_CYGWIN_NET__ + +#define FD_SETSIZE 16384 // lots of fds +#include "winsup.h" +#include <sys/time.h> +#include <sys/poll.h> +#include <sys/socket.h> +#include <stdlib.h> +#define USE_SYS_TYPES_FD_SET +#include <winsock2.h> +#include "cygerrno.h" +#include "security.h" +#include "path.h" +#include "fhandler.h" +#include "dtable.h" +#include "cygheap.h" +#include "sigproc.h" + +extern "C" int +poll (struct pollfd *fds, unsigned int nfds, int timeout) +{ + int max_fd = 0; + fd_set *read_fds, *write_fds, *except_fds; + struct timeval tv = { timeout / 1000, (timeout % 1000) * 1000 }; + + for (unsigned int i = 0; i < nfds; ++i) + if (fds[i].fd > max_fd) + max_fd = fds[i].fd; + + size_t fds_size = howmany (max_fd + 1, NFDBITS) * sizeof (fd_mask); + + read_fds = (fd_set *) alloca (fds_size); + write_fds = (fd_set *) alloca (fds_size); + except_fds = (fd_set *) alloca (fds_size); + + if (!read_fds || !write_fds || !except_fds) + { + set_errno (EINVAL); /* According to SUSv3. */ + return -1; + } + + memset (read_fds, 0, fds_size); + memset (write_fds, 0, fds_size); + memset (except_fds, 0, fds_size); + + int invalid_fds = 0; + for (unsigned int i = 0; i < nfds; ++i) + { + fds[i].revents = 0; + if (!cygheap->fdtab.not_open (fds[i].fd)) + { + if (fds[i].events & POLLIN) + FD_SET(fds[i].fd, read_fds); + if (fds[i].events & POLLOUT) + FD_SET(fds[i].fd, write_fds); + /* On sockets, except_fds is needed to catch failed connects. */ + if ((fds[i].events & POLLPRI) + || cygheap->fdtab[fds[i].fd]->is_socket ()) + FD_SET(fds[i].fd, except_fds); + } + else if (fds[i].fd >= 0) + { + ++invalid_fds; + fds[i].revents = POLLNVAL; + } + } + + if (invalid_fds) + return invalid_fds; + + int ret = cygwin_select (max_fd + 1, read_fds, write_fds, except_fds, timeout < 0 ? NULL : &tv); + + if (ret > 0) + for (unsigned int i = 0; i < nfds; ++i) + { + if (fds[i].fd >= 0) + { + if (cygheap->fdtab.not_open (fds[i].fd)) + fds[i].revents = POLLHUP; + else + { + fhandler_socket *sock; + + if (FD_ISSET(fds[i].fd, read_fds)) + { + char peek[1]; + sock = cygheap->fdtab[fds[i].fd]->is_socket (); + if (!sock) + fds[i].revents |= POLLIN; + else if (sock->listener ()) + { + fds[i].revents |= POLLIN; + } + else + { + /* The following action can change errno. We have to + reset it to it's old value. */ + int old_errno = get_errno (); + switch (sock->recvfrom (peek, sizeof (peek), MSG_PEEK, + NULL, NULL)) + { + case -1: + fds[i].revents |= POLLERR; + break; + case 0: /* Closed on the read side... */ + /* ...or shutdown(SHUT_WR) on the write side. + We set revents to POLLHUP until 1.5.18, but + this is semantically borderline. */ + fds[i].revents |= POLLIN; + break; + default: + fds[i].revents |= POLLIN; + break; + } + set_errno (old_errno); + } + } + /* Handle failed connect. */ + if (FD_ISSET(fds[i].fd, write_fds) + && FD_ISSET(fds[i].fd, except_fds) + && (sock = cygheap->fdtab[fds[i].fd]->is_socket ()) + && sock->connect_state () == connect_failed) + fds[i].revents |= (POLLIN | POLLERR); + else + { + if (FD_ISSET(fds[i].fd, write_fds)) + fds[i].revents |= POLLOUT; + if (FD_ISSET(fds[i].fd, except_fds)) + fds[i].revents |= POLLPRI; + } + } + } + } + + return ret; +} |