mirror of
https://git.kernel.org/pub/scm/fs/ext2/e2fsprogs.git
synced 2024-12-19 23:11:55 +08:00
d2dd606fab
Add the ability to save a log of problems found by e2fsck in a log file that can be specified via /etc/e2fsck.conf. Signed-off-by: Theodore Ts'o <tytso@mit.edu>
409 lines
7.7 KiB
C
409 lines
7.7 KiB
C
/*
|
|
* logfile.c --- set up e2fsck log files
|
|
*
|
|
* Copyright 1996, 1997 by Theodore Ts'o
|
|
*
|
|
* %Begin-Header%
|
|
* This file may be redistributed under the terms of the GNU Public
|
|
* License.
|
|
* %End-Header%
|
|
*/
|
|
|
|
#include "config.h"
|
|
#ifdef HAVE_ERRNO_H
|
|
#include <errno.h>
|
|
#endif
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "e2fsck.h"
|
|
#include <pwd.h>
|
|
|
|
extern e2fsck_t e2fsck_global_ctx; /* Try your very best not to use this! */
|
|
|
|
struct string {
|
|
char *s;
|
|
int len;
|
|
int end;
|
|
};
|
|
|
|
static void alloc_string(struct string *s, int len)
|
|
{
|
|
s->s = malloc(len);
|
|
/* e2fsck_allocate_memory(ctx, len, "logfile name"); */
|
|
s->len = len;
|
|
s->end = 0;
|
|
}
|
|
|
|
static void append_string(struct string *s, const char *a, int len)
|
|
{
|
|
int needlen;
|
|
|
|
if (!len)
|
|
len = strlen(a);
|
|
|
|
needlen = s->end + len + 1;
|
|
if (needlen > s->len) {
|
|
char *n;
|
|
|
|
if (s->len * 2 > needlen)
|
|
needlen = s->len * 2;
|
|
n = realloc(s->s, needlen);
|
|
|
|
if (n) {
|
|
s->s = n;
|
|
s->len = needlen;
|
|
} else {
|
|
/* Don't append if we ran out of memory */
|
|
return;
|
|
}
|
|
}
|
|
memcpy(s->s + s->end, a, len);
|
|
s->end += len;
|
|
s->s[s->end] = 0;
|
|
}
|
|
|
|
#define FLAG_UTC 0x0001
|
|
|
|
static void expand_percent_expression(e2fsck_t ctx, char ch,
|
|
struct string *s, int *flags)
|
|
{
|
|
struct tm *tm = NULL, tm_struct;
|
|
struct passwd *pw = NULL, pw_struct;
|
|
char *cp;
|
|
char buf[256];
|
|
|
|
if ((ch == 'D') || (ch == 'd') || (ch == 'm') || (ch == 'y') ||
|
|
(ch == 'Y') ||
|
|
(ch == 'T') || (ch == 'H') || (ch == 'M') || (ch == 'S')) {
|
|
tzset();
|
|
tm = (*flags & FLAG_UTC) ? gmtime_r(&ctx->now, &tm_struct) :
|
|
localtime_r(&ctx->now, &tm_struct);
|
|
}
|
|
|
|
switch (ch) {
|
|
case '%':
|
|
append_string(s, "%", 1);
|
|
return;
|
|
case 'd':
|
|
sprintf(buf, "%02d", tm->tm_mday);
|
|
break;
|
|
case 'D':
|
|
sprintf(buf, "%d%02d%02d", tm->tm_year + 1900, tm->tm_mon + 1,
|
|
tm->tm_mday);
|
|
break;
|
|
case 'h':
|
|
#ifdef TEST_PROGRAM
|
|
strcpy(buf, "server");
|
|
#else
|
|
buf[0] = 0;
|
|
gethostname(buf, sizeof(buf));
|
|
buf[sizeof(buf)-1] = 0;
|
|
#endif
|
|
break;
|
|
case 'H':
|
|
sprintf(buf, "%02d", tm->tm_hour);
|
|
break;
|
|
case 'm':
|
|
sprintf(buf, "%02d", tm->tm_mon + 1);
|
|
break;
|
|
case 'M':
|
|
sprintf(buf, "%02d", tm->tm_min);
|
|
break;
|
|
case 'N': /* block device name */
|
|
cp = strrchr(ctx->filesystem_name, '/');
|
|
if (cp)
|
|
cp++;
|
|
else
|
|
cp = ctx->filesystem_name;
|
|
append_string(s, cp, 0);
|
|
return;
|
|
case 'p':
|
|
sprintf(buf, "%lu", (unsigned long) getpid());
|
|
break;
|
|
case 's':
|
|
sprintf(buf, "%lu", (unsigned long) ctx->now);
|
|
break;
|
|
case 'S':
|
|
sprintf(buf, "%02d", tm->tm_sec);
|
|
break;
|
|
case 'T':
|
|
sprintf(buf, "%02d%02d%02d", tm->tm_hour, tm->tm_min,
|
|
tm->tm_sec);
|
|
break;
|
|
case 'u':
|
|
#ifdef TEST_PROGRAM
|
|
strcpy(buf, "tytso");
|
|
break;
|
|
#else
|
|
#ifdef HAVE_GETPWUID_R
|
|
getpwuid_r(getuid(), &pw_struct, buf, sizeof(buf), &pw);
|
|
#else
|
|
pw = getpwuid(getuid());
|
|
#endif
|
|
if (pw)
|
|
append_string(s, pw->pw_name, 0);
|
|
return;
|
|
#endif
|
|
case 'U':
|
|
*flags |= FLAG_UTC;
|
|
return;
|
|
case 'y':
|
|
sprintf(buf, "%02d", tm->tm_year % 100);
|
|
break;
|
|
case 'Y':
|
|
sprintf(buf, "%d", tm->tm_year + 1900);
|
|
break;
|
|
}
|
|
append_string(s, buf, 0);
|
|
}
|
|
|
|
static void expand_logfn(e2fsck_t ctx, const char *log_fn, struct string *s)
|
|
{
|
|
const char *cp;
|
|
int i;
|
|
int flags = 0;
|
|
|
|
alloc_string(s, 100);
|
|
for (cp = log_fn; *cp; cp++) {
|
|
if (cp[0] == '%') {
|
|
cp++;
|
|
expand_percent_expression(ctx, *cp, s, &flags);
|
|
continue;
|
|
}
|
|
for (i = 0; cp[i]; i++)
|
|
if (cp[i] == '%')
|
|
break;
|
|
append_string(s, cp, i);
|
|
cp += i-1;
|
|
}
|
|
}
|
|
|
|
static int outbufsize;
|
|
static void *outbuf;
|
|
|
|
static int do_read(int fd)
|
|
{
|
|
int c;
|
|
char *n;
|
|
char buffer[4096];
|
|
|
|
c = read(fd, buffer, sizeof(buffer)-1);
|
|
if (c <= 0)
|
|
return c;
|
|
|
|
n = realloc(outbuf, outbufsize + c);
|
|
if (n) {
|
|
outbuf = n;
|
|
memcpy(((char *)outbuf)+outbufsize, buffer, c);
|
|
outbufsize += c;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
/*
|
|
* Fork a child process to save the output of the logfile until the
|
|
* appropriate file system is mounted read/write.
|
|
*/
|
|
static FILE *save_output(const char *s0, const char *s1, const char *s2)
|
|
{
|
|
int c, fd, fds[2];
|
|
char *cp;
|
|
pid_t pid;
|
|
FILE *ret;
|
|
|
|
if (s0 && *s0 == 0)
|
|
s0 = 0;
|
|
if (s1 && *s1 == 0)
|
|
s1 = 0;
|
|
if (s2 && *s2 == 0)
|
|
s2 = 0;
|
|
|
|
/* At least one potential output file name is valid */
|
|
if (!s0 && !s1 && !s2)
|
|
return NULL;
|
|
if (pipe(fds) < 0) {
|
|
perror("pipe");
|
|
exit(1);
|
|
}
|
|
|
|
pid = fork();
|
|
if (pid < 0) {
|
|
perror("fork");
|
|
exit(1);
|
|
}
|
|
|
|
if (pid == 0) {
|
|
if (e2fsck_global_ctx && e2fsck_global_ctx->progress_fd)
|
|
close(e2fsck_global_ctx->progress_fd);
|
|
if (daemon(0, 0) < 0) {
|
|
perror("daemon");
|
|
exit(1);
|
|
}
|
|
/*
|
|
* Grab the output from our parent
|
|
*/
|
|
close(fds[1]);
|
|
while (do_read(fds[0]) > 0)
|
|
;
|
|
close(fds[0]);
|
|
|
|
/* OK, now let's try to open the output file */
|
|
fd = -1;
|
|
while (1) {
|
|
if (fd < 0 && s0)
|
|
fd = open(s0, O_WRONLY|O_CREAT|O_TRUNC, 0644);
|
|
if (fd < 0 && s1)
|
|
fd = open(s1, O_WRONLY|O_CREAT|O_TRUNC, 0644);
|
|
if (fd < 0 && s2)
|
|
fd = open(s2, O_WRONLY|O_CREAT|O_TRUNC, 0644);
|
|
if (fd >= 0)
|
|
break;
|
|
sleep(1);
|
|
}
|
|
|
|
cp = outbuf;
|
|
while (outbufsize > 0) {
|
|
c = write(fd, cp, outbufsize);
|
|
if (c < 0) {
|
|
if ((errno == EAGAIN) || (errno == EINTR))
|
|
continue;
|
|
break;
|
|
}
|
|
outbufsize -= c;
|
|
cp += c;
|
|
}
|
|
exit(0);
|
|
}
|
|
|
|
close(fds[0]);
|
|
ret = fdopen(fds[1], "w");
|
|
if (!ret)
|
|
close(fds[1]);
|
|
return ret;
|
|
}
|
|
|
|
#ifndef TEST_PROGRAM
|
|
static FILE *set_up_log_file(e2fsck_t ctx, const char *key, const char *fn)
|
|
{
|
|
FILE *f = NULL;
|
|
struct string s, s1, s2;
|
|
char *s0 = 0, *log_dir = 0, *log_fn = 0;
|
|
int log_dir_wait = 0;
|
|
|
|
s.s = s1.s = s2.s = 0;
|
|
|
|
profile_get_boolean(ctx->profile, "options", "log_dir_wait", 0, 0,
|
|
&log_dir_wait);
|
|
if (fn)
|
|
log_fn = string_copy(ctx, fn, 0);
|
|
else
|
|
profile_get_string(ctx->profile, "options", key,
|
|
0, 0, &log_fn);
|
|
profile_get_string(ctx->profile, "options", "log_dir", 0, 0, &log_dir);
|
|
|
|
if (!log_fn || !log_fn[0])
|
|
goto out;
|
|
|
|
expand_logfn(ctx, log_fn, &s);
|
|
if ((log_fn[0] == '/') || !log_dir || !log_dir[0])
|
|
s0 = s.s;
|
|
|
|
if (log_dir && log_dir[0]) {
|
|
alloc_string(&s1, strlen(log_dir) + strlen(s.s) + 2);
|
|
append_string(&s1, log_dir, 0);
|
|
append_string(&s1, "/", 1);
|
|
append_string(&s1, s.s, 0);
|
|
}
|
|
|
|
free(log_dir);
|
|
profile_get_string(ctx->profile, "options", "log_dir_fallback", 0, 0,
|
|
&log_dir);
|
|
if (log_dir && log_dir[0]) {
|
|
alloc_string(&s2, strlen(log_dir) + strlen(s.s) + 2);
|
|
append_string(&s2, log_dir, 0);
|
|
append_string(&s2, "/", 1);
|
|
append_string(&s2, s.s, 0);
|
|
printf("%s\n", s2.s);
|
|
}
|
|
|
|
if (s0)
|
|
f = fopen(s0, "w");
|
|
if (!f && s1.s)
|
|
f = fopen(s1.s, "w");
|
|
if (!f && s2.s)
|
|
f = fopen(s2.s, "w");
|
|
if (!f && log_dir_wait)
|
|
f = save_output(s0, s1.s, s2.s);
|
|
|
|
out:
|
|
free(s.s);
|
|
free(s1.s);
|
|
free(s2.s);
|
|
free(log_fn);
|
|
free(log_dir);
|
|
return f;
|
|
}
|
|
|
|
void set_up_logging(e2fsck_t ctx)
|
|
{
|
|
ctx->logf = set_up_log_file(ctx, "log_filename", ctx->log_fn);
|
|
ctx->problem_logf = set_up_log_file(ctx, "problem_log_filename",
|
|
ctx->problem_log_fn);
|
|
}
|
|
#else
|
|
void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
|
|
const char *description)
|
|
{
|
|
void *ret;
|
|
char buf[256];
|
|
|
|
ret = malloc(size);
|
|
if (!ret) {
|
|
sprintf(buf, "Can't allocate %s\n", description);
|
|
exit(1);
|
|
}
|
|
memset(ret, 0, size);
|
|
return ret;
|
|
}
|
|
|
|
errcode_t e2fsck_allocate_context(e2fsck_t *ret)
|
|
{
|
|
e2fsck_t context;
|
|
errcode_t retval;
|
|
char *time_env;
|
|
|
|
context = malloc(sizeof(struct e2fsck_struct));
|
|
if (!context)
|
|
return ENOMEM;
|
|
|
|
memset(context, 0, sizeof(struct e2fsck_struct));
|
|
|
|
context->now = 1332006474;
|
|
|
|
context->filesystem_name = "/dev/sda3";
|
|
context->device_name = "fslabel";
|
|
|
|
*ret = context;
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
e2fsck_t ctx;
|
|
struct string s;
|
|
|
|
putenv("TZ=EST+5:00");
|
|
e2fsck_allocate_context(&ctx);
|
|
expand_logfn(ctx, "e2fsck-%N.%h.%u.%D-%T", &s);
|
|
printf("%s\n", s.s);
|
|
free(s.s);
|
|
expand_logfn(ctx, "e2fsck-%N.%h.%u.%Y%m%d-%H%M%S", &s);
|
|
printf("%s\n", s.s);
|
|
free(s.s);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|