logfile.c revision e0ed7404719a9ddd2ba427a80db5365c8bad18c0
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
22struct string {
23	char	*s;
24	int	len;
25	int	end;
26};
27
28static 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
36static 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
66static 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
159static 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
180static int	outbufsize;
181static void	*outbuf;
182
183static 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 */
206static 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
283void 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
334out:
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
343void *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
358errcode_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
379int 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