Home | History | Annotate | Download | only in e2fsck
      1 /*
      2  * logfile.c --- set up e2fsck log files
      3  *
      4  * Copyright 1996, 1997 by Theodore Ts'o
      5  *
      6  * %Begin-Header%
      7  * This file may be redistributed under the terms of the GNU Public
      8  * License.
      9  * %End-Header%
     10  */
     11 
     12 #include "config.h"
     13 #ifdef HAVE_ERRNO_H
     14 #include <errno.h>
     15 #endif
     16 #include <sys/types.h>
     17 #include <sys/stat.h>
     18 #include <fcntl.h>
     19 
     20 #include "e2fsck.h"
     21 #include <pwd.h>
     22 
     23 extern e2fsck_t e2fsck_global_ctx;   /* Try your very best not to use this! */
     24 
     25 struct string {
     26 	char	*s;
     27 	int	len;
     28 	int	end;
     29 };
     30 
     31 static void alloc_string(struct string *s, int len)
     32 {
     33 	s->s = malloc(len);
     34 /* e2fsck_allocate_memory(ctx, len, "logfile name"); */
     35 	s->len = len;
     36 	s->end = 0;
     37 }
     38 
     39 static void append_string(struct string *s, const char *a, int len)
     40 {
     41 	int needlen;
     42 
     43 	if (!len)
     44 		len = strlen(a);
     45 
     46 	needlen = s->end + len + 1;
     47 	if (needlen > s->len) {
     48 		char *n;
     49 
     50 		if (s->len * 2 > needlen)
     51 			needlen = s->len * 2;
     52 	        n = realloc(s->s, needlen);
     53 
     54 		if (n) {
     55 			s->s = n;
     56 			s->len = needlen;
     57 		} else {
     58 			/* Don't append if we ran out of memory */
     59 			return;
     60 		}
     61 	}
     62 	memcpy(s->s + s->end, a, len);
     63 	s->end += len;
     64 	s->s[s->end] = 0;
     65 }
     66 
     67 #define FLAG_UTC	0x0001
     68 
     69 static void expand_percent_expression(e2fsck_t ctx, char ch,
     70 				      struct string *s, int *flags)
     71 {
     72 	struct tm	*tm = NULL, tm_struct;
     73 	struct passwd	*pw = NULL, pw_struct;
     74 	char		*cp;
     75 	char		buf[256];
     76 
     77 	if ((ch == 'D') || (ch == 'd') || (ch == 'm') || (ch == 'y') ||
     78 	    (ch == 'Y') ||
     79 	    (ch == 'T') || (ch == 'H') || (ch == 'M') || (ch == 'S')) {
     80 		tzset();
     81 		tm = (*flags & FLAG_UTC) ? gmtime_r(&ctx->now, &tm_struct) :
     82 			localtime_r(&ctx->now, &tm_struct);
     83 	}
     84 
     85 	switch (ch) {
     86 	case '%':
     87 		append_string(s, "%", 1);
     88 		return;
     89 	case 'd':
     90 		sprintf(buf, "%02d", tm->tm_mday);
     91 		break;
     92 	case 'D':
     93 		sprintf(buf, "%d%02d%02d", tm->tm_year + 1900, tm->tm_mon + 1,
     94 			tm->tm_mday);
     95 		break;
     96 	case 'h':
     97 #ifdef TEST_PROGRAM
     98 		strcpy(buf, "server");
     99 #else
    100 		buf[0] = 0;
    101 		gethostname(buf, sizeof(buf));
    102 		buf[sizeof(buf)-1] = 0;
    103 #endif
    104 		break;
    105 	case 'H':
    106 		sprintf(buf, "%02d", tm->tm_hour);
    107 		break;
    108 	case 'm':
    109 		sprintf(buf, "%02d", tm->tm_mon + 1);
    110 		break;
    111 	case 'M':
    112 		sprintf(buf, "%02d", tm->tm_min);
    113 		break;
    114 	case 'N':		/* block device name */
    115 		cp = strrchr(ctx->filesystem_name, '/');
    116 		if (cp)
    117 			cp++;
    118 		else
    119 			cp = ctx->filesystem_name;
    120 		append_string(s, cp, 0);
    121 		return;
    122 	case 'p':
    123 		sprintf(buf, "%lu", (unsigned long) getpid());
    124 		break;
    125 	case 's':
    126 		sprintf(buf, "%lu", (unsigned long) ctx->now);
    127 		break;
    128 	case 'S':
    129 		sprintf(buf, "%02d", tm->tm_sec);
    130 		break;
    131 	case 'T':
    132 		sprintf(buf, "%02d%02d%02d", tm->tm_hour, tm->tm_min,
    133 			tm->tm_sec);
    134 		break;
    135 	case 'u':
    136 #ifdef TEST_PROGRAM
    137 		strcpy(buf, "tytso");
    138 		break;
    139 #else
    140 #ifdef HAVE_GETPWUID_R
    141 		getpwuid_r(getuid(), &pw_struct, buf, sizeof(buf), &pw);
    142 #else
    143 		pw = getpwuid(getuid());
    144 #endif
    145 		if (pw)
    146 			append_string(s, pw->pw_name, 0);
    147 		return;
    148 #endif
    149 	case 'U':
    150 		*flags |= FLAG_UTC;
    151 		return;
    152 	case 'y':
    153 		sprintf(buf, "%02d", tm->tm_year % 100);
    154 		break;
    155 	case 'Y':
    156 		sprintf(buf, "%d", tm->tm_year + 1900);
    157 		break;
    158 	}
    159 	append_string(s, buf, 0);
    160 }
    161 
    162 static void expand_logfn(e2fsck_t ctx, const char *log_fn, struct string *s)
    163 {
    164 	const char	*cp;
    165 	int		i;
    166 	int		flags = 0;
    167 
    168 	alloc_string(s, 100);
    169 	for (cp = log_fn; *cp; cp++) {
    170 		if (cp[0] == '%') {
    171 			cp++;
    172 			expand_percent_expression(ctx, *cp, s, &flags);
    173 			continue;
    174 		}
    175 		for (i = 0; cp[i]; i++)
    176 			if (cp[i] == '%')
    177 				break;
    178 		append_string(s, cp, i);
    179 		cp += i-1;
    180 	}
    181 }
    182 
    183 static int	outbufsize;
    184 static void	*outbuf;
    185 
    186 static int do_read(int fd)
    187 {
    188 	int	c;
    189 	char		*n;
    190 	char	buffer[4096];
    191 
    192 	c = read(fd, buffer, sizeof(buffer)-1);
    193 	if (c <= 0)
    194 		return c;
    195 
    196 	n = realloc(outbuf, outbufsize + c);
    197 	if (n) {
    198 		outbuf = n;
    199 		memcpy(((char *)outbuf)+outbufsize, buffer, c);
    200 		outbufsize += c;
    201 	}
    202 	return c;
    203 }
    204 
    205 /*
    206  * Fork a child process to save the output of the logfile until the
    207  * appropriate file system is mounted read/write.
    208  */
    209 static FILE *save_output(const char *s0, const char *s1, const char *s2)
    210 {
    211 	int c, fd, fds[2];
    212 	char *cp;
    213 	pid_t pid;
    214 	FILE *ret;
    215 
    216 	if (s0 && *s0 == 0)
    217 		s0 = 0;
    218 	if (s1 && *s1 == 0)
    219 		s1 = 0;
    220 	if (s2 && *s2 == 0)
    221 		s2 = 0;
    222 
    223 	/* At least one potential output file name is valid */
    224 	if (!s0 && !s1 && !s2)
    225 		return NULL;
    226 	if (pipe(fds) < 0) {
    227 		perror("pipe");
    228 		exit(1);
    229 	}
    230 
    231 	pid = fork();
    232 	if (pid < 0) {
    233 		perror("fork");
    234 		exit(1);
    235 	}
    236 
    237 	if (pid == 0) {
    238 		if (e2fsck_global_ctx && e2fsck_global_ctx->progress_fd)
    239 			close(e2fsck_global_ctx->progress_fd);
    240 		if (daemon(0, 0) < 0) {
    241 			perror("daemon");
    242 			exit(1);
    243 		}
    244 		/*
    245 		 * Grab the output from our parent
    246 		 */
    247 		close(fds[1]);
    248 		while (do_read(fds[0]) > 0)
    249 			;
    250 		close(fds[0]);
    251 
    252 		/* OK, now let's try to open the output file */
    253 		fd = -1;
    254 		while (1) {
    255 			if (fd < 0 && s0)
    256 				fd = open(s0, O_WRONLY|O_CREAT|O_TRUNC, 0644);
    257 			if (fd < 0 && s1)
    258 				fd = open(s1, O_WRONLY|O_CREAT|O_TRUNC, 0644);
    259 			if (fd < 0 && s2)
    260 				fd = open(s2, O_WRONLY|O_CREAT|O_TRUNC, 0644);
    261 			if (fd >= 0)
    262 				break;
    263 			sleep(1);
    264 		}
    265 
    266 		cp = outbuf;
    267 		while (outbufsize > 0) {
    268 			c = write(fd, cp, outbufsize);
    269 			if (c < 0) {
    270 				if ((errno == EAGAIN) || (errno == EINTR))
    271 					continue;
    272 				break;
    273 			}
    274 			outbufsize -= c;
    275 			cp += c;
    276 		}
    277 		exit(0);
    278 	}
    279 
    280 	close(fds[0]);
    281 	ret = fdopen(fds[1], "w");
    282 	if (!ret)
    283 		close(fds[1]);
    284 	return ret;
    285 }
    286 
    287 #ifndef TEST_PROGRAM
    288 void set_up_logging(e2fsck_t ctx)
    289 {
    290 	struct string s, s1, s2;
    291 	char *s0 = 0, *log_dir = 0, *log_fn = 0;
    292 	int log_dir_wait = 0;
    293 
    294 	s.s = s1.s = s2.s = 0;
    295 
    296 	profile_get_boolean(ctx->profile, "options", "log_dir_wait", 0, 0,
    297 			    &log_dir_wait);
    298 	if (ctx->log_fn)
    299 		log_fn = string_copy(ctx, ctx->log_fn, 0);
    300 	else
    301 		profile_get_string(ctx->profile, "options", "log_filename",
    302 				   0, 0, &log_fn);
    303 	profile_get_string(ctx->profile, "options", "log_dir", 0, 0, &log_dir);
    304 
    305 	if (!log_fn || !log_fn[0])
    306 		goto out;
    307 
    308 	expand_logfn(ctx, log_fn, &s);
    309 	if ((log_fn[0] == '/') || !log_dir || !log_dir[0])
    310 		s0 = s.s;
    311 
    312 	if (log_dir && log_dir[0]) {
    313 		alloc_string(&s1, strlen(log_dir) + strlen(s.s) + 2);
    314 		append_string(&s1, log_dir, 0);
    315 		append_string(&s1, "/", 1);
    316 		append_string(&s1, s.s, 0);
    317 	}
    318 
    319 	free(log_dir);
    320 	profile_get_string(ctx->profile, "options", "log_dir_fallback", 0, 0,
    321 			   &log_dir);
    322 	if (log_dir && log_dir[0]) {
    323 		alloc_string(&s2, strlen(log_dir) + strlen(s.s) + 2);
    324 		append_string(&s2, log_dir, 0);
    325 		append_string(&s2, "/", 1);
    326 		append_string(&s2, s.s, 0);
    327 		printf("%s\n", s2.s);
    328 	}
    329 
    330 	if (s0)
    331 		ctx->logf = fopen(s0, "w");
    332 	if (!ctx->logf && s1.s)
    333 		ctx->logf = fopen(s1.s, "w");
    334 	if (!ctx->logf && s2.s)
    335 		ctx->logf = fopen(s2.s, "w");
    336 	if (!ctx->logf && log_dir_wait)
    337 		ctx->logf = save_output(s0, s1.s, s2.s);
    338 
    339 out:
    340 	free(s.s);
    341 	free(s1.s);
    342 	free(s2.s);
    343 	free(log_fn);
    344 	free(log_dir);
    345 	return;
    346 }
    347 #else
    348 void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
    349 			     const char *description)
    350 {
    351 	void *ret;
    352 	char buf[256];
    353 
    354 	ret = malloc(size);
    355 	if (!ret) {
    356 		sprintf(buf, "Can't allocate %s\n", description);
    357 		exit(1);
    358 	}
    359 	memset(ret, 0, size);
    360 	return ret;
    361 }
    362 
    363 errcode_t e2fsck_allocate_context(e2fsck_t *ret)
    364 {
    365 	e2fsck_t	context;
    366 	errcode_t	retval;
    367 	char		*time_env;
    368 
    369 	context = malloc(sizeof(struct e2fsck_struct));
    370 	if (!context)
    371 		return ENOMEM;
    372 
    373 	memset(context, 0, sizeof(struct e2fsck_struct));
    374 
    375 	context->now = 1332006474;
    376 
    377 	context->filesystem_name = "/dev/sda3";
    378 	context->device_name = "fslabel";
    379 
    380 	*ret = context;
    381 	return 0;
    382 }
    383 
    384 int main(int argc, char **argv)
    385 {
    386 	e2fsck_t	ctx;
    387 	struct string	s;
    388 
    389 	putenv("TZ=EST+5:00");
    390 	e2fsck_allocate_context(&ctx);
    391 	expand_logfn(ctx, "e2fsck-%N.%h.%u.%D-%T", &s);
    392 	printf("%s\n", s.s);
    393 	free(s.s);
    394 	expand_logfn(ctx, "e2fsck-%N.%h.%u.%Y%m%d-%H%M%S", &s);
    395 	printf("%s\n", s.s);
    396 	free(s.s);
    397 
    398 	return 0;
    399 }
    400 #endif
    401