plover/log.c
author J. Ali Harlow <ali@juiblex.co.uk>
Thu Jun 16 12:11:14 2016 +0100 (2016-06-16)
changeset 42 419a02fa70db
parent 24 2b9f54d14cc2
child 44 43ffed8669ce
permissions -rw-r--r--
Port to razor 0.6.3.59
ali@24
     1
/*
ali@24
     2
 * Copyright (C) 2014  J. Ali Harlow <ali@juiblex.co.uk>
ali@24
     3
 *
ali@24
     4
 * This program is free software; you can redistribute it and/or modify
ali@24
     5
 * it under the terms of the GNU General Public License as published by
ali@24
     6
 * the Free Software Foundation; either version 2 of the License, or
ali@24
     7
 * (at your option) any later version.
ali@24
     8
 *
ali@24
     9
 * This program is distributed in the hope that it will be useful,
ali@24
    10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
ali@24
    11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
ali@24
    12
 * GNU General Public License for more details.
ali@24
    13
 *
ali@24
    14
 * You should have received a copy of the GNU General Public License along
ali@24
    15
 * with this program; if not, write to the Free Software Foundation, Inc.,
ali@24
    16
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
ali@24
    17
 */
ali@24
    18
ali@24
    19
#include <stdlib.h>
ali@24
    20
#include <stddef.h>
ali@24
    21
#include <stdio.h>
ali@24
    22
#include <string.h>
ali@24
    23
#include <sys/types.h>
ali@24
    24
#include <sys/stat.h>
ali@24
    25
#include <unistd.h>
ali@24
    26
#include <time.h>
ali@24
    27
#include <errno.h>
ali@24
    28
#include <fcntl.h>
ali@24
    29
#ifdef WIN32
ali@24
    30
#include <windows.h>
ali@24
    31
#include <io.h>
ali@24
    32
#else
ali@24
    33
#include <dirent.h>
ali@24
    34
#endif
ali@24
    35
#include "config.h"
ali@24
    36
#include "plover.h"
ali@24
    37
ali@24
    38
#ifndef FALSE
ali@24
    39
#define FALSE	0
ali@24
    40
#endif
ali@24
    41
ali@24
    42
#ifndef TRUE
ali@24
    43
#define TRUE	(!FALSE)
ali@24
    44
#endif
ali@24
    45
ali@24
    46
#define MAX_OLD_LOGFILES	4	/* (5 including the current) */
ali@24
    47
ali@24
    48
#ifdef WIN32
ali@24
    49
ali@24
    50
struct find_suffixed_data {
ali@24
    51
    HANDLE handle;
ali@24
    52
    WIN32_FIND_DATA wfd;
ali@24
    53
    int base_len;
ali@24
    54
    char *suffix;
ali@24
    55
};
ali@24
    56
ali@24
    57
static int find_suffixed_first(const char *path,struct find_suffixed_data *data)
ali@24
    58
{
ali@24
    59
    const char *t1,*t2;
ali@24
    60
    gchar *s;
ali@24
    61
    s=g_strconcat(path,"-*",NULL);
ali@24
    62
    data->handle=FindFirstFile(s,&data->wfd);
ali@24
    63
    g_free(s);
ali@24
    64
    if (data->handle==INVALID_HANDLE_VALUE)
ali@24
    65
	return FALSE;
ali@24
    66
    t1=strrchr(path,'/');
ali@24
    67
    t2=strrchr(t1?t1:path,'\\');
ali@24
    68
    if (t2)
ali@24
    69
	data->base_len=strlen(t2+1);
ali@24
    70
    else if (t1)
ali@24
    71
	data->base_len=strlen(t1+1);
ali@24
    72
    else
ali@24
    73
	data->base_len=strlen(path);
ali@24
    74
    data->suffix=strdup(data->wfd.cFileName+data->base_len);
ali@24
    75
    return TRUE;
ali@24
    76
}
ali@24
    77
ali@24
    78
static int find_suffixed_next(struct find_suffixed_data *data)
ali@24
    79
{
ali@24
    80
    if (!FindNextFile(data->handle,&data->wfd))
ali@24
    81
	return FALSE;
ali@24
    82
    free(data->suffix);
ali@24
    83
    data->suffix=strdup(data->wfd.cFileName+data->base_len);
ali@24
    84
    return TRUE;
ali@24
    85
}
ali@24
    86
ali@24
    87
static void find_suffixed_close(struct find_suffixed_data *data)
ali@24
    88
{
ali@24
    89
    free(data->suffix);
ali@24
    90
    FindClose(data->handle);
ali@24
    91
}
ali@24
    92
ali@24
    93
#else	/* WIN32 */
ali@24
    94
ali@24
    95
struct find_suffixed_data {
ali@24
    96
    DIR *dir;
ali@24
    97
    struct dirent *entry;
ali@24
    98
    char *base;
ali@24
    99
    int base_len;
ali@24
   100
    char *suffix;
ali@24
   101
};
ali@24
   102
ali@24
   103
static int find_suffixed_next(struct find_suffixed_data *data)
ali@24
   104
{
ali@24
   105
    struct dirent *entry_result;
ali@24
   106
    while (!readdir_r(data->dir,data->entry,&entry_result) && entry_result)
ali@24
   107
    {
ali@24
   108
	if (strncmp(data->entry->d_name,data->base,data->base_len) ||
ali@24
   109
	  data->entry->d_name[data->base_len]!='-')
ali@24
   110
	    continue;
ali@24
   111
	free(data->suffix);
ali@24
   112
	data->suffix=strdup(data->entry->d_name+data->base_len);
ali@24
   113
	return TRUE;
ali@24
   114
    }
ali@24
   115
    return FALSE;
ali@24
   116
}
ali@24
   117
ali@24
   118
/*
ali@24
   119
 * From http://womble.decadent.org.uk/readdir_r-advisory.html
ali@24
   120
 *
ali@24
   121
 * Calculate the required buffer size (in bytes) for directory
ali@24
   122
 * entries read from the given directory handle. Return -1 if this
ali@24
   123
 * this cannot be done.
ali@24
   124
 *
ali@24
   125
 * This code does not trust values of NAME_MAX that are less than
ali@24
   126
 * 255, since some systems (including at least HP-UX) incorrectly
ali@24
   127
 * define it to be a smaller value.
ali@24
   128
 *
ali@24
   129
 * If you use autoconf, include fpathconf and dirfd in your
ali@24
   130
 * AC_CHECK_FUNCS list. Otherwise use some other method to detect
ali@24
   131
 * and use them where available.
ali@24
   132
 */
ali@24
   133
ali@24
   134
static size_t dirent_buf_size(DIR * dirp)
ali@24
   135
{
ali@24
   136
    long name_max;
ali@24
   137
    size_t name_end;
ali@24
   138
#if defined(HAVE_FPATHCONF) && defined(HAVE_DIRFD) && defined(_PC_NAME_MAX)
ali@24
   139
    name_max=fpathconf(dirfd(dirp),_PC_NAME_MAX);
ali@24
   140
    if (name_max==-1)
ali@24
   141
#if defined(NAME_MAX)
ali@24
   142
	name_max=(NAME_MAX>255)?NAME_MAX:255;
ali@24
   143
#else
ali@24
   144
	return (size_t)-1;
ali@24
   145
#endif	/* NAME_MAX */
ali@24
   146
#else
ali@24
   147
#if defined(NAME_MAX)
ali@24
   148
    name_max=(NAME_MAX>255)?NAME_MAX:255;
ali@24
   149
#else
ali@24
   150
#error "buffer size for readdir_r cannot be determined"
ali@24
   151
#endif	/* NAME_MAX */
ali@24
   152
#endif	/* HAVE_FPATHCONF && HAVE_DIRFD && _PC_NAME_MAX */
ali@24
   153
    name_end=(size_t)offsetof(struct dirent,d_name)+name_max+1;
ali@24
   154
    return (name_end>sizeof(struct dirent)?name_end:sizeof(struct dirent));
ali@24
   155
}
ali@24
   156
ali@24
   157
static int find_suffixed_first(const char *path,struct find_suffixed_data *data)
ali@24
   158
{
ali@24
   159
    int len;
ali@24
   160
    char *s,*base;
ali@24
   161
    base=strrchr(path,'/');
ali@24
   162
    if (base)
ali@24
   163
    {
ali@24
   164
	if (base==path)
ali@24
   165
	    data->dir=opendir("/");
ali@24
   166
	else
ali@24
   167
	{
ali@24
   168
	    s=strndup(path,base-path);
ali@24
   169
	    data->dir=opendir(s);
ali@24
   170
	    free(s);
ali@24
   171
	}
ali@38
   172
	data->base=strdup(base+1);
ali@24
   173
    }
ali@24
   174
    else
ali@24
   175
    {
ali@24
   176
	data->dir=opendir(".");
ali@24
   177
	data->base=strdup(path);
ali@24
   178
    }
ali@24
   179
    if (!data->dir)
ali@24
   180
    {
ali@24
   181
	free(data->base);
ali@24
   182
	return FALSE;
ali@24
   183
    }
ali@24
   184
    data->base_len=strlen(data->base);
ali@24
   185
    len=dirent_buf_size(data->dir);
ali@24
   186
    if (len<0)
ali@24
   187
    {
ali@24
   188
	closedir(data->dir);
ali@24
   189
	return FALSE;
ali@24
   190
    }
ali@24
   191
    data->entry=malloc(len);
ali@24
   192
    if (!data->entry)
ali@24
   193
    {
ali@24
   194
	closedir(data->dir);
ali@24
   195
	return FALSE;
ali@24
   196
    }
ali@38
   197
    data->suffix=NULL;
ali@24
   198
    if (find_suffixed_next(data))
ali@24
   199
	return TRUE;
ali@24
   200
    free(data->entry);
ali@24
   201
    closedir(data->dir);
ali@24
   202
    return FALSE;
ali@24
   203
}
ali@24
   204
ali@24
   205
static void find_suffixed_close(struct find_suffixed_data *data)
ali@24
   206
{
ali@38
   207
    free(data->base);
ali@24
   208
    free(data->suffix);
ali@24
   209
    free(data->entry);
ali@24
   210
    closedir(data->dir);
ali@24
   211
}
ali@24
   212
ali@24
   213
#endif	/* WIN32 */
ali@24
   214
ali@24
   215
static int prune_old_logfiles(const char *path)
ali@24
   216
{
ali@24
   217
    int i,n_suffixes;
ali@24
   218
    gchar *s;
ali@24
   219
    char *suffix,*suffixes[MAX_OLD_LOGFILES];
ali@24
   220
    struct find_suffixed_data fsd;
ali@24
   221
    if (find_suffixed_first(path,&fsd))
ali@24
   222
    {
ali@24
   223
	n_suffixes=0;
ali@24
   224
	do
ali@24
   225
	{
ali@24
   226
	    suffix=strdup(fsd.suffix);
ali@24
   227
	    if (n_suffixes<MAX_OLD_LOGFILES)
ali@24
   228
		suffixes[n_suffixes++]=suffix;
ali@24
   229
	    else
ali@24
   230
	    {
ali@24
   231
		for(i=0;i<MAX_OLD_LOGFILES;i++)
ali@24
   232
		    if (strcmp(suffix,suffixes[i])>0)
ali@24
   233
		    {
ali@24
   234
			s=suffixes[i];
ali@24
   235
			suffixes[i]=suffix;
ali@24
   236
			suffix=s;
ali@24
   237
		    }
ali@24
   238
		s=g_strconcat(path,suffix,NULL);
ali@24
   239
		(void)remove(s);
ali@24
   240
		g_free(s);
ali@24
   241
		free(suffix);
ali@24
   242
	    }
ali@24
   243
	} while(find_suffixed_next(&fsd));
ali@24
   244
	find_suffixed_close(&fsd);
ali@24
   245
	for(i=0;i<n_suffixes;i++)
ali@24
   246
	    free(suffixes[i]);
ali@24
   247
    }
ali@24
   248
    return 0;
ali@24
   249
}
ali@24
   250
ali@24
   251
static int rotate_logfile(const char *path,struct tm *modified)
ali@24
   252
{
ali@38
   253
    gboolean okay_to_replace;
ali@24
   254
    gchar *s;
ali@24
   255
    char serial;
ali@24
   256
    char suffix[11];			/* -yyyymmdd or -yyyymmdds */
ali@24
   257
    sprintf(suffix,"-%04d%02d%02d",modified->tm_year+1900,modified->tm_mon+1,
ali@24
   258
      modified->tm_mday);
ali@24
   259
    s=g_strconcat(path,suffix,NULL);
ali@38
   260
    if (g_file_test(s,G_FILE_TEST_EXISTS) || g_rename(path,s))
ali@24
   261
    {
ali@24
   262
	suffix[10]='\0';
ali@24
   263
	for(serial='a';serial<='z';serial++)
ali@24
   264
	{
ali@38
   265
	    free(s);
ali@38
   266
	    suffix[9]=serial;
ali@38
   267
	    s=g_strconcat(path,suffix,NULL);
ali@38
   268
	    if (serial=='z')
ali@38
   269
		okay_to_replace=TRUE;
ali@38
   270
	    else
ali@38
   271
		okay_to_replace=!g_file_test(s,G_FILE_TEST_EXISTS);
ali@38
   272
	    if (okay_to_replace && !g_rename(path,s))
ali@38
   273
		break;
ali@38
   274
	    else if (serial=='z')
ali@24
   275
	    {
ali@38
   276
		fprintf(stderr,"%s%s: Failed to rotate logfile\n",path,suffix);
ali@24
   277
		free(s);
ali@24
   278
		return -1;
ali@24
   279
	    }
ali@24
   280
	}
ali@24
   281
    }
ali@24
   282
    g_free(s);
ali@24
   283
    return prune_old_logfiles(path);
ali@24
   284
}
ali@24
   285
ali@24
   286
int plover_log_open(const char *path)
ali@24
   287
{
ali@24
   288
    int retval;
ali@24
   289
    char *root;
ali@24
   290
    gchar *filename;
ali@24
   291
    struct stat sb;
ali@24
   292
    time_t t;
ali@24
   293
    struct tm today,modified;
ali@24
   294
    struct razor_atomic *atomic;
ali@24
   295
    FILE *fp;
ali@24
   296
    root=getenv("RAZOR_ROOT");
ali@24
   297
    if (root)
ali@24
   298
	filename=g_strconcat(root,path,NULL);
ali@24
   299
    else
ali@24
   300
	filename=g_strdup(path);
ali@24
   301
    atomic=razor_atomic_open("Open log");
ali@24
   302
    razor_atomic_make_dirs(atomic,"",filename);
ali@24
   303
    retval=razor_atomic_commit(atomic);
ali@24
   304
    if (retval)
ali@24
   305
	fprintf(stderr,"Can't open log: %s\n",
ali@24
   306
	  razor_atomic_get_error_msg(atomic));
ali@24
   307
    razor_atomic_destroy(atomic);
ali@24
   308
    if (retval)
ali@38
   309
    {
ali@38
   310
	g_free(filename);
ali@24
   311
	return retval;
ali@38
   312
    }
ali@24
   313
    if (stat(filename,&sb)<0)
ali@24
   314
    {
ali@24
   315
	if (errno!=ENOENT)
ali@24
   316
	{
ali@24
   317
	    fprintf(stderr,"Can't open log: ");
ali@24
   318
	    perror(filename);
ali@24
   319
	    g_free(filename);
ali@24
   320
	    return -1;
ali@24
   321
	}
ali@24
   322
    }
ali@24
   323
    else if (!S_ISREG(sb.st_mode))
ali@24
   324
    {
ali@24
   325
	fprintf(stderr,"Can't open log: %s: Not a regular file\n",filename);
ali@24
   326
	g_free(filename);
ali@24
   327
	return -1;
ali@24
   328
    }
ali@24
   329
    else
ali@24
   330
    {
ali@24
   331
	time(&t);
ali@24
   332
#ifdef WIN32
ali@24
   333
	localtime_s(&today,&t);
ali@24
   334
	localtime_s(&modified,&sb.st_mtime);
ali@24
   335
#else
ali@24
   336
	localtime_r(&t,&today);
ali@24
   337
	localtime_r(&sb.st_mtime,&modified);
ali@24
   338
#endif
ali@24
   339
	if (modified.tm_yday!=today.tm_yday || modified.tm_year!=today.tm_year)
ali@24
   340
	    rotate_logfile(filename,&modified);
ali@24
   341
    }
ali@24
   342
    fp=fopen(filename,"a");
ali@24
   343
    if (!fp)
ali@24
   344
    {
ali@24
   345
	fprintf(stderr,"Can't open log: ");
ali@24
   346
	perror(filename);
ali@24
   347
	g_free(filename);
ali@24
   348
	return -1;
ali@24
   349
    }
ali@24
   350
    g_free(filename);
ali@24
   351
#ifdef WIN32
ali@24
   352
    /*
ali@24
   353
     * The situation under MS-Windows is a little complicated. If standard
ali@24
   354
     * output and standard error are valid then the normal code will work.
ali@24
   355
     * This applies in console applications and even in GUI applications
ali@24
   356
     * if standard output and standard error are redirected by the parent
ali@24
   357
     * process (eg., by cmd.exe). However GUI applications started in
ali@24
   358
     * typical fashion will have invalid standard output and standard error.
ali@24
   359
     * See http://support.microsoft.com/kb/105305 for some more detail.
ali@24
   360
     * NB: This solution assumes that fd 1 and 2 are either used for
ali@24
   361
     * standard output/error or are unused.
ali@24
   362
     */
ali@24
   363
    fclose(stdout);
ali@24
   364
    fclose(stderr);
ali@24
   365
#else
ali@24
   366
    fflush(stdout);
ali@24
   367
    fflush(stderr);
ali@24
   368
#endif
ali@24
   369
    if (dup2(fileno(fp),1)<0 || dup2(fileno(fp),2)<0)
ali@24
   370
    {
ali@24
   371
	perror("Failed to redirect standard error/output");
ali@24
   372
	fclose(fp);
ali@24
   373
	return -1;
ali@24
   374
    }
ali@24
   375
    fclose(fp);
ali@24
   376
#ifdef WIN32
ali@24
   377
    *stdout=*fdopen(1,"a");
ali@24
   378
    setvbuf(stdout,NULL,_IONBF,0);
ali@24
   379
    *stderr=*fdopen(2,"a");
ali@24
   380
    setvbuf(stderr,NULL,_IONBF,0);
ali@24
   381
    SetStdHandle(STD_OUTPUT_HANDLE,(HANDLE)_get_osfhandle(1));
ali@24
   382
    SetStdHandle(STD_ERROR_HANDLE,(HANDLE)_get_osfhandle(2));
ali@24
   383
#endif
ali@24
   384
    time(&t);
ali@24
   385
    printf("Run started on %s",ctime(&t));
ali@24
   386
    fflush(stdout);
ali@24
   387
    return 0;
ali@24
   388
}