Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 41 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ Build command:
Basic usage:

```txt
ps2client [-h hostname] [-t timeout] <command> [arguments]
ps2client [-h hostname] [-t timeout] [-e marker] <command> [arguments]
fsclient [-h hostname] <command> <arguments>
```

Expand All @@ -54,6 +54,26 @@ The address of the remote ps2. This can be an IP or a hostname. If none is given
#### \[-t timeout]

An idle timeout period in seconds that the client will wait before exiting. This is useful to allow a script to continue after calling ps2client to send a command to ps2link or ps2netfs.
For `ping`, it is how long to try (default 2 seconds).

#### \[-e marker]

Exit as soon as a line of console output contains `marker`. The exit status
is the number after `status=` on that line, or 0 if there is none. A test
program can print for example `@@END@@ status=1` as its last line, so a script
gets the result without waiting for a timeout.

### EXIT STATUS

* `0`: done (or the idle timeout passed, without `-e`)
* `64`: usage error
* `69`: ps2link is not answering
* `74`: ps2link closed the connection (reset, reboot or crash of the ps2)
* `75`: the console port is in use, probably by another ps2client
* `124`: with `-e`: the idle timeout passed before the marker
* with `-e`: the status from the marker line

Error messages go to stderr; stdout carries only the console output of the ps2.

### PS2LINK COMMANDS

Expand Down Expand Up @@ -95,7 +115,9 @@ Tell the specified vector unit to stop operation.

* `dumpreg <type> <filename>`

Dump the registers of a given type into a file.
Dump the registers of a given type into a file. The type is a number or one
of `dma`, `intc`, `timer`, `gs`, `sif`, `fifo`, `gif`, `vif0`, `vif1`, `ipu`,
`all`, `vu0`, `vu1`.

* `gsexec <size> <filename>`

Expand All @@ -113,6 +135,23 @@ I really don't know! OH NOES!!

Listen to the ps2link network console.

* `ping`

Check that ps2link is up: ps2link is asked to dump two registers to a host
file, which the client discards, so this checks the EE side, the IOP side and
the host connection. Exits 0 and prints the round trip time, or exits 69.
Note that ps2link serves one host connection, and a new client replaces the
old one: do not ping while another client is serving a running program.

Commands that load host files (`execee`, `execiop`, ...) do the same check
before they send the command, instead of waiting a fixed time.

### TESTS

`tests/run-tests.sh` runs the client against a fake ps2link
(`tests/fake_ps2link.py`) in unprivileged Linux network namespaces, so no ps2
is needed.

## Community

Links for discussion and chat are available
Expand Down
102 changes: 92 additions & 10 deletions src/network.c
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@

#ifndef _WIN32
#include <errno.h>
#include <fcntl.h>
#include <netdb.h>
#include <signal.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#endif

#include <string.h>

#include "network.h"

///////////////////////
Expand All @@ -21,18 +26,46 @@
// Start up winsock.
if (WSAStartup(MAKEWORD(2, 0), &wsaData) != 0) { return -1; }

#else
// A send on a connection the ps2 has closed must fail, not kill us.
signal(SIGPIPE, SIG_IGN);
#endif
// End function.
return 0;
}

int network_connect(char *hostname, int port, int type) { int sock = -1;
struct sockaddr_in sockaddr;
static int network_set_blocking(int sock, int blocking) {
#ifdef _WIN32
u_long mode = blocking ? 0 : 1;

return ioctlsocket(sock, FIONBIO, &mode) == 0 ? 0 : -1;
#else
int flags = fcntl(sock, F_GETFL, 0); if (flags < 0) { return -1; }

if (blocking) { flags &= ~O_NONBLOCK; } else { flags |= O_NONBLOCK; }

return fcntl(sock, F_SETFL, flags);
#endif
}

int network_connect(char *hostname, int port, int type) {

// Connect without a timeout.
return network_connect_timeout(hostname, port, type, -1);

}

int network_connect_timeout(char *hostname, int port, int type, int timeout_ms) { int sock = -1;
struct sockaddr_in sockaddr; struct hostent *host;

// Resolve the hostname.
host = gethostbyname(hostname); if (host == NULL || host->h_addr == NULL) { return -1; }

// Populate the sockaddr structure.
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(port);
sockaddr.sin_addr = *(struct in_addr *)gethostbyname(hostname)->h_addr;
sockaddr.sin_addr = *(struct in_addr *)host->h_addr;

// Open the socket.
sock = socket(AF_INET, type, 0); if (sock < 0) { return -1; }
Expand All @@ -46,8 +79,32 @@
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void *)&nodelay, sizeof(nodelay));
}

// Connect the socket.
if (connect(sock, (struct sockaddr *)&sockaddr, sizeof(struct sockaddr)) < 0) { return -2; }
// Without a timeout, connect the socket and wait as long as it takes.
if (timeout_ms < 0) {
if (connect(sock, (struct sockaddr *)&sockaddr, sizeof(struct sockaddr)) < 0) { network_disconnect(sock); return -2; }
return sock;
}

// Else, start a non-blocking connect and wait for it until the timeout.
if (network_set_blocking(sock, 0) < 0) { network_disconnect(sock); return -1; }
if (connect(sock, (struct sockaddr *)&sockaddr, sizeof(struct sockaddr)) < 0) {
fd_set wfds, efds; struct timeval tv; int error = 0; socklen_t len = sizeof(error);

#ifdef _WIN32
if (WSAGetLastError() != WSAEWOULDBLOCK) { network_disconnect(sock); return -2; }
#else
if (errno != EINPROGRESS) { network_disconnect(sock); return -2; }
#endif

FD_ZERO(&wfds); FD_SET(sock, &wfds);
FD_ZERO(&efds); FD_SET(sock, &efds);
tv.tv_sec = timeout_ms / 1000; tv.tv_usec = (timeout_ms % 1000) * 1000;

// Timed out, or the connect failed.
if (select(sock + 1, NULL, &wfds, &efds, &tv) <= 0) { network_disconnect(sock); return -2; }
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *)&error, &len) < 0 || error != 0) { network_disconnect(sock); return -2; }
}
if (network_set_blocking(sock, 1) < 0) { network_disconnect(sock); return -1; }

// Return the socket.
return sock;
Expand All @@ -58,6 +115,7 @@
struct sockaddr_in sockaddr;

// Populate the sockaddr structure.
memset(&sockaddr, 0, sizeof(sockaddr));
sockaddr.sin_family = AF_INET;
sockaddr.sin_port = htons(port);
sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
Expand All @@ -66,7 +124,7 @@
sock = socket(AF_INET, type, 0); if (sock < 0) { return -1; }

// Bind the socket.
if (bind(sock, (struct sockaddr *)&sockaddr, sizeof(struct sockaddr)) < 0) { return -1; }
if (bind(sock, (struct sockaddr *)&sockaddr, sizeof(struct sockaddr)) < 0) { network_disconnect(sock); return -2; }

// Return the socket.
return sock;
Expand All @@ -76,7 +134,19 @@
int network_send(int sock, void *buffer, int size) { int total = 0;

// Keep sending data until it has all been sent.
while (total < size) { total += send(sock, &((char *)buffer)[total], size - total, 0); }
while (total < size) {
int sent = send(sock, &((char *)buffer)[total], size - total, 0);

// The connection is gone.
if (sent <= 0) {
#ifndef _WIN32
if (sent < 0 && errno == EINTR) { continue; }
#endif
return -1;
}

total += sent;
}

// Return the total bytes sent.
return total;
Expand All @@ -93,10 +163,10 @@
tv.tv_sec = timeout; tv.tv_usec = 0;

// No timeout was specified, so wait forever.
if (timeout < 0) { return select(FD_SETSIZE, &nfds, NULL, NULL, NULL); }
if (timeout < 0) { return select(sock + 1, &nfds, NULL, NULL, NULL); }

// A timeout was specified, so wait until the time has elapsed.
else { return select(FD_SETSIZE, &nfds, NULL, NULL, &tv); }
else { return select(sock + 1, &nfds, NULL, NULL, &tv); }

// End function.
return 0;
Expand All @@ -113,7 +183,19 @@
int network_receive_all(int sock, void *buffer, int size) { int total = 0;

// Receive the data from the socket.
while (total < size) { total += recvfrom(sock, &((char *)buffer)[total], size - total, 0, NULL, NULL); }
while (total < size) {
int received = recvfrom(sock, &((char *)buffer)[total], size - total, 0, NULL, NULL);

// The connection was closed (0) or failed (<0).
if (received <= 0) {
#ifndef _WIN32
if (received < 0 && errno == EINTR) { continue; }
#endif
return -1;
}

total += received;
}

// Return the total bytes received.
return total;
Expand Down
9 changes: 7 additions & 2 deletions src/network.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#ifdef _WIN32
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#else
#include <sys/socket.h>
Expand All @@ -16,16 +17,20 @@

int network_connect(char *hostname, int port, int type);

int network_listen(int port, int type);
// Like network_connect, but give up after timeout_ms (-1: no timeout).
// Returns -1 if the hostname does not resolve, -2 if the connect fails.
int network_connect_timeout(char *hostname, int port, int type, int timeout_ms);

int network_accept(int sock);
// Returns -2 if the port is in use.
int network_listen(int port, int type);

int network_send(int sock, void *buffer, int size);

int network_wait(int sock, int timeout);

int network_receive(int sock, void *buffer, int size);

// Returns -1 if the connection was closed or failed before size bytes came in.
int network_receive_all(int sock, void *buffer, int size);

int network_disconnect(int sock);
Expand Down
Loading
Loading