1/************************************************
2
3  unixserver.c -
4
5  created at: Thu Mar 31 12:21:29 JST 1994
6
7  Copyright (C) 1993-2007 Yukihiro Matsumoto
8
9************************************************/
10
11#include "rubysocket.h"
12
13#ifdef HAVE_SYS_UN_H
14/*
15 * call-seq:
16 *   UNIXServer.new(path) => unixserver
17 *
18 * Creates a new UNIX server socket bound to _path_.
19 *
20 *   serv = UNIXServer.new("/tmp/sock")
21 *   s = serv.accept
22 *   p s.read
23 */
24static VALUE
25unix_svr_init(VALUE sock, VALUE path)
26{
27    return rsock_init_unixsock(sock, path, 1);
28}
29
30/*
31 * call-seq:
32 *   unixserver.accept => unixsocket
33 *
34 * Accepts a new connection.
35 * It returns new UNIXSocket object.
36 *
37 *   UNIXServer.open("/tmp/sock") {|serv|
38 *     UNIXSocket.open("/tmp/sock") {|c|
39 *       s = serv.accept
40 *       s.puts "hi"
41 *       s.close
42 *       p c.read #=> "hi\n"
43 *     }
44 *   }
45 *
46 */
47static VALUE
48unix_accept(VALUE sock)
49{
50    rb_io_t *fptr;
51    struct sockaddr_un from;
52    socklen_t fromlen;
53
54    GetOpenFile(sock, fptr);
55    fromlen = (socklen_t)sizeof(struct sockaddr_un);
56    return rsock_s_accept(rb_cUNIXSocket, fptr->fd,
57		          (struct sockaddr*)&from, &fromlen);
58}
59
60/*
61 * call-seq:
62 * 	unixserver.accept_nonblock => unixsocket
63 *
64 * Accepts an incoming connection using accept(2) after
65 * O_NONBLOCK is set for the underlying file descriptor.
66 * It returns an accepted UNIXSocket for the incoming connection.
67 *
68 * === Example
69 * 	require 'socket'
70 * 	serv = UNIXServer.new("/tmp/sock")
71 * 	begin # emulate blocking accept
72 * 	  sock = serv.accept_nonblock
73 * 	rescue IO::WaitReadable, Errno::EINTR
74 * 	  IO.select([serv])
75 * 	  retry
76 * 	end
77 * 	# sock is an accepted socket.
78 *
79 * Refer to Socket#accept for the exceptions that may be thrown if the call
80 * to UNIXServer#accept_nonblock fails.
81 *
82 * UNIXServer#accept_nonblock may raise any error corresponding to accept(2) failure,
83 * including Errno::EWOULDBLOCK.
84 *
85 * If the exception is Errno::EWOULDBLOCK, Errno::AGAIN, Errno::ECONNABORTED or Errno::EPROTO,
86 * it is extended by IO::WaitReadable.
87 * So IO::WaitReadable can be used to rescue the exceptions for retrying accept_nonblock.
88 *
89 * === See
90 * * UNIXServer#accept
91 * * Socket#accept
92 */
93static VALUE
94unix_accept_nonblock(VALUE sock)
95{
96    rb_io_t *fptr;
97    struct sockaddr_un from;
98    socklen_t fromlen;
99
100    GetOpenFile(sock, fptr);
101    fromlen = (socklen_t)sizeof(from);
102    return rsock_s_accept_nonblock(rb_cUNIXSocket, fptr,
103			           (struct sockaddr *)&from, &fromlen);
104}
105
106/*
107 * call-seq:
108 *   unixserver.sysaccept => file_descriptor
109 *
110 * Accepts a new connection.
111 * It returns the new file descriptor which is an integer.
112 *
113 *   UNIXServer.open("/tmp/sock") {|serv|
114 *     UNIXSocket.open("/tmp/sock") {|c|
115 *       fd = serv.sysaccept
116 *       s = IO.new(fd)
117 *       s.puts "hi"
118 *       s.close
119 *       p c.read #=> "hi\n"
120 *     }
121 *   }
122 *
123 */
124static VALUE
125unix_sysaccept(VALUE sock)
126{
127    rb_io_t *fptr;
128    struct sockaddr_un from;
129    socklen_t fromlen;
130
131    GetOpenFile(sock, fptr);
132    fromlen = (socklen_t)sizeof(struct sockaddr_un);
133    return rsock_s_accept(0, fptr->fd, (struct sockaddr*)&from, &fromlen);
134}
135
136#endif
137
138void
139rsock_init_unixserver(void)
140{
141#ifdef HAVE_SYS_UN_H
142    /*
143     * Document-class: UNIXServer < UNIXSocket
144     *
145     * UNIXServer represents a UNIX domain stream server socket.
146     *
147     */
148    rb_cUNIXServer = rb_define_class("UNIXServer", rb_cUNIXSocket);
149    rb_define_method(rb_cUNIXServer, "initialize", unix_svr_init, 1);
150    rb_define_method(rb_cUNIXServer, "accept", unix_accept, 0);
151    rb_define_method(rb_cUNIXServer, "accept_nonblock", unix_accept_nonblock, 0);
152    rb_define_method(rb_cUNIXServer, "sysaccept", unix_sysaccept, 0);
153    rb_define_method(rb_cUNIXServer, "listen", rsock_sock_listen, 1); /* in socket.c */
154#endif
155}
156