initial commit
This commit is contained in:
commit
8c8158ad5b
7 changed files with 724 additions and 0 deletions
258
prezzyd.c
Normal file
258
prezzyd.c
Normal file
|
@ -0,0 +1,258 @@
|
|||
/*
|
||||
* prezzyd
|
||||
* Copyright (c) 2024 Jeremy Baxter.
|
||||
*/
|
||||
|
||||
#define _PREZZYD_C_
|
||||
#define _DEFAULT_SOURCE
|
||||
|
||||
#ifndef BUILD_DATE
|
||||
#define BUILD_DATE "1970-01-01"
|
||||
#endif
|
||||
|
||||
#include <err.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "prezzyipc.h"
|
||||
#include "discord_rpc.h"
|
||||
|
||||
void
|
||||
connectHandler(const DiscordUser *user)
|
||||
{
|
||||
warnx("established connection to Discord (%s/%s)",
|
||||
user->username, user->userId);
|
||||
}
|
||||
|
||||
void
|
||||
disconnectHandler(int code, const char *message)
|
||||
{
|
||||
warnx("disconnected from client (%d: %s)", code, message);
|
||||
}
|
||||
|
||||
void
|
||||
errorHandler(int code, const char *message)
|
||||
{
|
||||
errx(1, "rpc error %d: %s", code, message);
|
||||
}
|
||||
|
||||
int
|
||||
canReadFrom(int fd)
|
||||
{
|
||||
struct pollfd *p;
|
||||
int ret;
|
||||
|
||||
p = malloc(sizeof(struct pollfd));
|
||||
p->fd = fd;
|
||||
p->events = POLLIN;
|
||||
|
||||
if (poll(p, 1, 5000) == -1)
|
||||
err(1, "failed to poll client");
|
||||
|
||||
ret = p->revents & POLLIN;
|
||||
free(p);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
ipcClose(int fd, byte code, const char *message)
|
||||
{
|
||||
write(fd, (char []){code}, 1);
|
||||
write(fd, message, strlen(message));
|
||||
if (close(fd) == -1)
|
||||
err(1, "failed to close IPC connection");
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
void
|
||||
ipcSucceed(int fd)
|
||||
{
|
||||
ipcClose(fd, 0, "OK");
|
||||
}
|
||||
|
||||
int
|
||||
makeIpcServer(const char *bindPath, int backlog)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
int fd;
|
||||
|
||||
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
|
||||
err(1, "failed to create IPC socket");
|
||||
|
||||
if (remove(bindPath) == -1) {
|
||||
if (errno != ENOENT)
|
||||
err(1, "could not remove %s", bindPath);
|
||||
}
|
||||
|
||||
memset(&addr, 0, sizeof(struct sockaddr_un));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strbcpy(addr.sun_path, bindPath, sizeof(addr.sun_path) - 1);
|
||||
|
||||
if (bind(fd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) == -1)
|
||||
err(1, "failed to bind to %s", bindPath);
|
||||
if (listen(fd, backlog) == -1)
|
||||
err(1, "failed to listen on IPC socket %s", bindPath);
|
||||
|
||||
if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK) == -1)
|
||||
err(1, "failed to create non-blocking IPC socket");
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
void *propertyMapping[PREZZY_PROPERTY_MAX];
|
||||
DiscordRichPresence presence;
|
||||
DiscordEventHandlers handlers;
|
||||
const char *clientId;
|
||||
int ch, sd;
|
||||
|
||||
while ((ch = getopt(argc, argv, "V")) != -1) {
|
||||
switch (ch) {
|
||||
case 'V':
|
||||
printf("prezzyd version 0.1 built on " BUILD_DATE "\n");
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (argc - optind != 1) {
|
||||
fputs("usage: prezzyd [-V] client-id\n", stderr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
clientId = argv[optind];
|
||||
|
||||
memset(&presence, 0, sizeof(DiscordRichPresence));
|
||||
memset(&handlers, 0, sizeof(DiscordEventHandlers));
|
||||
handlers.ready = connectHandler;
|
||||
handlers.disconnected = disconnectHandler;
|
||||
handlers.errored = errorHandler;
|
||||
|
||||
Discord_Initialize(clientId, &handlers, 1, NULL);
|
||||
|
||||
propertyMapping[PREZZY_STATE] = &presence.state;
|
||||
propertyMapping[PREZZY_DETAILS] = &presence.details;
|
||||
propertyMapping[PREZZY_LARGE_IMAGE] = &presence.largeImageKey;
|
||||
propertyMapping[PREZZY_LARGE_TOOLTIP] = &presence.largeImageText;
|
||||
propertyMapping[PREZZY_ICON] = &presence.smallImageKey;
|
||||
propertyMapping[PREZZY_ICON_TOOLTIP] = &presence.smallImageText;
|
||||
propertyMapping[PREZZY_PARTY_SIZE] = &presence.partySize;
|
||||
propertyMapping[PREZZY_PARTY_LIMIT] = &presence.partyMax;
|
||||
propertyMapping[PREZZY_TIMESTAMP_START] = &presence.startTimestamp;
|
||||
propertyMapping[PREZZY_TIMESTAMP_END] = &presence.endTimestamp;
|
||||
|
||||
/* start ipc server */
|
||||
sd = makeIpcServer(PREZZY_SOCK, 8);
|
||||
|
||||
for (;;) {
|
||||
PrezzyIpcPacket **packets;
|
||||
char buffer[PREZZY_PACKET_MAX];
|
||||
ssize_t sz;
|
||||
int client, i, packetCount;
|
||||
char ch;
|
||||
|
||||
packets = NULL;
|
||||
packetCount = 0;
|
||||
client = accept(sd, NULL, NULL);
|
||||
|
||||
if (client == -1) {
|
||||
switch (errno) {
|
||||
case EWOULDBLOCK:
|
||||
goto update;
|
||||
default:
|
||||
err(1, "failed to accept incoming IPC connection");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* IPC error codes:
|
||||
* 1: error during initial read
|
||||
* 2: bad request format
|
||||
*/
|
||||
|
||||
if (!canReadFrom(client))
|
||||
ipcClose(client, 1, "Timed out");
|
||||
|
||||
for (i = 0; (sz = read(client, &ch, 1)) > 0; i++) {
|
||||
if (i == PREZZY_PACKET_MAX) {
|
||||
ipcClose(client, 0, "OK (truncated)");
|
||||
buffer[i - 1] = '\0';
|
||||
break;
|
||||
}
|
||||
|
||||
switch (ch) {
|
||||
case '\n':
|
||||
case '\0':
|
||||
/* end current packet */
|
||||
if (i + 1 < 2 && /* are we before the payload? */
|
||||
/* accept null bytes after newlines */
|
||||
!(ch == '\0' && i == 0)) {
|
||||
ipcClose(client, 2, "Request missing payload");
|
||||
goto update;
|
||||
}
|
||||
if (buffer[0] < 0 || buffer[0] >= PREZZY_PROPERTY_MAX) {
|
||||
ipcClose(client, 2, "Invalid property byte");
|
||||
goto update;
|
||||
}
|
||||
buffer[i] = '\0';
|
||||
|
||||
packetCount++;
|
||||
packets = realloc(packets, packetCount *
|
||||
sizeof(PrezzyIpcPacket *));
|
||||
packets[packetCount - 1] = makeIpcPacket(buffer);
|
||||
|
||||
if (ch == '\0') {
|
||||
ipcSucceed(client);
|
||||
goto process;
|
||||
}
|
||||
/* '\n'; more requests later */
|
||||
i = -1;
|
||||
continue;
|
||||
default:
|
||||
buffer[i] = ch;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (sz == -1)
|
||||
err(1, "failed to read from IPC client");
|
||||
|
||||
process:
|
||||
for (i = 0; i < packetCount; i++) {
|
||||
size_t len;
|
||||
|
||||
if (packets[i]->type == PLT_STRING) {
|
||||
len = strlen(packets[i]->pString) + 1;
|
||||
*(char **)propertyMapping[packets[i]->property] =
|
||||
malloc(len * sizeof(char));
|
||||
strlcpy(*(char **)propertyMapping[packets[i]->property],
|
||||
packets[i]->pString, len);
|
||||
} else {
|
||||
if (packets[i]->type == PLT_INT64) {
|
||||
*(int64_t *)propertyMapping[packets[i]->property] =
|
||||
packets[i]->pInteger;
|
||||
} else {
|
||||
*(int *)propertyMapping[packets[i]->property] =
|
||||
squashLong(packets[i]->pInteger);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
update:
|
||||
Discord_UpdatePresence(&presence);
|
||||
Discord_RunCallbacks();
|
||||
usleep(64000);
|
||||
}
|
||||
|
||||
Discord_Shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue