From d279b7b38fab20d993bef61991dd77e22a701f49 Mon Sep 17 00:00:00 2001 From: Samuel Fadel Date: Tue, 21 Jul 2015 13:38:00 -0300 Subject: The T client and daemon are now the same program. Added the -d flag to run as daemon. --- T.c | 228 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 220 insertions(+), 8 deletions(-) (limited to 'T.c') diff --git a/T.c b/T.c index dc62c3d..a531b82 100644 --- a/T.c +++ b/T.c @@ -17,28 +17,71 @@ * T. If not, see . */ +#include #include +#include #include #include -#include #include + +#include +#include +#include +#include +#include +#include + +#include #include +#include + +#include "config.h" + +#define PROGRAM_NAME "T" + +#define SOCKPATH CONFIG_SOCKDIR "/" CONFIG_SOCKNAME +#define SOCKPATH_MAXLEN sizeof((SOCKPATH)) + +#define MSGBUF_MAXLEN 64 + +#define ARG_DAEMON "-d" +#define ARG_NEWWIN "-n" +#define ARG_EXIT "-x" -#include "T.h" +typedef enum { + MSG_NEWWIN, + MSG_EXIT, + MSG_OK, + MSG_ERROR +} TMessage; static int window_count = 0; -/* Event callbacks */ +/* UI event callbacks */ static void window_destroy_callback(GtkWidget *widget, gpointer data); static gboolean key_press_callback(GtkWidget *widget, GdkEvent *event, gpointer data); static void terminal_child_exited_callback(VteTerminal *terminal, gpointer data); static void terminal_window_title_changed_callback(VteTerminal *terminal, gpointer data); -/* Helper functions */ +/* UI helper functions */ static void change_font_size(VteTerminal *terminal, int delta); static void set_preferences(VteTerminal *terminal); static void setup_terminal(GtkWindow *window, VteTerminal *terminal); +/* (local) Networking & utility functions */ +static int num_open_windows(); +static void new_window(); +static void wrn(const char *message); +static void err(const char *message, int ecode); +static int conn_socket(); +static int bind_socket(); +static void cleanup_socket(int sock_fd); +static TMessage handle_message(TMessage msg); +static gboolean socket_g_io_watch(GIOChannel *source, GIOCondition condition, gpointer data); +static int send_message(TMessage msg); +static int run_daemon(); +static void usage(); + static void window_destroy_callback(GtkWidget *widget, gpointer data) { @@ -193,13 +236,13 @@ setup_terminal(GtkWindow *window, VteTerminal *terminal) set_preferences(terminal); } -int +static int num_open_windows() { return window_count; } -void +static void new_window() { GtkWindow *window = (GtkWindow *) gtk_window_new(GTK_WINDOW_TOPLEVEL); @@ -227,15 +270,184 @@ new_window() window_count++; } -void +static void wrn(const char *message) { fprintf(stderr, "%s: %s\n", PROGRAM_NAME, message); } -void +static void err(const char *message, int ecode) { wrn(message); exit(ecode); } + +static int +conn_socket() +{ + struct sockaddr_un sock_addr; + + int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock_fd < 0) + err("Failed to create socket", EXIT_FAILURE); + + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sun_family = AF_UNIX; + strncpy(sock_addr.sun_path, SOCKPATH, sizeof(sock_addr.sun_path) - 1); + + if (connect(sock_fd, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) + err("Failed to connect to daemon", EXIT_FAILURE); + + return sock_fd; +} + +static int +bind_socket() +{ + struct sockaddr_un sock_addr; + + memset(&sock_addr, 0, sizeof(sock_addr)); + sock_addr.sun_family = AF_UNIX; + strncpy(sock_addr.sun_path, SOCKPATH, sizeof(sock_addr.sun_path) - 1); + + /* Remove old socket */ + if (!access(sock_addr.sun_path, F_OK) && unlink(sock_addr.sun_path) < 0) + err("Failed to remove socket file at " SOCKPATH, EXIT_FAILURE); + if (!access(CONFIG_SOCKDIR, F_OK) && rmdir(CONFIG_SOCKDIR) < 0) + err("Failed to remove socket directory at " CONFIG_SOCKDIR, EXIT_FAILURE); + + /* Create a new directory */ + if (mkdir(CONFIG_SOCKDIR, S_IRWXU) < 0) + err("Failed to create socket directory at " CONFIG_SOCKDIR, EXIT_FAILURE); + + int sock_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock_fd < 0) + err("Failed to create socket", EXIT_FAILURE); + + if (bind(sock_fd, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) < 0) + err("Failed to bind the socket to the file at " SOCKPATH, EXIT_FAILURE); + + return sock_fd; +} + +static void +cleanup_socket(int sock_fd) +{ + if (close(sock_fd) < 0) + err("Failed to close socket", EXIT_FAILURE); + + if (unlink(SOCKPATH) < 0) + err("Failed to remove socket file at " SOCKPATH, EXIT_FAILURE); + if (rmdir(CONFIG_SOCKDIR) < 0) + err("Failed to remove socket directory at " CONFIG_SOCKDIR, EXIT_FAILURE); +} + +static TMessage +handle_message(TMessage msg) +{ + switch (msg) { + case MSG_NEWWIN: + new_window(); + return MSG_OK; + + case MSG_EXIT: + if (num_open_windows() == 0) { + gtk_main_quit(); + return MSG_OK; + } + return MSG_ERROR; + + default: + return MSG_ERROR; + } +} + +static gboolean +socket_g_io_watch(GIOChannel *source, GIOCondition condition, gpointer data) +{ + int sock_fd = g_io_channel_unix_get_fd(source); + int client_fd = accept(sock_fd, NULL, NULL); + if (client_fd < 0) { + wrn("accept() failed"); + return TRUE; + } + + TMessage msg; + int len = recv(client_fd, &msg, sizeof(msg), 0); + if (len < 0) { + wrn("recv() failed"); + goto socket_g_io_watch_cleanup; + } + + msg = handle_message(msg); + write(client_fd, &msg, sizeof(msg)); + +socket_g_io_watch_cleanup: + close(client_fd); + return TRUE; +} + +static void +usage() +{ + puts("usage:"); + puts("\t" PROGRAM_NAME " [ARG]"); + puts("\nwhere ARG is one of:"); + puts("\t" ARG_NEWWIN "\tRequests a new window; default if ommited"); + puts("\t" ARG_EXIT "\tRequests daemon termination; only successful if there are no open windows"); + puts("\t" ARG_DAEMON "\tRun in daemon mode"); +} + +static int +send_message(TMessage msg) +{ + int sock_fd = conn_socket(); + if (write(sock_fd, &msg, sizeof(msg)) < 0) + err("Failed to send data", EXIT_FAILURE); + if (recv(sock_fd, &msg, sizeof(msg), 0) < 0) + err("Failed to recv data", EXIT_FAILURE); + close(sock_fd); + + return msg == MSG_OK ? EXIT_SUCCESS : EXIT_FAILURE; +} + +static int +run_daemon(int *argc, char ***argv) +{ + int sock_fd; + gtk_init(argc, argv); + + sock_fd = bind_socket(); + if (listen(sock_fd, SOMAXCONN) < 0) + err("Failed to listen to socket", EXIT_SUCCESS); + + /* poll the socket in the GTK+ main loop */ + GIOChannel *sock_chan = g_io_channel_unix_new(sock_fd); + g_io_add_watch(sock_chan, G_IO_IN, socket_g_io_watch, NULL); + + gtk_main(); + + /* clean up */ + cleanup_socket(sock_fd); + g_io_channel_unref(sock_chan); + + return EXIT_SUCCESS; +} + +int +main(int argc, char *argv[]) +{ + if (argc > 2) /* More than 1 arg means derp */ + usage(); + if (argc == 1 || strcmp(argv[1], ARG_NEWWIN) == 0) + return send_message(MSG_NEWWIN); + else if (strcmp(argv[1], ARG_EXIT) == 0) + return send_message(MSG_EXIT); + else if (strcmp(argv[1], ARG_DAEMON) == 0) + return run_daemon(&argc, &argv); + else /* derp */ + usage(); + + return EXIT_FAILURE; +} -- cgit v1.2.3