2 * irreco - Ir Remote Control
3 * Copyright (C) 2007 Arto Karppinen (arto.karppinen@iki.fi)
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.
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"
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()
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
,
47 IRRECO_RETURN_PTR(self
);
50 void irtrans_wrap_delete(IRTransWrap
*self
)
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
);
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
)
74 GString
*link_path
= NULL
;
75 gchar
*real_path
= NULL
;
78 /* Location of the link which should point to the executable
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
);
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
);
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
);
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
,
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",
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
)
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"));
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
)
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");
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
);
233 static void irtrans_wrap_append_to_list(IRTransWrap
*self
, const gchar
*string
)
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
);
246 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
248 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
251 * Depending on remote_server setting, opens connection to either local or
254 IrrecoBackendStatus
irtrans_wrap_connect(IRTransWrap
*self
)
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
));
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
;
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 "
282 IRRECO_RETURN_ENUM(IRRECO_BACKEND_OK
);
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
;
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
);
315 IRRECO_PRINTF("Connection is already open to remote "
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
)
334 if (self
->server_socket
!= -1) {
335 DisconnectIRTransServer(self
->server_socket
);
336 self
->server_socket
= -1;
338 IRRECO_PRINTF("Connection is not open, doing nothing.\n");
340 irtrans_wrap_stop_server(self
);
344 void irtrans_wrap_set_remote_server(IRTransWrap
*self
, gboolean remote_server
)
347 irtrans_wrap_disconnect(self
);
348 self
->remote_server
= remote_server
;
352 gboolean
irtrans_wrap_get_remote_server(IRTransWrap
*self
)
355 IRRECO_RETURN_BOOL(self
->remote_server
);
358 void irtrans_wrap_set_hostname(IRTransWrap
*self
, const gchar
*string
)
361 irtrans_wrap_disconnect(self
);
362 if (string
== NULL
) {
363 g_string_assign(self
->hostname
, "");
365 g_string_assign(self
->hostname
, string
);
370 const gchar
*irtrans_wrap_get_hostname(IRTransWrap
*self
)
373 if (irreco_str_isempty(self
->hostname
->str
)) {
374 IRRECO_RETURN_STR(NULL
);
376 IRRECO_RETURN_STR(self
->hostname
->str
);
380 void irtrans_wrap_clear_list(IRTransWrap
*self
)
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;
393 gboolean
irtrans_wrap_get_from_list(IRTransWrap
*self
, const gchar
**string
)
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
);
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
,
419 IrrecoBackendStatus status
;
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
);
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){
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
,
494 IrrecoBackendStatus status
;
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. */
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
] == ' ') {
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
,
550 IrrecoBackendStatus status
;
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
,
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. */
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
] == ' ') {
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
);
600 IrrecoBackendStatus
irtrans_wrap_send_command(IRTransWrap
*self
,
602 const gchar
*command
)
604 IrrecoBackendStatus status
;
605 NETWORKSTATUS
*net_status
= NULL
;
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
,
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
);
624 IRRECO_RETURN_ENUM(IRTRANS_SEND_FAILURE
);
631 IrrecoBackendStatus
irtrans_wrap_learn_command(IRTransWrap
*self
,
633 const gchar
*command
,
636 IrrecoBackendStatus status
;
637 NETWORKSTATUS
*net_status
;
640 status
= irtrans_wrap_connect(self
);
641 if (status
!= IRRECO_BACKEND_OK
) IRRECO_RETURN_INT(status
);
644 net_status
= LearnIRCode(self
->server_socket
,
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
);
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 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/