Trunk cleanup: dummy
[irreco.git] / backend / irtrans / trunk / src / irtrans_wrap.c
blobd8eeedb33a75e7113fecf514fbed1fc92ad9f877
1 /*
2 * irreco - Ir Remote Control
3 * Copyright (C) 2007 Arto Karppinen (arto.karppinen@iki.fi)
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "irtrans_wrap.h"
23 /*
24 * IRTransWrap
26 * - Object Oriented wrapper for IRTrans shlib API.
27 * - Can automatically start and stop local irserver when needed.
28 * - Will retry all commands util timeout.
33 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
34 /* Construction & Destruction */
35 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
37 IRTransWrap *irtrans_wrap_new()
39 IRTransWrap *self;
40 IRRECO_ENTER
42 self = g_slice_new0(IRTransWrap);
43 self->server_socket = -1;
44 self->hostname = g_string_new(NULL);
45 self->loop = irreco_retry_loop_new(IRTRANS_WAIT_SLEEP,
46 IRTRANS_WAIT_MAX);
47 IRRECO_RETURN_PTR(self);
50 void irtrans_wrap_delete(IRTransWrap *self)
52 IRRECO_ENTER
53 irreco_retry_loop_free(self->loop);
54 if (self->list_array) g_ptr_array_free(self->list_array, TRUE);
55 if (self->list_chunk) g_string_chunk_free(self->list_chunk);
56 g_string_free(self->hostname, TRUE);
57 g_slice_free(IRTransWrap, self);
58 IRRECO_RETURN
63 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
64 /* Private Functions */
65 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
68 * Check if irserver is using the given process identifier.
70 static gboolean irtrans_wrap_is_irserver_running_with_pid(pid_t pid)
72 gboolean is_irserver;
73 GError *error = NULL;
74 GString *link_path = NULL;
75 gchar *real_path = NULL;
76 IRRECO_ENTER
78 /* Location of the link which should point to the executable
79 using the pid. */
80 link_path = g_string_new(NULL);
81 g_string_printf(link_path, "/proc/%i/exe", pid);
82 IRRECO_DEBUG("Link location \"%s\".\n", link_path->str);
84 /* Read link. */
85 real_path = g_file_read_link(link_path->str, &error);
86 if (irreco_gerror_check_free(&error)) {
87 g_string_free(link_path, TRUE);
88 g_free(real_path);
89 IRRECO_RETURN_BOOL(FALSE);
91 IRRECO_DEBUG("Executable location \"%s\".\n", real_path);
93 /* Does it point to irserver. */
94 is_irserver = g_str_has_suffix(real_path, "/irserver");
95 g_string_free(link_path, TRUE);
96 g_free(real_path);
97 IRRECO_RETURN_BOOL(is_irserver);
101 * Check if pidfile exists, and that the pid is used by irserver.
103 static gboolean irtrans_wrap_is_irserver_running(IRTransWrap *self,
104 pid_t *pid)
106 gint pid_int;
107 gchar pid_str[80];
108 IRRECO_ENTER
110 /* Attempt to read the pidfile. */
111 if (!irreco_read_line(IRTRANS_PIDFILE, pid_str, sizeof(pid_str))) {
112 IRRECO_DEBUG("Could not read pidfile.\n");
113 IRRECO_RETURN_BOOL(FALSE);
116 /* Attempt to convert the pid to int. */
117 if (!sscanf(pid_str, "%i", &pid_int)) {
118 IRRECO_DEBUG("Pidfile did not contain a valid pid.\n");
119 g_unlink(IRTRANS_PIDFILE);
120 IRRECO_RETURN_BOOL(FALSE);
123 /* Is the pid inside the pidfile a valid one? */
124 if (!irtrans_wrap_is_irserver_running_with_pid(pid_int)) {
125 IRRECO_DEBUG("irserver is no longer using pid \"%i\".\n",
126 pid_int);
127 g_unlink(IRTRANS_PIDFILE);
128 IRRECO_RETURN_BOOL(FALSE);
131 IRRECO_DEBUG("irserver is using pid \"%i\".\n", pid_int);
132 if (pid) *pid = pid_int;
133 IRRECO_RETURN_BOOL(TRUE);
137 * Start local irserver.
139 static IrrecoBackendStatus
140 irtrans_wrap_start_server(IRTransWrap *self, const gchar *hostname)
142 gint rvalue;
143 GString *cmdline;
144 IRRECO_ENTER
146 /* Local server is already running. */
147 if (irtrans_wrap_is_irserver_running(self, NULL)) {
148 IRRECO_RETURN_ENUM(IRRECO_BACKEND_OK);
151 /* Migrate to new directory structure from old versions. */
152 if(irreco_is_dir(IRTRANS_OLD_REMOTES_DIR) == TRUE &&
153 irreco_is_dir(IRTRANS_REMOTES_DIR) == FALSE) {
154 IRRECO_PRINTF("Renaming remotes directory from \"%s\" "
155 "to \"%s\"\n", IRTRANS_OLD_REMOTES_DIR,
156 IRTRANS_REMOTES_DIR);
157 g_rename(IRTRANS_OLD_REMOTES_DIR, IRTRANS_REMOTES_DIR);
160 /* Create directory structure needed for IRTrans. */
161 if(irreco_is_dir(IRTRANS_HOME_DIR) == FALSE) {
162 if (g_mkdir(IRTRANS_HOME_DIR, 0700) != 0) {
163 IRRECO_RETURN_ENUM(IRTRANS_HOME_DIR_MISSING);
166 if(irreco_is_dir(IRTRANS_REMOTES_DIR) == FALSE) {
167 if (g_mkdir(IRTRANS_REMOTES_DIR, 0700) != 0) {
168 IRRECO_RETURN_ENUM(IRTRANS_REMOTES_DIR_MISSING);
171 if(irreco_is_dir(IRTRANS_REMOTES_SYMLINK) == FALSE) {
172 if (symlink(IRTRANS_REMOTES_DIR,
173 IRTRANS_REMOTES_SYMLINK) != 0) {
174 IRRECO_RETURN_ENUM(IRTRANS_SYMLINK_REMOTES_FAILED);
178 /* change working directory */
179 if (chdir(IRTRANS_HOME_DIR) != 0) {
180 IRRECO_RETURN_ENUM(IRTRANS_CHDIR_HOME_FAILED);
183 /* Check if irserver exists. */
184 if (system("which irserver > /dev/null") != 0) {
185 IRRECO_RETURN_ENUM(IRTRANS_IRSERVER_MISSING);
188 /* Start irserver. */
189 cmdline = g_string_new(NULL);
190 g_string_printf(cmdline, "irserver -pidfile %s -no_lirc %s &",
191 IRTRANS_PIDFILE, hostname);
192 IRRECO_PRINTF("Executing: %s\n", cmdline->str);
193 rvalue = system(cmdline->str);
194 g_string_free(cmdline, TRUE);
196 /* To old working dir. */
197 chdir(getenv("OLDPWD"));
199 if(rvalue != 0) {
200 IRRECO_PRINTF("Failed to start irserver.\n");
201 IRRECO_RETURN_ENUM(IRTRANS_START_FAILURE);
204 IRRECO_RETURN_ENUM(IRRECO_BACKEND_OK);
208 * Terminates local irserver process.
210 static void irtrans_wrap_stop_server(IRTransWrap *self)
212 pid_t pid;
213 IRRECO_ENTER
215 if (irtrans_wrap_is_irserver_running(self, &pid)) {
217 /* It is, so kill irserver. */
218 IRRECO_PRINTF("Killing irserver with pid \"%i\"\n", pid);
219 if (kill(pid, 2) != 0) {
220 IRRECO_ERROR("Failed to kill irserver.\n");
221 IRRECO_RETURN
224 /* Loop until irserver dies. */
225 while (irtrans_wrap_is_irserver_running_with_pid(pid)) {
226 g_usleep(IRTRANS_WAIT_SLEEP);
228 g_usleep(IRTRANS_WAIT_SLEEP);
230 IRRECO_RETURN
233 static void irtrans_wrap_append_to_list(IRTransWrap *self, const gchar *string)
235 gchar* chuck_str;
236 IRRECO_ENTER
238 IRRECO_PRINTF("Appending to list \"%s\".\n", string);
239 chuck_str = g_string_chunk_insert(self->list_chunk, string);
240 g_ptr_array_add(self->list_array, chuck_str);
241 IRRECO_RETURN
246 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
247 /* Functions */
248 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
251 * Depending on remote_server setting, opens connection to either local or
252 * remote server.
254 IrrecoBackendStatus irtrans_wrap_connect(IRTransWrap *self)
256 IRRECO_ENTER
258 if (irreco_str_isempty(self->hostname->str)) {
259 IRRECO_RETURN_ENUM(IRTRANS_HOST_FAILURE);
262 if(self->remote_server) {
263 IRRECO_RETURN_INT(irtrans_wrap_connect_to_remote(self));
264 } else {
265 IRRECO_RETURN_INT(irtrans_wrap_connect_to_local(self));
270 * Start local irserver and connect to it.
272 IrrecoBackendStatus irtrans_wrap_connect_to_local(IRTransWrap *self)
274 IrrecoBackendStatus status;
275 IRRECO_ENTER
277 /* Are we already connected to something? */
278 if (self->server_socket != -1) {
279 if (irtrans_wrap_is_irserver_running(self, NULL)) {
280 IRRECO_PRINTF("Connection is already open to local "
281 "server.\n");
282 IRRECO_RETURN_ENUM(IRRECO_BACKEND_OK);
283 } else {
284 irtrans_wrap_disconnect(self);
288 IRRECO_PRINTF("Connecting to local server.\n");
289 IRRECO_RETRY_LOOP_START(self->loop)
290 status = irtrans_wrap_start_server(self, self->hostname->str);
291 if (status != IRRECO_BACKEND_OK) break;
293 status = ConnectIRTransServer("127.0.0.1",
294 &self->server_socket);
295 if (status == 0) break;
296 IRRECO_RETRY_LOOP_END(self->loop)
298 if (status != IRRECO_BACKEND_OK) irtrans_wrap_stop_server(self);
299 IRRECO_RETURN_INT(status);
303 * Open connection to remote irserver.
305 IrrecoBackendStatus irtrans_wrap_connect_to_remote(IRTransWrap *self)
307 IrrecoBackendStatus status;
308 IRRECO_ENTER
310 /* Are we already connected to something? */
311 if (self->server_socket != -1) {
312 if (irtrans_wrap_is_irserver_running(self, NULL)) {
313 irtrans_wrap_disconnect(self);
314 } else {
315 IRRECO_PRINTF("Connection is already open to remote "
316 "server.\n");
317 IRRECO_RETURN_ENUM(IRRECO_BACKEND_OK);
321 IRRECO_PRINTF("Connecting to remote server.\n");
322 IRRECO_RETRY_LOOP_START(self->loop)
323 status = ConnectIRTransServer(self->hostname->str,
324 &self->server_socket);
325 if (status == 0) break;
326 IRRECO_RETRY_LOOP_END(self->loop)
327 IRRECO_RETURN_INT(status);
330 void irtrans_wrap_disconnect(IRTransWrap *self)
332 IRRECO_ENTER
334 if (self->server_socket != -1) {
335 DisconnectIRTransServer(self->server_socket);
336 self->server_socket = -1;
337 } else {
338 IRRECO_PRINTF("Connection is not open, doing nothing.\n");
340 irtrans_wrap_stop_server(self);
341 IRRECO_RETURN
344 void irtrans_wrap_set_remote_server(IRTransWrap *self, gboolean remote_server)
346 IRRECO_ENTER
347 irtrans_wrap_disconnect(self);
348 self->remote_server = remote_server;
349 IRRECO_RETURN
352 gboolean irtrans_wrap_get_remote_server(IRTransWrap *self)
354 IRRECO_ENTER
355 IRRECO_RETURN_BOOL(self->remote_server);
358 void irtrans_wrap_set_hostname(IRTransWrap *self, const gchar *string)
360 IRRECO_ENTER
361 irtrans_wrap_disconnect(self);
362 if (string == NULL) {
363 g_string_assign(self->hostname, "");
364 } else {
365 g_string_assign(self->hostname, string);
367 IRRECO_RETURN
370 const gchar *irtrans_wrap_get_hostname(IRTransWrap *self)
372 IRRECO_ENTER
373 if (irreco_str_isempty(self->hostname->str)) {
374 IRRECO_RETURN_STR(NULL);
375 } else {
376 IRRECO_RETURN_STR(self->hostname->str);
380 void irtrans_wrap_clear_list(IRTransWrap *self)
382 IRRECO_ENTER
384 if (self->list_array) g_ptr_array_free(self->list_array, TRUE);
385 if (self->list_chunk) g_string_chunk_free(self->list_chunk);
387 self->list_chunk = g_string_chunk_new(0);
388 self->list_array = g_ptr_array_new();
389 self->list_get_pos = 0;
390 IRRECO_RETURN
393 gboolean irtrans_wrap_get_from_list(IRTransWrap *self, const gchar **string)
395 IRRECO_ENTER
397 if (self->list_array != NULL &&
398 self->list_get_pos < self->list_array->len) {
399 *string = (const gchar *) g_ptr_array_index(
400 self->list_array, self->list_get_pos);
401 IRRECO_PRINTF("Getting \"%s\" from list index \"%i\".\n",
402 *string, self->list_get_pos);
403 self->list_get_pos = self->list_get_pos + 1;
404 IRRECO_RETURN_BOOL(TRUE);
405 } else {
406 *string = NULL;
407 IRRECO_RETURN_BOOL(FALSE);
412 * Autodetect IRTrans modules, and add ip addresses to IRTransWrap string list.
414 * Args: count Will receive number of modules detected, if set.
416 IrrecoBackendStatus irtrans_wrap_get_autodetect_list(IRTransWrap *self,
417 gint *count)
419 IrrecoBackendStatus status;
420 NETWORKMODEEXN data;
421 gint i, j;
422 IRRECO_ENTER
424 /* Prepare eviroment. */
425 if (count) *count = 0;
426 irtrans_wrap_disconnect(self);
427 irtrans_wrap_clear_list(self);
429 IRRECO_PRINTF("Starting local server for autodetection.\n");
430 IRRECO_RETRY_LOOP_START(self->loop)
431 status = irtrans_wrap_start_server(self, "lan");
432 if (status != IRRECO_BACKEND_OK) break;
434 status = ConnectIRTransServer("127.0.0.1",
435 &self->server_socket);
436 if (status == 0) break;
437 IRRECO_RETRY_LOOP_END(self->loop)
439 if (status != IRRECO_BACKEND_OK) {
440 irtrans_wrap_stop_server(self);
441 IRRECO_RETURN_INT(status);
444 /* Nullify structure, just in case. */
445 memset(&data, '\0', sizeof(data));
447 /* Fetch list of IRTrans-modules. */
448 status = GetDeviceStatusExN(self->server_socket, 0, &data);
449 if (status != 0) {
450 IRRECO_PRINTF("Failed to get list of irtrans modules.\n");
451 irtrans_wrap_stop_server(self);
452 IRRECO_RETURN_INT(status);
455 /* Remote2 field should be in format:
456 '@@@~~~lan~~~@@@ __MAC__ __IP__ __PADDING__'
457 Which should look like this:
458 '@@@~~~lan~~~@@@ 00-50-c2-52-78-f2 172.23.118.19 '
461 for (i = 0; i < data.count; i++) {
463 if(memcmp(data.stat[i][0].remote2, "@@@~~~lan~~~@@@", 15) == 0||
464 memcmp(data.stat[i][0].remote2, "@@@~~~LAN~~~@@@", 15) == 0){
466 /* Get ip. */
467 gchar buffer[81];
468 memset(buffer, '\0', sizeof(buffer));
469 memcpy(buffer, data.stat[i][0].remote2 + 34, 46);
471 /* Replace padding spaces at the end with nulls. */
472 for (j = sizeof(buffer) - 1; j-- > 0;) {
473 if (buffer[j] == ' ') buffer[j] = '\0';
476 /* Append to list. */
477 if (count) *count = *count + 1;
478 irtrans_wrap_append_to_list(self, buffer);
482 irtrans_wrap_stop_server(self);
483 IRRECO_RETURN_ENUM(IRRECO_BACKEND_OK);
487 * Get list of devices.
489 * Args: count Will receive number of modules detected, if set.
491 IrrecoBackendStatus irtrans_wrap_get_remote_list(IRTransWrap *self,
492 gint *count)
494 IrrecoBackendStatus status;
495 REMOTEBUFFER data;
496 gint i, j;
497 IRRECO_ENTER
499 /* Prepare eviroment. */
500 irtrans_wrap_clear_list(self);
501 if (count) *count = 0;
503 /* Nullify structure, just in case. */
504 memset(&data, '\0', sizeof(data));
506 /* Fetch list of devices. */
507 IRRECO_RETRY_LOOP_START(self->loop)
508 status = irtrans_wrap_connect(self);
509 if (status != IRRECO_BACKEND_OK) break;
510 status = GetRemotes(self->server_socket, 0, &data);
511 if (status == 0) break;
512 irtrans_wrap_disconnect(self);
513 IRRECO_RETRY_LOOP_END(self->loop)
514 if (status != 0) IRRECO_RETURN_INT(status);
516 for (i = 0; i < data.count_buffer; i++) {
518 /* Get device name. */
519 gchar buffer[81];
520 memcpy(buffer, data.remotes[i].name, sizeof(buffer) - 1);
521 buffer[sizeof(buffer) - 1] = '\0';
523 /* Replace padding spaces at the end with nulls. */
524 for (j = sizeof(buffer) - 1; j-- > 0;) {
525 if (buffer[j] == ' ') {
526 buffer[j] = '\0';
527 } else {
528 break;
532 /* Append to list. */
533 if (count) *count = *count + 1;
534 irtrans_wrap_append_to_list(self, buffer);
537 IRRECO_RETURN_ENUM(IRRECO_BACKEND_OK);
541 * Get list of commands a given device has.
543 * Args: device Get command from this device.
544 * count Will receive number of modules detected, if set.
546 IrrecoBackendStatus irtrans_wrap_get_command_list(IRTransWrap *self,
547 const gchar *device,
548 gint *count)
550 IrrecoBackendStatus status;
551 COMMANDBUFFER data;
552 gint i, j;
553 IRRECO_ENTER
555 /* Prepare eviroment. */
556 irtrans_wrap_clear_list(self);
557 if (count) *count = 0;
559 /* Nullify structure, just in case. */
560 memset(&data, '\0', sizeof(data));
562 /* Fetch list of devices. */
563 IRRECO_RETRY_LOOP_START(self->loop)
564 status = irtrans_wrap_connect(self);
565 if (status != IRRECO_BACKEND_OK) break;
566 status = GetCommands(self->server_socket, (gchar *) device,
567 0, &data);
568 if (status == 0) break;
569 irtrans_wrap_disconnect(self);
570 IRRECO_RETRY_LOOP_END(self->loop)
571 if (status != 0) IRRECO_RETURN_INT(status);
573 for (i = 0; i < data.count_buffer; i++) {
575 /* Get device name. */
576 gchar buffer[21];
577 memcpy(buffer, data.commands[i], sizeof(buffer) - 1);
578 buffer[sizeof(buffer) - 1] = '\0';
580 /* Replace padding spaces at the end with nulls. */
581 for (j = sizeof(buffer) - 1; j-- > 0;) {
582 if (buffer[j] == ' ') {
583 buffer[j] = '\0';
584 } else {
585 break;
589 /* Append to list. */
590 if (count) *count = *count + 1;
591 irtrans_wrap_append_to_list(self, buffer);
594 IRRECO_RETURN_ENUM(IRRECO_BACKEND_OK);
598 * Send command.
600 IrrecoBackendStatus irtrans_wrap_send_command(IRTransWrap *self,
601 const gchar *device,
602 const gchar *command)
604 IrrecoBackendStatus status;
605 NETWORKSTATUS *net_status = NULL;
606 IRRECO_ENTER
608 /* Send command. */
609 IRRECO_RETRY_LOOP_START(self->loop)
610 status = irtrans_wrap_connect(self);
611 if (status != IRRECO_BACKEND_OK) break;
612 net_status = SendRemoteCommand(self->server_socket,
613 (gchar *) device,
614 (gchar *) command,
615 0, 0, 0);
616 if (net_status == NULL) break;
617 irtrans_wrap_disconnect(self);
618 IRRECO_RETRY_LOOP_END(self->loop)
619 if (status != 0) IRRECO_RETURN_INT(status);
621 if (net_status == NULL) {
622 IRRECO_RETURN_ENUM(IRRECO_BACKEND_OK);
623 } else {
624 IRRECO_RETURN_ENUM(IRTRANS_SEND_FAILURE);
629 * Learn command.
631 IrrecoBackendStatus irtrans_wrap_learn_command(IRTransWrap *self,
632 const gchar *device,
633 const gchar *command,
634 gushort timeout)
636 IrrecoBackendStatus status;
637 NETWORKSTATUS *net_status;
638 IRRECO_ENTER
640 status = irtrans_wrap_connect(self);
641 if (status != IRRECO_BACKEND_OK) IRRECO_RETURN_INT(status);
643 /* Learn command. */
644 net_status = LearnIRCode(self->server_socket,
645 (gchar *) device,
646 (gchar *) command,
647 timeout);
649 if (net_status == NULL) {
650 IRRECO_PRINTF("Learned IR code for command \"%s\" of "
651 "device \"%s\".\n", command, device);
652 IRRECO_RETURN_ENUM(IRRECO_BACKEND_OK);
653 } else {
654 IRRECO_PRINTF("Failed to learn IR code for command \"%s\" of "
655 "device \"%s\".\n", command, device);
656 IRRECO_RETURN_ENUM(IRTRANS_LEARN_FAILURE);
662 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
663 /* Events and Callbacks */
664 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/