2
* Copyright (C) 2003 Bastien Nocera <hadess@hadess.net>
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; either version 2 of the License, or
7
* (at your option) any later version.
9
* This program is distributed in the hope that it will be useful,
10
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
* GNU General Public License for more details.
14
* You should have received a copy of the GNU General Public License
15
* along with this program; if not, write to the Free Software
16
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
#include <sys/types.h>
26
#include <sys/types.h>
27
#include <sys/socket.h>
31
#include "bacon-message-connection.h"
34
#define UNIX_PATH_MAX 108
37
struct BaconMessageConnection {
38
/* A server accepts connections */
41
/* The socket path itself */
44
/* File descriptor of the socket */
46
/* Channel to watch */
48
/* Event id returned by g_io_add_watch() */
51
/* Connections accepted by this connection */
52
GSList *accepted_connections;
55
void (*func) (const char *message, gpointer user_data);
60
test_is_socket (const char *path)
64
if (stat (path, &s) == -1)
67
if (S_ISSOCK (s.st_mode))
74
is_owned_by_user_and_socket (const char *path)
78
if (stat (path, &s) == -1)
81
if (s.st_uid != geteuid ())
84
if ((s.st_mode & S_IFSOCK) != S_IFSOCK)
90
static gboolean server_cb (GIOChannel *source,
91
GIOCondition condition, gpointer data);
94
setup_connection (BaconMessageConnection *conn)
96
g_return_val_if_fail (conn->chan == NULL, FALSE);
98
conn->chan = g_io_channel_unix_new (conn->fd);
102
g_io_channel_set_line_term (conn->chan, "\n", 1);
103
conn->conn_id = g_io_add_watch (conn->chan, G_IO_IN, server_cb, conn);
109
accept_new_connection (BaconMessageConnection *server_conn)
111
BaconMessageConnection *conn;
114
g_return_if_fail (server_conn->is_server);
116
conn = g_new0 (BaconMessageConnection, 1);
117
conn->is_server = FALSE;
118
conn->func = server_conn->func;
119
conn->data = server_conn->data;
121
conn->fd = accept (server_conn->fd, NULL, (guint *)&alen);
123
server_conn->accepted_connections =
124
g_slist_prepend (server_conn->accepted_connections, conn);
126
setup_connection (conn);
130
server_cb (GIOChannel *source, GIOCondition condition, gpointer data)
132
BaconMessageConnection *conn = (BaconMessageConnection *)data;
133
char *message, *subs, buf;
138
if (conn->is_server && conn->fd == g_io_channel_unix_get_fd (source)) {
139
accept_new_connection (conn);
142
message = g_malloc (1);
144
rc = read (cd, &buf, 1);
145
while (rc > 0 && buf != '\n')
147
message = g_realloc (message, rc + offset + 1);
148
message[offset] = buf;
149
offset = offset + rc;
150
rc = read (cd, &buf, 1);
153
g_io_channel_shutdown (conn->chan, FALSE, NULL);
154
g_io_channel_unref (conn->chan);
163
message[offset] = '\0';
168
while (finished == FALSE && *subs != '\0')
170
if (conn->func != NULL)
171
(*conn->func) (subs, conn->data);
173
subs += strlen (subs) + 1;
174
if (subs - message >= offset)
184
find_file_with_pattern (const char *dir, const char *pattern)
187
char *found_filename;
188
const char *filename;
191
filedir = g_dir_open (dir, 0, NULL);
195
pat = g_pattern_spec_new (pattern);
198
g_dir_close (filedir);
202
found_filename = NULL;
204
while ((filename = g_dir_read_name (filedir)))
206
if (g_pattern_match_string (pat, filename))
208
char *tmp = g_build_filename (dir, filename, NULL);
209
if (is_owned_by_user_and_socket (tmp))
210
found_filename = g_strdup (filename);
214
if (found_filename != NULL)
218
g_pattern_spec_free (pat);
219
g_dir_close (filedir);
221
return found_filename;
225
socket_filename (const char *prefix)
227
char *pattern, *newfile, *path, *filename;
230
pattern = g_strdup_printf ("%s.%s.*", prefix, g_get_user_name ());
231
tmpdir = g_get_tmp_dir ();
232
filename = find_file_with_pattern (tmpdir, pattern);
233
if (filename == NULL)
235
newfile = g_strdup_printf ("%s.%s.%u", prefix,
236
g_get_user_name (), g_random_int ());
237
path = g_build_filename (tmpdir, newfile, NULL);
240
path = g_build_filename (tmpdir, filename, NULL);
249
try_server (BaconMessageConnection *conn)
251
struct sockaddr_un uaddr;
253
uaddr.sun_family = AF_UNIX;
254
strncpy (uaddr.sun_path, conn->path,
255
MIN (strlen(conn->path)+1, UNIX_PATH_MAX));
256
conn->fd = socket (PF_UNIX, SOCK_STREAM, 0);
257
if (bind (conn->fd, (struct sockaddr *) &uaddr, sizeof (uaddr)) == -1)
262
listen (conn->fd, 5);
264
if (!setup_connection (conn))
270
try_client (BaconMessageConnection *conn)
272
struct sockaddr_un uaddr;
274
uaddr.sun_family = AF_UNIX;
275
strncpy (uaddr.sun_path, conn->path,
276
MIN(strlen(conn->path)+1, UNIX_PATH_MAX));
277
conn->fd = socket (PF_UNIX, SOCK_STREAM, 0);
278
if (connect (conn->fd, (struct sockaddr *) &uaddr,
279
sizeof (uaddr)) == -1)
285
return setup_connection (conn);
288
BaconMessageConnection *
289
bacon_message_connection_new (const char *prefix)
291
BaconMessageConnection *conn;
293
g_return_val_if_fail (prefix != NULL, NULL);
295
conn = g_new0 (BaconMessageConnection, 1);
296
conn->path = socket_filename (prefix);
298
if (test_is_socket (conn->path) == FALSE)
300
if (!try_server (conn))
302
bacon_message_connection_free (conn);
306
conn->is_server = TRUE;
310
if (try_client (conn) == FALSE)
316
bacon_message_connection_free (conn);
320
conn->is_server = TRUE;
324
conn->is_server = FALSE;
329
bacon_message_connection_free (BaconMessageConnection *conn)
333
g_return_if_fail (conn != NULL);
334
/* Only servers can accept other connections */
335
g_return_if_fail (conn->is_server != FALSE ||
336
conn->accepted_connections == NULL);
338
child_conn = conn->accepted_connections;
339
while (child_conn != NULL) {
340
bacon_message_connection_free (child_conn->data);
341
child_conn = g_slist_next (child_conn);
343
g_slist_free (conn->accepted_connections);
346
g_source_remove (conn->conn_id);
350
g_io_channel_shutdown (conn->chan, FALSE, NULL);
351
g_io_channel_unref (conn->chan);
354
if (conn->is_server != FALSE) {
357
if (conn->fd != -1) {
366
bacon_message_connection_set_callback (BaconMessageConnection *conn,
367
BaconMessageReceivedFunc func,
370
g_return_if_fail (conn != NULL);
373
conn->data = user_data;
377
bacon_message_connection_send (BaconMessageConnection *conn,
380
g_return_if_fail (conn != NULL);
381
g_return_if_fail (message != NULL);
383
g_io_channel_write_chars (conn->chan, message, strlen (message),
385
g_io_channel_write_chars (conn->chan, "\n", 1, NULL, NULL);
386
g_io_channel_flush (conn->chan, NULL);
390
bacon_message_connection_get_is_server (BaconMessageConnection *conn)
392
g_return_val_if_fail (conn != NULL, FALSE);
394
return conn->is_server;