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