1 /*
2 * Copyright © 2016 Aidan Holm <aidanholm@gmail.com>
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18
19 #include "globalconf.h"
20 #include "ipc.h"
21
22 #include <assert.h>
23 #include <webkit2/webkit2.h>
24 #include <sys/socket.h>
25 #include <sys/types.h>
26 #include <sys/un.h>
27 #include <glib.h>
28 #include <glib/gstdio.h>
29 #include <errno.h>
30 #include <stdlib.h>
31
32 #include "clib/web_module.h"
33 #include "clib/luakit.h"
34 #include "clib/widget.h"
35 #include "common/luaserialize.h"
36 #include "common/clib/ipc.h"
37 #include "web_context.h"
38 #include "widgets/webview.h"
39
40 void webview_scroll_recv(void *d, const ipc_scroll_t *ipc);
41 void run_javascript_finished(const guint8 *msg, guint length);
42
43 static char *socket_path;
44 GMutex socket_path_lock;
45 GCond socket_path_cond;
46
47 IPC_NO_HANDLER(lua_require_module)
IPC_NO_HANDLER(web_extension_loaded)48 IPC_NO_HANDLER(web_extension_loaded)
49 IPC_NO_HANDLER(crash)
50
51 void
52 ipc_recv_extension_init(ipc_endpoint_t *ipc, const gpointer UNUSED(msg), guint UNUSED(length))
53 {
54 web_module_load_modules_on_endpoint(ipc);
55
56 /* Notify web extension that pending signals can be released */
57 ipc_header_t header = { .type = IPC_TYPE_extension_init, .length = 0 };
58 ipc_send(ipc, &header, NULL);
59 }
60
61 void
ipc_recv_lua_ipc(ipc_endpoint_t * UNUSED (ipc),const ipc_lua_ipc_t * msg,guint length)62 ipc_recv_lua_ipc(ipc_endpoint_t *UNUSED(ipc), const ipc_lua_ipc_t *msg, guint length)
63 {
64 ipc_channel_recv(common.L, msg->arg, length);
65 }
66
67 void
ipc_recv_scroll(ipc_endpoint_t * UNUSED (ipc),ipc_scroll_t * msg,guint UNUSED (length))68 ipc_recv_scroll(ipc_endpoint_t *UNUSED(ipc), ipc_scroll_t *msg, guint UNUSED(length))
69 {
70 g_ptr_array_foreach(globalconf.webviews, (GFunc)webview_scroll_recv, msg);
71 }
72
73 void
ipc_recv_eval_js(ipc_endpoint_t * UNUSED (ipc),const guint8 * msg,guint length)74 ipc_recv_eval_js(ipc_endpoint_t *UNUSED(ipc), const guint8 *msg, guint length)
75 {
76 run_javascript_finished(msg, length);
77 }
78
79 void
ipc_recv_page_created(ipc_endpoint_t * ipc,const ipc_page_created_t * msg,guint UNUSED (length))80 ipc_recv_page_created(ipc_endpoint_t *ipc, const ipc_page_created_t *msg, guint UNUSED(length))
81 {
82 widget_t *w = webview_get_by_id(msg->page_id);
83
84 /* Page may already have been closed */
85 if (!w) return;
86
87 webview_connect_to_endpoint(w, ipc);
88 webview_set_web_process_id(w, msg->pid);
89 }
90
91 static gchar *
build_socket_path(void)92 build_socket_path(void)
93 {
94 char suffix[11] = {0};
95 retry:
96 for (unsigned i=0; i < sizeof(suffix)-1; i++) {
97 int c = g_random_int_range(0, 10+26+26), base = '0';
98 if (c >= 10) { base = 'A'; c -= 10; }
99 if (c >= 26) { base = 'a'; c -= 26; }
100 suffix[i] = base + c;
101 }
102 gchar *socket_name = g_strdup_printf("luakit-ipc-%d-%s", getpid(), suffix);
103 gchar *socket_path = g_build_filename(g_get_tmp_dir(), socket_name, NULL);
104 g_free(socket_name);
105
106 if (g_file_test(socket_path, G_FILE_TEST_EXISTS)) {
107 g_free(socket_path);
108 goto retry;
109 }
110 return socket_path;
111 }
112
113 static gpointer
web_extension_connect_thread(gpointer UNUSED (data))114 web_extension_connect_thread(gpointer UNUSED(data))
115 {
116 gchar *path = build_socket_path();
117
118 int sock;
119 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
120 fatal("Error calling socket(): %s", strerror(errno));
121
122 struct sockaddr_un local;
123 memset(&local, 0, sizeof(local));
124 local.sun_family = AF_UNIX;
125 strcpy(local.sun_path, path);
126 int len = offsetof(struct sockaddr_un, sun_path) + strlen(local.sun_path);
127
128 /* Remove any pre-existing socket, before opening */
129 unlink(local.sun_path);
130
131 if (bind(sock, (struct sockaddr *)&local, len) == -1)
132 fatal("Error calling bind() on socket %s: %s", path, strerror(errno));
133
134 if (listen(sock, 5) == -1)
135 fatal("Error calling listen() on socket %s: %s", path, strerror(errno));
136
137 g_mutex_lock(&socket_path_lock);
138 socket_path = path;
139 g_cond_signal(&socket_path_cond);
140 g_mutex_unlock(&socket_path_lock);
141
142 while (TRUE) {
143 debug("Waiting for a connection...");
144
145 int web_socket;
146 struct sockaddr_un remote;
147 socklen_t size = sizeof(remote);
148 if ((web_socket = accept(sock, (struct sockaddr *)&remote, &size)) == -1)
149 fatal("Error calling accept(): %s", strerror(errno));
150
151 ipc_endpoint_t *ipc = ipc_endpoint_new("UI");
152 ipc_endpoint_connect_to_socket(ipc, web_socket);
153 }
154
155 return NULL;
156 }
157
158 static void
initialize_web_extensions_cb(WebKitWebContext * context,gpointer UNUSED (data))159 initialize_web_extensions_cb(WebKitWebContext *context, gpointer UNUSED(data))
160 {
161 char *dirs[] = { g_get_current_dir(), LUAKIT_LIB_PATH }, *dir = NULL;
162
163 for (unsigned i = 0; !dir && i < LENGTH(dirs); ++i) {
164 char *extension_file = g_build_filename(dirs[i], "luakit.so", NULL);
165 verbose("checking for luakit extension at '%s'", dirs[i]);
166 if (!access(extension_file, R_OK))
167 dir = dirs[i];
168 g_free(extension_file);
169 }
170
171 if (dir)
172 verbose("found luakit extension at '%s'", dir);
173 else
174 fatal("cannot find luakit extension 'luakit.so'");
175
176 const char *path;
177 g_mutex_lock (&socket_path_lock);
178 while (!socket_path)
179 g_cond_wait (&socket_path_cond, &socket_path_lock);
180 path = socket_path;
181 g_mutex_unlock (&socket_path_lock);
182
183 lua_getglobal(common.L, "package");
184 lua_getfield(common.L, -1, "path");
185 const char *package_path = lua_tostring(common.L, -1);
186 lua_getfield(common.L, -2, "cpath");
187 const char *package_cpath = lua_tostring(common.L, -1);
188 lua_pop(common.L, 3);
189
190 GVariant *payload = g_variant_new("(sss)", path, package_path, package_cpath);
191 webkit_web_context_set_web_extensions_initialization_user_data(context, payload);
192 webkit_web_context_set_web_extensions_directory(context, dir);
193
194 g_free(dirs[0]);
195 }
196
197 void
ipc_remove_socket_file(void)198 ipc_remove_socket_file(void)
199 {
200 g_mutex_lock(&socket_path_lock);
201 g_unlink(socket_path);
202 g_free(socket_path);
203 socket_path = NULL;
204 g_mutex_unlock(&socket_path_lock);
205 }
206
207 void
ipc_init(void)208 ipc_init(void)
209 {
210 /* Start web extension connection accept thread */
211 g_thread_new("accept_thread", web_extension_connect_thread, NULL);
212 g_signal_connect(web_context_get(), "initialize-web-extensions",
213 G_CALLBACK (initialize_web_extensions_cb), NULL);
214 atexit(ipc_remove_socket_file);
215 }
216
217 // vim: ft=c:et:sw=4:ts=8:sts=4:tw=80
218