Files
2026-05-18 20:43:01 -07:00

443 lines
12 KiB
C

/*
*
* DBGLOGGER - debug logger library / (c) 2019 El Bucanero <www.bucanero.com.ar>
*
* Small library for network and local file debug logging in PSL1GHT/Open Orbis SDKs.
*
*/
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
#include <unistd.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <time.h>
#define netConnect connect
#define netClose close
#define netSend send
#define netSocket socket
#define netInitialize(...)
#define netDeinitialize(...)
#include "dbglogger.h"
typedef enum {
ENCODE_BASE64,
ENCODE_UUENCODE
} B64ENC_MODES;
static int loggerMode = NO_LOGGER;
static int socketFD;
static char logFile[256];
#define UDP_INI_STR "udp"
#define TCP_INI_STR "tcp"
#define FILE_INI_STR "file"
#define TTY_INI_STR "tty"
#define DEBUG_PORT 18194
#define BASE64_LENGTH(X) (4 * ((X + 2) / 3))
#define B64_SRC_BUF_SIZE 45 // This *MUST* be a multiple of 3
#define B64_DST_BUF_SIZE BASE64_LENGTH(B64_SRC_BUF_SIZE)
static const char encode_table[2][65] = {
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
"`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`"};
/*
* Copyright (C) 2000 by Glenn McGrath
*
* based on the function base64_encode from http.c in wget v1.6
* Copyright (C) 1995, 1996, 1997, 1998, 2000 Free Software Foundation, Inc.
*
* Encode the string S of length LENGTH to base64 format and place it
* to STORE. STORE will be 0-terminated, and must point to a writable
* buffer of at least 1+BASE64_LENGTH(length) bytes.
* where BASE64_LENGTH(len) = (4 * ((LENGTH + 2) / 3))
*/
static void uuencode(const unsigned char *s, char *store, const int length, const char *tbl)
{
int i;
unsigned char *p = (unsigned char *)store;
/* Transform the 3x8 bits to 4x6 bits, as required by base64. */
for (i = 0; i < length; i += 3) {
*p++ = tbl[s[0] >> 2];
*p++ = tbl[((s[0] & 0x03) << 4) + (s[1] >> 4)];
*p++ = tbl[((s[1] & 0x0f) << 2) + (s[2] >> 6)];
*p++ = tbl[s[2] & 0x3f];
s += 3;
}
/* Pad the result if necessary... */
if (i == length + 1) {
*(p - 1) = tbl[64];
}
else if (i == length + 2) {
*(p - 1) = *(p - 2) = tbl[64];
}
/* ...and zero-terminate it. */
*p = '\0';
}
static int dbglogger_base64(const char *filename, const unsigned int encode)
{
FILE *src_stream;
size_t size;
unsigned char *src_buf;
char *dst_buf;
src_stream = fopen(filename, "rb");
if (!src_stream) {
return(0);
}
src_buf = malloc(B64_SRC_BUF_SIZE + 1);
dst_buf = malloc(B64_DST_BUF_SIZE + 1);
dbglogger_printf("begin%s %o %s", encode == ENCODE_UUENCODE ? "" : "-base64", 0644, strrchr(filename, '/')+1);
while ((size = fread(src_buf, 1, B64_SRC_BUF_SIZE, src_stream)) > 0) {
if (size != B64_SRC_BUF_SIZE) {
/* pad with 0s so we can just encode extra bits */
memset(&src_buf[size], 0, B64_SRC_BUF_SIZE - size);
}
/* Encode the buffer we just read in */
uuencode(src_buf, dst_buf, size, encode_table[encode]);
switch (encode) {
case ENCODE_BASE64:
dbglogger_printf("\n%s", dst_buf);
break;
case ENCODE_UUENCODE:
dbglogger_printf("\n%c%s", encode_table[encode][size], dst_buf);
break;
}
}
dbglogger_printf(encode == ENCODE_UUENCODE ? "\n`\nend\n" : "\n====\n");
free(src_buf);
free(dst_buf);
fclose(src_stream);
return(1);
}
int dbglogger_b64encode(const char *filename)
{
return dbglogger_base64(filename, ENCODE_BASE64);
}
char* dbg_base64_encode(const unsigned char *data, int data_len)
{
char* out = malloc(BASE64_LENGTH(data_len) + 1);
if (!out)
return NULL;
uuencode(data, out, data_len, encode_table[ENCODE_BASE64]);
return (out);
}
/*
* base64_decode - Base64 decode
* @src: Data to be decoded
* @out_len: Pointer to output length variable
* Returns: Allocated buffer of out_len bytes of decoded data, or NULL on failure
*
* Caller is responsible for freeing the returned buffer.
*/
unsigned char * dbg_base64_decode(const char *src, size_t *out_len)
{
unsigned char dtable[256], *out, *pos, block[4], tmp;
size_t i, count, len = strlen(src);
int pad = 0;
memset(dtable, 0x80, 256);
for (i = 0; i < sizeof(encode_table[ENCODE_BASE64]) - 1; i++)
dtable[(unsigned char)encode_table[ENCODE_BASE64][i]] = (unsigned char) i;
dtable['='] = 0;
count = 0;
for (i = 0; i < len; i++) {
if (dtable[(unsigned char)src[i]] != 0x80)
count++;
}
if (count == 0 || count % 4)
return NULL;
pos = out = malloc(count / 4 * 3);
if (out == NULL)
return NULL;
count = 0;
for (i = 0; i < len; i++) {
tmp = dtable[(unsigned char)src[i]];
if (tmp == 0x80)
continue;
if (src[i] == '=')
pad++;
block[count] = tmp;
count++;
if (count == 4) {
*pos++ = (block[0] << 2) | (block[1] >> 4);
*pos++ = (block[1] << 4) | (block[2] >> 2);
*pos++ = (block[2] << 6) | block[3];
count = 0;
if (pad) {
if (pad == 1)
pos--;
else if (pad == 2)
pos -= 2;
else {
// Invalid padding
free(out);
return NULL;
}
break;
}
}
}
*out_len = pos - out;
return out;
}
#ifdef __PPU__
// check if we receive a connection and kill the process
static void debug_netkill_thread(void *port)
{
struct sockaddr_in sa;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons(strtoul(port, NULL, 0));
sa.sin_addr.s_addr = htonl(INADDR_ANY);
int list_s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if ((bind(list_s, (struct sockaddr *)&sa, sizeof(sa)) == -1) || (listen(list_s, 4) == -1))
{
return;
}
while(accept(list_s, NULL, NULL) <= 0)
{
usleep(1000*1000);
}
shutdown(list_s, SHUT_RDWR);
dbglogger_stop();
sysProcessExit(1);
}
// check if the file exists and kill the process
static void debug_kill_thread(void* path)
{
struct stat sb;
while ((stat((char*) path, &sb) != 0) || !S_ISREG(sb.st_mode))
{
usleep(1000*1000);
}
chmod((char*) path, 0777);
sysLv2FsUnlink((char*) path);
dbglogger_stop();
sysProcessExit(1);
}
int dbglogger_failsafe(const char* fpath)
{
sys_ppu_thread_t tid;
return sysThreadCreate(&tid, (fpath[0] == '/' ? debug_kill_thread : debug_netkill_thread), (void*) fpath, 1000, 16*1024, THREAD_JOINABLE, "debug_wait");
}
#endif
static void networkInit(const char* dbglog_ip, const unsigned short dbglog_port) {
struct sockaddr_in stSockAddr;
memset(&stSockAddr, 0, sizeof(stSockAddr));
stSockAddr.sin_family = AF_INET;
stSockAddr.sin_port = htons(dbglog_port);
#ifdef __PS2__
stSockAddr.sin_addr.s_addr = inet_addr(dbglog_ip);
#else
inet_pton(AF_INET, dbglog_ip, &stSockAddr.sin_addr);
#endif
netConnect(socketFD, (struct sockaddr *)&stSockAddr, sizeof(stSockAddr));
}
static int logFileInit(const char* file_path, unsigned short overwrite) {
snprintf(logFile, sizeof(logFile), "%s", file_path);
FILE *fp = fopen(logFile, overwrite ? "w" : "a");
if (fp) {
fclose(fp);
} else {
loggerMode = NO_LOGGER;
}
return(loggerMode);
}
static void fileLog(const char* str) {
FILE *fp = fopen(logFile, "a");
if (fp) {
fputs(str, fp);
fclose(fp);
}
}
void dbglogger_printf(const char* fmt, ...) {
if (loggerMode) {
char buffer[0x800];
va_list arg;
va_start(arg, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, arg);
va_end(arg);
switch (loggerMode) {
case UDP_LOGGER:
case TCP_LOGGER:
netSend(socketFD, buffer, strlen(buffer), 0);
break;
case FILE_LOGGER:
fileLog(buffer);
break;
case TTY_LOGGER:
printf("%s", buffer); // puts always append newline
break;
}
}
}
void dbglogger_log(const char* fmt, ...) {
if (loggerMode) {
char buffer[0x800];
va_list arg;
va_start(arg, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, arg);
va_end(arg);
#ifdef __PSP__
ScePspDateTime t;
sceRtcGetCurrentClockLocalTime(&t);
dbglogger_printf("[%d-%02d-%02d %02d:%02d:%02d] %s\n", t.year, t.month, t.day, t.hour, t.minute, t.second, buffer);
#else
struct tm t = *gmtime(&(time_t){time(NULL)});
dbglogger_printf("[%d-%02d-%02d %02d:%02d:%02d] %s\n", t.tm_year+1900, t.tm_mon+1, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, buffer);
#endif
}
}
int dbglogger_init_mode(const unsigned char log_mode, const char* dest, const unsigned short port) {
loggerMode = log_mode;
switch (log_mode) {
case UDP_LOGGER:
netInitialize();
socketFD = netSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
networkInit(dest, port);
dbglogger_log("------ UDP (%s:%d) network debug logger initialized -----", dest, port);
break;
case TCP_LOGGER:
netInitialize();
socketFD = netSocket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
networkInit(dest, port);
dbglogger_log("------ TCP (%s:%d) network debug logger initialized -----", dest, port);
break;
case FILE_LOGGER:
if (logFileInit(dest, port))
dbglogger_log("----- File (%s) debug logger initialized -----", dest);
break;
case TTY_LOGGER:
dbglogger_log("------ TTY logger initialized ------");
break;
default:
loggerMode = NO_LOGGER;
// Logging disabled
break;
}
return(loggerMode);
}
int dbglogger_init_str(const char* ini_str) {
char str[128];
strcpy(str, ini_str);
char *mode = strtok(str, ":");
char *data = strtok(NULL, ":");
char *tmp = strtok(NULL, ":");
unsigned short port = DEBUG_PORT;
if (tmp)
port = strtoul(tmp, NULL, 0);
if (strcmp(mode, UDP_INI_STR) == 0) {
return dbglogger_init_mode(UDP_LOGGER, data, port);
} else
if (strcmp(mode, TCP_INI_STR) == 0) {
return dbglogger_init_mode(TCP_LOGGER, data, port);
} else
if (strcmp(mode, FILE_INI_STR) == 0) {
return dbglogger_init_mode(FILE_LOGGER, data, 0);
} else
if (strcmp(mode, TTY_INI_STR) == 0) {
return dbglogger_init_mode(TTY_LOGGER, NULL, 0);
}
return(NO_LOGGER);
}
int dbglogger_init_file(const char* ini_file) {
char str[128];
FILE *fp = fopen(ini_file, "r");
if (fp) {
fgets(str, sizeof(str), fp);
fclose(fp);
return(dbglogger_init_str(str));
}
return(NO_LOGGER);
}
int dbglogger_init(void) {
return(dbglogger_init_str(DEFAULT_LOG_INIT));
}
int dbglogger_stop(void) {
switch (loggerMode) {
case UDP_LOGGER:
case TCP_LOGGER:
dbglogger_log("------ network debug logger terminated -----");
netClose(socketFD);
netDeinitialize();
break;
case FILE_LOGGER:
dbglogger_log("------ file debug logger terminated -----");
break;
default:
// Logging disabled
break;
}
loggerMode = NO_LOGGER;
return(loggerMode);
}