From 707fb62c04db12c04f075e170f28879fe23fcd46 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Fri, 4 Oct 2024 12:54:49 +0200 Subject: [PATCH 01/12] log-driver.php: split up log driver --- inc/Data/Driver/ErrorLogLogDriver.php | 28 +++++++++++++ inc/Data/Driver/FileLogDriver.php | 60 +++++++++++++++++++++++++++ inc/Data/Driver/LogDriver.php | 22 ++++++++++ inc/Data/Driver/LogTrait.php | 26 ++++++++++++ inc/Data/Driver/StderrLogDriver.php | 27 ++++++++++++ inc/Data/Driver/SyslogLogDriver.php | 35 ++++++++++++++++ 6 files changed, 198 insertions(+) create mode 100644 inc/Data/Driver/ErrorLogLogDriver.php create mode 100644 inc/Data/Driver/FileLogDriver.php create mode 100644 inc/Data/Driver/LogDriver.php create mode 100644 inc/Data/Driver/LogTrait.php create mode 100644 inc/Data/Driver/StderrLogDriver.php create mode 100644 inc/Data/Driver/SyslogLogDriver.php diff --git a/inc/Data/Driver/ErrorLogLogDriver.php b/inc/Data/Driver/ErrorLogLogDriver.php new file mode 100644 index 00000000..e2050606 --- /dev/null +++ b/inc/Data/Driver/ErrorLogLogDriver.php @@ -0,0 +1,28 @@ +name = $name; + $this->level = $level; + } + + public function log(int $level, string $message): void { + if ($level <= $this->level) { + $lv = $this->levelToString($level); + $line = "{$this->name} $lv: $message"; + \error_log($line, 0, null, null); + } + } +} diff --git a/inc/Data/Driver/FileLogDriver.php b/inc/Data/Driver/FileLogDriver.php new file mode 100644 index 00000000..18423cf1 --- /dev/null +++ b/inc/Data/Driver/FileLogDriver.php @@ -0,0 +1,60 @@ +fd = \fopen($file_path, 'a'); + if ($this->fd === false) { + throw new \RuntimeException("Unable to open log file at $file_path"); + } + + $this->name = $name; + $this->level = $level; + + // In some cases PHP does not run the destructor. + \register_shutdown_function([$this, 'close']); + } + + public function __destruct() { + $this->close(); + } + + public function log(int $level, string $message): void { + if ($level <= $this->level) { + $lv = $this->levelToString($level); + $line = "{$this->name} $lv: $message\n"; + \flock($this->fd, LOCK_EX); + \fwrite($this->fd, $line); + \flock($this->fd, LOCK_UN); + } + } + + public function close() { + \flock($this->fd, LOCK_UN); + \fclose($this->fd); + } +} diff --git a/inc/Data/Driver/LogDriver.php b/inc/Data/Driver/LogDriver.php new file mode 100644 index 00000000..fddc3f27 --- /dev/null +++ b/inc/Data/Driver/LogDriver.php @@ -0,0 +1,22 @@ +name = $name; + $this->level = $level; + } + + public function log(int $level, string $message): void { + if ($level <= $this->level) { + $lv = $this->levelToString($level); + \fwrite(\STDERR, "{$this->name} $lv: $message\n"); + } + } +} diff --git a/inc/Data/Driver/SyslogLogDriver.php b/inc/Data/Driver/SyslogLogDriver.php new file mode 100644 index 00000000..c0df5304 --- /dev/null +++ b/inc/Data/Driver/SyslogLogDriver.php @@ -0,0 +1,35 @@ +level = $level; + } + + public function log(int $level, string $message): void { + if ($level <= $this->level) { + if (isset($_SERVER['REMOTE_ADDR'], $_SERVER['REQUEST_METHOD'], $_SERVER['REQUEST_URI'])) { + // CGI + \syslog($level, "$message - client: {$_SERVER['REMOTE_ADDR']}, request: \"{$_SERVER['REQUEST_METHOD']} {$_SERVER['REQUEST_URI']}\""); + } else { + \syslog($level, $message); + } + } + } +} From 79523f8251fa2fde9bbc582ce73893a9cb0def7f Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 19 Dec 2024 00:23:39 +0100 Subject: [PATCH 02/12] context.php: initial add LogDriver --- inc/context.php | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/inc/context.php b/inc/context.php index 3207d7ea..75c107d0 100644 --- a/inc/context.php +++ b/inc/context.php @@ -3,6 +3,7 @@ namespace Vichan; use Vichan\Data\Driver\CacheDriver; use Vichan\Data\{IpNoteQueries, ReportQueries, UserPostQueries}; +use Vichan\Data\Driver\{LogDriver, LogDrivers}; defined('TINYBOARD') or exit; @@ -31,6 +32,26 @@ class Context { function build_context(array $config): Context { return new Context([ 'config' => $config, + LogDriver::class => function($c) { + $config = $c->get('config'); + + $name = $config['log_system']['name']; + $level = $config['debug'] ? LogDriver::DEBUG : LogDriver::NOTICE; + $backend = $config['log_system']['type']; + + // Check 'syslog' for backwards compatibility. + if ((isset($config['syslog']) && $config['syslog']) || $backend === 'syslog') { + return LogDrivers::syslog($name, $level, $this->config['log_system']['syslog_stderr']); + } elseif ($backend === 'file') { + return LogDrivers::file($name, $level, $this->config['log_system']['file_path']); + } elseif ($backend === 'stderr') { + return LogDrivers::stderr($name, $level); + } elseif ($backend === 'none') { + return LogDrivers::none(); + } else { + return LogDrivers::error_log($name, $level); + } + }, CacheDriver::class => function($c) { // Use the global for backwards compatibility. return \cache::getCache(); From 6132084b4b7a567e746a39eec4f24142b57502c8 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Fri, 4 Oct 2024 12:57:37 +0200 Subject: [PATCH 03/12] context.php: update LogDriver --- inc/context.php | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/inc/context.php b/inc/context.php index 75c107d0..933b5e5f 100644 --- a/inc/context.php +++ b/inc/context.php @@ -1,9 +1,8 @@ config['log_system']['syslog_stderr']); + return new SyslogLogDriver($name, $level, $this->config['log_system']['syslog_stderr']); } elseif ($backend === 'file') { - return LogDrivers::file($name, $level, $this->config['log_system']['file_path']); + return new FileLogDriver($name, $level, $this->config['log_system']['file_path']); } elseif ($backend === 'stderr') { - return LogDrivers::stderr($name, $level); - } elseif ($backend === 'none') { - return LogDrivers::none(); + return new StderrLogDriver($name, $level); } else { - return LogDrivers::error_log($name, $level); + return new ErrorLogLogDriver($name, $level); } }, CacheDriver::class => function($c) { From fe4813867b757eef27279af9eaa5ccc5235147ba Mon Sep 17 00:00:00 2001 From: Zankaria Date: Wed, 11 Dec 2024 15:18:26 +0100 Subject: [PATCH 04/12] context.php: fix log init --- inc/context.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/inc/context.php b/inc/context.php index 933b5e5f..f451034d 100644 --- a/inc/context.php +++ b/inc/context.php @@ -40,9 +40,9 @@ function build_context(array $config): Context { // Check 'syslog' for backwards compatibility. if ((isset($config['syslog']) && $config['syslog']) || $backend === 'syslog') { - return new SyslogLogDriver($name, $level, $this->config['log_system']['syslog_stderr']); + return new SyslogLogDriver($name, $level, $config['log_system']['syslog_stderr']); } elseif ($backend === 'file') { - return new FileLogDriver($name, $level, $this->config['log_system']['file_path']); + return new FileLogDriver($name, $level, $config['log_system']['file_path']); } elseif ($backend === 'stderr') { return new StderrLogDriver($name, $level); } else { From 2c0c003b2cbc64a99bf726d1cf5be6f2e51fc0b6 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Wed, 11 Dec 2024 15:34:51 +0100 Subject: [PATCH 05/12] context.php: report deprecation notice on syslog option --- inc/context.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/inc/context.php b/inc/context.php index f451034d..631483e7 100644 --- a/inc/context.php +++ b/inc/context.php @@ -38,9 +38,15 @@ function build_context(array $config): Context { $level = $config['debug'] ? LogDriver::DEBUG : LogDriver::NOTICE; $backend = $config['log_system']['type']; + $legacy_syslog = isset($config['syslog']) && $config['syslog']; + // Check 'syslog' for backwards compatibility. - if ((isset($config['syslog']) && $config['syslog']) || $backend === 'syslog') { - return new SyslogLogDriver($name, $level, $config['log_system']['syslog_stderr']); + if ($legacy_syslog || $backend === 'syslog') { + $log_driver = new SyslogLogDriver($name, $level, $config['log_system']['syslog_stderr']); + if ($legacy_syslog) { + $log_driver->log(LogDriver::NOTICE, 'The configuration setting \'syslog\' is deprecated. Please use \'log_system\' instead'); + } + return $log_driver; } elseif ($backend === 'file') { return new FileLogDriver($name, $level, $config['log_system']['file_path']); } elseif ($backend === 'stderr') { From 268bd84128609a53459f6480d75985733e0c031d Mon Sep 17 00:00:00 2001 From: Zankaria Date: Sun, 4 Feb 2024 12:42:21 +0100 Subject: [PATCH 06/12] Refactor the logging system --- inc/config.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/inc/config.php b/inc/config.php index def2cd27..64fd9fe3 100644 --- a/inc/config.php +++ b/inc/config.php @@ -63,9 +63,22 @@ // been generated. This keeps the script from querying the database and causing strain when not needed. $config['has_installed'] = '.installed'; - // Use syslog() for logging all error messages and unauthorized login attempts. + // Deprecated, use 'log_system'. $config['syslog'] = false; + $config['log_system'] = []; + // Log all error messages and unauthorized login attempts. + // Can be "syslog", "error_log" (default), "file", "stderr" or "none". + $config['log_system']['type'] = 'error_log'; + // The application name used by the logging system. Defaults to "tinyboard" for backwards compatibility. + $config['log_system']['name'] = 'tinyboard'; + // Only relevant if 'log_system' is set to "syslog". If true, double print the logs also in stderr. + // Defaults to false. + $config['log_system']['syslog_stderr'] = false; + // Only relevant if "log_system" is set to `file`. Sets the file that vichan will log to. + // Defaults to '/var/log/vichan.log'. + $config['log_system']['file_path'] = '/var/log/vichan.log'; + // Use `host` via shell_exec() to lookup hostnames, avoiding query timeouts. May not work on your system. // Requires safe_mode to be disabled. $config['dns_system'] = false; From cca8d88d91dca6706a9bfd53488ed52de264ffad Mon Sep 17 00:00:00 2001 From: Zankaria Date: Fri, 4 Oct 2024 13:10:38 +0200 Subject: [PATCH 07/12] config.php: update LogDriver configuration --- inc/config.php | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/inc/config.php b/inc/config.php index 64fd9fe3..25031bfb 100644 --- a/inc/config.php +++ b/inc/config.php @@ -66,18 +66,25 @@ // Deprecated, use 'log_system'. $config['syslog'] = false; - $config['log_system'] = []; - // Log all error messages and unauthorized login attempts. - // Can be "syslog", "error_log" (default), "file", "stderr" or "none". - $config['log_system']['type'] = 'error_log'; - // The application name used by the logging system. Defaults to "tinyboard" for backwards compatibility. - $config['log_system']['name'] = 'tinyboard'; - // Only relevant if 'log_system' is set to "syslog". If true, double print the logs also in stderr. - // Defaults to false. - $config['log_system']['syslog_stderr'] = false; - // Only relevant if "log_system" is set to `file`. Sets the file that vichan will log to. - // Defaults to '/var/log/vichan.log'. - $config['log_system']['file_path'] = '/var/log/vichan.log'; + $config['log_system'] = [ + /* + * Log all error messages and unauthorized login attempts. + * Can be "syslog", "error_log" (default), "file", or "stderr". + */ + 'type' => 'error_log', + // The application name used by the logging system. Defaults to "tinyboard" for backwards compatibility. + 'name' => 'tinyboard', + /* + * Only relevant if 'log_system' is set to "syslog". If true, double print the logs also in stderr. Defaults to + * false. + */ + 'syslog_stderr' => false, + /* + * Only relevant if "log_system" is set to `file`. Sets the file that vichan will log to. Defaults to + * '/var/log/vichan.log'. + */ + 'file_path' => '/var/log/vichan.log', + ]; // Use `host` via shell_exec() to lookup hostnames, avoiding query timeouts. May not work on your system. // Requires safe_mode to be disabled. From 8f7db3bdef17003b42da484471821a1f9faf10ab Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 19 Dec 2024 00:33:54 +0100 Subject: [PATCH 08/12] FileLogDriver.php: flush writes to file --- inc/Data/Driver/FileLogDriver.php | 1 + 1 file changed, 1 insertion(+) diff --git a/inc/Data/Driver/FileLogDriver.php b/inc/Data/Driver/FileLogDriver.php index 18423cf1..2c9f14a0 100644 --- a/inc/Data/Driver/FileLogDriver.php +++ b/inc/Data/Driver/FileLogDriver.php @@ -49,6 +49,7 @@ class FileLogDriver implements LogDriver { $line = "{$this->name} $lv: $message\n"; \flock($this->fd, LOCK_EX); \fwrite($this->fd, $line); + \fflush($this->fd); \flock($this->fd, LOCK_UN); } } From 6be3f4bbff8426064671c3945c76375a7b49c1b9 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Fri, 4 Oct 2024 13:01:32 +0200 Subject: [PATCH 09/12] post.php: update LogDriver --- post.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/post.php b/post.php index 6e7c6ab6..d3ba4728 100644 --- a/post.php +++ b/post.php @@ -5,9 +5,11 @@ use Vichan\Context; use Vichan\Data\ReportQueries; +use Vichan\Data\Driver\LogDriver; require_once 'inc/bootstrap.php'; + /** * Utility functions */ From 665e3d339a7a310a9fe2ebc90b30ef4da7f72293 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 19 Dec 2024 12:13:04 +0100 Subject: [PATCH 10/12] post.php: use LogDriver --- post.php | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/post.php b/post.php index d3ba4728..27a45413 100644 --- a/post.php +++ b/post.php @@ -9,7 +9,6 @@ use Vichan\Data\Driver\LogDriver; require_once 'inc/bootstrap.php'; - /** * Utility functions */ @@ -356,7 +355,7 @@ function db_select_ban_appeals($ban_id) $dropped_post = false; -function handle_nntpchan() +function handle_nntpchan(Context $ctx) { global $config; if ($_SERVER['REMOTE_ADDR'] != $config['nntpchan']['trusted_peer']) { @@ -435,7 +434,7 @@ function handle_nntpchan() if ($ct == 'text/plain') { $content = file_get_contents("php://input"); } elseif ($ct == 'multipart/mixed' || $ct == 'multipart/form-data') { - _syslog(LOG_INFO, "MM: Files: " . print_r($GLOBALS, true)); // Debug + $ctx->get(LogDriver::class)->log(LogDriver::DEBUG, 'MM: Files: ' . print_r($GLOBALS, true)); $content = ''; @@ -612,8 +611,8 @@ function handle_delete(Context $ctx) modLog("User at $ip deleted his own post #$id"); } - _syslog( - LOG_INFO, + $ctx->get(LogDriver::class)->log( + LogDriver::INFO, 'Deleted post: ' . '/' . $board['dir'] . $config['dir']['res'] . sprintf($config['file_page'], $post['thread'] ? $post['thread'] : $id) . ($post['thread'] ? '#' . $id : '') ); @@ -701,9 +700,7 @@ function handle_report(Context $ctx) foreach ($report as $id) { $post = db_select_post_minimal($board['uri'], $id); if ($post === false) { - if ($config['syslog']) { - _syslog(LOG_INFO, "Failed to report non-existing post #{$id} in {$board['dir']}"); - } + $ctx->get(LogDriver::class)->log(LogDriver::INFO, "Failed to report non-existing post #{$id} in {$board['dir']}"); error($config['error']['nopost']); } @@ -718,13 +715,12 @@ function handle_report(Context $ctx) error($error); } - if ($config['syslog']) - _syslog( - LOG_INFO, - 'Reported post: ' . - '/' . $board['dir'] . $config['dir']['res'] . link_for($post) . ($post['thread'] ? '#' . $id : '') . - ' for "' . $reason . '"' - ); + $ctx->get(LogDriver::class)->log( + LogDriver::INFO, + 'Reported post: ' . + '/' . $board['dir'] . $config['dir']['res'] . link_for($post) . ($post['thread'] ? '#' . $id : '') . + ' for "' . $reason . '"' + ); $report_queries->add($_SERVER['REMOTE_ADDR'], $board['uri'], $id, $reason); @@ -1785,10 +1781,10 @@ function handle_post(Context $ctx) buildThread($post['op'] ? $id : $post['thread']); - if ($config['syslog']) { - _syslog(LOG_INFO, 'New post: /' . $board['dir'] . $config['dir']['res'] . - link_for($post) . (!$post['op'] ? '#' . $id : '')); - } + $ctx->get(LogDriver::class)->log( + LogDriver::INFO, + 'New post: /' . $board['dir'] . $config['dir']['res'] . link_for($post) . (!$post['op'] ? '#' . $id : '') + ); if (!$post['mod']) { header('X-Associated-Content: "' . $redirect . '"'); @@ -1881,17 +1877,17 @@ function handle_appeal(Context $ctx) displayBan($ban); } +$ctx = Vichan\build_context($config); + // Is it a post coming from NNTP? Let's extract it and pretend it's a normal post. if (isset($_GET['Newsgroups'])) { if ($config['nntpchan']['enabled']) { - handle_nntpchan(); + handle_nntpchan($ctx); } else { error("NNTPChan: NNTPChan support is disabled"); } } -$ctx = Vichan\build_context($config); - if (isset($_POST['delete'])) { handle_delete($ctx); } elseif (isset($_POST['report'])) { From 5b4d1b7f4c7b420c5ccbf8a0f6a780e594da83e4 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Thu, 19 Dec 2024 12:22:24 +0100 Subject: [PATCH 11/12] pages.php: use LogDriver --- inc/mod/pages.php | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/inc/mod/pages.php b/inc/mod/pages.php index ec33b128..155c19a6 100644 --- a/inc/mod/pages.php +++ b/inc/mod/pages.php @@ -4,16 +4,19 @@ */ use Vichan\Context; use Vichan\Data\{IpNoteQueries, UserPostQueries, ReportQueries}; +use Vichan\Data\Driver\LogDriver; use Vichan\Functions\Net; defined('TINYBOARD') or exit; -function _link_or_copy(string $target, string $link): bool { - if (!link($target, $link)) { - error_log("Failed to link() $target to $link. FAlling back to copy()"); - return copy($target, $link); - } - return true; +function _link_or_copy_factory(Context $ctx): callable { + return function(string $target, string $link) use ($ctx) { + if (!\link($target, $link)) { + $ctx->get(LogDriver::class)->log(LogDriver::NOTICE, "Failed to link() $target to $link. FAlling back to copy()"); + return \copy($target, $link); + } + return true; + }; } function mod_page($title, $template, $args, $subtitle = false) { @@ -54,8 +57,7 @@ function mod_login(Context $ctx, $redirect = false) { if (!isset($_POST['username'], $_POST['password']) || $_POST['username'] == '' || $_POST['password'] == '') { $args['error'] = $config['error']['invalid']; } elseif (!login($_POST['username'], $_POST['password'])) { - if ($config['syslog']) - _syslog(LOG_WARNING, 'Unauthorized login attempt!'); + $ctx->get(LogDriver::class)->log(LogDriver::INFO, 'Unauthorized login attempt!'); $args['error'] = $config['error']['invalid']; } else { @@ -1489,8 +1491,9 @@ function mod_move(Context $ctx, $originBoard, $postID) { if ($targetBoard === $originBoard) error(_('Target and source board are the same.')); + $_link_or_copy = _link_or_copy_factory($ctx); // link() if leaving a shadow thread behind; else, rename(). - $clone = $shadow ? '_link_or_copy' : 'rename'; + $clone = $shadow ? $_link_or_copy : 'rename'; // indicate that the post is a thread $post['op'] = true; @@ -1784,7 +1787,8 @@ function mod_merge(Context $ctx, $originBoard, $postID) { $op = $post; $op['id'] = $newID; - $clone = $shadow ? '_link_or_copy' : 'rename'; + $_link_or_copy = _link_or_copy_factory($ctx); + $clone = $shadow ? $_link_or_copy : 'rename'; if ($post['has_file']) { // copy image From b2029d2533a90be56248434ff310a46ef48aa123 Mon Sep 17 00:00:00 2001 From: Zankaria Date: Sun, 16 Mar 2025 18:17:42 +0100 Subject: [PATCH 12/12] context.php: log error if log_system is erroneous --- inc/context.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/inc/context.php b/inc/context.php index 631483e7..11a153ec 100644 --- a/inc/context.php +++ b/inc/context.php @@ -51,8 +51,12 @@ function build_context(array $config): Context { return new FileLogDriver($name, $level, $config['log_system']['file_path']); } elseif ($backend === 'stderr') { return new StderrLogDriver($name, $level); - } else { + } elseif ($backend === 'error_log') { return new ErrorLogLogDriver($name, $level); + } else { + $log_driver = new ErrorLogLogDriver($name, $level); + $log_driver->log(LogDriver::ERROR, "Unknown 'log_system' value '$backend', using 'error_log' default"); + return $log_driver; } }, CacheDriver::class => function($c) {