Major changes to how data is stored on the server. Client changes will follow.
This commit is contained in:
parent
ebea70dd0b
commit
049f81ed50
12 changed files with 671 additions and 183 deletions
|
@ -1,3 +1,3 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
mysqldump -d -h192.168.0.3 -udosThing -p dosThing > schema.sql
|
mysqldump -d -h192.168.0.3 -ukangaworld -p kangaworld > schema.sql
|
||||||
|
|
95
schema.sql
95
schema.sql
|
@ -1,6 +1,6 @@
|
||||||
-- MySQL dump 10.19 Distrib 10.3.32-MariaDB, for debian-linux-gnu (x86_64)
|
-- MySQL dump 10.19 Distrib 10.3.34-MariaDB, for debian-linux-gnu (x86_64)
|
||||||
--
|
--
|
||||||
-- Host: 192.168.0.3 Database: dosThing
|
-- Host: 192.168.0.3 Database: kangaworld
|
||||||
-- ------------------------------------------------------
|
-- ------------------------------------------------------
|
||||||
-- Server version 10.5.13-MariaDB-log
|
-- Server version 10.5.13-MariaDB-log
|
||||||
|
|
||||||
|
@ -15,6 +15,23 @@
|
||||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Table structure for table `client`
|
||||||
|
--
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `client`;
|
||||||
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
|
CREATE TABLE `client` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(32) NOT NULL,
|
||||||
|
`data` varchar(1024) NOT NULL,
|
||||||
|
`description` varchar(255) NOT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `nameIDX` (`name`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=43 DEFAULT CHARSET=latin1;
|
||||||
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Table structure for table `config`
|
-- Table structure for table `config`
|
||||||
--
|
--
|
||||||
|
@ -43,48 +60,58 @@ DROP TABLE IF EXISTS `files`;
|
||||||
/*!40101 SET character_set_client = utf8 */;
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
CREATE TABLE `files` (
|
CREATE TABLE `files` (
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
`realPath` varchar(1024) NOT NULL,
|
`path` varchar(1024) NOT NULL,
|
||||||
`virtualPath` varchar(512) NOT NULL,
|
`length` int(11) NOT NULL DEFAULT 0,
|
||||||
`sha256` varchar(64) NOT NULL,
|
`sha256` varchar(64) NOT NULL,
|
||||||
`description` varchar(255) NOT NULL,
|
`modified` datetime NOT NULL,
|
||||||
|
`description` varchar(255) NOT NULL DEFAULT '',
|
||||||
|
`touched` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
UNIQUE KEY `virtualPath` (`virtualPath`),
|
UNIQUE KEY `pathIndex` (`path`)
|
||||||
UNIQUE KEY `realPath` (`realPath`)
|
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=latin1;
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
|
|
||||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Table structure for table `numbers`
|
-- Table structure for table `games`
|
||||||
--
|
--
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `numbers`;
|
DROP TABLE IF EXISTS `games`;
|
||||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
||||||
/*!40101 SET character_set_client = utf8 */;
|
/*!40101 SET character_set_client = utf8 */;
|
||||||
CREATE TABLE `numbers` (
|
CREATE TABLE `games` (
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
`name` varchar(32) NOT NULL,
|
`active` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
`data` int(11) NOT NULL,
|
`title` varchar(255) DEFAULT NULL,
|
||||||
`description` varchar(255) NOT NULL,
|
`developer` varchar(255) DEFAULT NULL,
|
||||||
|
`publisher` varchar(255) DEFAULT NULL,
|
||||||
|
`description` text DEFAULT NULL,
|
||||||
|
`releaseDate` datetime DEFAULT NULL,
|
||||||
|
`stars` double DEFAULT NULL,
|
||||||
|
`starCount` int(11) DEFAULT NULL,
|
||||||
|
`rating` varchar(255) DEFAULT NULL,
|
||||||
|
`series` varchar(255) DEFAULT NULL,
|
||||||
|
`origin` varchar(255) DEFAULT NULL,
|
||||||
|
`region` varchar(255) DEFAULT NULL,
|
||||||
|
`shortName` varchar(8) DEFAULT NULL,
|
||||||
|
`type` varchar(8) DEFAULT NULL,
|
||||||
|
`maxPlayers` int(11) DEFAULT 2,
|
||||||
|
`joinable` tinyint(1) DEFAULT 0,
|
||||||
|
`mobyGames` varchar(255) DEFAULT NULL,
|
||||||
|
`wiki` varchar(255) DEFAULT NULL,
|
||||||
|
`manual` varchar(255) DEFAULT NULL,
|
||||||
|
`root` varchar(255) DEFAULT NULL,
|
||||||
|
`worksWith` varchar(255) DEFAULT NULL,
|
||||||
|
`configSys` text DEFAULT NULL,
|
||||||
|
`autoexecBat` text DEFAULT NULL,
|
||||||
|
`options` text DEFAULT NULL,
|
||||||
|
`mods` text DEFAULT NULL,
|
||||||
|
`notes` text DEFAULT NULL,
|
||||||
|
`added` datetime DEFAULT NULL,
|
||||||
|
`modified` datetime DEFAULT NULL,
|
||||||
|
`touched` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
UNIQUE KEY `nameIDX` (`name`)
|
KEY `active_index` (`active`)
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=latin1;
|
) ENGINE=InnoDB AUTO_INCREMENT=313 DEFAULT CHARSET=utf8mb4;
|
||||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
|
||||||
|
|
||||||
--
|
|
||||||
-- Table structure for table `strings`
|
|
||||||
--
|
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `strings`;
|
|
||||||
/*!40101 SET @saved_cs_client = @@character_set_client */;
|
|
||||||
/*!40101 SET character_set_client = utf8 */;
|
|
||||||
CREATE TABLE `strings` (
|
|
||||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
|
||||||
`name` varchar(32) NOT NULL,
|
|
||||||
`data` varchar(1024) NOT NULL,
|
|
||||||
`description` varchar(255) NOT NULL,
|
|
||||||
PRIMARY KEY (`id`),
|
|
||||||
UNIQUE KEY `nameIDX` (`name`)
|
|
||||||
) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=latin1;
|
|
||||||
/*!40101 SET character_set_client = @saved_cs_client */;
|
/*!40101 SET character_set_client = @saved_cs_client */;
|
||||||
|
|
||||||
--
|
--
|
||||||
|
@ -118,4 +145,4 @@ CREATE TABLE `users` (
|
||||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
||||||
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
|
||||||
|
|
||||||
-- Dump completed on 2022-02-14 20:13:22
|
-- Dump completed on 2022-03-21 20:14:03
|
||||||
|
|
|
@ -33,6 +33,7 @@ INCLUDEPATH += \
|
||||||
/usr/include/mariadb \
|
/usr/include/mariadb \
|
||||||
/usr/include/mariadb/mysql \
|
/usr/include/mariadb/mysql \
|
||||||
$$SHARED \
|
$$SHARED \
|
||||||
|
$$PWD/src/thirdparty/sha256.c/deps \
|
||||||
$$PWD/src
|
$$PWD/src
|
||||||
|
|
||||||
HEADERS = \
|
HEADERS = \
|
||||||
|
@ -50,6 +51,7 @@ HEADERS = \
|
||||||
$$SHARED/packets.h \
|
$$SHARED/packets.h \
|
||||||
$$SHARED/thirdparty/tiny-AES-c/aes.h \
|
$$SHARED/thirdparty/tiny-AES-c/aes.h \
|
||||||
$$SHARED/thirdparty/tiny-AES128-C/pkcs7_padding.h \
|
$$SHARED/thirdparty/tiny-AES128-C/pkcs7_padding.h \
|
||||||
|
src/thirdparty/sha256.c/sha256.h \
|
||||||
src/client.h \
|
src/client.h \
|
||||||
src/client/file.h \
|
src/client/file.h \
|
||||||
src/client/login.h \
|
src/client/login.h \
|
||||||
|
@ -63,7 +65,8 @@ HEADERS = \
|
||||||
src/rest.h \
|
src/rest.h \
|
||||||
src/database.h \
|
src/database.h \
|
||||||
src/server.h \
|
src/server.h \
|
||||||
src/settings.h
|
src/settings.h \
|
||||||
|
src/update.h
|
||||||
|
|
||||||
SOURCES = \
|
SOURCES = \
|
||||||
$$SHARED/thirdparty/memwatch/memwatch.c \
|
$$SHARED/thirdparty/memwatch/memwatch.c \
|
||||||
|
@ -75,6 +78,7 @@ SOURCES = \
|
||||||
$$SHARED/packet.c \
|
$$SHARED/packet.c \
|
||||||
$$SHARED/thirdparty/tiny-AES-c/aes.c \
|
$$SHARED/thirdparty/tiny-AES-c/aes.c \
|
||||||
$$SHARED/thirdparty/tiny-AES128-C/pkcs7_padding.c \
|
$$SHARED/thirdparty/tiny-AES128-C/pkcs7_padding.c \
|
||||||
|
src/thirdparty/sha256.c/sha256.c \
|
||||||
src/client.c \
|
src/client.c \
|
||||||
src/client/file.c \
|
src/client/file.c \
|
||||||
src/client/login.c \
|
src/client/login.c \
|
||||||
|
@ -88,7 +92,8 @@ SOURCES = \
|
||||||
src/rest.c \
|
src/rest.c \
|
||||||
src/database.c \
|
src/database.c \
|
||||||
src/server.c \
|
src/server.c \
|
||||||
src/settings.c
|
src/settings.c \
|
||||||
|
src/update.c
|
||||||
|
|
||||||
LIBS = \
|
LIBS = \
|
||||||
-L/usr/lib/x86_64-linux-gnu/ \
|
-L/usr/lib/x86_64-linux-gnu/ \
|
||||||
|
@ -96,11 +101,12 @@ LIBS = \
|
||||||
-lm \
|
-lm \
|
||||||
-lmariadb \
|
-lmariadb \
|
||||||
-lpthread \
|
-lpthread \
|
||||||
-lgnutls \
|
|
||||||
-lcrypt \
|
-lcrypt \
|
||||||
-lcurl \
|
-lcurl \
|
||||||
-ljson-c
|
-ljson-c
|
||||||
|
|
||||||
|
#-lgnutls \
|
||||||
|
|
||||||
OTHER_FILES =
|
OTHER_FILES =
|
||||||
|
|
||||||
ASAN {
|
ASAN {
|
||||||
|
|
|
@ -76,7 +76,7 @@ static void clientApiFileRequestCheck(ClientThreadT *client, PacketDecodeDataT *
|
||||||
uint16_t length = 0;
|
uint16_t length = 0;
|
||||||
char shaInDB[128] = { 0 };
|
char shaInDB[128] = { 0 };
|
||||||
char buffer[1024] = { 0 };
|
char buffer[1024] = { 0 };
|
||||||
char buffer2[2048] = { 0 };
|
char buffer2[2064] = { 0 };
|
||||||
uint32_t temp = 0;
|
uint32_t temp = 0;
|
||||||
|
|
||||||
// Is something still open?
|
// Is something still open?
|
||||||
|
@ -102,9 +102,18 @@ static void clientApiFileRequestCheck(ClientThreadT *client, PacketDecodeDataT *
|
||||||
packetSend(client->packetThreadData, &encoded);
|
packetSend(client->packetThreadData, &encoded);
|
||||||
DEL(packetData);
|
DEL(packetData);
|
||||||
} else {
|
} else {
|
||||||
// Get real path.
|
// Get real path by flipping colons to slashes.
|
||||||
dbFileRealPathGet(path, buffer, FILE_VIRTUAL_PATH_MAX);
|
temp = 0;
|
||||||
snprintf(buffer2, 2048, "%s%s", __settingsFile, buffer);
|
while (path[temp] != 0) {
|
||||||
|
if (path[temp] == ':') {
|
||||||
|
buffer[temp] = '/';
|
||||||
|
} else {
|
||||||
|
buffer[temp] = path[temp];
|
||||||
|
}
|
||||||
|
temp++;
|
||||||
|
}
|
||||||
|
buffer[temp] = 0;
|
||||||
|
snprintf(buffer2, 2064, "%s/files/%s", __settingsFile, buffer);
|
||||||
// Open file & get file size.
|
// Open file & get file size.
|
||||||
client->handle = fopen(buffer2, "rb");
|
client->handle = fopen(buffer2, "rb");
|
||||||
if (!client->handle) {
|
if (!client->handle) {
|
||||||
|
|
|
@ -31,89 +31,10 @@ void clientApiVersionBad(ClientThreadT *client, PacketDecodeDataT *data) {
|
||||||
|
|
||||||
|
|
||||||
void clientApiVersionOkay(ClientThreadT *client, PacketDecodeDataT *data) {
|
void clientApiVersionOkay(ClientThreadT *client, PacketDecodeDataT *data) {
|
||||||
uint64_t x = 0;
|
|
||||||
uint32_t y = 0;
|
|
||||||
uint16_t length = 0;
|
|
||||||
uint8_t *buffer = NULL;
|
|
||||||
PacketEncodeDataT encoded = { 0 };
|
PacketEncodeDataT encoded = { 0 };
|
||||||
DBTableT *record = NULL;
|
|
||||||
DBTableT **table = NULL;
|
|
||||||
|
|
||||||
(void)data;
|
(void)data;
|
||||||
|
|
||||||
// Fetch string table from DB.
|
|
||||||
if (dbTableGet("strings", &table) == SUCCESS) {
|
|
||||||
while (arrlen(table)) {
|
|
||||||
record = table[0];
|
|
||||||
arrdel(table, 0);
|
|
||||||
// Strings are encoded in a single buffer as: KEY\0DATA\0
|
|
||||||
x = strlen(record->name);
|
|
||||||
y = strlen(record->data);
|
|
||||||
length = x + y + 2;
|
|
||||||
buffer = (uint8_t *)malloc(length);
|
|
||||||
if (!buffer) {
|
|
||||||
consoleMessageQueue("%ld: Unable to allocate buffer for string packet!\n", client->threadIndex);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
memcpy(buffer, record->name, x + 1);
|
|
||||||
memcpy(&buffer[x + 1], record->data, y + 1);
|
|
||||||
// Build packet.
|
|
||||||
encoded.control = PACKET_CONTROL_DAT;
|
|
||||||
encoded.packetType = PACKET_TYPE_STRING;
|
|
||||||
encoded.channel = data->channel;
|
|
||||||
encoded.encrypt = 0;
|
|
||||||
packetEncode(client->packetThreadData, &encoded, buffer, length);
|
|
||||||
// Send it.
|
|
||||||
packetSend(client->packetThreadData, &encoded);
|
|
||||||
DEL(buffer);
|
|
||||||
//consoleMessageQueue("PACKET_TYPE_STRING [%s] sent.\n", record->name);
|
|
||||||
DEL(record->name);
|
|
||||||
DEL(record->data);
|
|
||||||
DEL(record);
|
|
||||||
}
|
|
||||||
arrfree(table);
|
|
||||||
} else {
|
|
||||||
consoleMessageQueue("%ld: Unable to fetch strings!\n", client->threadIndex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch number table from DB.
|
|
||||||
if (dbTableGet("numbers", &table) == SUCCESS) {
|
|
||||||
while (arrlen(table)) {
|
|
||||||
record = table[0];
|
|
||||||
arrdel(table, 0);
|
|
||||||
// Integers are encoded in a single buffer as: 1234DATA\0
|
|
||||||
// Integers are 64 bit until sent to the client when they are truncated to 32.
|
|
||||||
x = strlen(record->name);
|
|
||||||
y = atol(record->data);
|
|
||||||
length = x + 5;
|
|
||||||
buffer = (uint8_t *)malloc(length);
|
|
||||||
if (!buffer) {
|
|
||||||
consoleMessageQueue("%ld: Unable to allocate buffer for number packet!\n", client->threadIndex);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
memcpy(buffer, &y, 4);
|
|
||||||
memcpy(&buffer[4], record->name, x + 1);
|
|
||||||
// Build packet.
|
|
||||||
encoded.control = PACKET_CONTROL_DAT;
|
|
||||||
encoded.packetType = PACKET_TYPE_NUMBER;
|
|
||||||
encoded.channel = data->channel;
|
|
||||||
encoded.encrypt = 0;
|
|
||||||
packetEncode(client->packetThreadData, &encoded, buffer, length);
|
|
||||||
// Send it.
|
|
||||||
packetSend(client->packetThreadData, &encoded);
|
|
||||||
DEL(buffer);
|
|
||||||
//consoleMessageQueue("PACKET_TYPE_NUMBER [%s] sent.\n", record->name);
|
|
||||||
DEL(record->name);
|
|
||||||
DEL(record->data);
|
|
||||||
DEL(record);
|
|
||||||
}
|
|
||||||
arrfree(table);
|
|
||||||
} else {
|
|
||||||
consoleMessageQueue("%ld: Unable to fetch numbers!\n", client->threadIndex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build PROCEED packet.
|
// Build PROCEED packet.
|
||||||
encoded.control = PACKET_CONTROL_DAT;
|
encoded.control = PACKET_CONTROL_DAT;
|
||||||
encoded.packetType = PACKET_TYPE_PROCEED;
|
encoded.packetType = PACKET_TYPE_PROCEED;
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
#include "array.h"
|
#include "array.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "update.h"
|
||||||
|
|
||||||
|
|
||||||
static struct termios _termios = { 0 };
|
static struct termios _termios = { 0 };
|
||||||
|
@ -61,6 +62,7 @@ void consoleRun(void) {
|
||||||
char command[256] = { 0 };
|
char command[256] = { 0 };
|
||||||
uint16_t commandIndex = 0;
|
uint16_t commandIndex = 0;
|
||||||
char c = 0;
|
char c = 0;
|
||||||
|
char *p = NULL;
|
||||||
uint8_t commandOk = 0;
|
uint8_t commandOk = 0;
|
||||||
struct timespec remaining = { 0, 0 };
|
struct timespec remaining = { 0, 0 };
|
||||||
struct timespec sleepTime = { 0, 1000000000/4 }; // 1/4th second
|
struct timespec sleepTime = { 0, 1000000000/4 }; // 1/4th second
|
||||||
|
@ -114,15 +116,30 @@ void consoleRun(void) {
|
||||||
printf("\n\r");
|
printf("\n\r");
|
||||||
logWriteToFileOnly(">%s\n", command);
|
logWriteToFileOnly(">%s\n", command);
|
||||||
commandOk = 0;
|
commandOk = 0;
|
||||||
|
// Is there a space in the command line?
|
||||||
|
p = strstr(command, " ");
|
||||||
|
if (p) {
|
||||||
|
// Replace it with zero, move P to next character for parameters.
|
||||||
|
*p = 0;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
// Help.
|
||||||
if (!strcasecmp(command, "HELP") || !strcasecmp(command, "?")) {
|
if (!strcasecmp(command, "HELP") || !strcasecmp(command, "?")) {
|
||||||
sendToConsole("HELP or ? - This message.\n");
|
sendToConsole("HELP or ? - This message.\n");
|
||||||
sendToConsole("SHUTDOWN - Stop the server.\n");
|
sendToConsole("SHUTDOWN - Stop the server.\n");
|
||||||
|
sendToConsole("UPDATE [what] - Update client data.\n");
|
||||||
commandOk = 1;
|
commandOk = 1;
|
||||||
}
|
}
|
||||||
|
// Shutdown.
|
||||||
if (!strcasecmp(command, "SHUTDOWN")) {
|
if (!strcasecmp(command, "SHUTDOWN")) {
|
||||||
__running = 0;
|
__running = 0;
|
||||||
commandOk = 1;
|
commandOk = 1;
|
||||||
}
|
}
|
||||||
|
// Update.
|
||||||
|
if (!strcasecmp(command, "UPDATE")) {
|
||||||
|
update(p);
|
||||||
|
commandOk = 1;
|
||||||
|
}
|
||||||
// Did we grok it?
|
// Did we grok it?
|
||||||
if (!commandOk) {
|
if (!commandOk) {
|
||||||
sendToConsole("Unknown command! Type HELP (or ?) for help.\n\r");
|
sendToConsole("Unknown command! Type HELP (or ?) for help.\n\r");
|
||||||
|
|
|
@ -22,7 +22,12 @@
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <crypt.h>
|
#include <crypt.h>
|
||||||
|
|
||||||
|
#include "thirdparty/sha256.c/sha256.h"
|
||||||
|
|
||||||
#include "array.h"
|
#include "array.h"
|
||||||
|
#include "console.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
#include "database.h"
|
#include "database.h"
|
||||||
|
|
||||||
|
|
||||||
|
@ -32,10 +37,26 @@
|
||||||
#define STATEMENT_MAX 2048
|
#define STATEMENT_MAX 2048
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct DbFileInfoS {
|
||||||
|
uint64_t id;
|
||||||
|
char *path;
|
||||||
|
uint64_t length;
|
||||||
|
char *sha;
|
||||||
|
char *modified;
|
||||||
|
uint8_t touched;
|
||||||
|
} DbFileInfoT;
|
||||||
|
|
||||||
|
|
||||||
static MYSQL *_sql = NULL;
|
static MYSQL *_sql = NULL;
|
||||||
static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
|
static pthread_mutex_t _mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||||
|
|
||||||
|
|
||||||
|
static DbFileInfoT *dbFileInfoGet(char *vpath);
|
||||||
|
static void dbFileInfoRelease(DbFileInfoT **info);
|
||||||
|
static void dbFileInfoSet(DbFileInfoT *info);
|
||||||
|
static void dbFileSha256Create(char *file, char *buf);
|
||||||
|
|
||||||
|
|
||||||
uint8_t dbConnect(char *host, uint16_t port, char *database, char *user, char *password) {
|
uint8_t dbConnect(char *host, uint16_t port, char *database, char *user, char *password) {
|
||||||
uint8_t reconnect = 1;
|
uint8_t reconnect = 1;
|
||||||
|
|
||||||
|
@ -68,99 +89,160 @@ uint8_t dbDisconnect(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint8_t dbFileRealPathGet(char *vpath, char *value, uint32_t max) {
|
static DbFileInfoT *dbFileInfoGet(char *vpath) {
|
||||||
char statement[STATEMENT_MAX];
|
char statement[STATEMENT_MAX];
|
||||||
char *p = statement;
|
char *p = statement;
|
||||||
MYSQL_RES *result = NULL;
|
MYSQL_RES *result = NULL;
|
||||||
MYSQL_ROW row;
|
MYSQL_ROW row = NULL;
|
||||||
int count;
|
int32_t count = 0;
|
||||||
|
DbFileInfoT *info = NULL;
|
||||||
|
|
||||||
pthread_mutex_lock(&_mutex);
|
pthread_mutex_lock(&_mutex);
|
||||||
|
|
||||||
if (!_sql) {
|
if (!_sql) {
|
||||||
pthread_mutex_unlock(&_mutex);
|
pthread_mutex_unlock(&_mutex);
|
||||||
return FAIL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
p += sprintf(p, "SELECT realPath FROM files WHERE virtualPath='");
|
// If they passed in a colon-separated path, flip it to slashes.
|
||||||
|
while (vpath[count] != 0) {
|
||||||
|
if (vpath[count] == ':') vpath[count] = '/';
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
p += sprintf(p, "SELECT id, path, length, sha256, modified, touched FROM files WHERE path='");
|
||||||
p += mysql_real_escape_string(_sql, p, vpath, strlen(vpath));
|
p += mysql_real_escape_string(_sql, p, vpath, strlen(vpath));
|
||||||
p += sprintf(p, "'");
|
p += sprintf(p, "'");
|
||||||
if (mysql_real_query(_sql, statement, p - statement) != 0) {
|
if (mysql_real_query(_sql, statement, p - statement) != 0) {
|
||||||
logWrite("dbFileRealPathGet: %s\n", mysql_error(_sql));
|
logWrite("dbFileInfoGet: %s\n", mysql_error(_sql));
|
||||||
pthread_mutex_unlock(&_mutex);
|
pthread_mutex_unlock(&_mutex);
|
||||||
return FAIL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = mysql_store_result(_sql);
|
result = mysql_store_result(_sql);
|
||||||
count = mysql_num_rows(result);
|
count = mysql_num_rows(result);
|
||||||
if (count != 1) {
|
if (count != 1) {
|
||||||
logWrite("dbFileRealPathGet: Wrong number of rows returned: %d.\n", count);
|
|
||||||
mysql_free_result(result);
|
mysql_free_result(result);
|
||||||
pthread_mutex_unlock(&_mutex);
|
pthread_mutex_unlock(&_mutex);
|
||||||
return FAIL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((row = mysql_fetch_row(result)) == NULL) {
|
if ((row = mysql_fetch_row(result)) == NULL) {
|
||||||
logWrite("dbFileRealPathGet: %s\n", mysql_error(_sql));
|
logWrite("dbFileInfoGet: %s\n", mysql_error(_sql));
|
||||||
pthread_mutex_unlock(&_mutex);
|
pthread_mutex_unlock(&_mutex);
|
||||||
return FAIL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy(value, row[0], max);
|
NEW(DbFileInfoT, info);
|
||||||
|
if (!info) {
|
||||||
|
logWrite("dbFileInfoGet: Unable to allocate DbFileInfoT.\n");
|
||||||
|
pthread_mutex_unlock(&_mutex);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
info->id = atol(row[0]);
|
||||||
|
info->path = strdup(row[1]);
|
||||||
|
info->length = atol(row[2]);
|
||||||
|
info->sha = strdup(row[3]);
|
||||||
|
info->modified = strdup(row[4]);
|
||||||
|
info->touched = atoi(row[5]);
|
||||||
|
|
||||||
mysql_free_result(result);
|
mysql_free_result(result);
|
||||||
|
|
||||||
pthread_mutex_unlock(&_mutex);
|
pthread_mutex_unlock(&_mutex);
|
||||||
|
|
||||||
return SUCCESS;
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void dbFileInfoRelease(DbFileInfoT **info) {
|
||||||
|
DbFileInfoT *i = *info;
|
||||||
|
|
||||||
|
if (i) {
|
||||||
|
DEL(i->path);
|
||||||
|
DEL(i->modified);
|
||||||
|
DEL(i->sha);
|
||||||
|
DEL(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void dbFileInfoSet(DbFileInfoT *info) {
|
||||||
|
char statement[STATEMENT_MAX];
|
||||||
|
char *p = statement;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&_mutex);
|
||||||
|
|
||||||
|
if (!_sql) {
|
||||||
|
pthread_mutex_unlock(&_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If id is 0, it's a new file to insert. Otherwise update.
|
||||||
|
if (info->id == 0) {
|
||||||
|
// New record.
|
||||||
|
p += sprintf(p, "INSERT INTO files (path, length, sha256, modified, touched) VALUES ('");
|
||||||
|
p += mysql_real_escape_string(_sql, p, info->path, strlen(info->path));
|
||||||
|
p += sprintf(p, "', %ld, '", info->length);
|
||||||
|
p += mysql_real_escape_string(_sql, p, info->sha, strlen(info->sha));
|
||||||
|
p += sprintf(p, "', '");
|
||||||
|
p += mysql_real_escape_string(_sql, p, info->modified, strlen(info->modified));
|
||||||
|
p += sprintf(p, "', %d)", info->touched);
|
||||||
|
} else {
|
||||||
|
// Update record.
|
||||||
|
p += sprintf(p, "UPDATE files SET length=%ld, sha256='", info->length);
|
||||||
|
p += mysql_real_escape_string(_sql, p, info->sha, strlen(info->sha));
|
||||||
|
p += sprintf(p, "', modified='");
|
||||||
|
p += mysql_real_escape_string(_sql, p, info->modified, strlen(info->modified));
|
||||||
|
p += sprintf(p, "', touched=%d WHERE id=%ld", info->touched, info->id);
|
||||||
|
}
|
||||||
|
if (mysql_real_query(_sql, statement, p - statement) != 0) {
|
||||||
|
logWrite("dbFileInfoSet: %s\n", mysql_error(_sql));
|
||||||
|
pthread_mutex_unlock(&_mutex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void dbFileSha256Create(char *file, char *buf) {
|
||||||
|
FILE *f = NULL;
|
||||||
|
char buffer[1024] = { 0 };
|
||||||
|
char hex[3] = { 0 };
|
||||||
|
size_t bytes = 0;
|
||||||
|
sha256_t hash = { 0 };
|
||||||
|
|
||||||
|
// buf has to be at least 65 bytes.
|
||||||
|
buf[0] = 0;
|
||||||
|
|
||||||
|
f = fopen(file, "rb");
|
||||||
|
if (!f) return;
|
||||||
|
|
||||||
|
sha256_init(&hash);
|
||||||
|
while (bytes = fread(buffer, 1, sizeof(buffer), f), bytes > 0) {
|
||||||
|
sha256_update(&hash, (unsigned char *)buffer, bytes);
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
sha256_final(&hash, (unsigned char *)buffer);
|
||||||
|
|
||||||
|
for (bytes = 0; bytes < 32; bytes++) {
|
||||||
|
sprintf(hex, "%0x", (unsigned char)buffer[bytes]);
|
||||||
|
strcat(buf, hex);
|
||||||
|
}
|
||||||
|
buf[64] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
uint8_t dbFileSha256Get(char *vpath, char *value, uint32_t max) {
|
uint8_t dbFileSha256Get(char *vpath, char *value, uint32_t max) {
|
||||||
char statement[STATEMENT_MAX];
|
DbFileInfoT *info = dbFileInfoGet(vpath);
|
||||||
char *p = statement;
|
|
||||||
MYSQL_RES *result = NULL;
|
|
||||||
MYSQL_ROW row;
|
|
||||||
int count;
|
|
||||||
|
|
||||||
pthread_mutex_lock(&_mutex);
|
if (info) {
|
||||||
|
strncpy(value, info->sha, max);
|
||||||
if (!_sql) {
|
dbFileInfoRelease(&info);
|
||||||
pthread_mutex_unlock(&_mutex);
|
return SUCCESS;
|
||||||
return FAIL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p += sprintf(p, "SELECT sha256 FROM files WHERE virtualPath='");
|
return FAIL;
|
||||||
p += mysql_real_escape_string(_sql, p, vpath, strlen(vpath));
|
|
||||||
p += sprintf(p, "'");
|
|
||||||
if (mysql_real_query(_sql, statement, p - statement) != 0) {
|
|
||||||
logWrite("dbFileSha256Get: %s\n", mysql_error(_sql));
|
|
||||||
pthread_mutex_unlock(&_mutex);
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = mysql_store_result(_sql);
|
|
||||||
count = mysql_num_rows(result);
|
|
||||||
if (count != 1) {
|
|
||||||
logWrite("dbFileSha256Get: Wrong number of rows returned: %d.\n", count);
|
|
||||||
mysql_free_result(result);
|
|
||||||
pthread_mutex_unlock(&_mutex);
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((row = mysql_fetch_row(result)) == NULL) {
|
|
||||||
logWrite("dbFileSha256Get: %s\n", mysql_error(_sql));
|
|
||||||
pthread_mutex_unlock(&_mutex);
|
|
||||||
return FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
strncpy(value, row[0], max);
|
|
||||||
|
|
||||||
mysql_free_result(result);
|
|
||||||
|
|
||||||
pthread_mutex_unlock(&_mutex);
|
|
||||||
|
|
||||||
return SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -233,6 +315,8 @@ uint8_t dbTableGet(char *which, DBTableT ***table) {
|
||||||
int count;
|
int count;
|
||||||
DBTableT *record = NULL;
|
DBTableT *record = NULL;
|
||||||
|
|
||||||
|
// By "table" we mean string or number table, not a database table.
|
||||||
|
|
||||||
pthread_mutex_lock(&_mutex);
|
pthread_mutex_lock(&_mutex);
|
||||||
|
|
||||||
if (!_sql) {
|
if (!_sql) {
|
||||||
|
@ -270,6 +354,136 @@ uint8_t dbTableGet(char *which, DBTableT ***table) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t dbUpdateFileData(char *file, uint64_t len, char *time) {
|
||||||
|
DbFileInfoT *info = NULL;
|
||||||
|
char prefix[1064] = { 0 };
|
||||||
|
char *p = NULL;
|
||||||
|
unsigned char *buf[65] = { 0 };
|
||||||
|
FILE *f = NULL;
|
||||||
|
|
||||||
|
// This is the prefix we need to remove to match names in the database.
|
||||||
|
snprintf(prefix, 1064, "%s/files/", __settingsFile);
|
||||||
|
p = strstr(file, prefix);
|
||||||
|
if (!p || p != file) return FAIL;
|
||||||
|
p += strlen(prefix);
|
||||||
|
|
||||||
|
// If the date or size change, re-calculate the SHA and update the record.
|
||||||
|
info = dbFileInfoGet(p);
|
||||||
|
if (info) {
|
||||||
|
if (info->length != len || strcasecmp(info->modified, time)) {
|
||||||
|
// File changed. Update & fix SHA.
|
||||||
|
dbFileSha256Create(file, (char *)buf);
|
||||||
|
DEL(info->sha);
|
||||||
|
info->sha = strdup((char *)buf);
|
||||||
|
DEL(info->modified);
|
||||||
|
info->modified = strdup(time);
|
||||||
|
info->length = 0; // Needs updated.
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// File not in database, add it.
|
||||||
|
NEW(DbFileInfoT, info);
|
||||||
|
if (!info) return FAIL;
|
||||||
|
info->id = 0; // Indicates a new record.
|
||||||
|
info->modified = strdup(time);
|
||||||
|
info->path = strdup(p);
|
||||||
|
dbFileSha256Create(file, (char *)buf);
|
||||||
|
info->sha = strdup((char *)buf);
|
||||||
|
info->length = 0; // Needs updated.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do we need to update the length?
|
||||||
|
if (info->length == 0) {
|
||||||
|
f = fopen(file, "rb");
|
||||||
|
if (f) {
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
info->length = ftell(f);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Every time we check a file, we set "touched" to 1.
|
||||||
|
info->touched = 1;
|
||||||
|
|
||||||
|
dbFileInfoSet(info);
|
||||||
|
|
||||||
|
dbFileInfoRelease(&info);
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t dbUpdateFilesFinish(void) {
|
||||||
|
char statement[STATEMENT_MAX];
|
||||||
|
char *p = statement;
|
||||||
|
MYSQL_RES *result = NULL;
|
||||||
|
MYSQL_ROW row;
|
||||||
|
int32_t count = 0;
|
||||||
|
uint8_t status = FAIL;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&_mutex);
|
||||||
|
|
||||||
|
if (!_sql) {
|
||||||
|
pthread_mutex_unlock(&_mutex);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We double-check to be sure not everything is zero - that would be bad.
|
||||||
|
p += sprintf(p, "SELECT COUNT(id) AS total FROM files WHERE touched=1");
|
||||||
|
if (mysql_real_query(_sql, statement, p - statement) != 0) {
|
||||||
|
logWrite("dbUpdateFilesFinish: %s\n", mysql_error(_sql));
|
||||||
|
pthread_mutex_unlock(&_mutex);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = mysql_store_result(_sql);
|
||||||
|
count = mysql_num_rows(result);
|
||||||
|
|
||||||
|
if (count == 1) {
|
||||||
|
if ((row = mysql_fetch_row(result)) != NULL) {
|
||||||
|
// Once the scan is complete, anything with "touched" set to 0 no longer exists. Delete the records.
|
||||||
|
p = statement;
|
||||||
|
p += sprintf(p, "DELETE FROM files WHERE touched=0");
|
||||||
|
if (mysql_real_query(_sql, statement, p - statement) != 0) {
|
||||||
|
logWrite("dbUpdateFilesFinish: %s\n", mysql_error(_sql));
|
||||||
|
pthread_mutex_unlock(&_mutex);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mysql_free_result(result);
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&_mutex);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint8_t dbUpdateFilesStart(void) {
|
||||||
|
char statement[STATEMENT_MAX];
|
||||||
|
char *p = statement;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&_mutex);
|
||||||
|
|
||||||
|
if (!_sql) {
|
||||||
|
pthread_mutex_unlock(&_mutex);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We start by ensuring the "touched" column is zeroed.
|
||||||
|
p += sprintf(p, "UPDATE files SET touched=0");
|
||||||
|
if (mysql_real_query(_sql, statement, p - statement) != 0) {
|
||||||
|
logWrite("dbUpdateFilesStart: %s\n", mysql_error(_sql));
|
||||||
|
pthread_mutex_unlock(&_mutex);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&_mutex);
|
||||||
|
|
||||||
|
return SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
uint8_t dbUserCreate(char *first, char *last, char *user, char *pass, char *email) {
|
uint8_t dbUserCreate(char *first, char *last, char *user, char *pass, char *email) {
|
||||||
char statement[STATEMENT_MAX];
|
char statement[STATEMENT_MAX];
|
||||||
char *p = statement;
|
char *p = statement;
|
||||||
|
|
|
@ -36,11 +36,13 @@ typedef struct DBTableS {
|
||||||
|
|
||||||
uint8_t dbConnect(char *host, uint16_t port, char *database, char *user, char *password);
|
uint8_t dbConnect(char *host, uint16_t port, char *database, char *user, char *password);
|
||||||
uint8_t dbDisconnect(void);
|
uint8_t dbDisconnect(void);
|
||||||
uint8_t dbFileRealPathGet(char *vpath, char *value, uint32_t max);
|
|
||||||
uint8_t dbFileSha256Get(char *vpath, char *value, uint32_t max);
|
uint8_t dbFileSha256Get(char *vpath, char *value, uint32_t max);
|
||||||
uint8_t dbSettingsStringGet(char *host, char *key, char *value, uint32_t max);
|
uint8_t dbSettingsStringGet(char *host, char *key, char *value, uint32_t max);
|
||||||
uint8_t dbSettingsValueGet(char *host, char *key, int32_t *value);
|
uint8_t dbSettingsValueGet(char *host, char *key, int32_t *value);
|
||||||
uint8_t dbTableGet(char *which, DBTableT ***table);
|
uint8_t dbTableGet(char *which, DBTableT ***table);
|
||||||
|
uint8_t dbUpdateFileData(char *file, uint64_t len, char *time);
|
||||||
|
uint8_t dbUpdateFilesFinish(void);
|
||||||
|
uint8_t dbUpdateFilesStart(void);
|
||||||
uint8_t dbUserCreate(char *first, char *last, char *user, char *pass, char *email);
|
uint8_t dbUserCreate(char *first, char *last, char *user, char *pass, char *email);
|
||||||
uint8_t dbUserEmailExists(char *email);
|
uint8_t dbUserEmailExists(char *email);
|
||||||
uint8_t dbUserLogin(char *user, char *password);
|
uint8_t dbUserLogin(char *user, char *password);
|
||||||
|
|
|
@ -65,8 +65,8 @@ static void configRead(char *file) {
|
||||||
|
|
||||||
// String defaults.
|
// String defaults.
|
||||||
if (!_configServer) strdup("kanga.world");
|
if (!_configServer) strdup("kanga.world");
|
||||||
if (!_configDatabase) strdup("kpmpgsmkii");
|
if (!_configDatabase) strdup("kangaworld");
|
||||||
if (!_configUser) strdup("");
|
if (!_configUser) strdup("kangaworld");
|
||||||
if (!_configPassword) strdup("");
|
if (!_configPassword) strdup("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,8 @@
|
||||||
#define SUCCESS 1
|
#define SUCCESS 1
|
||||||
#define FAIL 0
|
#define FAIL 0
|
||||||
|
|
||||||
|
#define MAX_PATH 1024
|
||||||
|
|
||||||
|
|
||||||
// Declared in main.c
|
// Declared in main.c
|
||||||
extern uint8_t __running;
|
extern uint8_t __running;
|
||||||
|
|
262
server/src/update.c
Normal file
262
server/src/update.c
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
/*
|
||||||
|
* Kangaroo Punch MultiPlayer Game Server Mark II
|
||||||
|
* Copyright (C) 2020-2021 Scott Duensing
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#define _XOPEN_SOURCE 500
|
||||||
|
#include <ftw.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <strings.h>
|
||||||
|
|
||||||
|
#include "console.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "database.h"
|
||||||
|
#include "array.h"
|
||||||
|
|
||||||
|
#include "update.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef void *(*UpdateThreadT)(void *);
|
||||||
|
|
||||||
|
|
||||||
|
static pthread_t _updateThreadHandle = { 0 };
|
||||||
|
static uint8_t _updateThreadRunning = 0;
|
||||||
|
static uint8_t _updateAllRunning = 0;
|
||||||
|
|
||||||
|
|
||||||
|
static void *updateAll(void *data);
|
||||||
|
static void *updateConfig(void *data);
|
||||||
|
static void *updateFiles(void *path);
|
||||||
|
static int32_t updateFileEntry(const char *filepath, const struct stat *info, const int typeflag, struct FTW *pathinfo);
|
||||||
|
static void *updateGames(void *data);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void update(char *params) {
|
||||||
|
|
||||||
|
char *p = NULL;
|
||||||
|
pthread_attr_t updateThreadAttributes = { 0 };
|
||||||
|
UpdateThreadT updateThread = NULL;
|
||||||
|
|
||||||
|
if (params == NULL) {
|
||||||
|
consoleMessageQueue("UPDATE [what] - where [what] is:\n");
|
||||||
|
consoleMessageQueue(" ALL - Update everything.\n");
|
||||||
|
consoleMessageQueue(" CONFIG - Rebuild client configuration.\n");
|
||||||
|
consoleMessageQueue(" FILES [path] - Update file data from [path].\n");
|
||||||
|
consoleMessageQueue(" GAMES - Rebuild game database.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Is there a space in the parameters line?
|
||||||
|
p = strstr(params, " ");
|
||||||
|
if (p) {
|
||||||
|
// Replace it with zero, move P to next character for more parameters.
|
||||||
|
*p = 0;
|
||||||
|
p++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_updateThreadRunning) {
|
||||||
|
consoleMessageQueue("An update is already running. Wait until it completes.\n\r");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcasecmp(params, "ALL")) { updateThread = updateAll; _updateAllRunning = 1; }
|
||||||
|
if (!strcasecmp(params, "CONFIG")) { updateThread = updateConfig; _updateAllRunning = 0; }
|
||||||
|
if (!strcasecmp(params, "FILES")) { updateThread = updateFiles; _updateAllRunning = 0; }
|
||||||
|
if (!strcasecmp(params, "GAMES")) { updateThread = updateGames; _updateAllRunning = 0; }
|
||||||
|
|
||||||
|
if (!updateThread) {
|
||||||
|
consoleMessageQueue("Unknown UPDATE option! Type UPDATE for help.\n\r");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start update thread.
|
||||||
|
_updateThreadRunning = 1;
|
||||||
|
if (pthread_attr_init(&updateThreadAttributes) != 0) utilDie("Unable to create update thread attributes.\n");
|
||||||
|
pthread_attr_setdetachstate(&updateThreadAttributes, PTHREAD_CREATE_JOINABLE);
|
||||||
|
if (pthread_create(&_updateThreadHandle, &updateThreadAttributes, updateThread, (void *)p) != 0) utilDie("Unable to start update thread.\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void *updateAll(void *data) {
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
updateConfig(NULL);
|
||||||
|
updateFiles("/");
|
||||||
|
updateGames(NULL);
|
||||||
|
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void *updateConfig(void *data) {
|
||||||
|
DBTableT *record = NULL;
|
||||||
|
DBTableT **table = NULL;
|
||||||
|
char file[MAX_PATH] = { 0 };
|
||||||
|
FILE *f = NULL;
|
||||||
|
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
consoleMessageQueue("Updating client configuration data.\n");
|
||||||
|
|
||||||
|
snprintf(file, MAX_PATH, "%s/files/generated/client.dat", __settingsFile);
|
||||||
|
f = fopen(file, "wb");
|
||||||
|
if (f) {
|
||||||
|
// Fetch string table from DB.
|
||||||
|
if (dbTableGet("client", &table) == SUCCESS) {
|
||||||
|
while (arrlen(table)) {
|
||||||
|
record = table[0];
|
||||||
|
arrdel(table, 0);
|
||||||
|
// Write to config file that is sent to the client.
|
||||||
|
fwrite(record->name, strlen(record->name) + 1, 1, f);
|
||||||
|
fwrite(record->data, strlen(record->data) + 1, 1, f);
|
||||||
|
DEL(record->name);
|
||||||
|
DEL(record->data);
|
||||||
|
DEL(record);
|
||||||
|
}
|
||||||
|
arrfree(table);
|
||||||
|
}
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_updateAllRunning) pthread_exit(NULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void *updateFiles(void *data) {
|
||||||
|
char file[MAX_PATH] = { 0 };
|
||||||
|
char *path = (char *)data;
|
||||||
|
int32_t result = 0;
|
||||||
|
|
||||||
|
consoleMessageQueue("Updating file information starting from: %s\n", data == NULL ? "/" : (char *)data);
|
||||||
|
|
||||||
|
if (!path) {
|
||||||
|
snprintf(file, MAX_PATH, "%s/files", __settingsFile);
|
||||||
|
} else {
|
||||||
|
if (path[0] == '/') {
|
||||||
|
snprintf(file, MAX_PATH, "%s/files%s", __settingsFile, path);
|
||||||
|
} else {
|
||||||
|
snprintf(file, MAX_PATH, "%s/files/%s", __settingsFile, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dbUpdateFilesStart() == SUCCESS) {
|
||||||
|
result = nftw(file, updateFileEntry, 15, FTW_PHYS);
|
||||||
|
dbUpdateFilesFinish();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_updateAllRunning) pthread_exit(NULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int32_t updateFileEntry(const char *filepath, const struct stat *info, const int typeflag, struct FTW *pathinfo) {
|
||||||
|
// const char *const filename = filepath + pathinfo->base;
|
||||||
|
const double bytes = (double)info->st_size; // Not exact if large!
|
||||||
|
struct tm mtime;
|
||||||
|
char *target;
|
||||||
|
size_t maxlen = MAX_PATH;
|
||||||
|
ssize_t len;
|
||||||
|
char time[64];
|
||||||
|
|
||||||
|
(void)pathinfo;
|
||||||
|
|
||||||
|
localtime_r(&(info->st_mtime), &mtime);
|
||||||
|
|
||||||
|
snprintf(time, 64, "%04d-%02d-%02d %02d:%02d:%02d",
|
||||||
|
mtime.tm_year + 1900, mtime.tm_mon + 1, mtime.tm_mday,
|
||||||
|
mtime.tm_hour, mtime.tm_min, mtime.tm_sec);
|
||||||
|
|
||||||
|
consoleMessageQueue("%s", time);
|
||||||
|
|
||||||
|
if (bytes >= 1099511627776.0) {
|
||||||
|
consoleMessageQueue(" %9.3f TiB", bytes / 1099511627776.0);
|
||||||
|
} else {
|
||||||
|
if (bytes >= 1073741824.0) {
|
||||||
|
consoleMessageQueue(" %9.3f GiB", bytes / 1073741824.0);
|
||||||
|
} else {
|
||||||
|
if (bytes >= 1048576.0) {
|
||||||
|
consoleMessageQueue(" %9.3f MiB", bytes / 1048576.0);
|
||||||
|
} else {
|
||||||
|
if (bytes >= 1024.0) {
|
||||||
|
consoleMessageQueue(" %9.3f KiB", bytes / 1024.0);
|
||||||
|
} else {
|
||||||
|
consoleMessageQueue(" %9.0f B ", bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeflag == FTW_SL) {
|
||||||
|
while (1) {
|
||||||
|
target = malloc(maxlen + 1);
|
||||||
|
if (target == NULL) return ENOMEM;
|
||||||
|
len = readlink(filepath, target, maxlen);
|
||||||
|
if (len == (ssize_t) - 1) {
|
||||||
|
const int saved_errno = errno;
|
||||||
|
free(target);
|
||||||
|
return saved_errno;
|
||||||
|
}
|
||||||
|
if (len >= (ssize_t)maxlen) {
|
||||||
|
free(target);
|
||||||
|
maxlen += 1024;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
target[len] = '\0';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
consoleMessageQueue(" %s -> %s", filepath, target);
|
||||||
|
dbUpdateFileData((char *)filepath, (uint64_t)bytes, time);
|
||||||
|
free(target);
|
||||||
|
} else {
|
||||||
|
if (typeflag == FTW_SLN) {
|
||||||
|
consoleMessageQueue(" %s (dangling symlink)", filepath);
|
||||||
|
} else {
|
||||||
|
if (typeflag == FTW_F) {
|
||||||
|
consoleMessageQueue(" %s", filepath);
|
||||||
|
dbUpdateFileData((char *)filepath, (uint64_t)bytes, time);
|
||||||
|
} else {
|
||||||
|
if (typeflag == FTW_D || typeflag == FTW_DP) {
|
||||||
|
consoleMessageQueue(" %s/", filepath);
|
||||||
|
} else {
|
||||||
|
if (typeflag == FTW_DNR) {
|
||||||
|
consoleMessageQueue(" %s/ (unreadable)", filepath);
|
||||||
|
} else {
|
||||||
|
consoleMessageQueue(" %s (unknown)", filepath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
consoleMessageQueue("\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void *updateGames(void *data) {
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
consoleMessageQueue("Updating client game database.\n");
|
||||||
|
|
||||||
|
if (!_updateAllRunning) pthread_exit(NULL);
|
||||||
|
return NULL;
|
||||||
|
}
|
28
server/src/update.h
Normal file
28
server/src/update.h
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
/*
|
||||||
|
* Kangaroo Punch MultiPlayer Game Server Mark II
|
||||||
|
* Copyright (C) 2020-2021 Scott Duensing
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef UPDATE_H
|
||||||
|
#define UPDATE_H
|
||||||
|
|
||||||
|
|
||||||
|
void update(char *params);
|
||||||
|
|
||||||
|
|
||||||
|
#endif // UPDATE_H
|
Loading…
Add table
Reference in a new issue