Skip to content

Commit d967fe8

Browse files
committed
add polling detection to js-interposer
1 parent 02d6b3c commit d967fe8

File tree

1 file changed

+156
-45
lines changed

1 file changed

+156
-45
lines changed

addons/js-interposer/joystick_interposer.c

Lines changed: 156 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ file, You can obtain one at https://mozilla.org/MPL/2.0/.
3535
#include <arpa/inet.h>
3636
#include <sys/un.h>
3737
#include <sys/ioctl.h>
38+
#include <sys/epoll.h>
3839
#include <unistd.h>
3940
#include <errno.h>
4041
#include <time.h>
@@ -58,10 +59,14 @@ file, You can obtain one at https://mozilla.org/MPL/2.0/.
5859

5960
// Define the function signature for the original open and ioctl syscalls
6061
typedef int (*open_func_t)(const char *pathname, int flags, ...);
62+
typedef int (*open64_func_t)(const char *pathname, int flags, ...);
6163
typedef int (*ioctl_func_t)(int fd, unsigned long request, ...);
6264

65+
static int (*real_epoll_ctl)(int epfd, int op, int fd, struct epoll_event *event) = NULL;
66+
6367
// Function pointers to the original open and ioctl syscalls
6468
static open_func_t real_open = NULL;
69+
static open64_func_t real_open64 = NULL;
6570
static ioctl_func_t real_ioctl = NULL;
6671

6772
// type definition for correction struct
@@ -155,6 +160,41 @@ void init_real_open()
155160
real_open = (open_func_t)dlsym(RTLD_NEXT, "open");
156161
}
157162

163+
void init_real_open64()
164+
{
165+
if (real_open64 != NULL)
166+
return;
167+
real_open64 = (open64_func_t)dlsym(RTLD_NEXT, "open64");
168+
}
169+
170+
void init_real_epoll_ctl()
171+
{
172+
if (real_epoll_ctl != NULL)
173+
return;
174+
real_epoll_ctl = dlsym(RTLD_NEXT, "epoll_ctl");
175+
}
176+
177+
int make_nonblocking(int sockfd)
178+
{
179+
// Get the current file descriptor flags
180+
int flags = fcntl(sockfd, F_GETFL, 0);
181+
if (flags == -1)
182+
{
183+
interposer_log(LOG_ERROR, "Failed to get current flags on socket fd to make non-blocking");
184+
return -1;
185+
}
186+
187+
// Set the non-blocking flag
188+
flags |= O_NONBLOCK;
189+
if (fcntl(sockfd, F_SETFL, flags) == -1)
190+
{
191+
interposer_log(LOG_ERROR, "Failed to set flags on socket fd to make non-blocking");
192+
return -1;
193+
}
194+
195+
return 0; // Success
196+
}
197+
158198
int read_config(int fd, js_config_t *js_config)
159199
{
160200
ssize_t bytesRead;
@@ -182,13 +222,83 @@ int read_config(int fd, js_config_t *js_config)
182222
return 0;
183223
}
184224

225+
int interposer_open_socket(js_interposer_t *interposer)
226+
{
227+
// Open the existing Unix socket
228+
interposer->sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
229+
if (interposer->sockfd == -1)
230+
{
231+
interposer_log(LOG_ERROR, "Failed to create socket file descriptor when opening device: %s", interposer->open_dev_name);
232+
return -1;
233+
}
234+
235+
struct sockaddr_un addr;
236+
memset(&addr, 0, sizeof(struct sockaddr_un));
237+
addr.sun_family = AF_UNIX;
238+
strncpy(addr.sun_path, interposer->socket_path, sizeof(addr.sun_path) - 1);
239+
240+
// Wait for socket to connect.
241+
int attempt = 0;
242+
while (attempt++ < SOCKET_CONNECT_TIMEOUT_MS)
243+
{
244+
if (connect(interposer->sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1)
245+
{
246+
// sleep for 1ms
247+
usleep(1000);
248+
continue;
249+
}
250+
break;
251+
}
252+
if (attempt >= SOCKET_CONNECT_TIMEOUT_MS)
253+
{
254+
interposer_log(LOG_ERROR, "Failed to connect to socket at %s", interposer->socket_path);
255+
close(interposer->sockfd);
256+
return -1;
257+
}
258+
259+
// Read the joystick config from the socket.
260+
if (read_config(interposer->sockfd, &(interposer->js_config)) != 0)
261+
{
262+
interposer_log(LOG_ERROR, "Failed to read config from socket: %s", interposer->socket_path);
263+
close(interposer->sockfd);
264+
return -1;
265+
}
266+
267+
return 0;
268+
}
269+
270+
// Interpose epoll_ctl to make joystck socket fd non-blocking.
271+
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)
272+
{
273+
init_real_epoll_ctl();
274+
275+
if (op == EPOLL_CTL_ADD)
276+
{
277+
// Find matching device in interposer list
278+
for (size_t i = 0; i < NUM_JS_INTERPOSERS; i++)
279+
{
280+
if (fd == interposers[i].sockfd)
281+
{
282+
interposer_log(LOG_INFO, "Socket %s (%d) was added to epoll (%d), set non-blocking", interposers[i].socket_path, fd, epfd);
283+
if (make_nonblocking(fd) == -1)
284+
{
285+
interposer_log(LOG_ERROR, "Failed to make socket non-blocking");
286+
}
287+
break;
288+
}
289+
}
290+
}
291+
292+
return real_epoll_ctl(epfd, op, fd, event);
293+
}
294+
185295
// Interposer function for open syscall
186296
int open(const char *pathname, int flags, ...)
187297
{
188298
init_real_open();
189299
if (real_open == NULL)
190300
{
191-
interposer_log("Error getting original open function: %s", dlerror());
301+
interposer_log(LOG_ERROR, "Error getting original open function: %s", dlerror());
192302
return -1;
193303
}
194304

@@ -208,52 +318,55 @@ int open(const char *pathname, int flags, ...)
208318
{
209319
va_list args;
210320
va_start(args, flags);
211-
mode_t mode = va_arg(args, mode_t);
321+
void *arg = va_arg(args, void *);
212322
va_end(args);
213-
return real_open(pathname, flags, mode);
323+
return real_open(pathname, flags, arg);
214324
}
215325

216-
interposer_log(LOG_INFO, "Intercepted open call for %s", interposer->open_dev_name);
326+
if (interposer_open_socket(interposer) == -1)
327+
return -1;
217328

218-
// Open the existing Unix socket
219-
interposer->sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
220-
if (interposer->sockfd == -1)
329+
interposer_log(LOG_INFO, "Started interposer for 'open' call on %s with fd: %d", interposer->open_dev_name, interposer->sockfd);
330+
331+
// Return the file descriptor of the unix socket.
332+
return interposer->sockfd;
333+
}
334+
335+
// Interposer function for open64
336+
int open64(const char *pathname, int flags, ...)
337+
{
338+
init_real_open64();
339+
if (real_open64 == NULL)
221340
{
222-
interposer_log(LOG_ERROR, "Failed to create socket file descriptor when opening devcie: %s", interposer->open_dev_name);
341+
interposer_log(LOG_ERROR, "Error getting original open64 function: %s", dlerror());
223342
return -1;
224343
}
225344

226-
struct sockaddr_un addr;
227-
memset(&addr, 0, sizeof(struct sockaddr_un));
228-
addr.sun_family = AF_UNIX;
229-
strncpy(addr.sun_path, interposer->socket_path, sizeof(addr.sun_path) - 1);
230-
231-
// Wait for socket to connect.
232-
int attempt = 0;
233-
while (attempt++ < SOCKET_CONNECT_TIMEOUT_MS)
345+
// Find matching device in interposer list
346+
js_interposer_t *interposer = NULL;
347+
for (size_t i = 0; i < NUM_JS_INTERPOSERS; i++)
234348
{
235-
if (connect(interposer->sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) == -1)
349+
if (strcmp(pathname, interposers[i].open_dev_name) == 0)
236350
{
237-
// sleep for 1ms
238-
usleep(1000);
239-
continue;
351+
interposer = &interposers[i];
352+
break;
240353
}
241-
break;
242354
}
243-
if (attempt >= SOCKET_CONNECT_TIMEOUT_MS)
355+
356+
// Call real open64
357+
if (interposer == NULL)
244358
{
245-
interposer_log(LOG_ERROR, "Failed to connect to socket at %s", interposer->socket_path);
246-
close(interposer->sockfd);
247-
return -1;
359+
va_list args;
360+
va_start(args, flags);
361+
void *arg = va_arg(args, void *);
362+
va_end(args);
363+
return real_open64(pathname, flags, arg);
248364
}
249365

250-
// Read the joystick config from the socket.
251-
if (read_config(interposer->sockfd, &(interposer->js_config)) != 0)
252-
{
253-
interposer_log(LOG_ERROR, "Failed to read config from socket: %s", interposer->socket_path);
254-
close(interposer->sockfd);
366+
if (interposer_open_socket(interposer) == -1)
255367
return -1;
256-
}
368+
369+
interposer_log(LOG_INFO, "Started interposer for 'open64' call on %s with fd: %d", interposer->open_dev_name, interposer->sockfd);
257370

258371
// Return the file descriptor of the unix socket.
259372
return interposer->sockfd;
@@ -305,78 +418,76 @@ int ioctl(int fd, unsigned long request, ...)
305418
switch (request & 0xFF)
306419
{
307420
case 0x01: /* JSIOCGVERSION get driver version */
308-
interposer_log(LOG_INFO, "Intercepted ioctl request %lu -> JSIOCGVERSION", request);
421+
interposer_log(LOG_INFO, "Intercepted ioctl JSIOCGVERSION(0x%08x) request for: %s", request, interposer->socket_path);
309422
uint32_t *version = va_arg(args, uint32_t *);
310423
*version = JS_VERSION;
311424

312425
va_end(args);
313426
return 0; // 0 indicates success
314427

315428
case 0x11: /* JSIOCGAXES get number of axes */
316-
interposer_log(LOG_INFO, "Intercepted ioctl request %lu -> JSIOCGAXES", request);
429+
interposer_log(LOG_INFO, "Intercepted ioctl JSIOCGAXES(0x%08x) request for: %s", request, interposer->socket_path);
317430
uint8_t *num_axes = va_arg(args, uint8_t *);
318431
*num_axes = interposer->js_config.num_axes;
319432

320433
va_end(args);
321434
return 0; // 0 indicates success
322435

323436
case 0x12: /* JSIOCGBUTTONS get number of buttons */
324-
interposer_log(LOG_INFO, "Intercepted ioctl request %lu -> JSIOCGBUTTONS", request);
437+
interposer_log(LOG_INFO, "Intercepted ioctl JSIOCGBUTTONS(0x%08x) request for: %s", request, interposer->socket_path);
325438
uint8_t *btn_count = va_arg(args, uint8_t *);
326439
*btn_count = interposer->js_config.num_btns;
327440

328441
va_end(args);
329442
return 0; // 0 indicates success
330443

331444
case 0x13: /* JSIOCGNAME(len) get identifier string */
332-
interposer_log(LOG_INFO, "Intercepted ioctl request %lu -> JSIOCGNAME", request);
445+
interposer_log(LOG_INFO, "Intercepted ioctl JSIOCGNAME(0x%08x) request for: %s", request, interposer->socket_path);
333446
char *name = va_arg(args, char *);
334-
size_t *len = va_arg(args, size_t *);
335-
strncpy(name, interposer->js_config.name, strlen(interposer->js_config.name));
336447
name[strlen(interposer->js_config.name)] = '\0';
337-
448+
strncpy(name, interposer->js_config.name, strlen(interposer->js_config.name));
338449
va_end(args);
339450
return 0; // 0 indicates success
340451

341452
case 0x21: /* JSIOCSCORR set correction values */
342-
interposer_log(LOG_INFO, "Intercepted ioctl request %lu -> JSIOCSCORR", request);
453+
interposer_log(LOG_INFO, "Intercepted ioctl JSIOCSCORR(0x%08x) request for: %s", request, interposer->socket_path);
343454
va_end(args);
344455
return 0;
345456

346457
case 0x22: /* JSIOCGCORR get correction values */
347-
interposer_log(LOG_INFO, "Intercepted ioctl request %lu -> JSIOCGCORR", request);
458+
interposer_log(LOG_INFO, "Intercepted ioctl JSIOCGCORR(0x%08x) request for: %s", request, interposer->socket_path);
348459
js_corr_t *corr = va_arg(args, js_corr_t *);
349460
memcpy(corr, &interposer->corr, sizeof(interposer->corr));
350461

351462
va_end(args);
352463
return 0; // 0 indicates success
353464

354465
case 0x31: /* JSIOCSAXMAP set axis mapping */
355-
interposer_log(LOG_INFO, "Intercepted ioctl request %lu -> JSIOCSAXMAP", request);
466+
interposer_log(LOG_INFO, "Intercepted ioctl JSIOCSAXMAP(0x%08x) request for: %s", request, interposer->socket_path);
356467
va_end(args);
357468
return 0; // 0 indicates success
358469

359470
case 0x32: /* JSIOCGAXMAP get axis mapping */
360-
interposer_log(LOG_INFO, "Intercepted ioctl request %lu -> JSIOCGAXMAP", request);
471+
interposer_log(LOG_INFO, "Intercepted ioctl JSIOCGAXMAP(0x%08x) request for: %s", request, interposer->socket_path);
361472
uint8_t *axmap = va_arg(args, uint8_t *);
362473
memcpy(axmap, interposer->js_config.axes_map, interposer->js_config.num_axes * sizeof(uint8_t));
363474
va_end(args);
364475
return 0; // 0 indicates success
365476

366477
case 0x33: /* JSIOCSBTNMAP set button mapping */
367-
interposer_log(LOG_INFO, "Intercepted ioctl request %lu -> JSIOCSBTNMAP", request);
478+
interposer_log(LOG_INFO, "Intercepted ioctl JSIOCSBTNMAP(0x%08x) request for: %s", request, interposer->socket_path);
368479
va_end(args);
369480
return 0; // 0 indicates success
370481

371482
case 0x34: /* JSIOCGBTNMAP get button mapping */
372-
interposer_log(LOG_INFO, "Intercepted ioctl request %lu -> JSIOCGBTNMAP", request);
483+
interposer_log(LOG_INFO, "Intercepted ioctl JSIOCGBTNMAP(0x%08x) request for: %s", request, interposer->socket_path);
373484
uint16_t *btn_map = va_arg(args, uint16_t *);
374485
memcpy(btn_map, interposer->js_config.btn_map, interposer->js_config.num_btns * sizeof(uint16_t));
375486
va_end(args);
376487
return 0; // 0 indicates success
377488

378489
default:
379-
interposer_log(LOG_WARN, "Unhandled Intercepted ioctl request %lu", request);
490+
interposer_log(LOG_WARN, "Unhandled Intercepted ioctl request %lu", request, interposer->socket_path);
380491
void *arg = va_arg(args, void *);
381492
va_end(args);
382493
return real_ioctl(fd, request, arg);

0 commit comments

Comments
 (0)