2015-11-06 14:25:48 +00:00
/*
* virrotatingfile . c : file I / O with size rotation
*
* Copyright ( C ) 2015 Red Hat , Inc .
*
* This library is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation ; either
* version 2.1 of the License , or ( at your option ) any later version .
*
* This library 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
* Lesser General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library . If not , see
* < http : //www.gnu.org/licenses/>.
*
*/
# include <config.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <unistd.h>
# include "virrotatingfile.h"
# include "viralloc.h"
# include "virerror.h"
# include "virfile.h"
# include "virlog.h"
VIR_LOG_INIT ( " util.rotatingfile " ) ;
# define VIR_FROM_THIS VIR_FROM_NONE
# define VIR_MAX_MAX_BACKUP 32
typedef struct virRotatingFileWriterEntry virRotatingFileWriterEntry ;
typedef struct virRotatingFileReaderEntry virRotatingFileReaderEntry ;
struct virRotatingFileWriterEntry {
int fd ;
off_t inode ;
off_t pos ;
off_t len ;
} ;
struct virRotatingFileWriter {
char * basepath ;
2021-03-11 08:16:13 +01:00
virRotatingFileWriterEntry * entry ;
2015-11-06 14:25:48 +00:00
size_t maxbackup ;
mode_t mode ;
size_t maxlen ;
} ;
struct virRotatingFileReaderEntry {
char * path ;
int fd ;
off_t inode ;
} ;
struct virRotatingFileReader {
2021-03-11 08:16:13 +01:00
virRotatingFileReaderEntry * * entries ;
2015-11-06 14:25:48 +00:00
size_t nentries ;
size_t current ;
} ;
static void
2021-03-11 08:16:13 +01:00
virRotatingFileWriterEntryFree ( virRotatingFileWriterEntry * entry )
2015-11-06 14:25:48 +00:00
{
if ( ! entry )
return ;
VIR_FORCE_CLOSE ( entry - > fd ) ;
2021-02-03 14:32:34 -05:00
g_free ( entry ) ;
2015-11-06 14:25:48 +00:00
}
static void
2021-03-11 08:16:13 +01:00
virRotatingFileReaderEntryFree ( virRotatingFileReaderEntry * entry )
2015-11-06 14:25:48 +00:00
{
if ( ! entry )
return ;
2021-02-03 14:32:34 -05:00
g_free ( entry - > path ) ;
2015-11-06 14:25:48 +00:00
VIR_FORCE_CLOSE ( entry - > fd ) ;
2021-02-03 14:32:34 -05:00
g_free ( entry ) ;
2015-11-06 14:25:48 +00:00
}
2021-03-11 08:16:13 +01:00
static virRotatingFileWriterEntry *
2015-11-06 14:25:48 +00:00
virRotatingFileWriterEntryNew ( const char * path ,
mode_t mode )
{
2021-03-11 08:16:13 +01:00
virRotatingFileWriterEntry * entry ;
2015-11-06 14:25:48 +00:00
struct stat sb ;
2017-09-25 11:43:33 +01:00
VIR_DEBUG ( " Opening %s mode=0%02o " , path , mode ) ;
2015-11-06 14:25:48 +00:00
2020-10-05 19:13:12 +02:00
entry = g_new0 ( virRotatingFileWriterEntry , 1 ) ;
2015-11-06 14:25:48 +00:00
2015-12-04 17:23:15 +00:00
if ( ( entry - > fd = open ( path , O_CREAT | O_APPEND | O_WRONLY | O_CLOEXEC , mode ) ) < 0 ) {
2015-11-06 14:25:48 +00:00
virReportSystemError ( errno ,
2023-03-09 15:13:48 +01:00
_ ( " Unable to open file: %1$s " ) , path ) ;
2015-11-06 14:25:48 +00:00
goto error ;
}
entry - > pos = lseek ( entry - > fd , 0 , SEEK_END ) ;
if ( entry - > pos = = ( off_t ) - 1 ) {
virReportSystemError ( errno ,
2023-03-09 15:13:48 +01:00
_ ( " Unable to determine current file offset: %1$s " ) ,
2015-11-06 14:25:48 +00:00
path ) ;
goto error ;
}
if ( fstat ( entry - > fd , & sb ) < 0 ) {
virReportSystemError ( errno ,
2023-03-09 15:13:48 +01:00
_ ( " Unable to determine current file inode: %1$s " ) ,
2015-11-06 14:25:48 +00:00
path ) ;
goto error ;
}
entry - > len = sb . st_size ;
entry - > inode = sb . st_ino ;
return entry ;
error :
virRotatingFileWriterEntryFree ( entry ) ;
return NULL ;
}
2021-03-11 08:16:13 +01:00
static virRotatingFileReaderEntry *
2015-11-06 14:25:48 +00:00
virRotatingFileReaderEntryNew ( const char * path )
{
2021-03-11 08:16:13 +01:00
virRotatingFileReaderEntry * entry ;
2015-11-06 14:25:48 +00:00
struct stat sb ;
VIR_DEBUG ( " Opening %s " , path ) ;
2020-10-05 19:13:12 +02:00
entry = g_new0 ( virRotatingFileReaderEntry , 1 ) ;
2015-11-06 14:25:48 +00:00
2015-12-04 17:23:15 +00:00
if ( ( entry - > fd = open ( path , O_RDONLY | O_CLOEXEC ) ) < 0 ) {
2015-11-06 14:25:48 +00:00
if ( errno ! = ENOENT ) {
virReportSystemError ( errno ,
2023-03-09 15:13:48 +01:00
_ ( " Unable to open file: %1$s " ) , path ) ;
2015-11-06 14:25:48 +00:00
goto error ;
}
}
if ( entry - > fd ! = - 1 ) {
if ( fstat ( entry - > fd , & sb ) < 0 ) {
virReportSystemError ( errno ,
2023-03-09 15:13:48 +01:00
_ ( " Unable to determine current file inode: %1$s " ) ,
2015-11-06 14:25:48 +00:00
path ) ;
goto error ;
}
entry - > inode = sb . st_ino ;
}
2019-10-20 13:49:46 +02:00
entry - > path = g_strdup ( path ) ;
2015-11-06 14:25:48 +00:00
return entry ;
error :
virRotatingFileReaderEntryFree ( entry ) ;
return NULL ;
}
static int
2021-03-11 08:16:13 +01:00
virRotatingFileWriterDelete ( virRotatingFileWriter * file )
2015-11-06 14:25:48 +00:00
{
size_t i ;
if ( unlink ( file - > basepath ) < 0 & &
errno ! = ENOENT ) {
virReportSystemError ( errno ,
2023-03-09 15:13:48 +01:00
_ ( " Unable to delete file %1$s " ) ,
2015-11-06 14:25:48 +00:00
file - > basepath ) ;
return - 1 ;
}
for ( i = 0 ; i < file - > maxbackup ; i + + ) {
char * oldpath ;
2019-10-22 15:26:14 +02:00
oldpath = g_strdup_printf ( " %s.%zu " , file - > basepath , i ) ;
2015-11-06 14:25:48 +00:00
if ( unlink ( oldpath ) < 0 & &
errno ! = ENOENT ) {
virReportSystemError ( errno ,
2023-03-09 15:13:48 +01:00
_ ( " Unable to delete file %1$s " ) ,
2015-11-06 14:25:48 +00:00
oldpath ) ;
VIR_FREE ( oldpath ) ;
return - 1 ;
}
VIR_FREE ( oldpath ) ;
}
return 0 ;
}
/**
* virRotatingFileWriterNew
* @ path : the base path for files
* @ maxlen : the maximum number of bytes to write before rollover
* @ maxbackup : number of backup files to keep when rolling over
2015-11-30 15:30:33 +01:00
* @ trunc : whether to truncate the current files when opening
2015-11-06 14:25:48 +00:00
* @ mode : the file mode to use for creating new files
*
* Create a new object for writing data to a file with
* automatic rollover . If @ maxbackup is zero , no backup
* files will be created . The primary file will just get
* truncated and reopened .
*
* The files will never exceed @ maxlen bytes in size ,
* but may be rolled over before they reach this size
2020-10-13 10:20:19 +01:00
* in order to avoid splitting lines . If @ maxlen is
* zero then no rollover will be performed .
2015-11-06 14:25:48 +00:00
*/
2021-03-11 08:16:13 +01:00
virRotatingFileWriter *
2015-11-06 14:25:48 +00:00
virRotatingFileWriterNew ( const char * path ,
off_t maxlen ,
size_t maxbackup ,
2015-11-30 15:30:33 +01:00
bool trunc ,
2015-11-06 14:25:48 +00:00
mode_t mode )
{
2021-03-11 08:16:13 +01:00
virRotatingFileWriter * file ;
2015-11-06 14:25:48 +00:00
2020-10-05 19:13:12 +02:00
file = g_new0 ( virRotatingFileWriter , 1 ) ;
2015-11-06 14:25:48 +00:00
2019-10-20 13:49:46 +02:00
file - > basepath = g_strdup ( path ) ;
2015-11-06 14:25:48 +00:00
if ( maxbackup > VIR_MAX_MAX_BACKUP ) {
virReportError ( VIR_ERR_CONFIG_UNSUPPORTED ,
2023-03-09 15:13:48 +01:00
_ ( " Max backup %1$zu must be less than or equal to %2$d " ) ,
2015-11-06 14:25:48 +00:00
maxbackup , VIR_MAX_MAX_BACKUP ) ;
goto error ;
}
file - > mode = mode ;
file - > maxbackup = maxbackup ;
file - > maxlen = maxlen ;
2015-11-30 15:30:33 +01:00
if ( trunc & &
2015-11-06 14:25:48 +00:00
virRotatingFileWriterDelete ( file ) < 0 )
goto error ;
if ( ! ( file - > entry = virRotatingFileWriterEntryNew ( file - > basepath ,
mode ) ) )
goto error ;
return file ;
error :
virRotatingFileWriterFree ( file ) ;
return NULL ;
}
/**
* virRotatingFileReaderNew :
* @ path : the base path for files
* @ maxbackup : number of backup files to read history from
*
* Create a new object for reading from a set of rolling files .
* I / O will start from the oldest file and proceed through
* files until the end of the newest one .
*
* If @ maxbackup is zero the only the newest file will be read .
*/
2021-03-11 08:16:13 +01:00
virRotatingFileReader *
2015-11-06 14:25:48 +00:00
virRotatingFileReaderNew ( const char * path ,
size_t maxbackup )
{
2021-03-11 08:16:13 +01:00
virRotatingFileReader * file ;
2015-11-06 14:25:48 +00:00
size_t i ;
2020-10-05 19:13:12 +02:00
file = g_new0 ( virRotatingFileReader , 1 ) ;
2015-11-06 14:25:48 +00:00
if ( maxbackup > VIR_MAX_MAX_BACKUP ) {
virReportError ( VIR_ERR_CONFIG_UNSUPPORTED ,
2023-03-09 15:13:48 +01:00
_ ( " Max backup %1$zu must be less than or equal to %2$d " ) ,
2015-11-06 14:25:48 +00:00
maxbackup , VIR_MAX_MAX_BACKUP ) ;
goto error ;
}
file - > nentries = maxbackup + 1 ;
2021-03-11 08:16:13 +01:00
file - > entries = g_new0 ( virRotatingFileReaderEntry * , file - > nentries ) ;
2015-11-06 14:25:48 +00:00
if ( ! ( file - > entries [ file - > nentries - 1 ] = virRotatingFileReaderEntryNew ( path ) ) )
goto error ;
for ( i = 0 ; i < maxbackup ; i + + ) {
char * tmppath ;
2019-10-22 15:26:14 +02:00
tmppath = g_strdup_printf ( " %s.%zu " , path , i ) ;
2015-11-06 14:25:48 +00:00
file - > entries [ file - > nentries - ( i + 2 ) ] = virRotatingFileReaderEntryNew ( tmppath ) ;
VIR_FREE ( tmppath ) ;
if ( ! file - > entries [ file - > nentries - ( i + 2 ) ] )
goto error ;
}
return file ;
error :
virRotatingFileReaderFree ( file ) ;
return NULL ;
}
/**
* virRotatingFileWriterGetPath :
* @ file : the file context
*
* Return the primary file path
*/
const char *
2021-03-11 08:16:13 +01:00
virRotatingFileWriterGetPath ( virRotatingFileWriter * file )
2015-11-06 14:25:48 +00:00
{
return file - > basepath ;
}
/**
* virRotatingFileWriterGetINode :
* @ file : the file context
*
* Return the inode of the file currently being written to
*/
ino_t
2021-03-11 08:16:13 +01:00
virRotatingFileWriterGetINode ( virRotatingFileWriter * file )
2015-11-06 14:25:48 +00:00
{
return file - > entry - > inode ;
}
/**
* virRotatingFileWriterGetOffset :
* @ file : the file context
*
* Return the offset at which data is currently being written
*/
off_t
2021-03-11 08:16:13 +01:00
virRotatingFileWriterGetOffset ( virRotatingFileWriter * file )
2015-11-06 14:25:48 +00:00
{
return file - > entry - > pos ;
}
static int
2021-03-11 08:16:13 +01:00
virRotatingFileWriterRollover ( virRotatingFileWriter * file )
2015-11-06 14:25:48 +00:00
{
size_t i ;
char * nextpath = NULL ;
char * thispath = NULL ;
int ret = - 1 ;
VIR_DEBUG ( " Rollover %s " , file - > basepath ) ;
if ( file - > maxbackup = = 0 ) {
if ( unlink ( file - > basepath ) < 0 & &
errno ! = ENOENT ) {
virReportSystemError ( errno ,
2023-03-09 15:13:48 +01:00
_ ( " Unable to remove %1$s " ) ,
2015-11-06 14:25:48 +00:00
file - > basepath ) ;
goto cleanup ;
}
} else {
2019-10-22 15:26:14 +02:00
nextpath = g_strdup_printf ( " %s.%zu " , file - > basepath , file - > maxbackup - 1 ) ;
2015-11-06 14:25:48 +00:00
for ( i = file - > maxbackup ; i > 0 ; i - - ) {
if ( i = = 1 ) {
2019-10-20 13:49:46 +02:00
thispath = g_strdup ( file - > basepath ) ;
2015-11-06 14:25:48 +00:00
} else {
2019-10-22 15:26:14 +02:00
thispath = g_strdup_printf ( " %s.%zu " , file - > basepath , i - 2 ) ;
2015-11-06 14:25:48 +00:00
}
VIR_DEBUG ( " Rollover %s -> %s " , thispath , nextpath ) ;
if ( rename ( thispath , nextpath ) < 0 & &
errno ! = ENOENT ) {
virReportSystemError ( errno ,
2023-03-09 15:13:48 +01:00
_ ( " Unable to rename %1$s to %2$s " ) ,
2015-11-06 14:25:48 +00:00
thispath , nextpath ) ;
goto cleanup ;
}
VIR_FREE ( nextpath ) ;
2019-10-16 13:43:52 +02:00
nextpath = g_steal_pointer ( & thispath ) ;
2015-11-06 14:25:48 +00:00
}
}
VIR_DEBUG ( " Rollover done %s " , file - > basepath ) ;
ret = 0 ;
cleanup :
VIR_FREE ( nextpath ) ;
VIR_FREE ( thispath ) ;
return ret ;
}
/**
* virRotatingFileWriterAppend :
* @ file : the file context
* @ buf : the data buffer
* @ len : the number of bytes in @ buf
*
* Append the data in @ buf to the file , performing rollover
* of the files if their size would exceed the limit
*
* Returns the number of bytes written , or - 1 on error
*/
ssize_t
2021-03-11 08:16:13 +01:00
virRotatingFileWriterAppend ( virRotatingFileWriter * file ,
2015-11-06 14:25:48 +00:00
const char * buf ,
size_t len )
{
ssize_t ret = 0 ;
size_t i ;
while ( len ) {
size_t towrite = len ;
bool forceRollover = false ;
2020-10-13 10:20:19 +01:00
if ( file - > maxlen ! = 0 ) {
if ( file - > entry - > pos > file - > maxlen ) {
/* If existing file is for some reason larger then max length we
* won ' t write to this file anymore , but we rollover this file . */
forceRollover = true ;
towrite = 0 ;
} else if ( ( file - > entry - > pos + towrite ) > file - > maxlen ) {
towrite = file - > maxlen - file - > entry - > pos ;
/*
* If there ' s a newline in the last 80 chars
* we ' re about to write , then break at that
* point to avoid splitting lines across
* separate files
*/
for ( i = 0 ; i < towrite & & i < 80 ; i + + ) {
if ( buf [ towrite - i - 1 ] = = ' \n ' ) {
towrite - = i ;
forceRollover = true ;
break ;
}
2015-11-06 14:25:48 +00:00
}
}
}
if ( towrite ) {
if ( safewrite ( file - > entry - > fd , buf , towrite ) ! = towrite ) {
virReportSystemError ( errno ,
2023-03-09 15:13:48 +01:00
_ ( " Unable to write to file %1$s " ) ,
2015-11-06 14:25:48 +00:00
file - > basepath ) ;
return - 1 ;
}
len - = towrite ;
buf + = towrite ;
ret + = towrite ;
file - > entry - > pos + = towrite ;
file - > entry - > len + = towrite ;
}
2020-10-13 10:20:19 +01:00
if ( file - > maxlen ! = 0 & &
( ( file - > entry - > pos = = file - > maxlen & & len ) | |
forceRollover ) ) {
2021-03-11 08:16:13 +01:00
virRotatingFileWriterEntry * tmp ;
2018-09-17 17:06:30 +02:00
VIR_DEBUG ( " Hit max size %zu on %s (force=%d) " ,
2015-11-06 14:25:48 +00:00
file - > maxlen , file - > basepath , forceRollover ) ;
if ( virRotatingFileWriterRollover ( file ) < 0 )
return - 1 ;
2017-04-12 16:44:04 +08:00
if ( ! ( tmp = virRotatingFileWriterEntryNew ( file - > basepath ,
file - > mode ) ) )
2015-11-06 14:25:48 +00:00
return - 1 ;
2017-04-12 16:44:04 +08:00
virRotatingFileWriterEntryFree ( file - > entry ) ;
file - > entry = tmp ;
2015-11-06 14:25:48 +00:00
}
}
return ret ;
}
/**
* virRotatingFileReaderSeek
* @ file : the file context
* @ inode : the inode of the file to seek to
* @ offset : the offset within the file to seek to
*
* Seek to @ offset in the file identified by @ inode .
* If no file with a inode matching @ inode currently
* exists , then seeks to the start of the oldest
* file , on the basis that the requested file has
2016-11-15 19:30:08 +05:30
* probably been rotated out of existence
2015-11-06 14:25:48 +00:00
*/
int
2021-03-11 08:16:13 +01:00
virRotatingFileReaderSeek ( virRotatingFileReader * file ,
2015-11-06 14:25:48 +00:00
ino_t inode ,
off_t offset )
{
size_t i ;
off_t ret ;
for ( i = 0 ; i < file - > nentries ; i + + ) {
2021-03-11 08:16:13 +01:00
virRotatingFileReaderEntry * entry = file - > entries [ i ] ;
2015-11-06 14:25:48 +00:00
if ( entry - > inode ! = inode | |
entry - > fd = = - 1 )
continue ;
ret = lseek ( entry - > fd , offset , SEEK_SET ) ;
if ( ret = = ( off_t ) - 1 ) {
virReportSystemError ( errno ,
2023-03-09 15:13:48 +01:00
_ ( " Unable to seek to inode %1$llu offset %2$llu " ) ,
2015-11-06 14:25:48 +00:00
( unsigned long long ) inode , ( unsigned long long ) offset ) ;
return - 1 ;
}
file - > current = i ;
return 0 ;
}
file - > current = 0 ;
ret = lseek ( file - > entries [ 0 ] - > fd , offset , SEEK_SET ) ;
if ( ret = = ( off_t ) - 1 ) {
virReportSystemError ( errno ,
2023-03-09 15:13:48 +01:00
_ ( " Unable to seek to inode %1$llu offset %2$llu " ) ,
2015-11-06 14:25:48 +00:00
( unsigned long long ) inode , ( unsigned long long ) offset ) ;
return - 1 ;
}
return 0 ;
}
/**
* virRotatingFileReaderConsume :
* @ file : the file context
* @ buf : the buffer to fill with data
* @ len : the size of @ buf
*
* Reads data from the file starting at the current offset .
* The returned data may be pulled from multiple files .
*
* Returns : the number of bytes read or - 1 on error
*/
ssize_t
2021-03-11 08:16:13 +01:00
virRotatingFileReaderConsume ( virRotatingFileReader * file ,
2015-11-06 14:25:48 +00:00
char * buf ,
size_t len )
{
ssize_t ret = 0 ;
2018-09-17 17:06:30 +02:00
VIR_DEBUG ( " Consume %p %zu " , buf , len ) ;
2015-11-06 14:25:48 +00:00
while ( len ) {
2021-03-11 08:16:13 +01:00
virRotatingFileReaderEntry * entry ;
2015-11-06 14:25:48 +00:00
ssize_t got ;
if ( file - > current > = file - > nentries )
break ;
entry = file - > entries [ file - > current ] ;
if ( entry - > fd = = - 1 ) {
file - > current + + ;
continue ;
}
got = saferead ( entry - > fd , buf + ret , len ) ;
if ( got < 0 ) {
virReportSystemError ( errno ,
2023-03-09 15:13:48 +01:00
_ ( " Unable to read from file %1$s " ) ,
2015-11-06 14:25:48 +00:00
entry - > path ) ;
return - 1 ;
}
if ( got = = 0 ) {
file - > current + + ;
continue ;
}
ret + = got ;
len - = got ;
}
return ret ;
}
/**
* virRotatingFileWriterFree :
* @ file : the file context
*
* Close the current file and release all resources
*/
void
2021-03-11 08:16:13 +01:00
virRotatingFileWriterFree ( virRotatingFileWriter * file )
2015-11-06 14:25:48 +00:00
{
if ( ! file )
return ;
virRotatingFileWriterEntryFree ( file - > entry ) ;
2021-02-03 14:32:34 -05:00
g_free ( file - > basepath ) ;
g_free ( file ) ;
2015-11-06 14:25:48 +00:00
}
/**
* virRotatingFileReaderFree :
* @ file : the file context
*
* Close the files and release all resources
*/
void
2021-03-11 08:16:13 +01:00
virRotatingFileReaderFree ( virRotatingFileReader * file )
2015-11-06 14:25:48 +00:00
{
size_t i ;
if ( ! file )
return ;
for ( i = 0 ; i < file - > nentries ; i + + )
virRotatingFileReaderEntryFree ( file - > entries [ i ] ) ;
2021-02-03 14:32:34 -05:00
g_free ( file - > entries ) ;
g_free ( file ) ;
2015-11-06 14:25:48 +00:00
}