//: Loading SubX programs from ELF binaries.
//: This will allow us to run them natively on a Linux kernel.
//: Based on https://github.com/kragen/stoneknifeforth/blob/702d2ebe1b/386.c
:(before "End Main")
assert(argc > 1);
if (is_equal(argv[1], "run")) {
// Outside of tests, traces must be explicitly requested.
if (Trace_file.is_open()) Trace_stream = new trace_stream;
trace(2, "run") << "=== Starting to run" << end();
if (argc <= 2) {
raise << "Not enough arguments provided.\n" << die();
}
reset();
cerr << std::hex;
load_elf(argv[2], argc, argv);
while (EIP < End_of_program) // weak final-gasp termination check
run_one_instruction();
raise << "executed past end of the world: " << EIP << " vs " << End_of_program << '\n' << end();
return 1;
}
:(code)
void load_elf(const string& filename, int argc, char* argv[]) {
int fd = open(filename.c_str(), O_RDONLY);
if (fd < 0) raise << filename.c_str() << ": open" << perr() << '\n' << die();
off_t size = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
uint8_t* elf_contents = static_cast<uint8_t*>(malloc(size));
if (elf_contents == NULL) raise << "malloc(" << size << ')' << perr() << '\n' << die();
ssize_t read_size = read(fd, elf_contents, size);
if (size != read_size) raise << "read → " << size << " (!= " << read_size << ')' << perr() << '\n' << die();
load_elf_contents(elf_contents, size, argc, argv);
free(elf_contents);
}
void load_elf_contents(uint8_t* elf_contents, size_t size, int argc, char* argv[]) {
uint8_t magic[5] = {0};
memcpy(magic, elf_contents, 4);
if (memcmp(magic, "\177ELF", 4) != 0)
raise << "Invalid ELF file; starts with \"" << magic << '"' << die();
if (elf_contents[4] != 1)
raise << "Only 32-bit ELF files (4-byte words; virtual addresses up to 4GB) supported.\n" << die();
if (elf_contents[5] != 1)
raise << "Only little-endian ELF files supported.\n" << die();
// unused: remaining 10 bytes of e_ident
uint32_t e_machine_type = u32_in(&elf_contents[16]);
if (e_machine_type != 0x00030002)
raise << "ELF type/machine 0x" << HEXWORD << e_machine_type << " isn't i386 executable\n" << die();
// unused: e_version. We only support version 1, and later versions will be backwards compatible.
uint32_t e_entry = u32_in(&elf_contents[24]);
uint32_t e_phoff = u32_in(&elf_contents[28]);
// unused: e_shoff
// unused: e_flags
uint32_t e_ehsize = u16_in(&elf_contents[40]);
if (e_ehsize < 52) raise << "Invalid binary; ELF header too small\n" << die();
uint32_t e_phentsize = u16_in(&elf_contents[42]);
uint32_t e_phnum = u16_in(&elf_contents[44]);
trace(90, "load") << e_phnum << " entries in the program header, each " << e_phentsize << " bytes long" << end();
// unused: e_shentsize
// unused: e_shnum
// unused: e_shstrndx
set<uint32_t> overlap; // to detect overlapping segments
for (size_t i = 0; i < e_phnum; ++i)
load_segment_from_program_header(elf_contents, i, size, e_phoff + i*e_phentsize, e_ehsize, overlap);
// initialize code and stack
assert(overlap.find(STACK_SEGMENT) == overlap.end());
Mem.push_back(vma(STACK_SEGMENT));
assert(overlap.find(AFTER_STACK) == overlap.end());
// The stack grows downward.
Reg[ESP].u = AFTER_STACK;
Reg[EBP].u = 0;
EIP = pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight .c { color: #888888 } /* Comment */
.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */
.highlight .k { color: #008800; font-weight: bold } /* Keyword */
.highlight .ch { color: #888888 } /* Comment.Hashbang */
.highlight .cm { color: #888888 } /* Comment.Multiline */
.highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */
.highlight .cpf { color: #888888 } /* Comment.PreprocFile */
.highlight .c1 { color: #888888 } /* Comment.Single */
.highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */
.highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #aa0000 } /* Generic.Error */
.highlight .gh { color: #333333 } /* Generic.Heading */
.highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
.highlight .go { color: #888888 } /* Generic.Output */
.highlight .gp { color: #555555 } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #666666 } /* Generic.Subheading */
.highlight .gt { color: #aa0000 } /* Generic.Traceback */
.highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008800 } /* Keyword.Pseudo */
.highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */
.highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */
.highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */
.highlight .na { color: #336699 } /* Name.Attribute */
.highlight .nb { color: #003388 } /* Name.Builtin */
.highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */
.highlight .no { color: #003366; font-weight: bold } /* Name.Constant */
.highlight .nd { color: #555555 } /* Name.Decorator */
.highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */
.highlight .nl { color: #336699; font-style: italic } /* Name.Label */
.highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */
.highlight .py { color: #336699; font-weight: bold } /* Name.Property */
.highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #336699 } /* Name.Variable */
.highlight .ow { color: #008800 } /* Operator.Word */
.highlight .w { color: #bbbbbb } /* Text.Whitespace */
.highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */
.highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */
.highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */
.highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */
.highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */
.highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */
.highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */
.highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */
.highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */
.highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */
.highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */
.highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */
.highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */
.highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */
.highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */
.highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */
.highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */
.highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */
.highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */
.highlight .vc { color: #336699 } /* Name.Variable.Class */
.highlight .vg { color: #dd7700 } /* Name.Variable.Global */
.highlight .vi { color: #3333bb } /* Name.Variable.Instance */
.highlight .vm { color: #336699 } /* Name.Variable.Magic */
.highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long *//*
* log.c
*
* Copyright (C) 2012, 2013 James Booth <boothj5@gmail.com>
*
* This file is part of Profanity.
*
* Profanity 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.
*
* Profanity 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 Profanity. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "glib.h"
#include "log.h"
#include "common.h"
#include "config/preferences.h"
#define PROF "prof"
static FILE *logp;
static GTimeZone *tz;
static GDateTime *dt;
static log_level_t level_filter;
static GHashTable *logs;
static GHashTable *groupchat_logs;
static GDateTime *session_started;
struct dated_chat_log {
gchar *filename;
GDateTime *date;
};
static gboolean _log_roll_needed(struct dated_chat_log *dated_log);
static struct dated_chat_log * _create_log(char *other, const char * const login);
static struct dated_chat_log * _create_groupchat_log(char *room, const char * const login);
static void _free_chat_log(struct dated_chat_log *dated_log);
static gboolean _key_equals(void *key1, void *key2);
static char * _get_log_filename(const char * const other, const char * const login,
GDateTime *dt, gboolean create);
static char * _get_groupchat_log_filename(const char * const room,
const char * const login, GDateTime *dt, gboolean create);
static gchar * _get_chatlog_dir(void);
static gchar * _get_log_file(void);
static void _rotate_log_file(void);
void
log_debug(const char * const msg, ...)
{
va_list arg;
va_start(arg, msg);
GString *fmt_msg = g_string_new(NULL);
g_string_vprintf(fmt_msg, msg, arg);
log_msg(PROF_LEVEL_DEBUG, PROF, fmt_msg->str);
g_string_free(fmt_msg, TRUE);
va_end(arg);
}
void
log_info(const char * const msg, ...)
{
va_list arg;
va_start(arg, msg);
GString *fmt_msg = g_string_new(NULL);
g_string_vprintf(fmt_msg, msg, arg);
log_msg(PROF_LEVEL_INFO, PROF, fmt_msg->str);
g_string_free(fmt_msg, TRUE);
va_end(arg);
}
void
log_warning(const char * const msg, ...)
{
va_list arg;
va_start(arg, msg);
GString *fmt_msg = g_string_new(NULL);
g_string_vprintf(fmt_msg, msg, arg);
log_msg(PROF_LEVEL_WARN, PROF, fmt_msg->str);
g_string_free(fmt_msg, TRUE);
va_end(arg);
}
void
log_error(const char * const msg, ...)
{
va_list arg;
va_start(arg, msg);
GString *fmt_msg = g_string_new(NULL);
g_string_vprintf(fmt_msg, msg, arg);
log_msg(PROF_LEVEL_ERROR, PROF, fmt_msg->str);
g_string_free(fmt_msg, TRUE);
va_end(arg);
}
void
log_init(log_level_t filter)
{
level_filter = filter;
tz = g_time_zone_new_local();
gchar *log_file = _get_log_file();
logp = fopen(log_file, "a");
free(log_file);
}
log_level_t
log_get_filter(void)
{
return level_filter;
}
void
log_close(void)
{
g_time_zone_unref(tz);
if (logp != NULL) {
fclose(logp);
}
}
void
log_msg(log_level_t level, const char * const area, const char * const msg)
{
if (level >= level_filter && logp != NULL) {
long result;
dt = g_date_time_new_now(tz);
gchar *date_fmt = g_date_time_format(dt, "%d/%m/%Y %H:%M:%S");
fprintf(logp, "%s: %s: %s\n", date_fmt, area, msg);
g_date_time_unref(dt);
fflush(logp);
g_free(date_fmt);
result = ftell(logp);
if (result != -1 && result >= prefs_get_max_log_size()) {
_rotate_log_file();
}
}
}
log_level_t
log_level_from_string(char *log_level)
{
assert(log_level != NULL);
if (strcmp(log_level, "DEBUG") == 0) {
return PROF_LEVEL_DEBUG;
} else if (strcmp(log_level, "INFO") == 0) {
return PROF_LEVEL_INFO;
} else if (strcmp(log_level, "WARN") == 0) {
return PROF_LEVEL_WARN;
} else if (strcmp(log_level, "ERROR") == 0) {
return PROF_LEVEL_ERROR;
} else { // default to info
return PROF_LEVEL_INFO;
}
}
static void
_rotate_log_file(void)
{
gchar *log_file = _get_log_file();
size_t len = strlen(log_file);
char *log_file_new = malloc(len + 3);
strncpy(log_file_new, log_file, len);
log_file_new[len] = '.';
log_file_new[len+1] = '1';
log_file_new[len+2] = 0;
log_close();
rename(log_file, log_file_new);
log_init(log_get_filter());
free(log_file_new);
free(log_file);
log_info("Log has been rotated");
}
void
chat_log_init(void)
{
session_started = g_date_time_new_now_local();
log_info("Initialising chat logs");
logs = g_hash_table_new_full(g_str_hash, (GEqualFunc) _key_equals, g_free,
(GDestroyNotify)_free_chat_log);
}
void
groupchat_log_init(void)
{
log_info("Initialising groupchat logs");
groupchat_logs = g_hash_table_new_full(g_str_hash, (GEqualFunc) _key_equals, g_free,
(GDestroyNotify)_free_chat_log);
}
void
chat_log_chat(const gchar * const login, gchar *other,
const gchar * const msg, chat_log_direction_t direction, GTimeVal *tv_stamp)
{
struct dated_chat_log *dated_log = g_hash_table_lookup(logs, other);
// no log for user
if (dated_log == NULL) {
dated_log = _create_log(other, login);
g_hash_table_insert(logs, strdup(other), dated_log);
// log exists but needs rolling
} else if (_log_roll_needed(dated_log)) {
dated_log = _create_log(other, login);
g_hash_table_replace(logs, strdup(other), dated_log);
}
gchar *date_fmt = NULL;
GDateTime *dt = NULL;
if (tv_stamp == NULL) {
dt = g_date_time_new_now_local();
} else {
dt = g_date_time_new_from_timeval_utc(tv_stamp);
}
date_fmt = g_date_time_format(dt, "%H:%M:%S");
FILE *logp = fopen(dated_log->filename, "a");
if (logp != NULL) {
if (direction == PROF_IN_LOG) {
if (strncmp(msg, "/me ", 4) == 0) {
fprintf(logp, "%s - *%s %s\n", date_fmt, other, msg + 4);
} else {
fprintf(logp, "%s - %s: %s\n", date_fmt, other, msg);
}
} else {
if (strncmp(msg, "/me ", 4) == 0) {
fprintf(logp, "%s - *me %s\n", date_fmt, msg + 4);
} else {
fprintf(logp, "%s - me: %s\n", date_fmt, msg);
}
}
fflush(logp);
int result = fclose(logp);
if (result == EOF) {
log_error("Error closing file %s, errno = %d", dated_log->filename, errno);
}
}
g_free(date_fmt);
g_date_time_unref(dt);
}
void
groupchat_log_chat(const gchar * const login, const gchar * const room,
const gchar * const nick, const gchar * const msg)
{
gchar *room_copy = strdup(room);
struct dated_chat_log *dated_log = g_hash_table_lookup(groupchat_logs, room_copy);
// no log for room
if (dated_log == NULL) {
dated_log = _create_groupchat_log(room_copy, login);
g_hash_table_insert(groupchat_logs, room_copy, dated_log);
// log exists but needs rolling
} else if (_log_roll_needed(dated_log)) {
dated_log = _create_groupchat_log(room_copy, login);
g_hash_table_replace(logs, room_copy, dated_log);
}
GDateTime *dt = g_date_time_new_now_local();
gchar *date_fmt = g_date_time_format(dt, "%H:%M:%S");
FILE *logp = fopen(dated_log->filename, "a");
if (logp != NULL) {
if (strncmp(msg, "/me ", 4) == 0) {
fprintf(logp, "%s - *%s %s\n", date_fmt, nick, msg + 4);
} else {
fprintf(logp, "%s - %s: %s\n", date_fmt, nick, msg);
}
fflush(logp);
int result = fclose(logp);
if (result == EOF) {
log_error("Error closing file %s, errno = %d", dated_log->filename, errno);
}
}
g_free(date_fmt);
g_date_time_unref(dt);
}
GSList *
chat_log_get_previous(const gchar * const login, const gchar * const recipient,
GSList *history)
{
GDateTime *now = g_date_time_new_now_local();
GDateTime *log_date = g_date_time_new(tz,
g_date_time_get_year(session_started),
g_date_time_get_month(session_started),
g_date_time_get_day_of_month(session_started),
g_date_time_get_hour(session_started),
g_date_time_get_minute(session_started),
g_date_time_get_second(session_started));
// get data from all logs from the day the session was started to today
while (g_date_time_compare(log_date, now) != 1) {
char *filename = _get_log_filename(recipient, login, log_date, FALSE);
FILE *logp = fopen(filename, "r");
char *line;
if (logp != NULL) {
GString *gs_header = g_string_new("");
g_string_append_printf(gs_header, "%d/%d/%d:",
g_date_time_get_day_of_month(log_date),
g_date_time_get_month(log_date),
g_date_time_get_year(log_date));
char *header = strdup(gs_header->str);
history = g_slist_append(history, header);
g_string_free(gs_header, TRUE);
while ((line = prof_getline(logp)) != NULL) {
history = g_slist_append(history, line);
}
fclose(logp);
}
free(filename);
GDateTime *next = g_date_time_add_days(log_date, 1);
g_date_time_unref(log_date);
log_date = g_date_time_ref(next);
}
return history;
}
void
chat_log_close(void)
{
g_hash_table_remove_all(logs);
g_hash_table_remove_all(groupchat_logs);
g_date_time_unref(session_started);
}
static struct dated_chat_log *
_create_log(char *other, const char * const login)
{
GDateTime *now = g_date_time_new_now_local();
char *filename = _get_log_filename(other, login, now, TRUE);
struct dated_chat_log *new_log = malloc(sizeof(struct dated_chat_log));
new_log->filename = strdup(filename);
new_log->date = now;
free(filename);
return new_log;
}
static struct dated_chat_log *
_create_groupchat_log(char *room, const char * const login)
{
GDateTime *now = g_date_time_new_now_local();
char *filename = _get_groupchat_log_filename(room, login, now, TRUE);
struct dated_chat_log *new_log = malloc(sizeof(struct dated_chat_log));
new_log->filename = strdup(filename);
new_log->date = now;
free(filename);
return new_log;
}
static gboolean
_log_roll_needed(struct dated_chat_log *dated_log)
{
gboolean result = FALSE;
GDateTime *now = g_date_time_new_now_local();
if (g_date_time_get_day_of_year(dated_log->date) !=
g_date_time_get_day_of_year(now)) {
result = TRUE;
}
g_date_time_unref(now);
return result;
}
static void
_free_chat_log(struct dated_chat_log *dated_log)
{
if (dated_log != NULL) {
if (dated_log->filename != NULL) {
g_free(dated_log->filename);
dated_log->filename = NULL;
}
if (dated_log->date != NULL) {
g_date_time_unref(dated_log->date);
dated_log->date = NULL;
}
free(dated_log);
}
}
static
gboolean _key_equals(void *key1, void *key2)
{
gchar *str1 = (gchar *) key1;
gchar *str2 = (gchar *) key2;
return (g_strcmp0(str1, str2) == 0);
}
static char *
_get_log_filename(const char * const other, const char * const login,
GDateTime *dt, gboolean create)
{
gchar *chatlogs_dir = _get_chatlog_dir();
GString *log_file = g_string_new(chatlogs_dir);
free(chatlogs_dir);
gchar *login_dir = str_replace(login, "@", "_at_");
g_string_append_printf(log_file, "/%s", login_dir);
if (create) {
create_dir(log_file->str);
}
free(login_dir);
gchar *other_file = str_replace(other, "@", "_at_");
g_string_append_printf(log_file, "/%s", other_file);
if (create) {
create_dir(log_file->str);
}
free(other_file);
gchar *date = g_date_time_format(dt, "/%Y_%m_%d.log");
g_string_append(log_file, date);
g_free(date);
char *result = strdup(log_file->str);
g_string_free(log_file, TRUE);
return result;
}
static char *
_get_groupchat_log_filename(const char * const room, const char * const login,
GDateTime *dt, gboolean create)
{
gchar *chatlogs_dir = _get_chatlog_dir();
GString *log_file = g_string_new(chatlogs_dir);
g_free(chatlogs_dir);
gchar *login_dir = str_replace(login, "@", "_at_");
g_string_append_printf(log_file, "/%s", login_dir);
if (create) {
create_dir(log_file->str);
}
free(login_dir);
g_string_append(log_file, "/rooms");
if (create) {
create_dir(log_file->str);
}
gchar *room_file = str_replace(room, "@", "_at_");
g_string_append_printf(log_file, "/%s", room_file);
if (create) {
create_dir(log_file->str);
}
free(room_file);
gchar *date = g_date_time_format(dt, "/%Y_%m_%d.log");
g_string_append(log_file, date);
g_free(date);
char *result = strdup(log_file->str);
g_string_free(log_file, TRUE);
return result;
}
static gchar *
_get_chatlog_dir(void)
{
gchar *xdg_data = xdg_get_data_home();
GString *chatlogs_dir = g_string_new(xdg_data);
g_string_append(chatlogs_dir, "/profanity/chatlogs");
gchar *result = strdup(chatlogs_dir->str);
free(xdg_data);
g_string_free(chatlogs_dir, TRUE);
return result;
}
static gchar *
_get_log_file(void)
{
gchar *xdg_data = xdg_get_data_home();
GString *logfile = g_string_new(xdg_data);
g_string_append(logfile, "/profanity/logs/profanity.log");
gchar *result = strdup(logfile->str);
free(xdg_data);
g_string_free(logfile, TRUE);
return result;
}