File: xioshutdown.c

package info (click to toggle)
socat 2.0.0~beta9-1
  • links: PTS
  • area: main
  • in suites: experimental
  • size: 3,740 kB
  • sloc: ansic: 30,875; sh: 11,630; makefile: 149
file content (281 lines) | stat: -rw-r--r-- 7,653 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
/* source: xioshutdown.c */
/* Copyright Gerhard Rieger */
/* Published under the GNU General Public License V.2, see file COPYING */

/* this is the source of the extended shutdown function */


#include "xiosysincludes.h"
#include "xioopen.h"


static int xioshut_sleep_kill(pid_t sub, unsigned long usec, int sig);

static pid_t socat_kill_pid;	/* here we pass the pid to be killed in sighandler */

static void signal_kill_pid(int dummy) {
   int _errno;
   _errno = errno;
   diag_in_handler = 1;
   Notice("SIGALRM while waiting for w/o child process to die, killing it now");
   Kill(socat_kill_pid, SIGTERM);
   diag_in_handler = 0;
   errno = _errno;
}

/* how: SHUT_RD, SHUT_WR, or SHUT_RDWR */
int xioshutdown(xiofile_t *sock, int how) {
   int fd;
   int result = 0;

   Debug2("xioshutdown(%p, %d)", sock, how);
   Debug2("xioshutdown(): dtype=0x%x, howtoshut=0x%04x",
	  sock->stream.dtype, sock->stream.howtoshut);

   if (sock->tag == XIO_TAG_INVALID) {
      Error("xioshutdown(): invalid file descriptor");
      errno = EINVAL;
      return -1;
   }

   /*Debug3("xioshutdown: flags=%d, dtype=%d, howtoclose=%d", sock->stream.flags, sock->stream.dtype, sock->stream.howtoclose);*/
   if (sock->tag == XIO_TAG_DUAL) {
      if ((how+1)&1) {
	 result = xioshutdown((xiofile_t *)sock->dual.stream[0], 0);
      }
      if ((how+1)&2) {
	 result |= xioshutdown((xiofile_t *)sock->dual.stream[1], 1);
      }
      return result;
   }

   fd = XIO_GETWRFD(sock);

   /* let us bring how nearer to the resulting action */
   if ((sock->stream.flags&XIO_ACCMODE) == XIO_WRONLY) {
      how = ((how+1) & ~(SHUT_RD+1)) - 1;
   } else if ((sock->stream.flags&XIO_ACCMODE) == XIO_RDONLY) {
      how = ((how+1) & ~(SHUT_WR+1)) - 1;
   }

   switch (sock->stream.howtoshut) {
#if WITH_PTY
   case XIOSHUT_PTYEOF:
      {
	 struct termios termarg;
	 int result;
	 Debug1("tcdrain(%d)", sock->stream.wfd);
	 result = tcdrain(sock->stream.wfd);
	 Debug1("tcdrain() -> %d", result);
	 if (Tcgetattr(sock->stream.wfd, &termarg) < 0) {
	    Error3("tcgetattr(%d, %p): %s",
		   sock->stream.wfd, &termarg, strerror(errno));
	 }
#if 0
	 /* these settings might apply to data still in the buff (despite the
	    TCSADRAIN */
	 termarg.c_iflag |= (IGNBRK | BRKINT | PARMRK | ISTRIP
                           | INLCR | IGNCR | ICRNL | IXON);
	 termarg.c_oflag |= OPOST;
	 termarg.c_lflag |= (/*ECHO | ECHONL |*/ ICANON | ISIG | IEXTEN);
	 //termarg.c_cflag |= (PARENB);
#else
	 termarg.c_lflag |= ICANON;
#endif
	 if (Tcsetattr(sock->stream.wfd, TCSADRAIN, &termarg) < 0) {
	    Error3("tcsetattr(%d, TCSADRAIN, %p): %s",
		   sock->stream.wfd, &termarg, strerror(errno));
	 }
	 if (Write(sock->stream.wfd, &termarg.c_cc[VEOF], 1) < 1) {
	    Warn3("write(%d, 0%o, 1): %s",
		  sock->stream.wfd, termarg.c_cc[VEOF], strerror(errno));
	 }
      }
      return 0;
#endif /* WITH_PTY */
#if WITH_OPENSSL
   case XIOSHUT_OPENSSL:
      sycSSL_shutdown(sock->stream.para.openssl.ssl);
      /*! what about half/full close? */
      return 0;
#endif /* WITH_OPENSSL */
   default:
      break;
   }

   /* here handle special shutdown functions */
   switch (sock->stream.howtoshut & XIOSHUTWR_MASK) {
      char writenull;
   case XIOSHUTWR_NONE:
      return 0;
   case XIOSHUTWR_CLOSE:
      if (Close(fd) < 0) {
	 Info2("close(%d): %s", fd, strerror(errno));
      }
      return 0;
   case XIOSHUTWR_DOWN:
      if ((result = Shutdown(fd, how)) < 0) {
	 Info3("shutdown(%d, %d): %s", fd, how, strerror(errno));
      }
      return 0;
#if _WITH_SOCKET
   case XIOSHUTWR_NULL:
      /* send an empty packet; only useful on datagram sockets? */
      xiowrite(sock, &writenull, 0);
      return 0;
#endif /* _WITH_SOCKET */
   default: break;
   }

#if 0
  if (how == SHUT_RDWR) {
     /* in this branch we handle only shutdown actions where read and write
	shutdown are not independent */

   switch (sock->stream.howtoshut) {
#if _WITH_SOCKET
     case XIOSHUT_DOWN:
      if ((result = Shutdown(fd, how)) < 0) {
	 Info3("shutdown(%d, %d): %s", fd, how, strerror(errno));
      }
      break;
     case XIOSHUT_KILL:
	if ((result = Shutdown(fd, how)) < 0) {
	 Info3("shutdown(%d, %d): %s", fd, how, strerror(errno));
      }
      break;
#endif /* _WITH_SOCKET */
     case XIOSHUT_CLOSE:
	Close(fd);
#if WITH_TERMIOS
	if (sock->stream.ttyvalid) {
	   if (Tcsetattr(fd, 0, &sock->stream.savetty) < 0) {
	      Warn2("cannot restore terminal settings on fd %d: %s",
		    fd, strerror(errno));
	   }
	}
#endif /* WITH_TERMIOS */
	/*PASSTHROUGH*/
     case XIOSHUT_NONE:
	break;
     default:
	Error1("xioshutdown(): bad shutdown action 0x%x", sock->stream.howtoshut);
	return -1;
     }

#if 0 && _WITH_SOCKET
   case XIODATA_RECVFROM:
      if (how >= 1) {
	 if (Close(fd) < 0) {
	    Info2("close(%d): %s",
		  fd, strerror(errno));
	 }
	 sock->stream.eof = 2;
	 sock->stream.rfd = -1;
      }
      break;
#endif /* _WITH_SOCKET */
  }
#endif

   if ((how+1) & 1) {	/* contains SHUT_RD */
      switch (sock->stream.dtype & XIODATA_READMASK) {
	 /* shutdown read channel */

      case XIOREAD_STREAM:
      case XIODATA_2PIPE:
	 if (Close(fd) < 0) {
	    Info2("close(%d): %s", fd, strerror(errno));
	 }
	 break;
      }
   }

   if ((how+1) & 2) {	/* contains SHUT_WR */
      /* shutdown write channel */

      switch (sock->stream.howtoshut & XIOSHUTWR_MASK) {

      case XIOSHUTWR_CLOSE:
	 if (Close(fd) < 0) {
	    Info2("close(%d): %s", fd, strerror(errno));
	 }
	 /*PASSTHROUGH*/
      case XIOSHUTWR_NONE:
	 break;

#if _WITH_SOCKET
      case XIOSHUTWR_DOWN:
	 if (Shutdown(fd, SHUT_WR) < 0) {
	    Info2("shutdown(%d, SHUT_WR): %s", fd, strerror(errno));
	 }
	 break;
#endif /* _WITH_SOCKET */

#if 0
      case XIOSHUTWR_DOWN_KILL:
	 if (Shutdown(fd, SHUT_WR) < 0) {
	    Info2("shutdown(%d, SHUT_WR): %s", fd, strerror(errno));
	 }
	 /*!!!*/
#endif
      case XIOSHUTWR_SIGHUP:
	 /* the child process might want to flush some data before
	    terminating */
	 xioshut_sleep_kill(sock->stream.child.pid, 0, SIGHUP);
	 break;
      case XIOSHUTWR_SIGTERM:
	 /* the child process might want to flush some data before
	    terminating */
	 xioshut_sleep_kill(sock->stream.child.pid, 1000000, SIGTERM);
	 break;
      case XIOSHUTWR_SIGKILL:
	 /* the child process might want to flush some data before
	    terminating */
	 xioshut_sleep_kill(sock->stream.child.pid, 1000000, SIGKILL);
	 break;

      default:
	 Error1("xioshutdown(): unhandled howtoshut=0x%x during SHUT_WR",
		sock->stream.howtoshut&XIOSHUTWR_MASK);
      }
      sock->stream.wfd = -1;
   }

   return result;
}

/* wait some time and then send signal to sub process. This is useful after
   shutting down the connection to give process some time to flush its output
   data */
static int xioshut_sleep_kill(pid_t sub, unsigned long usec, int sig) {
   struct sigaction act;
   int status = 0;

   /* we wait for the child process to die, but to prevent timeout
      we raise an alarm after some time.
      NOTE: the alarm does not terminate waitpid() on Linux/glibc
      (BUG?), 
      therefore we have to do the kill in the signal handler */
	 {
	    struct sigaction act;
	    sigfillset(&act.sa_mask);
	    act.sa_flags = 0;
	    act.sa_handler = signal_kill_pid;
	    Sigaction(SIGALRM, &act, NULL);
	 }

   socat_kill_pid = sub;
#if HAVE_SETITIMER
   /*! with next feature release, we get usec resolution and an option
	      */
#else
   Alarm(1 /*! sock->stream.child.waitdie */);
#endif /* !HAVE_SETITIMER */
   if (Waitpid(sub, &status, 0) < 0) {
      Warn3("waitpid("F_pid", %p, 0): %s",
	    sub, &status, strerror(errno));
   }
   Alarm(0);
   return 0;
}