tcpserversink: Port to GIO
[platform/upstream/gstreamer.git] / gst / tcp / gsttcp.c
1 /* GStreamer
2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
3  * Copyright (C) <2004> Thomas Vander Stichele <thomas at apestaart dot org>
4  *
5  * gsttcp.c: TCP functions
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <netdb.h>
32 #include <unistd.h>
33 #include <sys/ioctl.h>
34
35 #ifdef HAVE_FIONREAD_IN_SYS_FILIO
36 #include <sys/filio.h>
37 #endif
38
39 #include "gsttcp.h"
40 #include <gst/gst-i18n-plugin.h>
41
42 GST_DEBUG_CATEGORY_EXTERN (tcp_debug);
43 #define GST_CAT_DEFAULT tcp_debug
44
45 #ifndef MSG_NOSIGNAL
46 #define MSG_NOSIGNAL 0
47 #endif
48
49 /* resolve host to IP address, throwing errors if it fails */
50 /* host can already be an IP address */
51 /* returns a newly allocated gchar * with the dotted ip address,
52    or NULL, in which case it already fired an error. */
53 gchar *
54 gst_tcp_host_to_ip (GstElement * element, const gchar * host)
55 {
56   struct hostent *hostinfo;
57   char **addrs;
58   gchar *ip;
59   struct in_addr addr;
60
61   GST_DEBUG_OBJECT (element, "resolving host %s", host);
62
63   /* first check if it already is an IP address */
64   if (inet_aton (host, &addr)) {
65     ip = g_strdup (host);
66     goto beach;
67   }
68   /* FIXME: could do a localhost check here */
69
70   /* perform a name lookup */
71   if (!(hostinfo = gethostbyname (host)))
72     goto resolve_error;
73
74   if (hostinfo->h_addrtype != AF_INET)
75     goto not_ip;
76
77   addrs = hostinfo->h_addr_list;
78
79   /* There could be more than one IP address, but we just return the first */
80   ip = g_strdup (inet_ntoa (*(struct in_addr *) *addrs));
81
82 beach:
83   GST_DEBUG_OBJECT (element, "resolved to IP %s", ip);
84   return ip;
85
86 resolve_error:
87   {
88     GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND, (NULL),
89         ("Could not find IP address for host \"%s\".", host));
90     return NULL;
91   }
92 not_ip:
93   {
94     GST_ELEMENT_ERROR (element, RESOURCE, NOT_FOUND, (NULL),
95         ("host \"%s\" is not an IP host", host));
96     return NULL;
97   }
98 }
99
100 /* write buffer to given socket incrementally.
101  * Returns number of bytes written.
102  */
103 gint
104 gst_tcp_socket_write (int socket, const void *buf, size_t count)
105 {
106   size_t bytes_written = 0;
107
108   while (bytes_written < count) {
109     ssize_t wrote = send (socket, (const char *) buf + bytes_written,
110         count - bytes_written, MSG_NOSIGNAL);
111
112     if (wrote <= 0) {
113       GST_WARNING ("error while writing");
114       return bytes_written;
115     }
116     bytes_written += wrote;
117   }
118
119   GST_LOG ("wrote %" G_GSIZE_FORMAT " bytes successfully", bytes_written);
120   return bytes_written;
121 }
122
123 /* close the socket and reset the fd.  Used to clean up after errors. */
124 void
125 gst_tcp_socket_close (GstPollFD * socket)
126 {
127   if (socket->fd >= 0) {
128     close (socket->fd);
129     socket->fd = -1;
130   }
131 }
132
133 /* read a buffer from the given socket
134  * returns:
135  * - a GstBuffer in which data should be read
136  * - NULL, indicating a connection close or an error, to be handled with
137  *         EOS
138  */
139 GstFlowReturn
140 gst_tcp_read_buffer (GstElement * this, int socket, GstPoll * fdset,
141     GstBuffer ** buf)
142 {
143   int ret;
144   gssize bytes_read;
145   int readsize;
146   guint8 *data;
147
148   *buf = NULL;
149
150   /* do a blocking select on the socket */
151   /* no action (0) is an error too in our case */
152   if ((ret = gst_poll_wait (fdset, GST_CLOCK_TIME_NONE)) <= 0) {
153     if (ret == -1 && errno == EBUSY)
154       goto cancelled;
155     else
156       goto select_error;
157   }
158
159   /* ask how much is available for reading on the socket */
160   if (ioctl (socket, FIONREAD, &readsize) < 0)
161     goto ioctl_error;
162
163   if (readsize == 0)
164     goto got_eos;
165
166   /* sizeof(ssize_t) >= sizeof(int), so I know readsize <= SSIZE_MAX */
167
168   *buf = gst_buffer_new_and_alloc (readsize);
169
170   data = gst_buffer_map (*buf, NULL, NULL, GST_MAP_WRITE);
171   bytes_read = read (socket, data, readsize);
172   gst_buffer_unmap (*buf, data, bytes_read);
173
174   if (bytes_read < 0)
175     goto read_error;
176
177   if (bytes_read < readsize)
178     /* but mom, you promised to give me readsize bytes! */
179     goto short_read;
180
181   GST_LOG_OBJECT (this,
182       "returning buffer of size %" G_GSSIZE_FORMAT, bytes_read);
183   return GST_FLOW_OK;
184
185   /* ERRORS */
186 select_error:
187   {
188     GST_ELEMENT_ERROR (this, RESOURCE, READ, (NULL),
189         ("select failed: %s", g_strerror (errno)));
190     return GST_FLOW_ERROR;
191   }
192 cancelled:
193   {
194     GST_DEBUG_OBJECT (this, "Select was cancelled");
195     return GST_FLOW_WRONG_STATE;
196   }
197 ioctl_error:
198   {
199     GST_ELEMENT_ERROR (this, RESOURCE, READ, (NULL),
200         ("ioctl failed: %s", g_strerror (errno)));
201     return GST_FLOW_ERROR;
202   }
203 got_eos:
204   {
205     GST_DEBUG_OBJECT (this, "Got EOS on socket stream");
206     return GST_FLOW_EOS;
207   }
208 read_error:
209   {
210     GST_ELEMENT_ERROR (this, RESOURCE, READ, (NULL),
211         ("read failed: %s", g_strerror (errno)));
212     gst_buffer_unref (*buf);
213     *buf = NULL;
214     return GST_FLOW_ERROR;
215   }
216 short_read:
217   {
218     GST_ELEMENT_ERROR (this, RESOURCE, READ, (NULL),
219         ("short read: wanted %d bytes, got %" G_GSSIZE_FORMAT, readsize,
220             bytes_read));
221     gst_buffer_unref (*buf);
222     *buf = NULL;
223     return GST_FLOW_ERROR;
224   }
225 }