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