5
5
#include <sys/socket.h> // for socket and read
6
6
#include <errno.h>
7
7
8
+ /*
9
+ * Implements the read callback.
10
+ * Called when data has been sent by signald and is ready to be handled.
11
+ *
12
+ * Should probably be moved in another module.
13
+ */
8
14
void
9
15
signald_read_cb (gpointer data , gint source , PurpleInputCondition cond )
10
16
{
@@ -50,78 +56,103 @@ signald_read_cb(gpointer data, gint source, PurpleInputCondition cond)
50
56
}
51
57
}
52
58
53
- gboolean sockaddr_from_path (struct sockaddr_un * address , const gchar * path ) {
54
- memset (address , 0 , sizeof (struct sockaddr_un ));
55
- address -> sun_family = AF_UNIX ;
56
- if (strlen (path )- 1 > sizeof address -> sun_path ) {
57
- purple_debug_error (SIGNALD_PLUGIN_ID , "socket path %s exceeds maximum length %lu!\n" , path , sizeof address -> sun_path ); // TODO: show error in ui
58
- return FALSE;
59
- } else {
60
- strcpy (address -> sun_path , path );
61
- return TRUE;
62
- }
63
- }
64
-
59
+ /*
60
+ * This struct exchanges data between threads, see @try_connect.
61
+ */
65
62
typedef struct {
66
63
SignaldAccount * sa ;
67
64
gchar * socket_path ;
68
65
gchar * message ;
69
- } SignaldConnection ;
66
+ } SignaldConnectionAttempt ;
70
67
68
+ /*
69
+ * See @execute_on_main_thread.
70
+ */
71
71
static gboolean
72
72
display_connection_error (void * data ) {
73
- SignaldConnection * sc = data ;
73
+ SignaldConnectionAttempt * sc = data ;
74
74
purple_connection_error (sc -> sa -> pc , PURPLE_CONNECTION_ERROR_NETWORK_ERROR , sc -> message );
75
75
g_free (sc -> message );
76
76
g_free (sc );
77
77
return FALSE;
78
78
}
79
79
80
+ /*
81
+ * See @execute_on_main_thread.
82
+ */
80
83
static gboolean
81
84
display_debug_info (void * data ) {
82
- SignaldConnection * sc = data ;
83
- purple_debug_info (SIGNALD_PLUGIN_ID , "%s" ,sc -> message );
85
+ SignaldConnectionAttempt * sc = data ;
86
+ purple_debug_info (SIGNALD_PLUGIN_ID , "%s" , sc -> message );
84
87
g_free (sc -> message );
85
88
g_free (sc );
86
89
return FALSE;
87
90
}
88
91
92
+ /*
93
+ * Every function writing to the GTK UI must be executed from the GTK main thread.
94
+ * This function is a crutch for wrapping some purple functions:
95
+ *
96
+ * * purple_debug_info in display_debug_info
97
+ * * purple_connection_error in display_connection_error
98
+ *
99
+ * Can only handle one message string instead of variardic arguments.
100
+ */
101
+ static void
102
+ execute_on_main_thread (GSourceFunc function , SignaldConnectionAttempt * sc , gchar * message ) {
103
+ sc -> message = message ;
104
+ purple_timeout_add (0 , function , g_memdup2 (sc , sizeof * sc ));
105
+ }
106
+
107
+ /*
108
+ * Tries to connect to a socket at a given location.
109
+ * It is ought to be executed in a thread.
110
+ * Only in case it does noes not succeed AND is the last thread to stop trying,
111
+ * the situation is considered a connection failure.
112
+ */
89
113
static void *
90
114
do_try_connect (void * arg ) {
91
- SignaldConnection * sc = arg ;
115
+ SignaldConnectionAttempt * sc = arg ;
116
+
92
117
struct sockaddr_un address ;
93
- if (sockaddr_from_path (& address , sc -> socket_path ))
94
- {
118
+ if (strlen (sc -> socket_path )- 1 > sizeof address .sun_path ) {
119
+ execute_on_main_thread (display_connection_error , sc , g_strdup_printf ("socket path %s exceeds maximum length %lu!\n" , sc -> socket_path , sizeof address .sun_path ));
120
+ } else {
121
+ // convert path to sockaddr
122
+ memset (& address , 0 , sizeof (struct sockaddr_un ));
123
+ address .sun_family = AF_UNIX ;
124
+ strcpy (address .sun_path , sc -> socket_path );
125
+
95
126
// create a socket
96
127
int fd = socket (AF_UNIX , SOCK_STREAM , 0 );
97
128
if (fd < 0 ) {
98
- sc -> message = g_strdup_printf ("Could not create socket: %s" , strerror (errno ));
99
- purple_timeout_add (0 , display_connection_error , g_memdup2 (sc , sizeof * sc ));
129
+ execute_on_main_thread (display_connection_error , sc , g_strdup_printf ("Could not create socket: %s" , strerror (errno )));
100
130
} else {
101
131
int32_t err = -1 ;
132
+
102
133
// connect our socket to signald socket
103
134
for (int try = 1 ; try <= SIGNALD_TIMEOUT_SECONDS && err != 0 && sc -> sa -> fd == 0 ; try ++ ) {
104
135
err = connect (fd , (struct sockaddr * ) & address , sizeof (struct sockaddr_un ));
105
- sc -> message = g_strdup_printf ("Connecting to %s (try #%d)...\n" , address .sun_path , try );
106
- purple_timeout_add (0 , display_debug_info , g_memdup2 (sc , sizeof * sc ));
136
+ execute_on_main_thread (display_debug_info , sc , g_strdup_printf ("Connecting to %s (try #%d)...\n" , address .sun_path , try ));
107
137
sleep (1 ); // altogether wait SIGNALD_TIMEOUT_SECONDS
108
138
}
109
139
110
140
if (err == 0 ) {
111
141
// successfully connected, tell purple to use our socket
112
- sc -> message = g_strdup_printf ("Connected to %s.\n" , address .sun_path );
113
- purple_timeout_add (0 , display_debug_info , g_memdup2 (sc , sizeof * sc ));
142
+ execute_on_main_thread (display_debug_info , sc , g_strdup_printf ("Connected to %s.\n" , address .sun_path ));
114
143
sc -> sa -> fd = fd ;
115
144
sc -> sa -> watcher = purple_input_add (fd , PURPLE_INPUT_READ , signald_read_cb , sc -> sa );
116
145
}
117
146
if (sc -> sa -> fd == 0 ) {
118
- sc -> message = g_strdup_printf ("No connection to %s after %d tries.\n" , address .sun_path , SIGNALD_TIMEOUT_SECONDS );
119
- purple_timeout_add (0 , display_debug_info , g_memdup2 (sc , sizeof * sc ));
120
- sc -> sa -> socket_paths_count -- ;
147
+ // no concurrent connection attempt has been successful by now
148
+ execute_on_main_thread (display_debug_info , sc , g_strdup_printf ("No connection to %s after %d tries.\n" , address .sun_path , SIGNALD_TIMEOUT_SECONDS ));
149
+
150
+ sc -> sa -> socket_paths_count -- ; // this tread gives up trying
151
+ // NOTE: although unlikely, it is possible that above decrement and other modifications or checks happen concurrently.
152
+ // TODO: use a mutex where appropriate.
121
153
if (sc -> sa -> socket_paths_count == 0 ) {
122
-
123
- sc -> message = g_strdup ("Unable to connect to any socket location." );
124
- purple_timeout_add (0 , display_connection_error , g_memdup2 (sc , sizeof * sc ));
154
+ // no trying threads are remaining
155
+ execute_on_main_thread (display_connection_error , sc , sc -> message = g_strdup ("Unable to connect to any socket location." ));
125
156
}
126
157
}
127
158
}
@@ -131,16 +162,28 @@ do_try_connect(void * arg) {
131
162
return NULL ;
132
163
}
133
164
165
+ /*
166
+ * Starts a connection attempt in background.
167
+ */
134
168
static void
135
169
try_connect (SignaldAccount * sa , gchar * socket_path ) {
136
- SignaldConnection * sc = g_new0 (SignaldConnection , 1 );
170
+ SignaldConnectionAttempt * sc = g_new0 (SignaldConnectionAttempt , 1 );
137
171
sc -> sa = sa ;
138
172
sc -> socket_path = socket_path ;
139
173
pthread_t try_connect_thread ;
140
- // TODO: handle error int err =
141
- pthread_create (& try_connect_thread , NULL , do_try_connect , (void * )sc );
174
+ int err = pthread_create (& try_connect_thread , NULL , do_try_connect , (void * )sc );
175
+ if (err != 0 ) {
176
+ gchar * errmsg = g_strdup_printf ("Could not create thread for connecting in background: %s" , strerror (err ));
177
+ purple_connection_error (sa -> pc , PURPLE_CONNECTION_ERROR_NETWORK_ERROR , errmsg );
178
+ g_free (errmsg );
179
+ }
142
180
}
143
181
182
+ /*
183
+ * Connect to signald socket.
184
+ * Tries multiple possible default socket location at once in background.
185
+ * In case the user has explicitly defined a socket location, only that one is considered.
186
+ */
144
187
void
145
188
signald_connect_socket (SignaldAccount * sa ) {
146
189
purple_connection_set_state (sa -> pc , PURPLE_CONNECTION_CONNECTING );
0 commit comments