1 #include "syshdrs.h"
2 
3 #ifndef NO_SIGNALS
4 extern volatile Sjmp_buf gNetTimeoutJmp;
5 extern volatile Sjmp_buf gPipeJmp;
6 #endif
7 
8 int
9 SClose(int sfd, int tlen)
10 {
11 #ifndef NO_SIGNALS
12 	vsio_sigproc_t sigalrm, sigpipe;
13 
14 	if (sfd < 0) {
15 		errno = EBADF;
16 		return (-1);
17 	}
18 
19 	if (tlen < 1) {
20 		/* Don't time it, shut it down now. */
21 		if (SetSocketLinger(sfd, 0, 0) == 0) {
22 			/* Linger disabled, so close()
23 			 * should not block.
24 			 */
25 			return (closesocket(sfd));
26 		} else {
27 			/* This will result in a fd leak,
28 			 * but it's either that or hang forever.
29 			 */
30 			return (shutdown(sfd, 2));
31 		}
32 	}
33 
34 	if (SSetjmp(gNetTimeoutJmp) != 0) {
35 		alarm(0);
36 		(void) SSignal(SIGALRM, (sio_sigproc_t) sigalrm);
37 		(void) SSignal(SIGPIPE, (sio_sigproc_t) sigpipe);
38 		if (SetSocketLinger(sfd, 0, 0) == 0) {
39 			/* Linger disabled, so close()
40 			 * should not block.
41 			 */
42 			return closesocket(sfd);
43 		} else {
44 			/* This will result in a fd leak,
45 			 * but it's either that or hang forever.
46 			 */
47 			(void) shutdown(sfd, 2);
48 		}
49 		return (-1);
50 	}
51 
52 	sigalrm = (vsio_sigproc_t) SSignal(SIGALRM, SIOHandler);
53 	sigpipe = (vsio_sigproc_t) SSignal(SIGPIPE, SIG_IGN);
54 
55 	alarm((unsigned int) tlen);
56 	for (;;) {
57 		if (closesocket(sfd) == 0) {
58 			errno = 0;
59 			break;
60 		}
61 		if (errno != EINTR)
62 			break;
63 	}
64 	alarm(0);
65 	(void) SSignal(SIGALRM, (sio_sigproc_t) sigalrm);
66 
67 	if ((errno != 0) && (errno != EBADF)) {
68 		if (SetSocketLinger(sfd, 0, 0) == 0) {
69 			/* Linger disabled, so close()
70 			 * should not block.
71 			 */
72 			(void) closesocket(sfd);
73 		} else {
74 			/* This will result in a fd leak,
75 			 * but it's either that or hang forever.
76 			 */
77 			(void) shutdown(sfd, 2);
78 		}
79 	}
80 	(void) SSignal(SIGPIPE, (sio_sigproc_t) sigpipe);
81 
82 	return ((errno == 0) ? 0 : (-1));
83 #else
84 	struct timeval tv;
85 	int result;
86 	time_t done, now;
87 	fd_set ss;
88 
89 	if (sfd < 0) {
90 		errno = EBADF;
91 		return (-1);
92 	}
93 
94 	if (tlen < 1) {
95 		/* Don't time it, shut it down now. */
96 		if (SetSocketLinger(sfd, 0, 0) == 0) {
97 			/* Linger disabled, so close()
98 			 * should not block.
99 			 */
100 			return (closesocket(sfd));
101 		} else {
102 			/* This will result in a fd leak,
103 			 * but it's either that or hang forever.
104 			 */
105 			return (shutdown(sfd, 2));
106 		}
107 	}
108 
109 	/* Wait until the socket is ready for writing (usually easy). */
110 	time(&now);
111 	done = now + tlen;
112 
113 	forever {
114 		tlen = done - now;
115 		if (tlen <= 0) {
116 			/* timeout */
117 			if (SetSocketLinger(sfd, 0, 0) == 0) {
118 				/* Linger disabled, so close()
119 				 * should not block.
120 				 */
121 				(void) closesocket(sfd);
122 			} else {
123 				/* This will result in a fd leak,
124 				 * but it's either that or hang forever.
125 				 */
126 				(void) shutdown(sfd, 2);
127 			}
128 			errno = ETIMEDOUT;
129 			return (kTimeoutErr);
130 		}
131 
132 		errno = 0;
133 		FD_ZERO(&ss);
134 		FD_SET(sfd, &ss);
135 		tv.tv_sec = tlen;
136 		tv.tv_usec = 0;
137 		result = select(sfd + 1, NULL, SELECT_TYPE_ARG234 &ss, NULL, SELECT_TYPE_ARG5 &tv);
138 		if (result == 1) {
139 			/* ready */
140 			break;
141 		} else if (result == 0) {
142 			/* timeout */
143 			if (SetSocketLinger(sfd, 0, 0) == 0) {
144 				/* Linger disabled, so close()
145 				 * should not block.
146 				 */
147 				(void) closesocket(sfd);
148 			} else {
149 				/* This will result in a fd leak,
150 				 * but it's either that or hang forever.
151 				 */
152 				(void) shutdown(sfd, 2);
153 			}
154 			errno = ETIMEDOUT;
155 			return (kTimeoutErr);
156 		} else if (errno != EINTR) {
157 			/* Error, done. This end may have been shutdown. */
158 			break;
159 		}
160 		time(&now);
161 	}
162 
163 	/* Wait until the socket is ready for reading. */
164 	forever {
165 		tlen = done - now;
166 		if (tlen <= 0) {
167 			/* timeout */
168 			if (SetSocketLinger(sfd, 0, 0) == 0) {
169 				/* Linger disabled, so close()
170 				 * should not block.
171 				 */
172 				(void) closesocket(sfd);
173 			} else {
174 				/* This will result in a fd leak,
175 				 * but it's either that or hang forever.
176 				 */
177 				(void) shutdown(sfd, 2);
178 			}
179 			errno = ETIMEDOUT;
180 			return (kTimeoutErr);
181 		}
182 
183 		errno = 0;
184 		FD_ZERO(&ss);
185 		FD_SET(sfd, &ss);
186 		tv.tv_sec = tlen;
187 		tv.tv_usec = 0;
188 		result = select(sfd + 1, SELECT_TYPE_ARG234 &ss, NULL, NULL, SELECT_TYPE_ARG5 &tv);
189 		if (result == 1) {
190 			/* ready */
191 			break;
192 		} else if (result == 0) {
193 			/* timeout */
194 			if (SetSocketLinger(sfd, 0, 0) == 0) {
195 				/* Linger disabled, so close()
196 				 * should not block.
197 				 */
198 				(void) closesocket(sfd);
199 			} else {
200 				/* This will result in a fd leak,
201 				 * but it's either that or hang forever.
202 				 */
203 				(void) shutdown(sfd, 2);
204 			}
205 			errno = ETIMEDOUT;
206 			return (kTimeoutErr);
207 		} else if (errno != EINTR) {
208 			/* Error, done. This end may have been shutdown. */
209 			break;
210 		}
211 		time(&now);
212 	}
213 
214 	/* If we get here, close() won't block. */
215 	return closesocket(sfd);
216 #endif
217 }	/* SClose */
218