1 /**
2  *      Copyright 2008 Fred Chien <cfsghost@gmail.com>
3  *      Copyright (c) 2010 LxDE Developers, see the file AUTHORS for details.
4  *
5  *      This program is free software; you can redistribute it and/or modify
6  *      it under the terms of the GNU General Public License as published by
7  *      the Free Software Foundation; either version 2 of the License, or
8  *      (at your option) any later version.
9  *
10  *      This program is distributed in the hope that it will be useful,
11  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *      GNU General Public License for more details.
14  *
15  *      You should have received a copy of the GNU General Public License
16  *      along with this program; if not, write to the Free Software
17  *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18  *      MA 02110-1301, USA.
19  */
20 
21 #include <sys/socket.h>
22 #include <sys/un.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <glib.h>
28 #include <gtk/gtk.h>
29 
30 #include "lxterminal.h"
31 #include "unixsocket.h"
32 
33 typedef struct _client_info {
34     LXTermWindow * lxtermwin;
35     int fd;
36 } ClientInfo;
37 
38 static gboolean init(LXTermWindow* lxtermwin, gint argc, gchar** argv);
39 static void start_controller(struct sockaddr_un* sock_addr, LXTermWindow* lxtermwin, int fd);
40 static void send_msg_to_controller(int fd, gint argc, gchar** argv);
41 static gboolean handle_client(GIOChannel* source, GIOCondition condition, LXTermWindow* lxtermwin);
42 static void accept_client(GIOChannel* source, LXTermWindow* lxtermwin);
43 static gboolean handle_request(GIOChannel* gio, GIOCondition condition, ClientInfo* info);
44 
init(LXTermWindow * lxtermwin,gint argc,gchar ** argv)45 static gboolean init(LXTermWindow* lxtermwin, gint argc, gchar** argv) {
46     /* Normally, LXTerminal uses one process to control all of its windows.
47      * The first process to start will create a Unix domain socket in
48      * g_get_user_runtime_dir().  It will then bind and listen on this socket.
49      * The subsequent processes will connect to the controller that owns the
50      * Unix domain socket.  They will pass their command line over the socket
51      * and exit.
52      *
53      * If for any reason both the connect and bind fail, we will fall back to
54      * having that process be standalone; it will not be either the controller
55      * or a user of the controller.
56      *
57      * This function returns TRUE if this process should keep running and FALSE
58      * if it should exit. */
59 
60     /* Formulate the path for the Unix domain socket. */
61 #if GLIB_CHECK_VERSION (2, 28, 0)
62     gchar * socket_path = g_strdup_printf("%s/.lxterminal-socket-%s",
63             g_get_user_runtime_dir(),
64             gdk_display_get_name(gdk_display_get_default()));
65 #else
66     gchar * socket_path = g_strdup_printf("%s/.lxterminal-socket-%s",
67             g_get_user_cache_dir(),
68             gdk_display_get_name(gdk_display_get_default()));
69 #endif
70 
71     /* Create socket. */
72     int fd = socket(PF_UNIX, SOCK_STREAM, 0);
73     if (fd < 0) {
74         g_warning("Socket create failed: %s\n", g_strerror(errno));
75         goto err_socket;
76     }
77 
78     /* Initialize socket address for Unix domain socket. */
79     struct sockaddr_un sock_addr;
80     memset(&sock_addr, 0, sizeof(sock_addr));
81     sock_addr.sun_family = AF_UNIX;
82     snprintf(sock_addr.sun_path, sizeof(sock_addr.sun_path), "%s", socket_path);
83 
84     /* Try to connect to an existing LXTerminal process. */
85     if (connect(fd, (struct sockaddr *) &sock_addr, sizeof(sock_addr)) == 0) {
86         g_free(socket_path);
87         send_msg_to_controller(fd, argc, argv);
88         return FALSE;
89     }
90 
91     unlink(socket_path);
92     g_free(socket_path);
93     start_controller(&sock_addr, lxtermwin, fd);
94     return TRUE;
95 
96 err_socket:
97     g_free(socket_path);
98     return TRUE;
99 }
100 
start_controller(struct sockaddr_un * sock_addr,LXTermWindow * lxtermwin,int fd)101 static void start_controller(struct sockaddr_un* sock_addr, LXTermWindow* lxtermwin, int fd) {
102     /* Bind to socket. */
103     if (bind(fd, (struct sockaddr*) sock_addr, sizeof(*sock_addr)) < 0) {
104         g_warning("Bind on socket failed: %s\n", g_strerror(errno));
105         goto err_close_fd;
106     }
107 
108     /* Listen on socket. */
109     if (listen(fd, 5) < 0) {
110         g_warning("Listen on socket failed: %s\n", g_strerror(errno));
111         goto err_close_fd;
112     }
113 
114     /* Create a glib I/O channel. */
115     GIOChannel * gio = g_io_channel_unix_new(fd);
116     if (gio == NULL) {
117         g_warning("Cannot create GIOChannel\n");
118         goto err_close_fd;
119     }
120 
121     /* Set up GIOChannel. */
122     g_io_channel_set_encoding(gio, NULL, NULL);
123     g_io_channel_set_buffered(gio, FALSE);
124     g_io_channel_set_close_on_unref(gio, TRUE);
125 
126     /* Add I/O channel to the main event loop. */
127     if (!g_io_add_watch(gio, G_IO_IN | G_IO_HUP, (GIOFunc) handle_client, lxtermwin)) {
128         g_warning("Cannot add watch on GIOChannel\n");
129         goto err_gio_watch;
130     }
131 
132     /* Channel will automatically shut down when the watch returns FALSE. */
133     g_io_channel_unref(gio);
134     return;
135 
136 err_gio_watch:
137     g_io_channel_unref(gio);
138 
139 err_close_fd:
140     close(fd);
141     return;
142 }
143 
send_msg_to_controller(int fd,gint argc,gchar ** argv)144 static void send_msg_to_controller(int fd, gint argc, gchar** argv) {
145     /* Create a glib I/O channel. */
146     GIOChannel * gio = g_io_channel_unix_new(fd);
147     g_io_channel_set_encoding(gio, NULL, NULL);
148 
149     /* Push current dir in case it is needed later */
150     gchar * cur_dir = g_get_current_dir();
151     g_io_channel_write_chars(gio, cur_dir, -1, NULL, NULL);
152 
153     /* Use "" as a pointer to '\0' since g_io_channel_write_chars() won't
154      * accept NULL */
155     g_io_channel_write_chars(gio, "", 1, NULL, NULL);
156     g_free(cur_dir);
157 
158     /* push all of argv. */
159     gint i;
160     for (i = 0; i < argc; i ++) {
161         g_io_channel_write_chars(gio, argv[i], -1, NULL, NULL);
162         g_io_channel_write_chars(gio, "", 1, NULL, NULL);
163     }
164 
165     g_io_channel_flush(gio, NULL);
166     g_io_channel_unref(gio);
167     close(fd);
168 }
169 
handle_client(GIOChannel * source,GIOCondition condition,LXTermWindow * lxtermwin)170 static gboolean handle_client(GIOChannel* source, GIOCondition condition, LXTermWindow* lxtermwin) {
171     if (condition & G_IO_IN) {
172         accept_client(source, lxtermwin);
173     }
174 
175     if (condition & G_IO_HUP) {
176         g_error("Server listening socket closed unexpectedly\n");
177     }
178 
179     return TRUE;
180 }
181 
accept_client(GIOChannel * source,LXTermWindow * lxtermwin)182 static void accept_client(GIOChannel* source, LXTermWindow* lxtermwin) {
183     /* Accept the new connection. */
184     int fd = accept(g_io_channel_unix_get_fd(source), NULL, NULL);
185     if (fd < 0) {
186         g_warning("Accept failed: %s\n", g_strerror(errno));
187         return;
188     }
189 
190     /* Add O_NONBLOCK to the flags. */
191     fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK);
192 
193     /* Create a glib I/O channel. */
194     GIOChannel * gio = g_io_channel_unix_new(fd);
195     if (gio == NULL) {
196         g_warning("Cannot create new GIOChannel\n");
197         return;
198     }
199 
200     ClientInfo* info = g_malloc(sizeof(ClientInfo));
201     info->lxtermwin = lxtermwin;
202     info->fd = fd;
203 
204     /* Set up the glib I/O channel and add it to the event loop. */
205     g_io_channel_set_encoding(gio, NULL, NULL);
206     g_io_add_watch(gio, G_IO_IN | G_IO_HUP, (GIOFunc) handle_request, info);
207     g_io_channel_unref(gio);
208 }
209 
handle_request(GIOChannel * gio,GIOCondition condition,ClientInfo * info)210 static gboolean handle_request(GIOChannel* gio, GIOCondition condition, ClientInfo* info) {
211     LXTermWindow * lxtermwin = info->lxtermwin;
212     int fd = info->fd;
213     /* Read message. */
214     gchar * msg = NULL;
215     gsize len = 0;
216     GError * err = NULL;
217     GIOStatus ret = g_io_channel_read_to_end(gio, &msg, &len, &err);
218     if (ret == G_IO_STATUS_ERROR) {
219         g_warning("Error reading socket: %s\n", err->message);
220     }
221 
222     /* Process message. */
223     if (len > 0) {
224         /* Skip the the first (cur_dir) and last '\0' for argument count */
225         gint argc = -1;
226         gsize i;
227         for (i = 0; i < len; i ++) {
228             if (msg[i] == '\0') {
229                 argc ++;
230             }
231         }
232         gchar * cur_dir = msg;
233         gchar * * argv = g_malloc(argc * sizeof(char *));
234         gint nul_count = 0;
235         for (i = 0; i < len; i ++) {
236             if (msg[i] == '\0' && nul_count < argc) {
237                 argv[nul_count] = &msg[i + 1];
238                 nul_count ++;
239             }
240         }
241 
242         /* Parse arguments.
243          * Initialize a new LXTerminal and create a new window. */
244         CommandArguments arguments;
245         lxterminal_process_arguments(argc, argv, &arguments);
246         g_free(argv);
247 
248         /* Make sure working directory matches that of the client process */
249         if (arguments.working_directory == NULL) {
250             arguments.working_directory = g_strdup(cur_dir);
251         }
252         lxterminal_initialize(lxtermwin, &arguments);
253     }
254 
255     if (condition & G_IO_HUP) {
256         g_free(msg);
257         g_free(info);
258         close(fd);
259         return FALSE;
260     }
261 
262     return TRUE;
263 }
264 
lxterminal_socket_initialize(LXTermWindow * lxtermwin,gint argc,gchar ** argv)265 gboolean lxterminal_socket_initialize(LXTermWindow* lxtermwin, gint argc, gchar** argv) {
266     return init(lxtermwin, argc, argv);
267 }
268