Skip to content

Instantly share code, notes, and snippets.

@fjolnir
Created June 19, 2012 07:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save fjolnir/2952745 to your computer and use it in GitHub Desktop.
Save fjolnir/2952745 to your computer and use it in GitHub Desktop.
A remote debugging/inspection tool for Objective-C
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <dispatch/dispatch.h>
#include <readline/readline.h>
#include <readline/history.h>
bool running = true;
int sockfd, portNum;
struct sockaddr_in serverAddr;
struct hostent *server;
char buffer[1024];
dispatch_source_t readSource;
dispatch_queue_t queue;
bool multiline = false;
static int returnKey(int count, int key) {
if(!multiline)
rl_done = 1;
printf("\n");
}
static int toggleMultiline(int count, int key) {
if(multiline == false) {
multiline = true;
printf("\r~ ");
} else {
rl_done = 1;
printf("\n");
multiline = false;
}
}
int main(int argc, char *argv[])
{
bzero((char *)&serverAddr, sizeof(serverAddr));
if(argc < 3) {
fprintf(stderr, "Usage: client <server> <port>\n");
return 1;
}
// Connect to the server
portNum = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) {
fprintf(stderr, "ERROR opening socket\n");
return 1;
}
server = gethostbyname(argv[1]);
if(server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
return 1;
}
serverAddr.sin_family = AF_INET;
bcopy((char *)server->h_addr, (char *)&serverAddr.sin_addr.s_addr, server->h_length);
serverAddr.sin_port = htons(portNum);
if(connect(sockfd,(struct sockaddr *) &serverAddr,sizeof(serverAddr)) < 0) {
fprintf(stderr, "ERROR connecting\n");
return 1;
}
queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
readSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, sockfd, 0, queue);
dispatch_source_set_event_handler(readSource, ^{
char buf[1024];
bzero(buf,sizeof(buf));
int n = read(sockfd,buf, sizeof(buf)-1);
if(n < 0)
fprintf(stderr, "ERROR reading from socket\n");
// Print the output without messing up the prompt
int savedPoint = rl_point;
char *savedLine = rl_copy_text(0, rl_end);
rl_set_prompt("");
rl_replace_line("", 0);
rl_redisplay();
fprintf(stderr, "\r%s", buf);
rl_set_prompt("> ");
rl_replace_line(savedLine, 0);
rl_point = savedPoint;
rl_redisplay();
free(savedLine);
});
dispatch_resume(readSource);
rl_bind_key('\n', returnKey);
rl_bind_key('\r', returnKey);
rl_bind_key('\t', toggleMultiline);
char *line;
while((line = readline("> ")) != NULL) {
if(strlen(line) > 0) {
if(strcmp(line, "q") == 0 || strcmp(line, "quit") == 0)
break;
ssize_t n = write(sockfd,line,strlen(line));
if(n < 0)
fprintf(stderr, "ERROR writing to socket");
add_history(line);
}
free(line);
}
close(sockfd);
return 0;
}
-- A couple of functions to make working with the view hierarchy nicer
function keyWindow()
return UIApplication:sharedApplication():keyWindow()
end
function rootView()
return keyWindow():rootViewController():view()
end
function topView(controller)
local controller = controller or keyWindow():rootViewController()
if controller == nil then
return keyWindow()
end
local presented = controller:presentedViewController()
if presented ~= nil then
return topView(presented)
elseif controller:childViewControllers():count() > 0 then
return topView(controller:childViewController():objectAtIndex(0))
end
return controller:view()
end
objc = require("objc")
objc.fallbackOnMsgSend = false -- Prevent exceptions on typos
objc.debug = true
-- Make it nicer to use objc
setmetatable(_G, {__index=objc})
ffi = require("ffi")
require("debughelpers")
local portNum = 4391
ffi.cdef[[
typedef unsigned long size_t;
typedef long ssize_t;
typedef unsigned char __uint8_t;
typedef unsigned short __uint16_t;
typedef unsigned int __uint32_t;
typedef __uint8_t sa_family_t;
typedef size_t socklen_t;
struct sockaddr {
__uint8_t sa_len;
sa_family_t sa_family;
char sa_data[14];
};
typedef __uint32_t in_addr_t;
typedef __uint16_t in_port_t;
struct in_addr {
in_addr_t s_addr;
};
struct sockaddr_in {
__uint8_t sin_len;
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
int socket(int, int, int);
int bind(int, const struct sockaddr *, socklen_t);
int listen(int, int);
int accept(int, struct sockaddr * __restrict, socklen_t * __restrict);
ssize_t read(int fildes, void *buf, size_t nbyte);
ssize_t write(int fildes, const void *buf, size_t nbyte);
uint16_t htons(uint16_t hostshort);
// GCD
struct dispatch_source_type_s;
struct dispatch_source_s;
struct dispatch_queue_s;
typedef const struct dispatch_source_type_s *dispatch_source_type_t;
typedef struct dispatch_source_s *dispatch_source_t;
typedef dispatch_source_t dispatch_object_t; // Not the actual definition, ..but it's long
typedef struct dispatch_queue_s *dispatch_queue_t;
typedef int dispatch_fd_t;
typedef void (*dispatch_function_t)(void *);
const struct dispatch_source_type_s _dispatch_source_type_read;
dispatch_source_t dispatch_source_create(dispatch_source_type_t type,
uintptr_t handle, unsigned long mask,
dispatch_queue_t queue);
void dispatch_source_set_event_handler_f(
dispatch_source_t source,
dispatch_function_t handler);
void dispatch_resume(dispatch_object_t object);
void dispatch_suspend(dispatch_object_t object);
void dispatch_source_cancel(dispatch_source_t source);
dispatch_queue_t dispatch_get_global_queue(long priority, unsigned long flags);
typedef unsigned long dispatch_io_type_t;
typedef struct dispatch_io_s *dispatch_io_t;
typedef long off_t;
typedef struct dispatch_data_s *dispatch_data_t;
dispatch_io_t dispatch_io_create(
dispatch_io_type_t type,
dispatch_fd_t fd,
dispatch_queue_t queue,
id cleanup_handler);
void dispatch_io_read(
dispatch_io_t channel,
off_t offset,
size_t length,
dispatch_queue_t queue,
id io_handler);
void dispatch_write(
dispatch_fd_t fd,
dispatch_data_t data,
dispatch_queue_t queue,
id handler);
void dispatch_async_f(
dispatch_queue_t queue,
void *context,
dispatch_function_t work);
struct dispatch_queue_s _dispatch_main_q;
int dup(int fildes);
int dup2(int fildes, int filedes2);
int usleep(unsigned int useconds);
int pipe(int fildes[2]);
int close(int filedes);
typedef void NSUncaughtExceptionHandler(id exception);
void NSSetUncaughtExceptionHandler(NSUncaughtExceptionHandler *);
NSUncaughtExceptionHandler * NSGetUncaughtExceptionHandler (void);
id objc_begin_catch(void *exc_buf);
void objc_end_catch(void);
]]
local C = ffi.C
local function dispatch_get_main_queue()
return ffi.cast("dispatch_queue_t", C._dispatch_main_q)
end
function sleep(ms)
C.usleep(ms*1000)
end
local DISPATCH_IO_STREAM = 0
local DISPATCH_SOURCE_TYPE_READ = C._dispatch_source_type_read;
local queue = dispatch_get_main_queue() -- C.dispatch_get_global_queue(0,0) -- Get a normal priority queue
local connectedSocket
-- Reroute stdout to a socket
io.stdout:write(" ") -- open stdout
local origStdOut = C.dup(1)
local pipe = ffi.new("int[2]")
C.pipe(pipe)
C.dup2(pipe[1], 1)
C.close(pipe[1])
local stdoutsrc = C.dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, pipe[0], 0, queue);
C.dispatch_source_set_event_handler_f(stdoutsrc, function(ctx)
local buffer = ffi.new("char[512]")
ffi.fill(buffer, ffi.sizeof(buffer))
local n = C.read(pipe[0], buffer, 511)
io.stderr:write(ffi.string(buffer))
local output = ffi.string(buffer)
C.write(connectedSocket, output, #output)
end)
C.dispatch_resume(stdoutsrc)
-- Set up the server
local AF_INET = 2
local SOCK_STREAM = 1
local INADDR_ANY = 0
local cli_addr = ffi.new("struct sockaddr_in")
local clilen = ffi.new("socklen_t[1]")
local sockfd = C.socket(AF_INET, SOCK_STREAM, 0)
if sockfd < 0 then
error("Couldn't open socket")
end
local serv_addr = ffi.new("struct sockaddr_in")
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY
serv_addr.sin_port = C.htons(portNum);
if C.bind(sockfd, ffi.cast("struct sockaddr*", serv_addr), ffi.sizeof(serv_addr)) < 0 then
error("Error binding socket")
end
local connectionSource
local function connectionHandler(ctx)
local buffer = ffi.new("char[512]")
ffi.fill(buffer, ffi.sizeof(buffer))
local n = C.read(connectedSocket, buffer, 511)
if n <= 0 then
io.stderr:write("couldnt read from socket\n")
C.dispatch_source_cancel(connectionSource)
else
local response = ffi.string(buffer)
if #response > 0 then
local toExec, syntaxErr = loadstring(response)
if toExec ~= nil then
success,ret = pcall(toExec)
if success == false then
print("error: ", ret)
end
else
print(syntaxErr)
end
end
end
end
-- Listen for connections
C.listen(sockfd, 0)
local listensrc = C.dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, sockfd, 0, queue);
C.dispatch_source_set_event_handler_f(listensrc, function(ctx)
connectedSocket = C.accept(sockfd, ffi.cast("struct sockaddr *", cli_addr), clilen)
if connectedSocket < 0 then
error("Error on accept")
end
if connectionSource ~= nil then
C.dispatch_source_cancel(connectionSource)
connectionSource = nil
end
connectionSource = C.dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, connectedSocket, 0, queue);
C.dispatch_source_set_event_handler_f(connectionSource, connectionHandler)
C.dispatch_resume(connectionSource)
end)
C.dispatch_resume(listensrc)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment