Skip to content
Snippets Groups Projects
Commit 05bb7eb8 authored by Jaime Perez Crespo's avatar Jaime Perez Crespo
Browse files

Extract the logpeek module to its own repository. This closes #162.

parent e47ad555
No related branches found
No related tags found
No related merge requests found
<?php
/*
* Configuration for the module logpeek.
*/
$config = array (
'logfile' => '/var/log/simplesamlphp.log',
'lines' => 1500,
// Read block size. 8192 is max, limited by fread.
'blocksz' => 8192,
);
This file indicates that the default state of this module
is disabled. To enable, create a file named enable in the
same directory as this file.
<?php
/**
* Hook to add the logpeek module to the frontpage.
*
* @param array &$links The links on the frontpage, split into sections.
*/
function logpeek_hook_frontpage(&$links) {
assert('is_array($links)');
assert('array_key_exists("links", $links)');
$links['config'][] = array(
'href' => SimpleSAML_Module::getModuleURL('logpeek/'),
'text' => array('en' => 'SimpleSAMLphp logs access (Log peek)', 'no' => 'Vis simpleSAMLphp log'),
);
}
<?php
/**
* Functionatility for line by line reverse reading of a file. It is done by blockwise
* fetching the file from the end and putting the lines into an array.
*
* @author Thomas Graff<thomas.graff@uninett.no>
*
*/
class sspmod_logpeek_File_reverseRead{
// 8192 is max number of octets limited by fread.
private $blockSize;
private $blockStart;
private $fileHandle;
// fileSize may be changed after initial file size check
private $fileSize;
private $fileMtime;
// Array containing file lines
private $content;
// Leftover before first complete line
private $remainder;
// Count read lines from the end
private $readPointer;
/**
* File is checked and file handle to file is opend. But no data is read
* from the file.
*
* @param string $fileUrl Path and filename to file to be read
* @param int $blockSize File read block size in byte
* @return bool Success
*/
public function __construct($fileUrl, $blockSize = 8192){
if(!is_readable($fileUrl)){
return FALSE;
}
$this->blockSize = $blockSize;
$this->content = array();
$this->remainder = '';
$this->readPointer = 0;
$fileInfo = stat($fileUrl);
$this->fileSize = $this->blockStart = $fileInfo['size'];
$this->fileMtime = $fileInfo['mtime'];
if($this->fileSize > 0){
$this->fileHandle = fopen($fileUrl, 'rb');
return TRUE;
}else{
return FALSE;
}
}
public function __destruct(){
if(is_resource($this->fileHandle)){
fclose($this->fileHandle);
}
}
/**
* Fetch chunk of data from file.
* Each time this function is called, will it fetch a chunk
* of data from the file. It starts from the end of the file
* and work towards the beginning of the file.
*
* @return string buffer with datablock.
* Will return bool FALSE when there is no more data to get.
*/
private function readChunk(){
$splits = $this->blockSize;
$this->blockStart -= $splits;
if($this->blockStart < 0){
$splits += $this->blockStart;
$this->blockStart = 0;
}
// Return false if nothing more to read
if($splits === 0){
return FALSE;
}
fseek($this->fileHandle, $this->blockStart, SEEK_SET);
$buff = fread($this->fileHandle, $splits);
return $buff;
}
/**
* Get one line of data from the file, starting from the end of the file.
*
* @return string One line of data from the file.
* Bool FALSE when there is no more data to get.
*/
public function getPreviousLine(){
if(count($this->content) === 0 || $this->readPointer < 1){
do {
$buff = $this->readChunk();
if($buff !== FALSE){
$eolPos = strpos($buff, "\n");
}else{
// Empty buffer, no more to read.
if(strlen($this->remainder) > 0){
$buff = $this->remainder;
$this->remainder = '';
// Exit from while-loop
break;
}else{
// Remainder also empty.
return FALSE;
}
}
if($eolPos === FALSE){
// No eol found. Make buffer head of remainder and empty buffer.
$this->remainder = $buff . $this->remainder;
$buff = '';
}elseif($eolPos !== 0){
// eol found.
$buff .= $this->remainder;
$this->remainder = substr($buff, 0, $eolPos);
$buff = substr($buff, $eolPos+1);
}elseif($eolPos === 0){
$buff .= $this->remainder;
$buff = substr($buff, 1);
$this->remainder = '';
}
}while(($buff !== FALSE) && ($eolPos === FALSE));
$this->content = explode("\n", $buff);
$this->readPointer = count($this->content);
}
if(count($this->content) > 0){
return $this->content[--$this->readPointer];
}else{
return FALSE;
}
}
private function cutHead(&$haystack, $needle, $exit){
$pos = 0;
$cnt = 0;
// Holder på inntill antall ønskede linjer eller vi ikke finner flere linjer
while($cnt < $exit && ($pos = strpos($haystack, $needle, $pos)) !==false ){
$pos++;
$cnt++;
}
return ($pos === false) ? false : substr($haystack, $pos, strlen($haystack));
}
// FIXME: This function hawe som error, do not use before auditing and testing
public function getTail($lines = 10){
$this->blockStart = $this->fileSize;
$buff1 = Array();
$lastLines = array();
while($this->blockStart){
$buff = $this->readChunk();
if(!$buff)break;
$lines -= substr_count($buff, "\n");
if($lines <= 0)
{
$buff1[] = $this->cutHead($buff, "\n", abs($lines)+1);
break;
}
$buff1[] = $buff;
}
for($i = count($buff1); $i >= 0; $i--){
$lastLines = array_merge($lastLines, explode("\n", $buff1[$i]));
}
return $lastLines;
}
private function getLineAtPost($pos){
if($pos < 0 || $pos > $this->fileSize){
return FALSE;
}
$seeker = $pos;
fseek($this->fileHandle, $seeker, SEEK_SET);
while($seeker > 0 && fgetc($this->fileHandle) !== "\n"){
fseek($this->fileHandle, --$seeker, SEEK_SET);
}
return rtrim(fgets($this->fileHandle));
}
public function getFirstLine(){
return $this->getLineAtPost(0);
}
public function getLastLine(){
return $this->getLineAtPost($this->fileSize-2);
}
public function getFileSize(){
return $this->fileSize;
}
public function getFileMtime(){
return $this->fileMtime;
}
}
<?php
class sspmod_logpeek_Syslog_parseLine{
public static function isOlderThan($time, $logLine){
return true;
}
public static function getUnixTime($logLine, $year = NULL){
// I can read month and day and time from the file.
// but I will assum year is current year retured by time().
// Unless month and day in the file is bigger than current month and day,
// I will then asume prevous year.
// A better approach would be to get the year from last modification time (mtime) of the
// file this record is taken from. But that require knowledge about the file.
if(!$year){
$now = getdate();
$year = (int)$now['year'];
}
list($month, $day, $hour, $minute, $second) = sscanf($logLine, "%s %d %d:%d:%d ");
$time = sprintf("%d %s %d %d:%d:%d", $day, $month, $year, $hour, $minute, $second);
return strtotime($time);
}
}
<?php
$this->data['header'] = 'Log peek';
$this->includeAtTemplateBase('includes/header.php');
?>
<h2>SimpleSAMLphp logs (admin utility)</h2>
<form method="get" action="?">
<table>
<tr><th><label for="start">First entry in logfile</label></th><td id="star"><?php echo $this->data['timestart']; ?></td></tr>
<tr><th><label for="end">Last entry in logfile</label></th><td id="end"><?php echo $this->data['endtime']; ?></td></tr>
<tr><th><label for="size">Logfile size</label></th><td id="size"><?php echo $this->data['filesize']; ?></td></tr>
<tr><th><label for="tag">Tag id for search</label></th><td><input type="text" name="tag" id="tag" value="<?php echo $this->data['trackid']; ?>" /></td></tr>
<tr><th><input type="submit" value="Search log" /></th><td></td></tr>
</table>
</form>
<pre style="background: #eee; border: 1px solid #666; padding: 1em; margin: .4em; overflow: scroll">
<?php
if (!empty($this->data['results'])) {
foreach($this->data['results'] AS $line) {
echo htmlspecialchars($line) . "\n";
}
}
?>
</pre>
<?php $this->includeAtTemplateBase('includes/footer.php');
<?php
function logFilter($objFile, $tag, $cut){
if (!preg_match('/^[a-f0-9]{10}$/D', $tag)) throw new Exception('Invalid search tag');
$i = 0;
$results = array();
$line = $objFile->getPreviousLine();
while($line !== FALSE && ($i++ < $cut)){
if(strstr($line, '[' . $tag . ']')){
$results[] = $line;
}
$line = $objFile->getPreviousLine();
}
$results[] = 'Searched ' . $i . ' lines backward. ' . count($results) . ' lines found.';
$results = array_reverse($results);
return $results;
}
$config = SimpleSAML_Configuration::getInstance();
$session = SimpleSAML_Session::getSessionFromRequest();
SimpleSAML\Utils\Auth::requireAdmin();
$logpeekconfig = SimpleSAML_Configuration::getConfig('module_logpeek.php');
$logfile = $logpeekconfig->getValue('logfile', '/var/simplesamlphp.log');
$blockSize = $logpeekconfig->getValue('blocksz', 8192);
$myLog = new sspmod_logpeek_File_reverseRead($logfile, $blockSize);
$results = NULL;
if (isset($_REQUEST['tag'])) {
$results = logFilter($myLog, $_REQUEST['tag'], $logpeekconfig->getValue('lines', 500));
}
$fileModYear = date("Y", $myLog->getFileMtime());
$firstLine = $myLog->getFirstLine();
$firstTimeEpoch = sspmod_logpeek_Syslog_parseLine::getUnixTime($firstLine, $fileModYear);
$lastLine = $myLog->getLastLine();
$lastTimeEpoch = sspmod_logpeek_Syslog_parseLine::getUnixTime($lastLine, $fileModYear);
$fileSize = $myLog->getFileSize();
$t = new SimpleSAML_XHTML_Template($config, 'logpeek:logpeek.php');
$t->data['results'] = $results;
$t->data['trackid'] = $session->getTrackID();
$t->data['timestart'] = date(DATE_RFC822, $firstTimeEpoch);
$t->data['endtime'] = date(DATE_RFC822, $lastTimeEpoch);
$t->data['filesize'] = $fileSize;
$t->show();
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment