diff options
author | Samuel Fadel <samuelfadel@gmail.com> | 2015-07-21 13:38:00 -0300 |
---|---|---|
committer | Samuel Fadel <samuelfadel@gmail.com> | 2015-07-21 13:38:00 -0300 |
commit | d279b7b38fab20d993bef61991dd77e22a701f49 (patch) | |
tree | d8841c02ec8991810b9c621c08e7ffa75bdad8d8 | |
parent | e60b2d69bf6d6a43bc5c2a9386a472ed20281ebf (diff) |
The T client and daemon are now the same program. Added the -d flag to run as daemon.
-rw-r--r-- | Makefile | 20 | ||||
-rw-r--r-- | T.c | 228 | ||||
-rw-r--r-- | T.h | 43 | ||||
-rw-r--r-- | Tc.c | 100 | ||||
-rw-r--r-- | Td.c | 142 |
5 files changed, 227 insertions, 306 deletions
@@ -8,37 +8,31 @@ LDFLAGS = -s ${LIBS} VER = 0.9 CC = cc -SRC = T.c Tc.c Td.c +SRC = T.c OBJ = ${SRC:.c=.o} -all: Tc Td +all: T %.o: %.c @echo CC $< @${CC} -c ${CFLAGS} -o $@ $< -${OBJ}: config.h T.h +${OBJ}: config.h -Tc: T.o Tc.o - @echo CC -o $@ - @${CC} -o $@ $^ ${LDFLAGS} - -Td: T.o Td.o +T: T.o @echo CC -o $@ @${CC} -o $@ $^ ${LDFLAGS} clean: @echo cleaning - @rm -f Tc Td ${OBJ} + @rm -f T ${OBJ} install: all @echo installing executable files in ${DESTDIR}${PREFIX}/bin - @install -D -m755 Tc ${DESTDIR}${PREFIX}/bin/Tc - @install -D -m755 Td ${DESTDIR}${PREFIX}/bin/Td + @install -D -m755 T ${DESTDIR}${PREFIX}/bin/T uninstall: @echo removing executable files from ${DESTDIR}${PREFIX}/bin - @rm -f ${DESTDIR}${PREFIX}/bin/Tc - @rm -f ${DESTDIR}${PREFIX}/bin/Td + @rm -f ${DESTDIR}${PREFIX}/bin/T .PHONY: all clean install uninstall @@ -17,28 +17,71 @@ * T. If not, see <http://www.gnu.org/licenses/>. */ +#include <glib.h> #include <gdk/gdk.h> +#include <gdk/gdkx.h> #include <gdk/gdkkeysyms.h> #include <gtk/gtk.h> -#include <unistd.h> #include <vte/vte.h> + +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/un.h> +#include <unistd.h> + +#include <stdio.h> #include <stdlib.h> +#include <string.h> + +#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; +} @@ -1,43 +0,0 @@ -/* - * T.h - * Common definitions and utilities. - * - * This file is part of T. - * - * T is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * T is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * T. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef T_H -#define T_H - -#include "config.h" - -#define PROGRAM_NAME "T" - -#define SOCKPATH CONFIG_SOCKDIR "/" CONFIG_SOCKNAME -#define SOCKPATH_MAXLEN sizeof((SOCKPATH)) - -typedef enum { - MSG_NEWWIN, - MSG_EXIT, - MSG_OK, - MSG_ERROR -} TMessage; - -int num_open_windows(); -void new_window(); - -void wrn(const char *message); -void err(const char *message, int ecode); - -#endif /* T_H */ @@ -1,100 +0,0 @@ -/* - * Tc.c - * T client. Merely issues commands to the T daemon. - * - * T is a lean Terminal emulator. - * - * This file is part of T. - * - * T is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * T is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * T. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/un.h> -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "T.h" - -#define ARG_NEWWIN "-n" -#define ARG_EXIT "-x" - -static int open_conn(); -static TMessage parse_args(int argc, char *argv[]); -static void usage(); - -static int -open_conn() -{ - 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 TMessage -parse_args(int argc, char *argv[]) -{ - if (argc == 1) /* No args means new window */ - return MSG_NEWWIN; - if (argc > 2) /* More than 1 arg means derp */ - return MSG_ERROR; - if (strcmp(argv[1], ARG_NEWWIN) == 0) - return MSG_NEWWIN; - if (strcmp(argv[1], ARG_EXIT) == 0) - return MSG_EXIT; - - return MSG_ERROR; -} - -static void -usage() -{ - puts("usage:"); - puts("\tTc [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"); -} - -int -main(int argc, char *argv[]) -{ - TMessage msg = parse_args(argc, argv); - if (msg == MSG_ERROR) { - usage(); - return EXIT_FAILURE; - } - - int sock_fd = open_conn(); - 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; -} @@ -1,142 +0,0 @@ -/* - * Td.c - * T daemon. Saves resources by uniting all T windows under the same process. - * - * This file is part of T. - * - * T is free software: you can redistribute it and/or modify it under the terms - * of the GNU General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later - * version. - * - * T is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - * A PARTICULAR PURPOSE. See the GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along with - * T. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <glib.h> -#include <gdk/gdk.h> -#include <gdk/gdkx.h> -#include <gdk/gdkkeysyms.h> -#include <gtk/gtk.h> -#include <vte/vte.h> - -#include <sys/select.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <sys/un.h> -#include <unistd.h> - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "T.h" - -#define MSGBUF_MAXLEN 64 - -static int bind_socket(); -static TMessage handle_message(TMessage msg); -static gboolean socket_g_io_watch(GIOChannel *source, GIOCondition condition, gpointer data); - -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 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; -} - -int -main(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 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 */ - close(sock_fd); - unlink(SOCKPATH); - - return EXIT_SUCCESS; -} |