Mercurial > geeqie
view src/remote.c @ 606:408f36d536a5
search_status_update(): slightly reduce code redundancy.
author | zas_ |
---|---|
date | Thu, 08 May 2008 20:21:03 +0000 |
parents | f9bf33be53ff |
children | 3097880d7d95 |
line wrap: on
line source
/* * Geeqie * (C) 2004 John Ellis * Copyright (C) 2008 The Geeqie Team * * Author: John Ellis * * This software is released under the GNU General Public License (GNU GPL). * Please read the included file COPYING for more information. * This software comes with no warranty of any kind, use at your own risk! */ #include "main.h" #include "remote.h" #include "debug.h" #include <sys/types.h> #include <sys/socket.h> #include <sys/un.h> #include <signal.h> #include <errno.h> #define SERVER_MAX_CLIENTS 8 #define REMOTE_SERVER_BACKLOG 4 #ifndef UNIX_PATH_MAX #define UNIX_PATH_MAX 108 #endif typedef struct _RemoteClient RemoteClient; struct _RemoteClient { gint fd; gint channel_id; RemoteConnection *rc; }; static gboolean remote_server_client_cb(GIOChannel *source, GIOCondition condition, gpointer data) { RemoteClient *client = data; RemoteConnection *rc; rc = client->rc; if (condition & G_IO_IN) { GList *queue = NULL; GList *work; gchar *buffer = NULL; GError *error = NULL; guint termpos; while (g_io_channel_read_line(source, &buffer, NULL, &termpos, &error) == G_IO_STATUS_NORMAL) { if (buffer) { buffer[termpos] = '\0'; if (strlen(buffer) > 0) { queue = g_list_append(queue, buffer); } else { g_free(buffer); } buffer = NULL; } } if (error) { printf("error reading socket: %s\n", error->message); g_error_free(error); } work = queue; while (work) { gchar *command = work->data; work = work->next; if (rc->read_func) rc->read_func(rc, command, rc->read_data); g_free(command); } g_list_free(queue); } if (condition & G_IO_HUP) { rc->clients = g_list_remove(rc->clients, client); DEBUG_1("HUP detected, closing client."); DEBUG_1("client count %d", g_list_length(rc->clients)); g_source_remove(client->channel_id); close(client->fd); g_free(client); } return TRUE; } static void remote_server_client_add(RemoteConnection *rc, int fd) { RemoteClient *client; GIOChannel *channel; if (g_list_length(rc->clients) > SERVER_MAX_CLIENTS) { printf("maximum remote clients of %d exceeded, closing connection\n", SERVER_MAX_CLIENTS); close(fd); return; } client = g_new0(RemoteClient, 1); client->rc = rc; client->fd = fd; channel = g_io_channel_unix_new(fd); client->channel_id = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_IN | G_IO_HUP, remote_server_client_cb, client, NULL); g_io_channel_unref(channel); rc->clients = g_list_append(rc->clients, client); DEBUG_1("client count %d", g_list_length(rc->clients)); } static void remote_server_clients_close(RemoteConnection *rc) { while (rc->clients) { RemoteClient *client = rc->clients->data; rc->clients = g_list_remove(rc->clients, client); g_source_remove(client->channel_id); close(client->fd); g_free(client); } } static gboolean remote_server_read_cb(GIOChannel *source, GIOCondition condition, gpointer data) { RemoteConnection *rc = data; int fd; unsigned int alen; fd = accept(rc->fd, NULL, &alen); if (fd == -1) { printf("error accepting socket: %s\n", strerror(errno)); return TRUE; } remote_server_client_add(rc, fd); return TRUE; } static gint remote_server_exists(const gchar *path) { RemoteConnection *rc; /* verify server up */ rc = remote_client_open(path); remote_close(rc); if (rc) return TRUE; /* unable to connect, remove socket file to free up address */ unlink(path); return FALSE; } RemoteConnection *remote_server_open(const gchar *path) { RemoteConnection *rc; struct sockaddr_un addr; gint sun_path_len; int fd; GIOChannel *channel; if (remote_server_exists(path)) { printf("Address already in use: %s\n", path); return NULL; } fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd == -1) return NULL; addr.sun_family = AF_UNIX; sun_path_len = MIN(strlen(path) + 1, UNIX_PATH_MAX); strncpy(addr.sun_path, path, sun_path_len); if (bind(fd, &addr, sizeof(addr)) == -1 || listen(fd, REMOTE_SERVER_BACKLOG) == -1) { printf("error subscribing to socket: %s\n", strerror(errno)); close(fd); return NULL; } rc = g_new0(RemoteConnection, 1); rc->server = TRUE; rc->fd = fd; rc->path = g_strdup(path); rc->read_func = NULL; rc->read_data = NULL; rc->clients = NULL; channel = g_io_channel_unix_new(rc->fd); rc->channel_id = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, G_IO_IN, remote_server_read_cb, rc, NULL); g_io_channel_unref(channel); return rc; } void remote_server_subscribe(RemoteConnection *rc, RemoteReadFunc *func, gpointer data) { if (!rc || !rc->server) return; rc->read_func = func; rc->read_data = data; } RemoteConnection *remote_client_open(const gchar *path) { RemoteConnection *rc; struct stat st; struct sockaddr_un addr; gint sun_path_len; int fd; if (stat(path, &st) != 0 || !S_ISSOCK(st.st_mode)) return NULL; fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd == -1) return NULL; addr.sun_family = AF_UNIX; sun_path_len = MIN(strlen(path) + 1, UNIX_PATH_MAX); strncpy(addr.sun_path, path, sun_path_len); if (connect(fd, &addr, sizeof(addr)) == -1) { DEBUG_1("error connecting to socket: %s", strerror(errno)); close(fd); return NULL; } rc = g_new0(RemoteConnection, 1); rc->server = FALSE; rc->fd = fd; rc->path = g_strdup(path); /* this might fix the freezes on freebsd, solaris, etc. - completely untested */ remote_client_send(rc, "\n"); return rc; } static sig_atomic_t sigpipe_occured = FALSE; static void sighandler_sigpipe(int sig) { sigpipe_occured = TRUE; } gint remote_client_send(RemoteConnection *rc, const gchar *text) { struct sigaction new_action, old_action; gint ret = FALSE; if (!rc || rc->server) return FALSE; if (!text) return TRUE; sigpipe_occured = FALSE; new_action.sa_handler = sighandler_sigpipe; sigemptyset(&new_action.sa_mask); new_action.sa_flags = 0; /* setup our signal handler */ sigaction(SIGPIPE, &new_action, &old_action); if (write(rc->fd, text, strlen(text)) == -1 || write(rc->fd, "\n", 1) == -1) { if (sigpipe_occured) { printf("SIGPIPE writing to socket: %s\n", rc->path); } else { printf("error writing to socket: %s\n", strerror(errno)); } ret = FALSE;; } else { ret = TRUE; } /* restore the original signal handler */ sigaction(SIGPIPE, &old_action, NULL); return ret; } void remote_close(RemoteConnection *rc) { if (!rc) return; if (rc->server) { remote_server_clients_close(rc); g_source_remove(rc->channel_id); unlink(rc->path); } close(rc->fd); g_free(rc->path); g_free(rc); }