leo_sobral@1
|
1 |
/**
|
leo_sobral@1
|
2 |
* GMyth Library
|
leo_sobral@1
|
3 |
*
|
leo_sobral@1
|
4 |
* @file gmyth/gmyth_socket.c
|
leo_sobral@1
|
5 |
*
|
leo_sobral@1
|
6 |
* @brief <p> MythTV socket implementation, according to the MythTV Project
|
leo_sobral@1
|
7 |
* (www.mythtv.org).
|
leo_sobral@1
|
8 |
*
|
leo_sobral@1
|
9 |
* This component provides basic socket functionalities to interact with
|
leo_sobral@1
|
10 |
* the Mythtv backend.
|
leo_sobral@1
|
11 |
* <p>
|
leo_sobral@1
|
12 |
*
|
leo_sobral@1
|
13 |
* Copyright (C) 2006 INdT - Instituto Nokia de Tecnologia.
|
leo_sobral@1
|
14 |
* @author Rosfran Lins Borges <rosfran.borges@indt.org.br>
|
leo_sobral@1
|
15 |
*
|
rosfran@701
|
16 |
*
|
rosfran@701
|
17 |
* This program is free software; you can redistribute it and/or modify
|
rosfran@701
|
18 |
* it under the terms of the GNU Lesser General Public License as published by
|
rosfran@701
|
19 |
* the Free Software Foundation; either version 2 of the License, or
|
rosfran@701
|
20 |
* (at your option) any later version.
|
rosfran@701
|
21 |
*
|
rosfran@701
|
22 |
* This program is distributed in the hope that it will be useful,
|
rosfran@701
|
23 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
rosfran@701
|
24 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
rosfran@701
|
25 |
* GNU General Public License for more details.
|
rosfran@701
|
26 |
*
|
rosfran@701
|
27 |
* You should have received a copy of the GNU Lesser General Public License
|
rosfran@701
|
28 |
* along with this program; if not, write to the Free Software
|
rosfran@701
|
29 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
rosfran@701
|
30 |
*/
|
leo_sobral@1
|
31 |
|
rosfran@105
|
32 |
#ifdef HAVE_CONFIG_H
|
leo_sobral@213
|
33 |
#include "config.h"
|
rosfran@105
|
34 |
#endif
|
rosfran@105
|
35 |
|
leo_sobral@213
|
36 |
#include "gmyth_socket.h"
|
leo_sobral@213
|
37 |
|
rosfran@698
|
38 |
#include <glib.h>
|
leo_sobral@1
|
39 |
#include <glib/gprintf.h>
|
leo_sobral@1
|
40 |
|
leo_sobral@1
|
41 |
#include <arpa/inet.h>
|
leo_sobral@1
|
42 |
#include <sys/types.h>
|
leo_sobral@1
|
43 |
#include <sys/socket.h>
|
rosfran@225
|
44 |
#include <sys/param.h>
|
leo_sobral@1
|
45 |
#include <netdb.h>
|
rosfran@104
|
46 |
#include <net/if.h>
|
leo_sobral@1
|
47 |
#include <errno.h>
|
leo_sobral@446
|
48 |
#include <assert.h>
|
leo_sobral@1
|
49 |
#include <stdlib.h>
|
leo_sobral@1
|
50 |
|
rosfran@105
|
51 |
#include <unistd.h>
|
rosfran@105
|
52 |
#include <netinet/in.h>
|
rosfran@105
|
53 |
#include <fcntl.h>
|
rosfran@105
|
54 |
#include <signal.h>
|
rosfran@105
|
55 |
|
melunko@313
|
56 |
#include <sys/ioctl.h>
|
rosfran@104
|
57 |
|
leo_sobral@1
|
58 |
#include "gmyth_stringlist.h"
|
rosfran@104
|
59 |
#include "gmyth_uri.h"
|
renatofilho@131
|
60 |
#include "gmyth_debug.h"
|
leo_sobral@1
|
61 |
|
melunko@313
|
62 |
#define BUFLEN 512
|
melunko@313
|
63 |
#define MYTH_SEPARATOR "[]:[]"
|
rosfran@101
|
64 |
#define MYTH_PROTOCOL_FIELD_SIZE 8
|
rosfran@101
|
65 |
|
renatofilho@754
|
66 |
/*
|
renatofilho@754
|
67 |
* max number of iterations
|
renatofilho@754
|
68 |
*/
|
rosfran@105
|
69 |
#define MYTHTV_MAX_VERSION_CHECKS 40
|
leo_sobral@1
|
70 |
|
melunko@117
|
71 |
// FIXME: put this in the right place
|
rosfran@343
|
72 |
#define MYTHTV_VERSION_DEFAULT 31
|
melunko@117
|
73 |
|
renatofilho@754
|
74 |
/*
|
renatofilho@754
|
75 |
* static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
|
renatofilho@754
|
76 |
*/
|
leo_sobral@1
|
77 |
|
renatofilho@754
|
78 |
/*
|
renatofilho@754
|
79 |
* static GStaticRWLock rwlock = G_STATIC_RW_LOCK_INIT;
|
renatofilho@754
|
80 |
*/
|
rosfran@336
|
81 |
|
renatofilho@754
|
82 |
static gchar *local_hostname = NULL;
|
rosfran@177
|
83 |
|
renatofilho@754
|
84 |
static void gmyth_socket_class_init(GMythSocketClass * klass);
|
renatofilho@754
|
85 |
static void gmyth_socket_init(GMythSocket * object);
|
leo_sobral@1
|
86 |
|
renatofilho@754
|
87 |
static void gmyth_socket_dispose(GObject * object);
|
renatofilho@754
|
88 |
static void gmyth_socket_finalize(GObject * object);
|
leo_sobral@1
|
89 |
|
renatofilho@750
|
90 |
G_DEFINE_TYPE(GMythSocket, gmyth_socket, G_TYPE_OBJECT)
|
renatofilho@754
|
91 |
static void gmyth_socket_class_init(GMythSocketClass * klass)
|
leo_sobral@1
|
92 |
{
|
renatofilho@754
|
93 |
GObjectClass *gobject_class;
|
leo_sobral@1
|
94 |
|
renatofilho@754
|
95 |
gobject_class = (GObjectClass *) klass;
|
leo_sobral@1
|
96 |
|
renatofilho@754
|
97 |
gobject_class->dispose = gmyth_socket_dispose;
|
renatofilho@754
|
98 |
gobject_class->finalize = gmyth_socket_finalize;
|
leo_sobral@1
|
99 |
}
|
leo_sobral@1
|
100 |
|
leo_sobral@1
|
101 |
static void
|
renatofilho@750
|
102 |
gmyth_socket_init(GMythSocket * gmyth_socket)
|
leo_sobral@1
|
103 |
{
|
rosfran@698
|
104 |
|
renatofilho@754
|
105 |
/*
|
renatofilho@754
|
106 |
* gmyth_socket->local_hostname = NULL;
|
renatofilho@754
|
107 |
*/
|
rosfran@698
|
108 |
|
leo_sobral@1
|
109 |
}
|
leo_sobral@1
|
110 |
|
leo_sobral@1
|
111 |
/** Gets the some important address translation info, from the client socket
|
leo_sobral@1
|
112 |
* that will open a connection.
|
leo_sobral@1
|
113 |
*
|
leo_sobral@1
|
114 |
* @return gint that represents the error number from getaddrinfo().
|
leo_sobral@1
|
115 |
*/
|
renatofilho@754
|
116 |
static gint
|
renatofilho@750
|
117 |
gmyth_socket_toaddrinfo(const gchar * addr, gint port,
|
renatofilho@754
|
118 |
struct addrinfo **addrInfo)
|
leo_sobral@1
|
119 |
{
|
renatofilho@754
|
120 |
struct addrinfo hints;
|
renatofilho@754
|
121 |
gchar *portStr = NULL;
|
renatofilho@754
|
122 |
gint errorn = EADDRNOTAVAIL;
|
rosfran@154
|
123 |
|
renatofilho@754
|
124 |
g_return_val_if_fail(addr != NULL, -1);
|
rosfran@698
|
125 |
|
renatofilho@754
|
126 |
memset(&hints, 0, sizeof(struct addrinfo));
|
renatofilho@754
|
127 |
hints.ai_family = AF_INET;
|
renatofilho@754
|
128 |
hints.ai_socktype = SOCK_STREAM;
|
renatofilho@754
|
129 |
/*
|
renatofilho@754
|
130 |
* hints.ai_flags = AI_NUMERICHOST;
|
renatofilho@754
|
131 |
*/
|
rosfran@698
|
132 |
|
renatofilho@754
|
133 |
if (port != -1)
|
renatofilho@754
|
134 |
portStr = g_strdup_printf("%d", port);
|
renatofilho@754
|
135 |
else
|
renatofilho@754
|
136 |
portStr = NULL;
|
melunko@313
|
137 |
|
renatofilho@754
|
138 |
gmyth_debug("Getting name resolution for: %s, %d\n", addr, port);
|
leo_sobral@1
|
139 |
|
renatofilho@754
|
140 |
if ((errorn = getaddrinfo(addr, portStr, &hints, addrInfo)) != 0) {
|
renatofilho@754
|
141 |
gmyth_debug("[%s] Socket ERROR: %s\n", __FUNCTION__,
|
renatofilho@754
|
142 |
gai_strerror(errorn));
|
renatofilho@754
|
143 |
}
|
leo_sobral@446
|
144 |
|
renatofilho@754
|
145 |
g_free(portStr);
|
leo_sobral@446
|
146 |
|
renatofilho@754
|
147 |
return errorn;
|
leo_sobral@1
|
148 |
}
|
leo_sobral@1
|
149 |
|
renatofilho@464
|
150 |
/*
|
renatofilho@754
|
151 |
* static gint gmyth_socket_find_match_address_uri( GMythURI* uri, gchar
|
renatofilho@754
|
152 |
* *address ) { if ( g_ascii_strcasecmp( gmyth_uri_get_host( uri ),
|
renatofilho@754
|
153 |
* address ) == 0 ) { //gmyth_debug( "Found URI: %s !!!\n",
|
renatofilho@754
|
154 |
* rui_uri_getvalue(uri) ); return 0; } else { return -1; } }
|
renatofilho@754
|
155 |
*/
|
rosfran@104
|
156 |
|
renatofilho@754
|
157 |
const gchar *PATH_PROC_NET_DEV = "/proc/net/dev";
|
rosfran@104
|
158 |
|
rosfran@105
|
159 |
/** Gets the list of all local network interfaces (using the /proc/net/dev directory).
|
rosfran@105
|
160 |
*
|
rosfran@105
|
161 |
* @param current_connections A list with all the network interfaces are valid,
|
rosfran@105
|
162 |
* to be applied just like a filter.
|
rosfran@105
|
163 |
* @return List with all the local net interfaces.
|
rosfran@105
|
164 |
*/
|
renatofilho@754
|
165 |
GList *
|
renatofilho@750
|
166 |
gmyth_socket_get_local_addrs(GList * current_connections)
|
rosfran@104
|
167 |
{
|
rosfran@104
|
168 |
|
renatofilho@754
|
169 |
GList *local_addrs = NULL;
|
renatofilho@754
|
170 |
FILE *fd;
|
renatofilho@754
|
171 |
gint s;
|
renatofilho@754
|
172 |
gchar buffer[256 + 1];
|
renatofilho@754
|
173 |
gchar ifaddr[20 + 1];
|
renatofilho@754
|
174 |
gchar *ifname;
|
renatofilho@754
|
175 |
gchar *sep;
|
leo_sobral@387
|
176 |
|
renatofilho@754
|
177 |
s = socket(AF_INET, SOCK_DGRAM, 0);
|
renatofilho@754
|
178 |
if (s < 0)
|
renatofilho@754
|
179 |
return 0;
|
renatofilho@754
|
180 |
fd = fopen(PATH_PROC_NET_DEV, "r");
|
renatofilho@754
|
181 |
fgets(buffer, sizeof(buffer) - 1, fd);
|
renatofilho@754
|
182 |
fgets(buffer, sizeof(buffer) - 1, fd);
|
renatofilho@754
|
183 |
while (!feof(fd)) {
|
renatofilho@754
|
184 |
ifname = buffer;
|
rosfran@104
|
185 |
|
renatofilho@754
|
186 |
if (fgets(buffer, sizeof(buffer) - 1, fd) == NULL)
|
renatofilho@754
|
187 |
break;
|
renatofilho@754
|
188 |
sep = strrchr(buffer, ':');
|
renatofilho@754
|
189 |
if (sep)
|
renatofilho@754
|
190 |
*sep = 0;
|
renatofilho@754
|
191 |
while (*ifname == ' ')
|
renatofilho@754
|
192 |
ifname++;
|
renatofilho@754
|
193 |
struct ifreq req;
|
rosfran@698
|
194 |
|
renatofilho@754
|
195 |
strcpy(req.ifr_name, ifname);
|
renatofilho@754
|
196 |
if (ioctl(s, SIOCGIFFLAGS, &req) < 0)
|
renatofilho@754
|
197 |
continue;
|
renatofilho@754
|
198 |
if (!(req.ifr_flags & IFF_UP))
|
renatofilho@754
|
199 |
continue;
|
renatofilho@754
|
200 |
if (req.ifr_flags & IFF_LOOPBACK)
|
renatofilho@754
|
201 |
continue;
|
renatofilho@754
|
202 |
if (ioctl(s, SIOCGIFADDR, &req) < 0)
|
renatofilho@754
|
203 |
continue;
|
renatofilho@754
|
204 |
g_strlcpy(ifaddr,
|
renatofilho@754
|
205 |
inet_ntoa(((struct sockaddr_in *) &req.ifr_addr)->
|
renatofilho@754
|
206 |
sin_addr), sizeof(struct ifaddr) - 1);
|
renatofilho@754
|
207 |
local_addrs = g_list_append(local_addrs, g_strdup(ifaddr));
|
rosfran@698
|
208 |
|
renatofilho@754
|
209 |
gmyth_debug
|
renatofilho@754
|
210 |
("( from the /proc/net/dev) Interface name: %s, address: %s\n",
|
renatofilho@754
|
211 |
ifname, ifaddr);
|
renatofilho@754
|
212 |
}
|
renatofilho@754
|
213 |
fclose(fd);
|
renatofilho@754
|
214 |
close(s);
|
renatofilho@754
|
215 |
return local_addrs;
|
rosfran@104
|
216 |
}
|
rosfran@104
|
217 |
|
rosfran@104
|
218 |
/**
|
rosfran@104
|
219 |
* Get only the local addresses from the primary interface
|
rosfran@104
|
220 |
*/
|
renatofilho@754
|
221 |
gchar *
|
renatofilho@750
|
222 |
gmyth_socket_get_primary_addr(void)
|
rosfran@104
|
223 |
{
|
renatofilho@754
|
224 |
gchar *if_eth0 = g_new0(gchar, sizeof(struct ifaddr) - 1);
|
renatofilho@754
|
225 |
GList *if_tmp = NULL;
|
rosfran@104
|
226 |
|
renatofilho@754
|
227 |
GList *interfs = gmyth_socket_get_local_addrs(NULL);
|
rosfran@698
|
228 |
|
renatofilho@754
|
229 |
if (interfs != NULL && (g_list_length(interfs) > 0)) {
|
renatofilho@754
|
230 |
// get the first occurrence (primary interface)
|
renatofilho@754
|
231 |
if_tmp = g_list_first(interfs);
|
rosfran@698
|
232 |
|
renatofilho@754
|
233 |
if (if_tmp != NULL)
|
renatofilho@754
|
234 |
g_strlcpy(if_eth0, (gchar *) if_tmp->data,
|
renatofilho@754
|
235 |
sizeof(struct ifaddr) - 1);
|
rosfran@698
|
236 |
|
renatofilho@754
|
237 |
}
|
rosfran@698
|
238 |
|
renatofilho@754
|
239 |
if (interfs != NULL)
|
renatofilho@754
|
240 |
g_list_free(interfs);
|
rosfran@698
|
241 |
|
renatofilho@754
|
242 |
return if_eth0;
|
rosfran@104
|
243 |
}
|
rosfran@104
|
244 |
|
leo_sobral@1
|
245 |
/** This function retrieves the local hostname of the
|
leo_sobral@1
|
246 |
* client machine.
|
leo_sobral@1
|
247 |
*
|
leo_sobral@1
|
248 |
* @return GString* get local hostname.
|
leo_sobral@1
|
249 |
*/
|
renatofilho@754
|
250 |
GString *
|
renatofilho@750
|
251 |
gmyth_socket_get_local_hostname(void)
|
leo_sobral@1
|
252 |
{
|
renatofilho@754
|
253 |
char hname[50];
|
renatofilho@754
|
254 |
gint res = gethostname(hname, 50);
|
melunko@313
|
255 |
|
renatofilho@754
|
256 |
if (res == -1) {
|
renatofilho@754
|
257 |
gmyth_debug("Error while getting hostname");
|
renatofilho@754
|
258 |
return g_string_new("default");
|
renatofilho@754
|
259 |
}
|
melunko@313
|
260 |
|
renatofilho@754
|
261 |
return g_string_new(hname);
|
melunko@663
|
262 |
|
rosfran@698
|
263 |
#if 0
|
renatofilho@754
|
264 |
GString *str = NULL;
|
leo_sobral@1
|
265 |
|
renatofilho@754
|
266 |
if (local_hostname != NULL && strlen(local_hostname) > 0)
|
renatofilho@754
|
267 |
return g_string_new(local_hostname);
|
rosfran@698
|
268 |
|
renatofilho@754
|
269 |
gchar *localaddr = NULL;
|
renatofilho@754
|
270 |
gboolean found_addr = FALSE;
|
renatofilho@754
|
271 |
struct addrinfo *addr_info_data = NULL,
|
renatofilho@754
|
272 |
*addr_info0 = NULL;
|
renatofilho@754
|
273 |
struct sockaddr_in *sa = NULL;
|
renatofilho@754
|
274 |
gchar localhostname[MAXHOSTNAMELEN];
|
leo_sobral@1
|
275 |
|
melunko@313
|
276 |
|
renatofilho@754
|
277 |
if (gethostname(localhostname, MAXHOSTNAMELEN) != 0) {
|
renatofilho@754
|
278 |
gmyth_debug("Error on gethostname");
|
renatofilho@754
|
279 |
}
|
renatofilho@754
|
280 |
localhostname[MAXHOSTNAMELEN - 1] = 0;
|
rosfran@225
|
281 |
|
renatofilho@754
|
282 |
gint err =
|
renatofilho@754
|
283 |
gmyth_socket_toaddrinfo(localhostname, -1, &addr_info_data);
|
rosfran@698
|
284 |
|
renatofilho@754
|
285 |
if (err == EADDRNOTAVAIL) {
|
renatofilho@754
|
286 |
gmyth_debug("[%s] Address (%s) not available. (reason = %d)\n",
|
renatofilho@754
|
287 |
__FUNCTION__, localhostname, err);
|
renatofilho@754
|
288 |
return str;
|
renatofilho@754
|
289 |
}
|
rosfran@698
|
290 |
|
renatofilho@754
|
291 |
g_mutex_lock(gmyth_socket->mutex);
|
leo_sobral@1
|
292 |
|
renatofilho@754
|
293 |
addr_info0 = addr_info_data;
|
leo_sobral@1
|
294 |
|
renatofilho@754
|
295 |
while (addr_info0 != NULL && addr_info0->ai_addr != NULL &&
|
renatofilho@754
|
296 |
(sa = (struct sockaddr_in *) addr_info0->ai_addr) != NULL
|
renatofilho@754
|
297 |
&& !found_addr) {
|
renatofilho@754
|
298 |
localaddr = inet_ntoa(sa->sin_addr);
|
leo_sobral@1
|
299 |
|
renatofilho@754
|
300 |
if (localaddr != NULL && (g_strrstr(localaddr, "127") == NULL)) {
|
renatofilho@754
|
301 |
str = g_string_new(localaddr);
|
renatofilho@754
|
302 |
found_addr = TRUE;
|
renatofilho@754
|
303 |
g_free(localaddr);
|
renatofilho@754
|
304 |
break;
|
renatofilho@754
|
305 |
}
|
renatofilho@754
|
306 |
/*
|
renatofilho@754
|
307 |
* if (localaddr != NULL) { g_free (localaddr); localaddr = NULL;
|
renatofilho@754
|
308 |
* }
|
renatofilho@754
|
309 |
*/
|
rosfran@225
|
310 |
|
renatofilho@754
|
311 |
addr_info0 = addr_info0->ai_next;
|
renatofilho@754
|
312 |
};
|
rosfran@698
|
313 |
|
renatofilho@754
|
314 |
freeaddrinfo(addr_info_data);
|
renatofilho@754
|
315 |
addr_info_data = NULL;
|
rosfran@105
|
316 |
|
renatofilho@754
|
317 |
if (found_addr == FALSE) {
|
renatofilho@754
|
318 |
gchar *prim_addr = gmyth_socket_get_primary_addr();
|
rosfran@698
|
319 |
|
renatofilho@754
|
320 |
if (prim_addr != NULL) {
|
renatofilho@754
|
321 |
gmyth_debug
|
renatofilho@754
|
322 |
("[%s] Could not determine the local alphanumerical hostname. Setting to %s\n",
|
renatofilho@754
|
323 |
__FUNCTION__, prim_addr);
|
rosfran@698
|
324 |
|
renatofilho@754
|
325 |
str = g_string_new(prim_addr);
|
renatofilho@754
|
326 |
g_free(prim_addr);
|
renatofilho@754
|
327 |
} else {
|
renatofilho@754
|
328 |
str = g_string_new(localhostname);
|
renatofilho@754
|
329 |
}
|
renatofilho@754
|
330 |
}
|
leo_sobral@1
|
331 |
|
renatofilho@754
|
332 |
g_mutex_unlock(gmyth_socket->mutex);
|
rosfran@698
|
333 |
|
renatofilho@754
|
334 |
if (str != NULL && str->str != NULL)
|
renatofilho@754
|
335 |
local_hostname = g_strdup(str->str);
|
rosfran@225
|
336 |
|
renatofilho@754
|
337 |
return str;
|
rosfran@698
|
338 |
#endif
|
leo_sobral@1
|
339 |
}
|
leo_sobral@1
|
340 |
|
leo_sobral@1
|
341 |
static void
|
renatofilho@750
|
342 |
gmyth_socket_dispose(GObject * object)
|
leo_sobral@1
|
343 |
{
|
renatofilho@754
|
344 |
GMythSocket *gmyth_socket = GMYTH_SOCKET(object);
|
leo_sobral@1
|
345 |
|
renatofilho@754
|
346 |
/*
|
renatofilho@754
|
347 |
* disconnect socket
|
renatofilho@754
|
348 |
*/
|
renatofilho@754
|
349 |
gmyth_socket_close_connection(gmyth_socket);
|
rosfran@698
|
350 |
|
renatofilho@754
|
351 |
g_free(gmyth_socket->hostname);
|
rosfran@698
|
352 |
|
renatofilho@754
|
353 |
g_free(local_hostname);
|
rosfran@698
|
354 |
|
renatofilho@754
|
355 |
local_hostname = NULL;
|
rosfran@698
|
356 |
|
renatofilho@754
|
357 |
if (gmyth_socket->mutex != NULL) {
|
renatofilho@754
|
358 |
g_mutex_free(gmyth_socket->mutex);
|
renatofilho@754
|
359 |
gmyth_socket->mutex = NULL;
|
renatofilho@754
|
360 |
}
|
renatofilho@147
|
361 |
|
renatofilho@754
|
362 |
G_OBJECT_CLASS(gmyth_socket_parent_class)->dispose(object);
|
leo_sobral@1
|
363 |
}
|
leo_sobral@1
|
364 |
|
leo_sobral@1
|
365 |
static void
|
renatofilho@750
|
366 |
gmyth_socket_finalize(GObject * object)
|
leo_sobral@1
|
367 |
{
|
renatofilho@754
|
368 |
g_signal_handlers_destroy(object);
|
leo_sobral@1
|
369 |
|
renatofilho@754
|
370 |
G_OBJECT_CLASS(gmyth_socket_parent_class)->finalize(object);
|
leo_sobral@1
|
371 |
}
|
leo_sobral@1
|
372 |
|
leo_sobral@1
|
373 |
/** Creates a new instance of GMythSocket.
|
leo_sobral@1
|
374 |
*
|
leo_sobral@1
|
375 |
* @return a new instance of GMythSocket.
|
leo_sobral@1
|
376 |
*/
|
renatofilho@754
|
377 |
GMythSocket *
|
renatofilho@750
|
378 |
gmyth_socket_new()
|
leo_sobral@1
|
379 |
{
|
renatofilho@754
|
380 |
GMythSocket *gmyth_socket =
|
renatofilho@754
|
381 |
GMYTH_SOCKET(g_object_new(GMYTH_SOCKET_TYPE, NULL));
|
leo_sobral@1
|
382 |
|
renatofilho@754
|
383 |
gmyth_socket->mythtv_version = MYTHTV_VERSION_DEFAULT;
|
rosfran@698
|
384 |
|
renatofilho@754
|
385 |
gmyth_socket->mutex = g_mutex_new();
|
leo_sobral@1
|
386 |
|
renatofilho@754
|
387 |
return gmyth_socket;
|
leo_sobral@1
|
388 |
}
|
leo_sobral@1
|
389 |
|
rosfran@105
|
390 |
/** Try to open an asynchronous connection to the MythTV backend.
|
rosfran@105
|
391 |
*
|
leo_sobral@446
|
392 |
* @param fd Socket descriptor.
|
rosfran@105
|
393 |
* @param remote Remote address.
|
rosfran@105
|
394 |
* @param len Newly created socket length field.
|
rosfran@105
|
395 |
* @param timeout Timeval argument with the time interval to timeout before closing.
|
leo_sobral@446
|
396 |
* @param err Error message number.
|
rosfran@105
|
397 |
* @return Any numerical value below 0, if an error had been found.
|
rosfran@105
|
398 |
*/
|
renatofilho@754
|
399 |
static gint
|
renatofilho@750
|
400 |
gmyth_socket_try_connect(gint fd, struct sockaddr *remote, gint len,
|
renatofilho@754
|
401 |
struct timeval *timeout, gint * err)
|
rosfran@105
|
402 |
{
|
renatofilho@754
|
403 |
/*
|
renatofilho@754
|
404 |
* g_return_val_if_fail( timeout != NULL, 0 );
|
renatofilho@754
|
405 |
*/
|
renatofilho@754
|
406 |
gint saveflags,
|
renatofilho@754
|
407 |
ret,
|
renatofilho@754
|
408 |
back_err;
|
rosfran@698
|
409 |
|
renatofilho@754
|
410 |
fd_set fd_w;
|
rosfran@698
|
411 |
|
renatofilho@754
|
412 |
saveflags = fcntl(fd, F_GETFL, 0);
|
renatofilho@754
|
413 |
if (saveflags < 0) {
|
renatofilho@754
|
414 |
gmyth_debug("[%s] Problems when getting socket flags on fcntl.\n",
|
renatofilho@754
|
415 |
__FUNCTION__);
|
renatofilho@754
|
416 |
*err = errno;
|
renatofilho@754
|
417 |
return -1;
|
renatofilho@754
|
418 |
}
|
rosfran@698
|
419 |
|
renatofilho@754
|
420 |
/*
|
renatofilho@754
|
421 |
* Set non blocking
|
renatofilho@754
|
422 |
*/
|
renatofilho@754
|
423 |
if (fcntl(fd, F_SETFL, saveflags | O_NONBLOCK) < 0) {
|
renatofilho@754
|
424 |
gmyth_debug
|
renatofilho@754
|
425 |
("[%s] Problems when setting non-blocking using fcntl.\n",
|
renatofilho@754
|
426 |
__FUNCTION__);
|
renatofilho@754
|
427 |
*err = errno;
|
renatofilho@754
|
428 |
return -1;
|
renatofilho@754
|
429 |
}
|
rosfran@698
|
430 |
|
renatofilho@754
|
431 |
/*
|
renatofilho@754
|
432 |
* This will return immediately
|
renatofilho@754
|
433 |
*/
|
renatofilho@754
|
434 |
*err = connect(fd, remote, len);
|
renatofilho@754
|
435 |
back_err = errno;
|
rosfran@698
|
436 |
|
renatofilho@754
|
437 |
/*
|
renatofilho@754
|
438 |
* restore flags
|
renatofilho@754
|
439 |
*/
|
renatofilho@754
|
440 |
if (fcntl(fd, F_SETFL, saveflags) < 0) {
|
renatofilho@754
|
441 |
gmyth_debug
|
renatofilho@754
|
442 |
("[%s] Problems when trying to restore flags with fcntl.\n",
|
renatofilho@754
|
443 |
__FUNCTION__);
|
renatofilho@754
|
444 |
*err = errno;
|
renatofilho@754
|
445 |
return -1;
|
renatofilho@754
|
446 |
}
|
rosfran@698
|
447 |
|
renatofilho@754
|
448 |
/*
|
renatofilho@754
|
449 |
* return unless the connection was successful or the connect is still
|
renatofilho@754
|
450 |
* in progress.
|
renatofilho@754
|
451 |
*/
|
renatofilho@754
|
452 |
if (*err < 0 && back_err != EINPROGRESS) {
|
renatofilho@754
|
453 |
gmyth_debug
|
renatofilho@754
|
454 |
("[%s] Connection unsucessfully (it is not in progress).\n",
|
renatofilho@754
|
455 |
__FUNCTION__);
|
renatofilho@754
|
456 |
*err = errno;
|
renatofilho@754
|
457 |
return -1;
|
renatofilho@754
|
458 |
}
|
rosfran@698
|
459 |
|
renatofilho@754
|
460 |
FD_ZERO(&fd_w);
|
renatofilho@754
|
461 |
FD_SET(fd, &fd_w);
|
rosfran@698
|
462 |
|
renatofilho@754
|
463 |
*err = select(FD_SETSIZE, NULL, &fd_w, NULL, timeout);
|
renatofilho@754
|
464 |
if (*err < 0) {
|
renatofilho@754
|
465 |
gmyth_debug("[%s] Connection unsucessfull (timed out).\n",
|
renatofilho@754
|
466 |
__FUNCTION__);
|
renatofilho@754
|
467 |
*err = errno;
|
renatofilho@754
|
468 |
return -1;
|
renatofilho@754
|
469 |
}
|
rosfran@698
|
470 |
|
renatofilho@754
|
471 |
/*
|
renatofilho@754
|
472 |
* 0 means it timeout out & no fds changed
|
renatofilho@754
|
473 |
*/
|
renatofilho@754
|
474 |
if (*err == 0) {
|
renatofilho@754
|
475 |
gmyth_debug
|
renatofilho@754
|
476 |
("[%s] Connection unsucessfull [%d] - 0 means it timeout out & no fds changed\n",
|
renatofilho@754
|
477 |
__FUNCTION__, *err);
|
renatofilho@754
|
478 |
close(fd);
|
renatofilho@754
|
479 |
*err = ETIMEDOUT;
|
renatofilho@754
|
480 |
return -1;
|
renatofilho@754
|
481 |
}
|
rosfran@698
|
482 |
|
renatofilho@754
|
483 |
/*
|
renatofilho@754
|
484 |
* Get the return code from the connect
|
renatofilho@754
|
485 |
*/
|
renatofilho@754
|
486 |
len = sizeof(ret);
|
renatofilho@754
|
487 |
*err = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, (socklen_t *) & len);
|
rosfran@698
|
488 |
|
renatofilho@754
|
489 |
if (*err < 0) {
|
renatofilho@754
|
490 |
gmyth_debug("[%s] Connection unsucessfull.\n", __FUNCTION__);
|
renatofilho@754
|
491 |
*err = errno;
|
renatofilho@754
|
492 |
return -1;
|
renatofilho@754
|
493 |
}
|
rosfran@698
|
494 |
|
renatofilho@754
|
495 |
/*
|
renatofilho@754
|
496 |
* ret=0 means success, otherwise it contains the errno
|
renatofilho@754
|
497 |
*/
|
renatofilho@754
|
498 |
if (ret) {
|
renatofilho@754
|
499 |
gmyth_debug
|
renatofilho@754
|
500 |
("[%s] Connection unsucessfull - Couldn't connect to remote host!!!\n",
|
renatofilho@754
|
501 |
__FUNCTION__);
|
renatofilho@754
|
502 |
*err = ret;
|
renatofilho@754
|
503 |
return -1;
|
renatofilho@754
|
504 |
}
|
rosfran@698
|
505 |
|
renatofilho@754
|
506 |
*err = 0;
|
renatofilho@754
|
507 |
return 0;
|
rosfran@105
|
508 |
}
|
rosfran@105
|
509 |
|
leo_sobral@1
|
510 |
/** Connects to the backend.
|
leo_sobral@1
|
511 |
*
|
leo_sobral@1
|
512 |
* @param gmyth_socket The GMythSocket instance.
|
leo_sobral@1
|
513 |
* @param hostname The backend hostname or IP address.
|
leo_sobral@1
|
514 |
* @param port The backend port.
|
leo_sobral@1
|
515 |
* @return TRUE if success, FALSE if error.
|
leo_sobral@1
|
516 |
*/
|
melunko@258
|
517 |
|
melunko@258
|
518 |
|
leo_sobral@1
|
519 |
gboolean
|
renatofilho@750
|
520 |
gmyth_socket_connect(GMythSocket * gmyth_socket,
|
renatofilho@754
|
521 |
const gchar * hostname, gint port)
|
leo_sobral@1
|
522 |
{
|
renatofilho@754
|
523 |
return gmyth_socket_connect_with_timeout(gmyth_socket, hostname, port,
|
renatofilho@754
|
524 |
0);
|
melunko@258
|
525 |
}
|
melunko@258
|
526 |
|
melunko@258
|
527 |
gboolean
|
renatofilho@750
|
528 |
gmyth_socket_connect_with_timeout(GMythSocket * gmyth_socket,
|
renatofilho@754
|
529 |
const gchar * hostname, gint port,
|
renatofilho@754
|
530 |
guint timeout)
|
melunko@258
|
531 |
{
|
renatofilho@754
|
532 |
struct addrinfo *addr_info_data = NULL,
|
renatofilho@754
|
533 |
*addr_info0 = NULL;
|
renatofilho@754
|
534 |
struct linger ling;
|
renatofilho@754
|
535 |
gchar *tmp_str;
|
renatofilho@754
|
536 |
gint ret_code = 0; /* -1 */
|
rosfran@698
|
537 |
|
renatofilho@754
|
538 |
/*
|
renatofilho@754
|
539 |
* FIXME: add as function parameter
|
renatofilho@754
|
540 |
*/
|
renatofilho@754
|
541 |
gint err;
|
renatofilho@754
|
542 |
gint errno;
|
renatofilho@754
|
543 |
gboolean ret = TRUE;
|
leo_sobral@1
|
544 |
|
renatofilho@754
|
545 |
gmyth_debug("CONNECTING %s:%d", hostname, port);
|
rosfran@225
|
546 |
|
renatofilho@754
|
547 |
if (hostname == NULL)
|
renatofilho@754
|
548 |
gmyth_debug("Invalid hostname parameter!\n");
|
leo_sobral@1
|
549 |
|
renatofilho@754
|
550 |
/*
|
renatofilho@754
|
551 |
* store hostname and port number
|
renatofilho@754
|
552 |
*/
|
renatofilho@754
|
553 |
gmyth_debug("CONNECTING %s:%d", hostname, port);
|
renatofilho@206
|
554 |
|
renatofilho@754
|
555 |
errno = gmyth_socket_toaddrinfo(hostname, port, &addr_info_data);
|
rosfran@225
|
556 |
|
renatofilho@754
|
557 |
g_return_val_if_fail(addr_info_data != NULL
|
renatofilho@754
|
558 |
&& hostname != NULL, FALSE);
|
rosfran@225
|
559 |
|
renatofilho@754
|
560 |
/*
|
renatofilho@754
|
561 |
* hack to avoid deleting the hostname when gmyth_socket->hostname ==
|
renatofilho@754
|
562 |
* hostname
|
renatofilho@754
|
563 |
*/
|
renatofilho@754
|
564 |
tmp_str = gmyth_socket->hostname;
|
leo_sobral@446
|
565 |
|
renatofilho@754
|
566 |
gmyth_socket->hostname = g_strdup(hostname);
|
renatofilho@754
|
567 |
gmyth_socket->port = port;
|
leo_sobral@1
|
568 |
|
renatofilho@754
|
569 |
g_free(tmp_str);
|
leo_sobral@446
|
570 |
|
renatofilho@754
|
571 |
for (addr_info0 = addr_info_data; addr_info0;
|
renatofilho@754
|
572 |
addr_info0 = addr_info_data->ai_next) {
|
renatofilho@754
|
573 |
/*
|
renatofilho@754
|
574 |
* init socket descriptor
|
renatofilho@754
|
575 |
*/
|
renatofilho@754
|
576 |
gmyth_socket->sd =
|
renatofilho@754
|
577 |
socket(addr_info0->ai_family, addr_info0->ai_socktype,
|
renatofilho@754
|
578 |
addr_info0->ai_protocol);
|
leo_sobral@1
|
579 |
|
renatofilho@754
|
580 |
if (gmyth_socket->sd < 0)
|
renatofilho@754
|
581 |
continue;
|
leo_sobral@1
|
582 |
|
renatofilho@754
|
583 |
struct timeval *timeout_val = g_new0(struct timeval, 1);
|
rosfran@698
|
584 |
|
renatofilho@754
|
585 |
if (timeout != 0) {
|
renatofilho@754
|
586 |
timeout_val->tv_sec = timeout;
|
renatofilho@754
|
587 |
timeout_val->tv_usec = 0;
|
renatofilho@754
|
588 |
} else {
|
renatofilho@754
|
589 |
timeout_val->tv_sec = 5;
|
renatofilho@754
|
590 |
timeout_val->tv_usec = 100;
|
renatofilho@754
|
591 |
}
|
rosfran@698
|
592 |
|
renatofilho@754
|
593 |
if (gmyth_socket_try_connect
|
renatofilho@754
|
594 |
(gmyth_socket->sd, (struct sockaddr *) addr_info0->ai_addr,
|
renatofilho@754
|
595 |
addr_info0->ai_addrlen, timeout_val, &ret_code) < 0) {
|
renatofilho@754
|
596 |
gmyth_debug("[%s] Error connecting to backend!\n",
|
renatofilho@754
|
597 |
__FUNCTION__);
|
renatofilho@754
|
598 |
if (ret_code == ETIMEDOUT)
|
renatofilho@754
|
599 |
gmyth_debug("[%s]\tBackend host unreachable!\n",
|
renatofilho@754
|
600 |
__FUNCTION__);
|
leo_sobral@1
|
601 |
|
renatofilho@754
|
602 |
close(gmyth_socket->sd);
|
renatofilho@754
|
603 |
gmyth_socket->sd = -1;
|
renatofilho@754
|
604 |
gmyth_debug("ERROR: %s\n", gai_strerror(ret_code));
|
renatofilho@754
|
605 |
g_free(timeout_val);
|
renatofilho@754
|
606 |
continue;
|
renatofilho@754
|
607 |
}
|
leo_sobral@1
|
608 |
|
renatofilho@754
|
609 |
g_free(timeout_val);
|
melunko@258
|
610 |
|
renatofilho@754
|
611 |
/*
|
renatofilho@754
|
612 |
* only will be reached if none of the error above occurred
|
renatofilho@754
|
613 |
*/
|
renatofilho@754
|
614 |
break;
|
renatofilho@754
|
615 |
}
|
melunko@258
|
616 |
|
renatofilho@754
|
617 |
freeaddrinfo(addr_info_data);
|
renatofilho@754
|
618 |
addr_info_data = NULL;
|
leo_sobral@1
|
619 |
|
renatofilho@754
|
620 |
if (gmyth_socket->sd_io_ch != NULL) {
|
renatofilho@754
|
621 |
g_io_channel_unref(gmyth_socket->sd_io_ch);
|
renatofilho@754
|
622 |
gmyth_socket->sd_io_ch = NULL;
|
renatofilho@754
|
623 |
}
|
rosfran@698
|
624 |
|
rosfran@698
|
625 |
|
renatofilho@754
|
626 |
memset(&ling, 0, sizeof(struct linger));
|
renatofilho@754
|
627 |
ling.l_onoff = TRUE;
|
renatofilho@754
|
628 |
ling.l_linger = 1;
|
rosfran@698
|
629 |
|
renatofilho@754
|
630 |
err =
|
renatofilho@754
|
631 |
setsockopt(gmyth_socket->sd, SOL_SOCKET, SO_LINGER, &ling,
|
renatofilho@754
|
632 |
sizeof(struct linger));
|
rosfran@698
|
633 |
|
renatofilho@754
|
634 |
if (err < 0) {
|
renatofilho@754
|
635 |
gmyth_debug("[%s] Setting connection unsucessfull.\n",
|
renatofilho@754
|
636 |
__FUNCTION__);
|
renatofilho@754
|
637 |
err = errno;
|
renatofilho@754
|
638 |
ret = FALSE;
|
renatofilho@754
|
639 |
goto cleanup;
|
renatofilho@754
|
640 |
}
|
leo_sobral@446
|
641 |
|
renatofilho@754
|
642 |
gmyth_socket->sd_io_ch = g_io_channel_unix_new(gmyth_socket->sd);
|
rosfran@698
|
643 |
|
renatofilho@754
|
644 |
g_io_channel_set_close_on_unref(gmyth_socket->sd_io_ch, TRUE);
|
renatofilho@754
|
645 |
// g_io_channel_set_encoding (gmyth_socket->sd_io_ch, NULL, NULL );
|
leo_sobral@1
|
646 |
|
renatofilho@754
|
647 |
GIOFlags flags = g_io_channel_get_flags(gmyth_socket->sd_io_ch);
|
leo_sobral@446
|
648 |
|
renatofilho@754
|
649 |
/*
|
renatofilho@754
|
650 |
* unset the nonblock flag
|
renatofilho@754
|
651 |
*/
|
renatofilho@754
|
652 |
flags &= ~G_IO_FLAG_NONBLOCK;
|
renatofilho@754
|
653 |
/*
|
renatofilho@754
|
654 |
* unset the nonblocking stuff for some time, because GNUTLS doesn't
|
renatofilho@754
|
655 |
* like that
|
renatofilho@754
|
656 |
*/
|
renatofilho@754
|
657 |
g_io_channel_set_flags(gmyth_socket->sd_io_ch, flags, NULL);
|
rosfran@698
|
658 |
|
renatofilho@754
|
659 |
ret = (ret_code == 0) ? TRUE : FALSE;
|
rosfran@698
|
660 |
|
renatofilho@754
|
661 |
cleanup:
|
renatofilho@754
|
662 |
if (!ret)
|
renatofilho@754
|
663 |
gmyth_debug("GMythSocket error - return code error!");
|
rosfran@698
|
664 |
|
renatofilho@754
|
665 |
return ret;
|
leo_sobral@1
|
666 |
}
|
leo_sobral@1
|
667 |
|
leo_sobral@1
|
668 |
/** Gets the GIOChannel associated to the given GMythSocket.
|
leo_sobral@1
|
669 |
*
|
leo_sobral@1
|
670 |
* @param gmyth_socket The GMythSocket instance.
|
leo_sobral@1
|
671 |
*/
|
renatofilho@754
|
672 |
GIOChannel *
|
renatofilho@750
|
673 |
gmyth_socket_get_io_channel(GMythSocket * gmyth_socket)
|
leo_sobral@1
|
674 |
{
|
renatofilho@754
|
675 |
g_return_val_if_fail(gmyth_socket != NULL, NULL);
|
leo_sobral@1
|
676 |
|
renatofilho@754
|
677 |
return gmyth_socket->sd_io_ch;
|
leo_sobral@1
|
678 |
}
|
leo_sobral@1
|
679 |
|
leo_sobral@1
|
680 |
/** Verifies if the socket is able to read.
|
leo_sobral@1
|
681 |
*
|
leo_sobral@1
|
682 |
* @param gmyth_socket The GMythSocket instance.
|
leo_sobral@1
|
683 |
* @return TRUE if the socket is able to read, FALSE if not.
|
leo_sobral@1
|
684 |
*/
|
leo_sobral@1
|
685 |
gboolean
|
renatofilho@750
|
686 |
gmyth_socket_is_able_to_read(GMythSocket * gmyth_socket)
|
leo_sobral@1
|
687 |
{
|
renatofilho@754
|
688 |
gboolean ret = TRUE;
|
leo_sobral@1
|
689 |
|
renatofilho@754
|
690 |
/*
|
renatofilho@754
|
691 |
* verify if the input (read) buffer is ready to receive data
|
renatofilho@754
|
692 |
*/
|
renatofilho@754
|
693 |
GIOCondition io_cond =
|
renatofilho@754
|
694 |
g_io_channel_get_buffer_condition(gmyth_socket->sd_io_ch);
|
leo_sobral@1
|
695 |
|
renatofilho@754
|
696 |
if ((io_cond & G_IO_IN) == 0) {
|
renatofilho@754
|
697 |
gmyth_debug("[%s] IO channel is not able to send data!\n",
|
renatofilho@754
|
698 |
__FUNCTION__);
|
renatofilho@754
|
699 |
ret = FALSE;
|
renatofilho@754
|
700 |
}
|
leo_sobral@1
|
701 |
|
renatofilho@754
|
702 |
return ret;
|
leo_sobral@1
|
703 |
|
leo_sobral@1
|
704 |
}
|
leo_sobral@1
|
705 |
|
leo_sobral@1
|
706 |
/** Verifies if the socket is able to write.
|
leo_sobral@1
|
707 |
*
|
leo_sobral@1
|
708 |
* @param gmyth_socket The GMythSocket instance.
|
leo_sobral@1
|
709 |
* @return TRUE if the socket is able to write, FALSE if not.
|
leo_sobral@1
|
710 |
*/
|
leo_sobral@1
|
711 |
gboolean
|
renatofilho@750
|
712 |
gmyth_socket_is_able_to_write(GMythSocket * gmyth_socket)
|
leo_sobral@1
|
713 |
{
|
renatofilho@754
|
714 |
gboolean ret = TRUE;
|
leo_sobral@1
|
715 |
|
renatofilho@754
|
716 |
/*
|
renatofilho@754
|
717 |
* verify if the input (read) buffer is ready to receive data
|
renatofilho@754
|
718 |
*/
|
renatofilho@754
|
719 |
GIOCondition io_cond =
|
renatofilho@754
|
720 |
g_io_channel_get_buffer_condition(gmyth_socket->sd_io_ch);
|
leo_sobral@1
|
721 |
|
renatofilho@754
|
722 |
if (((io_cond & G_IO_OUT) == 0) || ((io_cond & G_IO_HUP) == 0)) {
|
renatofilho@754
|
723 |
gmyth_debug("[%s] IO channel is not able to send data!\n",
|
renatofilho@754
|
724 |
__FUNCTION__);
|
renatofilho@754
|
725 |
ret = FALSE;
|
renatofilho@754
|
726 |
}
|
leo_sobral@1
|
727 |
|
renatofilho@754
|
728 |
return ret;
|
leo_sobral@1
|
729 |
|
leo_sobral@1
|
730 |
}
|
leo_sobral@1
|
731 |
|
leo_sobral@1
|
732 |
/** Sends a command to the backend.
|
leo_sobral@1
|
733 |
*
|
leo_sobral@1
|
734 |
* @param gmyth_socket the GMythSocket instance.
|
leo_sobral@1
|
735 |
* @param command The string command to be sent.
|
leo_sobral@1
|
736 |
*/
|
leo_sobral@1
|
737 |
gboolean
|
renatofilho@750
|
738 |
gmyth_socket_send_command(GMythSocket * gmyth_socket, GString * command)
|
leo_sobral@1
|
739 |
{
|
renatofilho@754
|
740 |
gboolean ret = TRUE;
|
leo_sobral@1
|
741 |
|
renatofilho@754
|
742 |
GIOStatus io_status = G_IO_STATUS_NORMAL;
|
rosfran@698
|
743 |
|
renatofilho@754
|
744 |
// GIOCondition io_cond;
|
renatofilho@754
|
745 |
GError *error = NULL;
|
rosfran@698
|
746 |
|
renatofilho@754
|
747 |
gchar *buffer = NULL;
|
leo_sobral@1
|
748 |
|
renatofilho@754
|
749 |
gsize bytes_written = 0;
|
leo_sobral@1
|
750 |
|
renatofilho@754
|
751 |
g_return_val_if_fail(gmyth_socket->sd_io_ch != NULL, FALSE);
|
rosfran@698
|
752 |
|
renatofilho@754
|
753 |
if (command == NULL || (command->len <= 0) || command->str == NULL) {
|
renatofilho@754
|
754 |
gmyth_debug("[%s] Invalid NULL command parameter!\n",
|
renatofilho@754
|
755 |
__FUNCTION__);
|
renatofilho@754
|
756 |
ret = FALSE;
|
renatofilho@754
|
757 |
goto done;
|
renatofilho@754
|
758 |
}
|
leo_sobral@1
|
759 |
|
renatofilho@754
|
760 |
g_mutex_lock(gmyth_socket->mutex);
|
renatofilho@754
|
761 |
gmyth_debug("Sending command to backend: %s\n", command->str);
|
leo_sobral@1
|
762 |
|
renatofilho@754
|
763 |
buffer = g_strnfill(BUFLEN, ' ');
|
renatofilho@754
|
764 |
g_snprintf(buffer, MYTH_PROTOCOL_FIELD_SIZE + 1, "%-8d", command->len);
|
leo_sobral@1
|
765 |
|
renatofilho@754
|
766 |
command = g_string_prepend(command, buffer);
|
leo_sobral@1
|
767 |
|
renatofilho@754
|
768 |
/*
|
renatofilho@754
|
769 |
* write bytes to socket
|
renatofilho@754
|
770 |
*/
|
renatofilho@754
|
771 |
io_status =
|
renatofilho@754
|
772 |
g_io_channel_write_chars(gmyth_socket->sd_io_ch, command->str,
|
renatofilho@754
|
773 |
command->len, &bytes_written, &error);
|
leo_sobral@1
|
774 |
|
leo_sobral@1
|
775 |
|
renatofilho@754
|
776 |
if ((io_status == G_IO_STATUS_ERROR) || (bytes_written <= 0)) {
|
renatofilho@754
|
777 |
gmyth_debug("[%s] Error while writing to socket", __FUNCTION__);
|
renatofilho@754
|
778 |
ret = FALSE;
|
renatofilho@754
|
779 |
} else if (bytes_written < command->len) {
|
renatofilho@754
|
780 |
gmyth_debug("[%s] Not all data was written socket", __FUNCTION__);
|
renatofilho@754
|
781 |
ret = FALSE;
|
renatofilho@754
|
782 |
}
|
leo_sobral@1
|
783 |
|
renatofilho@754
|
784 |
io_status = g_io_channel_flush(gmyth_socket->sd_io_ch, &error);
|
leo_sobral@1
|
785 |
|
renatofilho@754
|
786 |
if ((bytes_written != command->len)
|
renatofilho@754
|
787 |
|| (io_status == G_IO_STATUS_ERROR)) {
|
renatofilho@754
|
788 |
gmyth_debug
|
renatofilho@754
|
789 |
("[%s] Some problem occurred when sending data to the socket\n",
|
renatofilho@754
|
790 |
__FUNCTION__);
|
rosfran@698
|
791 |
|
renatofilho@754
|
792 |
ret = TRUE;
|
renatofilho@754
|
793 |
}
|
leo_sobral@1
|
794 |
|
renatofilho@754
|
795 |
g_mutex_unlock(gmyth_socket->mutex);
|
renatofilho@754
|
796 |
done:
|
renatofilho@754
|
797 |
if (error != NULL) {
|
renatofilho@754
|
798 |
gmyth_debug
|
renatofilho@754
|
799 |
("[%s] Error found reading data from IO channel: (%d, %s)\n",
|
renatofilho@754
|
800 |
__FUNCTION__, error->code, error->message);
|
renatofilho@754
|
801 |
ret = FALSE;
|
renatofilho@754
|
802 |
g_error_free(error);
|
renatofilho@754
|
803 |
}
|
leo_sobral@1
|
804 |
|
renatofilho@754
|
805 |
if (buffer != NULL)
|
renatofilho@754
|
806 |
g_free(buffer);
|
leo_sobral@1
|
807 |
|
renatofilho@754
|
808 |
return ret;
|
leo_sobral@1
|
809 |
}
|
leo_sobral@1
|
810 |
|
leo_sobral@1
|
811 |
/** Starts Mythtv protocol level connection. Checks Mythtv protocol version
|
leo_sobral@1
|
812 |
* supported by the backend and send the "ANN" command.
|
leo_sobral@1
|
813 |
*
|
leo_sobral@1
|
814 |
* @param gmyth_socket the GMythSocket instance.
|
leo_sobral@1
|
815 |
* @param hostname_backend The backend hostname or IP address.
|
leo_sobral@1
|
816 |
* @param port The backend port to connect.
|
rosfran@177
|
817 |
* @param blocking_client A flag to choose between blocking and non-blocking
|
rosfran@177
|
818 |
* @param with_events Sets the connection flag to receive events.
|
rosfran@177
|
819 |
* backend connection.
|
leo_sobral@1
|
820 |
*/
|
renatofilho@754
|
821 |
static gboolean
|
renatofilho@750
|
822 |
gmyth_socket_connect_to_backend_and_events(GMythSocket * gmyth_socket,
|
renatofilho@754
|
823 |
const gchar * hostname_backend,
|
renatofilho@754
|
824 |
gint port,
|
renatofilho@754
|
825 |
gboolean blocking_client,
|
renatofilho@754
|
826 |
gboolean with_events)
|
leo_sobral@1
|
827 |
{
|
renatofilho@754
|
828 |
if (!gmyth_socket_connect(gmyth_socket, hostname_backend, port)) {
|
renatofilho@754
|
829 |
gmyth_debug("[%s] Could not open socket to backend machine [%s]\n",
|
renatofilho@754
|
830 |
__FUNCTION__, hostname_backend);
|
renatofilho@754
|
831 |
return FALSE;
|
renatofilho@754
|
832 |
}
|
leo_sobral@1
|
833 |
|
renatofilho@754
|
834 |
if (gmyth_socket_check_protocol_version(gmyth_socket)) {
|
renatofilho@754
|
835 |
GString *result;
|
renatofilho@754
|
836 |
GString *base_str = g_string_new("");
|
renatofilho@754
|
837 |
GString *hostname = NULL;
|
leo_sobral@1
|
838 |
|
renatofilho@754
|
839 |
hostname = gmyth_socket_get_local_hostname();
|
renatofilho@754
|
840 |
if (hostname == NULL) {
|
renatofilho@754
|
841 |
gmyth_debug
|
renatofilho@754
|
842 |
("Hostname not available, setting to n800frontend\n");
|
renatofilho@754
|
843 |
hostname = g_string_new("n800frontend");
|
renatofilho@754
|
844 |
}
|
rosfran@698
|
845 |
|
renatofilho@754
|
846 |
g_string_printf(base_str, "ANN %s %s %u",
|
renatofilho@754
|
847 |
(blocking_client ? "Playback" : "Monitor"),
|
renatofilho@754
|
848 |
hostname->str, with_events);
|
leo_sobral@1
|
849 |
|
renatofilho@754
|
850 |
gmyth_socket_send_command(gmyth_socket, base_str);
|
renatofilho@754
|
851 |
result = gmyth_socket_receive_response(gmyth_socket);
|
rosfran@698
|
852 |
|
renatofilho@754
|
853 |
if (result != NULL) {
|
renatofilho@754
|
854 |
gmyth_debug("Response received from backend: %s", result->str);
|
renatofilho@754
|
855 |
g_string_free(result, TRUE);
|
renatofilho@754
|
856 |
}
|
rosfran@698
|
857 |
|
renatofilho@754
|
858 |
g_string_free(hostname, TRUE);
|
renatofilho@754
|
859 |
g_string_free(base_str, TRUE);
|
rosfran@698
|
860 |
|
renatofilho@754
|
861 |
return TRUE;
|
renatofilho@754
|
862 |
} else {
|
renatofilho@754
|
863 |
gmyth_debug("[%s] GMythSocket could not connect to the backend",
|
renatofilho@754
|
864 |
__FUNCTION__);
|
renatofilho@754
|
865 |
return FALSE;
|
renatofilho@754
|
866 |
}
|
leo_sobral@1
|
867 |
}
|
leo_sobral@1
|
868 |
|
rosfran@177
|
869 |
/** Starts Mythtv protocol level connection. Checks Mythtv protocol version
|
rosfran@177
|
870 |
* supported by the backend and send the "ANN" command.
|
rosfran@177
|
871 |
*
|
rosfran@177
|
872 |
* @param gmyth_socket the GMythSocket instance.
|
rosfran@177
|
873 |
* @param hostname_backend The backend hostname or IP address.
|
rosfran@177
|
874 |
* @param port The backend port to connect.
|
rosfran@177
|
875 |
* @param blocking_client A flag to choose between blocking and non-blocking
|
rosfran@177
|
876 |
*/
|
rosfran@177
|
877 |
gboolean
|
renatofilho@750
|
878 |
gmyth_socket_connect_to_backend(GMythSocket * gmyth_socket,
|
renatofilho@754
|
879 |
const gchar * hostname_backend, gint port,
|
renatofilho@754
|
880 |
gboolean blocking_client)
|
rosfran@177
|
881 |
{
|
renatofilho@754
|
882 |
if (!gmyth_socket_connect_to_backend_and_events
|
renatofilho@754
|
883 |
(gmyth_socket, hostname_backend, port, blocking_client, FALSE)) {
|
renatofilho@754
|
884 |
gmyth_debug("Could not open socket to backend machine [%s]\n",
|
renatofilho@754
|
885 |
hostname_backend);
|
renatofilho@754
|
886 |
return FALSE;
|
renatofilho@754
|
887 |
}
|
rosfran@698
|
888 |
|
renatofilho@754
|
889 |
return TRUE;
|
rosfran@177
|
890 |
|
rosfran@177
|
891 |
}
|
rosfran@177
|
892 |
|
rosfran@177
|
893 |
/** Starts Mythtv protocol level connection. Checks Mythtv protocol version
|
rosfran@177
|
894 |
* supported by the backend and send the "ANN" command.
|
rosfran@177
|
895 |
*
|
rosfran@177
|
896 |
* @param gmyth_socket the GMythSocket instance.
|
rosfran@177
|
897 |
* @param hostname_backend The backend hostname or IP address.
|
rosfran@177
|
898 |
* @param port The backend port to connect.
|
rosfran@177
|
899 |
* @param blocking_client A flag to choose between blocking and non-blocking
|
rosfran@177
|
900 |
*/
|
rosfran@177
|
901 |
gboolean
|
renatofilho@750
|
902 |
gmyth_socket_connect_to_backend_events(GMythSocket * gmyth_socket,
|
renatofilho@754
|
903 |
const gchar * hostname_backend,
|
renatofilho@754
|
904 |
gint port, gboolean blocking_client)
|
rosfran@177
|
905 |
{
|
renatofilho@754
|
906 |
if (!gmyth_socket_connect_to_backend_and_events
|
renatofilho@754
|
907 |
(gmyth_socket, hostname_backend, port, blocking_client, TRUE)) {
|
renatofilho@754
|
908 |
gmyth_debug
|
renatofilho@754
|
909 |
("Could not open socket to backend machine in order to receive events [%s]\n",
|
renatofilho@754
|
910 |
hostname_backend);
|
renatofilho@754
|
911 |
return FALSE;
|
renatofilho@754
|
912 |
}
|
rosfran@698
|
913 |
|
renatofilho@754
|
914 |
return TRUE;
|
rosfran@177
|
915 |
}
|
rosfran@177
|
916 |
|
leo_sobral@1
|
917 |
/** Closes the socket connection to the backend.
|
leo_sobral@1
|
918 |
*
|
leo_sobral@1
|
919 |
* @param gmyth_socket The GMythSocket instance.
|
leo_sobral@1
|
920 |
*/
|
leo_sobral@1
|
921 |
void
|
renatofilho@750
|
922 |
gmyth_socket_close_connection(GMythSocket * gmyth_socket)
|
leo_sobral@1
|
923 |
{
|
renatofilho@754
|
924 |
/*
|
renatofilho@754
|
925 |
* if ( gmyth_socket->sd != -1 ) { close (gmyth_socket->sd);
|
renatofilho@754
|
926 |
* gmyth_socket->sd = -1; }
|
renatofilho@754
|
927 |
*/
|
renatofilho@754
|
928 |
|
renatofilho@754
|
929 |
if (gmyth_socket->sd_io_ch != NULL) {
|
renatofilho@754
|
930 |
g_io_channel_shutdown(gmyth_socket->sd_io_ch, TRUE, NULL);
|
renatofilho@754
|
931 |
g_io_channel_unref(gmyth_socket->sd_io_ch);
|
renatofilho@754
|
932 |
gmyth_socket->sd_io_ch = NULL;
|
renatofilho@754
|
933 |
gmyth_socket->sd = -1;
|
renatofilho@750
|
934 |
}
|
rosfran@698
|
935 |
|
leo_sobral@1
|
936 |
}
|
leo_sobral@1
|
937 |
|
leo_sobral@1
|
938 |
|
leo_sobral@1
|
939 |
/** Try the MythTV version numbers, and get the version returned by
|
leo_sobral@1
|
940 |
* the possible REJECT message, in order to contruct a new
|
leo_sobral@1
|
941 |
* MythTV version request.
|
leo_sobral@1
|
942 |
*
|
leo_sobral@1
|
943 |
* @param gmyth_socket The GMythSocket instance.
|
leo_sobral@1
|
944 |
* @param mythtv_version The Mythtv protocol version to be tested
|
rosfran@101
|
945 |
*
|
rosfran@101
|
946 |
* @return The actual MythTV the client is connected to.
|
leo_sobral@1
|
947 |
*/
|
rosfran@101
|
948 |
gint
|
renatofilho@750
|
949 |
gmyth_socket_check_protocol_version_number(GMythSocket * gmyth_socket,
|
renatofilho@754
|
950 |
gint mythtv_version)
|
leo_sobral@1
|
951 |
{
|
renatofilho@754
|
952 |
GString *response = NULL;
|
renatofilho@754
|
953 |
GString *payload = NULL;
|
renatofilho@754
|
954 |
gboolean res = TRUE;
|
renatofilho@754
|
955 |
gint mythtv_new_version = MYTHTV_CANNOT_NEGOTIATE_VERSION;
|
renatofilho@754
|
956 |
guint max_iterations = MYTHTV_MAX_VERSION_CHECKS;
|
leo_sobral@1
|
957 |
|
renatofilho@754
|
958 |
assert(gmyth_socket);
|
leo_sobral@446
|
959 |
|
renatofilho@754
|
960 |
try_new_version:
|
renatofilho@754
|
961 |
payload = g_string_new("MYTH_PROTO_VERSION");
|
renatofilho@754
|
962 |
g_string_append_printf(payload, " %d", mythtv_version);
|
leo_sobral@1
|
963 |
|
renatofilho@754
|
964 |
gmyth_socket_send_command(gmyth_socket, payload);
|
renatofilho@754
|
965 |
response = gmyth_socket_receive_response(gmyth_socket);
|
leo_sobral@1
|
966 |
|
renatofilho@754
|
967 |
if (response == NULL) {
|
renatofilho@754
|
968 |
gmyth_debug("[%s] Check protocol version error! Not answered!",
|
renatofilho@754
|
969 |
__FUNCTION__);
|
renatofilho@754
|
970 |
res = FALSE;
|
renatofilho@754
|
971 |
goto done;
|
renatofilho@754
|
972 |
}
|
leo_sobral@1
|
973 |
|
renatofilho@754
|
974 |
res = g_str_has_prefix(response->str, "ACCEPT");
|
renatofilho@754
|
975 |
if (!res) {
|
renatofilho@754
|
976 |
gmyth_debug("[%s] Protocol version request error: %s",
|
renatofilho@754
|
977 |
__FUNCTION__, response->str);
|
renatofilho@754
|
978 |
/*
|
renatofilho@754
|
979 |
* get the version number returned by the REJECT message
|
renatofilho@754
|
980 |
*/
|
renatofilho@754
|
981 |
if ((res = g_str_has_prefix(response->str, "REJECT")) == TRUE) {
|
renatofilho@754
|
982 |
gchar *new_version = NULL;
|
rosfran@698
|
983 |
|
renatofilho@754
|
984 |
new_version = g_strrstr(response->str, "]");
|
renatofilho@754
|
985 |
if (new_version != NULL) {
|
renatofilho@754
|
986 |
++new_version; /* skip ']' character */
|
renatofilho@754
|
987 |
if (new_version != NULL) {
|
renatofilho@754
|
988 |
gmyth_debug("[%s] got MythTV version = %s.\n",
|
renatofilho@754
|
989 |
__FUNCTION__, new_version);
|
renatofilho@754
|
990 |
mythtv_version =
|
renatofilho@754
|
991 |
(gint) g_ascii_strtoull(new_version, NULL, 10);
|
renatofilho@754
|
992 |
/*
|
renatofilho@754
|
993 |
* do reconnection to the socket (socket is closed if
|
renatofilho@754
|
994 |
* the MythTV version was wrong)
|
renatofilho@754
|
995 |
*/
|
renatofilho@754
|
996 |
gmyth_socket_connect(gmyth_socket,
|
renatofilho@754
|
997 |
gmyth_socket->hostname,
|
renatofilho@754
|
998 |
gmyth_socket->port);
|
renatofilho@754
|
999 |
new_version = NULL;
|
renatofilho@754
|
1000 |
if (--max_iterations > 0) {
|
renatofilho@754
|
1001 |
g_string_free(payload, TRUE);
|
renatofilho@754
|
1002 |
g_string_free(response, TRUE);
|
renatofilho@754
|
1003 |
goto try_new_version;
|
renatofilho@754
|
1004 |
} else
|
renatofilho@754
|
1005 |
goto done;
|
renatofilho@754
|
1006 |
}
|
renatofilho@754
|
1007 |
}
|
renatofilho@754
|
1008 |
}
|
renatofilho@754
|
1009 |
}
|
rosfran@698
|
1010 |
|
renatofilho@754
|
1011 |
/*
|
renatofilho@754
|
1012 |
* change the return value to a valid one
|
renatofilho@754
|
1013 |
*/
|
renatofilho@754
|
1014 |
if (res) {
|
renatofilho@754
|
1015 |
mythtv_new_version = mythtv_version;
|
renatofilho@754
|
1016 |
gmyth_socket->mythtv_version = mythtv_new_version;
|
renatofilho@754
|
1017 |
}
|
leo_sobral@1
|
1018 |
|
renatofilho@754
|
1019 |
done:
|
renatofilho@754
|
1020 |
g_string_free(payload, TRUE);
|
renatofilho@754
|
1021 |
g_string_free(response, TRUE);
|
leo_sobral@1
|
1022 |
|
renatofilho@754
|
1023 |
return mythtv_new_version;
|
leo_sobral@1
|
1024 |
}
|
leo_sobral@1
|
1025 |
|
leo_sobral@1
|
1026 |
/** Verifies if the Mythtv backend supported the GMyth supported version.
|
leo_sobral@1
|
1027 |
*
|
leo_sobral@1
|
1028 |
* @param gmyth_socket The GMythSocket instance.
|
leo_sobral@1
|
1029 |
* @return TRUE if supports, FALSE if not.
|
leo_sobral@1
|
1030 |
*/
|
leo_sobral@1
|
1031 |
gboolean
|
renatofilho@750
|
1032 |
gmyth_socket_check_protocol_version(GMythSocket * gmyth_socket)
|
leo_sobral@1
|
1033 |
{
|
renatofilho@754
|
1034 |
return ((gmyth_socket->mythtv_version =
|
renatofilho@754
|
1035 |
gmyth_socket_check_protocol_version_number(gmyth_socket,
|
renatofilho@754
|
1036 |
MYTHTV_VERSION_DEFAULT))
|
renatofilho@754
|
1037 |
!= MYTHTV_CANNOT_NEGOTIATE_VERSION);
|
rosfran@101
|
1038 |
}
|
rosfran@101
|
1039 |
|
rosfran@101
|
1040 |
/** Returns the Mythtv backend supported version.
|
rosfran@101
|
1041 |
*
|
rosfran@101
|
1042 |
* @param gmyth_socket The GMythSocket instance.
|
rosfran@101
|
1043 |
* @return The actual MythTV version number.
|
rosfran@101
|
1044 |
*/
|
rosfran@101
|
1045 |
gint
|
renatofilho@750
|
1046 |
gmyth_socket_get_protocol_version(GMythSocket * gmyth_socket)
|
rosfran@101
|
1047 |
{
|
renatofilho@754
|
1048 |
return gmyth_socket->mythtv_version;
|
leo_sobral@1
|
1049 |
}
|
leo_sobral@1
|
1050 |
|
leo_sobral@1
|
1051 |
/** Receives a backend answer after a gmyth_socket_send_command_call ().
|
leo_sobral@1
|
1052 |
*
|
leo_sobral@1
|
1053 |
* @param gmyth_socket The GMythSocket instance.
|
leo_sobral@1
|
1054 |
* @return The response received, or NULL if error or nothing was received.
|
leo_sobral@1
|
1055 |
*/
|
renatofilho@754
|
1056 |
GString *
|
renatofilho@750
|
1057 |
gmyth_socket_receive_response(GMythSocket * gmyth_socket)
|
leo_sobral@1
|
1058 |
{
|
renatofilho@754
|
1059 |
GIOStatus io_status = G_IO_STATUS_NORMAL;
|
renatofilho@754
|
1060 |
GError *error = NULL;
|
renatofilho@754
|
1061 |
gchar *buffer = NULL;
|
leo_sobral@1
|
1062 |
|
renatofilho@754
|
1063 |
GString *str = NULL;
|
leo_sobral@1
|
1064 |
|
renatofilho@754
|
1065 |
gsize bytes_read = 0;
|
renatofilho@754
|
1066 |
gint len = 0;
|
rosfran@698
|
1067 |
|
renatofilho@754
|
1068 |
if (gmyth_socket == NULL)
|
renatofilho@754
|
1069 |
return NULL;
|
rosfran@336
|
1070 |
|
renatofilho@754
|
1071 |
GIOCondition io_cond;
|
leo_sobral@1
|
1072 |
|
renatofilho@754
|
1073 |
/*
|
renatofilho@754
|
1074 |
* verify if the input (read) buffer is ready to receive data
|
renatofilho@754
|
1075 |
*/
|
renatofilho@754
|
1076 |
g_mutex_lock(gmyth_socket->mutex);
|
rosfran@698
|
1077 |
|
renatofilho@754
|
1078 |
buffer = g_strnfill(MYTH_PROTOCOL_FIELD_SIZE, ' ');
|
renatofilho@754
|
1079 |
if (NULL == gmyth_socket->sd_io_ch) {
|
renatofilho@754
|
1080 |
gmyth_socket_connect(gmyth_socket, gmyth_socket->hostname,
|
renatofilho@754
|
1081 |
gmyth_socket->port);
|
renatofilho@754
|
1082 |
}
|
rosfran@698
|
1083 |
|
renatofilho@754
|
1084 |
io_cond = g_io_channel_get_buffer_condition(gmyth_socket->sd_io_ch);
|
renatofilho@754
|
1085 |
/*
|
renatofilho@754
|
1086 |
* if ( NULL == gmyth_socket->sd_io_ch->read_buf || ( NULL ==
|
renatofilho@754
|
1087 |
* gmyth_socket->sd_io_ch->read_buf->str ) ) gmyth_socket->sd_io_ch =
|
renatofilho@754
|
1088 |
* g_io_channel_unix_new( gmyth_socket->sd );
|
renatofilho@754
|
1089 |
*/
|
rosfran@698
|
1090 |
|
renatofilho@754
|
1091 |
if (gmyth_socket->sd_io_ch->is_readable /* && !( ( io_cond & G_IO_IN )
|
renatofilho@754
|
1092 |
* == 0 ) */ )
|
renatofilho@754
|
1093 |
io_status =
|
renatofilho@754
|
1094 |
g_io_channel_read_chars(gmyth_socket->sd_io_ch, buffer,
|
renatofilho@754
|
1095 |
MYTH_PROTOCOL_FIELD_SIZE, &bytes_read,
|
renatofilho@754
|
1096 |
&error);
|
renatofilho@754
|
1097 |
else
|
renatofilho@754
|
1098 |
return g_string_new("");
|
leo_sobral@1
|
1099 |
|
renatofilho@754
|
1100 |
/*
|
renatofilho@754
|
1101 |
* verify if the input (read) buffer is ready to receive data
|
renatofilho@754
|
1102 |
*/
|
renatofilho@754
|
1103 |
io_cond = g_io_channel_get_buffer_condition(gmyth_socket->sd_io_ch);
|
rosfran@698
|
1104 |
|
renatofilho@754
|
1105 |
// if ( ( io_cond & G_IO_IN ) == 0 )
|
renatofilho@754
|
1106 |
// return NULL;
|
leo_sobral@1
|
1107 |
|
renatofilho@754
|
1108 |
gmyth_debug("[%s] Bytes read = %d\n", __FUNCTION__, bytes_read);
|
leo_sobral@1
|
1109 |
|
renatofilho@754
|
1110 |
if ((io_status == G_IO_STATUS_ERROR) || (bytes_read <= 0)) {
|
renatofilho@754
|
1111 |
gmyth_debug("[%s] Error in mythprotocol response from backend\n",
|
renatofilho@754
|
1112 |
__FUNCTION__);
|
renatofilho@754
|
1113 |
str = NULL;
|
renatofilho@754
|
1114 |
// return NULL;
|
renatofilho@754
|
1115 |
} else if (buffer != NULL && strlen(buffer) > 0) {
|
leo_sobral@1
|
1116 |
|
renatofilho@754
|
1117 |
// io_status = g_io_channel_flush( gmyth_socket->sd_io_ch, &error
|
renatofilho@754
|
1118 |
// );
|
renatofilho@754
|
1119 |
/*
|
renatofilho@754
|
1120 |
* verify if the input (read) buffer is ready to receive data
|
renatofilho@754
|
1121 |
*/
|
renatofilho@754
|
1122 |
// io_cond = g_io_channel_get_buffer_condition(
|
renatofilho@754
|
1123 |
// gmyth_socket->sd_io_ch );
|
rosfran@698
|
1124 |
|
renatofilho@754
|
1125 |
// if ( ( io_cond & G_IO_IN ) != 0 ) {
|
renatofilho@754
|
1126 |
// gchar *buffer_aux = NULL;
|
renatofilho@147
|
1127 |
|
renatofilho@754
|
1128 |
/*
|
renatofilho@754
|
1129 |
* removes trailing whitespace
|
renatofilho@754
|
1130 |
*/
|
renatofilho@754
|
1131 |
// buffer_aux = g_strstrip (buffer);
|
renatofilho@754
|
1132 |
len = (gint) g_ascii_strtoull(g_strstrip(buffer), NULL, 10);
|
renatofilho@147
|
1133 |
|
renatofilho@754
|
1134 |
g_free(buffer);
|
rosfran@698
|
1135 |
|
renatofilho@754
|
1136 |
/*
|
renatofilho@754
|
1137 |
* if (buffer_aux != NULL) { g_free (buffer_aux); buffer_aux =
|
renatofilho@754
|
1138 |
* NULL; }
|
renatofilho@754
|
1139 |
*/
|
renatofilho@147
|
1140 |
|
renatofilho@754
|
1141 |
buffer = g_new0(gchar, len + 1);
|
rosfran@698
|
1142 |
|
renatofilho@754
|
1143 |
bytes_read = 0;
|
renatofilho@754
|
1144 |
if (!(gmyth_socket != NULL && gmyth_socket->sd_io_ch != NULL))
|
renatofilho@754
|
1145 |
return NULL;
|
melunko@412
|
1146 |
|
renatofilho@754
|
1147 |
if (gmyth_socket->sd_io_ch->is_readable)
|
renatofilho@754
|
1148 |
io_status =
|
renatofilho@754
|
1149 |
g_io_channel_read_chars(gmyth_socket->sd_io_ch, buffer,
|
renatofilho@754
|
1150 |
len, &bytes_read, &error);
|
renatofilho@754
|
1151 |
else
|
renatofilho@754
|
1152 |
return g_string_new("");
|
rosfran@698
|
1153 |
|
renatofilho@754
|
1154 |
buffer[bytes_read] = '\0';
|
renatofilho@754
|
1155 |
// }
|
renatofilho@754
|
1156 |
}
|
leo_sobral@1
|
1157 |
|
renatofilho@754
|
1158 |
g_mutex_unlock(gmyth_socket->mutex);
|
renatofilho@754
|
1159 |
// g_static_rw_lock_reader_unlock (&rwlock);
|
leo_sobral@1
|
1160 |
|
renatofilho@754
|
1161 |
gmyth_debug("Response received from backend: ----- {%s}\n", buffer);
|
renatofilho@754
|
1162 |
if ((bytes_read != len) || (io_status == G_IO_STATUS_ERROR))
|
renatofilho@754
|
1163 |
str = NULL;
|
renatofilho@754
|
1164 |
else
|
renatofilho@754
|
1165 |
str = g_string_new(buffer);
|
leo_sobral@1
|
1166 |
|
renatofilho@754
|
1167 |
if (error != NULL) {
|
renatofilho@754
|
1168 |
gmyth_debug
|
renatofilho@754
|
1169 |
("[%s] Error found receiving response from the IO channel: (%d, %s)\n",
|
renatofilho@754
|
1170 |
__FUNCTION__, error->code, error->message);
|
renatofilho@754
|
1171 |
str = NULL;
|
renatofilho@754
|
1172 |
g_error_free(error);
|
renatofilho@754
|
1173 |
}
|
leo_sobral@1
|
1174 |
|
renatofilho@754
|
1175 |
g_free(buffer);
|
renatofilho@754
|
1176 |
return str;
|
leo_sobral@1
|
1177 |
}
|
leo_sobral@1
|
1178 |
|
leo_sobral@1
|
1179 |
/** Format a Mythtv command from the str_list entries and send it to backend.
|
leo_sobral@1
|
1180 |
*
|
leo_sobral@1
|
1181 |
* @param gmyth_socket The GMythSocket instance.
|
leo_sobral@1
|
1182 |
* @param str_list The string list to form the command
|
leo_sobral@1
|
1183 |
* @return TRUE if command was sent, FALSE if any error happens.
|
leo_sobral@1
|
1184 |
*/
|
leo_sobral@1
|
1185 |
gboolean
|
renatofilho@750
|
1186 |
gmyth_socket_write_stringlist(GMythSocket * gmyth_socket,
|
renatofilho@754
|
1187 |
GMythStringList * str_list)
|
leo_sobral@1
|
1188 |
{
|
leo_sobral@1
|
1189 |
|
renatofilho@754
|
1190 |
GList *tmp_list = NULL;
|
renatofilho@754
|
1191 |
GPtrArray *ptr_array = NULL;
|
renatofilho@754
|
1192 |
gchar *str_array = NULL;
|
leo_sobral@1
|
1193 |
|
renatofilho@754
|
1194 |
g_mutex_lock(gmyth_socket->mutex);
|
renatofilho@754
|
1195 |
// g_static_rw_lock_writer_lock (&rwlock);
|
leo_sobral@1
|
1196 |
|
renatofilho@754
|
1197 |
ptr_array = g_ptr_array_sized_new(g_list_length(str_list->glist));
|
leo_sobral@1
|
1198 |
|
renatofilho@754
|
1199 |
// FIXME: change this implementation!
|
renatofilho@754
|
1200 |
tmp_list = str_list->glist;
|
renatofilho@754
|
1201 |
for (; tmp_list; tmp_list = tmp_list->next) {
|
renatofilho@754
|
1202 |
if (tmp_list->data != NULL) {
|
renatofilho@754
|
1203 |
g_ptr_array_add(ptr_array, ((GString *) tmp_list->data)->str);
|
renatofilho@754
|
1204 |
} else {
|
renatofilho@754
|
1205 |
g_ptr_array_add(ptr_array, "");
|
renatofilho@754
|
1206 |
}
|
renatofilho@754
|
1207 |
}
|
renatofilho@754
|
1208 |
g_ptr_array_add(ptr_array, NULL); // g_str_joinv() needs a NULL
|
renatofilho@754
|
1209 |
// terminated string
|
leo_sobral@1
|
1210 |
|
renatofilho@754
|
1211 |
str_array = g_strjoinv(MYTH_SEPARATOR, (gchar **) (ptr_array->pdata));
|
leo_sobral@1
|
1212 |
|
renatofilho@754
|
1213 |
g_mutex_unlock(gmyth_socket->mutex);
|
renatofilho@754
|
1214 |
// g_static_rw_lock_writer_unlock (&rwlock);
|
leo_sobral@1
|
1215 |
|
renatofilho@754
|
1216 |
gmyth_debug("[%s] Sending socket request: %s\n", __FUNCTION__,
|
renatofilho@754
|
1217 |
str_array);
|
rosfran@29
|
1218 |
|
renatofilho@754
|
1219 |
// Sends message to backend
|
renatofilho@754
|
1220 |
// TODO: implement looping to send remaining data, and add timeout
|
renatofilho@754
|
1221 |
// testing!
|
renatofilho@754
|
1222 |
GString *command = g_string_new(str_array);
|
rosfran@698
|
1223 |
|
renatofilho@754
|
1224 |
gmyth_socket_send_command(gmyth_socket, command);
|
leo_sobral@446
|
1225 |
|
renatofilho@754
|
1226 |
g_string_free(command, TRUE);
|
leo_sobral@1
|
1227 |
|
renatofilho@754
|
1228 |
g_free(str_array);
|
leo_sobral@446
|
1229 |
|
renatofilho@754
|
1230 |
/*
|
renatofilho@754
|
1231 |
* ptr_array is pointing to data inside str_list->glist
|
renatofilho@754
|
1232 |
*/
|
renatofilho@754
|
1233 |
g_ptr_array_free(ptr_array, TRUE);
|
leo_sobral@1
|
1234 |
|
renatofilho@754
|
1235 |
return TRUE;
|
leo_sobral@1
|
1236 |
}
|
leo_sobral@1
|
1237 |
|
renatofilho@754
|
1238 |
/*
|
renatofilho@754
|
1239 |
* Receives a backend command response and split it into the given string
|
renatofilho@754
|
1240 |
* list. @param gmyth_socket The GMythSocket instance. @param str_list
|
renatofilho@754
|
1241 |
* the string list to be filled. @return The number of received strings.
|
leo_sobral@1
|
1242 |
*/
|
leo_sobral@1
|
1243 |
gint
|
renatofilho@750
|
1244 |
gmyth_socket_read_stringlist(GMythSocket * gmyth_socket,
|
renatofilho@754
|
1245 |
GMythStringList * str_list)
|
leo_sobral@1
|
1246 |
{
|
renatofilho@754
|
1247 |
GString *response;
|
renatofilho@754
|
1248 |
gint i;
|
leo_sobral@1
|
1249 |
|
renatofilho@754
|
1250 |
gmyth_string_list_clear_all(str_list);
|
leo_sobral@446
|
1251 |
|
renatofilho@754
|
1252 |
response = gmyth_socket_receive_response(gmyth_socket);
|
renatofilho@754
|
1253 |
if (response != NULL && response->str != NULL && response->len > 0) {
|
renatofilho@754
|
1254 |
gchar **str_array;
|
rosfran@698
|
1255 |
|
renatofilho@754
|
1256 |
g_mutex_lock(gmyth_socket->mutex);
|
rosfran@698
|
1257 |
|
renatofilho@754
|
1258 |
str_array = g_strsplit(response->str, MYTH_SEPARATOR, -1);
|
rosfran@698
|
1259 |
|
renatofilho@754
|
1260 |
for (i = 0; i < g_strv_length(str_array); i++) {
|
renatofilho@754
|
1261 |
// if ( str_array[i] != NULL && strlen( str_array[i] ) > 0 )
|
renatofilho@754
|
1262 |
gmyth_string_list_append_char_array(str_list, str_array[i]);
|
renatofilho@754
|
1263 |
}
|
rosfran@698
|
1264 |
|
renatofilho@754
|
1265 |
g_mutex_unlock(gmyth_socket->mutex);
|
renatofilho@754
|
1266 |
g_strfreev(str_array);
|
renatofilho@754
|
1267 |
}
|
leo_sobral@1
|
1268 |
|
renatofilho@754
|
1269 |
g_string_free(response, TRUE);
|
leo_sobral@1
|
1270 |
|
renatofilho@754
|
1271 |
return gmyth_string_list_length(str_list);
|
leo_sobral@1
|
1272 |
}
|
leo_sobral@1
|
1273 |
|
leo_sobral@1
|
1274 |
/** Formats a Mythtv protocol command based on str_list and sends it to
|
leo_sobral@1
|
1275 |
* the connected backend. The backend response is overwritten into str_list.
|
leo_sobral@1
|
1276 |
*
|
leo_sobral@1
|
1277 |
* @param gmyth_socket The GMythSocket instance.
|
leo_sobral@1
|
1278 |
* @param str_list The string list to be sent, and on which the answer
|
leo_sobral@1
|
1279 |
* will be written.
|
leo_sobral@1
|
1280 |
* @return TRUE if command was sent and an answer was received, FALSE if any
|
leo_sobral@1
|
1281 |
* error happens.
|
leo_sobral@1
|
1282 |
*/
|
leo_sobral@1
|
1283 |
gint
|
renatofilho@750
|
1284 |
gmyth_socket_sendreceive_stringlist(GMythSocket * gmyth_socket,
|
renatofilho@754
|
1285 |
GMythStringList * str_list)
|
leo_sobral@1
|
1286 |
{
|
renatofilho@754
|
1287 |
gmyth_socket_write_stringlist(gmyth_socket, str_list);
|
leo_sobral@1
|
1288 |
|
renatofilho@754
|
1289 |
return gmyth_socket_read_stringlist(gmyth_socket, str_list);
|
leo_sobral@1
|
1290 |
}
|