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