xref: /aosp_15_r20/external/e2fsprogs/e2fsck/logfile.c (revision 6a54128f25917bfc36a8a6e9d722c04a0b4641b6)
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 
alloc_string(struct string * s,int len)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 = s->s ? len : 0;
36 	s->end = 0;
37 }
38 
append_string(struct string * s,const char * a,int len)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 
expand_percent_expression(e2fsck_t ctx,char ch,struct string * s,int * flags)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 	default:
159 		sprintf(buf, "%%%c", ch);
160 		break;
161 	}
162 	append_string(s, buf, 0);
163 }
164 
expand_logfn(e2fsck_t ctx,const char * log_fn,struct string * s)165 static void expand_logfn(e2fsck_t ctx, const char *log_fn, struct string *s)
166 {
167 	const char	*cp;
168 	int		i;
169 	int		flags = 0;
170 
171 	alloc_string(s, 100);
172 	for (cp = log_fn; *cp; cp++) {
173 		if (cp[0] == '%') {
174 			cp++;
175 			expand_percent_expression(ctx, *cp, s, &flags);
176 			continue;
177 		}
178 		for (i = 0; cp[i]; i++)
179 			if (cp[i] == '%')
180 				break;
181 		append_string(s, cp, i);
182 		cp += i-1;
183 	}
184 }
185 
186 static int	outbufsize;
187 static void	*outbuf;
188 
do_read(int fd)189 static int do_read(int fd)
190 {
191 	int	c;
192 	char		*n;
193 	char	buffer[4096];
194 
195 	c = read(fd, buffer, sizeof(buffer)-1);
196 	if (c <= 0)
197 		return c;
198 
199 	n = realloc(outbuf, outbufsize + c);
200 	if (n) {
201 		outbuf = n;
202 		memcpy(((char *)outbuf)+outbufsize, buffer, c);
203 		outbufsize += c;
204 	}
205 	return c;
206 }
207 
208 /*
209  * Fork a child process to save the output of the logfile until the
210  * appropriate file system is mounted read/write.
211  */
save_output(const char * s0,const char * s1,const char * s2)212 static FILE *save_output(const char *s0, const char *s1, const char *s2)
213 {
214 	int c, fd, fds[2];
215 	char *cp;
216 	pid_t pid;
217 	FILE *ret;
218 
219 	if (s0 && *s0 == 0)
220 		s0 = 0;
221 	if (s1 && *s1 == 0)
222 		s1 = 0;
223 	if (s2 && *s2 == 0)
224 		s2 = 0;
225 
226 	/* At least one potential output file name is valid */
227 	if (!s0 && !s1 && !s2)
228 		return NULL;
229 	if (pipe(fds) < 0) {
230 		perror("pipe");
231 		exit(1);
232 	}
233 
234 	pid = fork();
235 	if (pid < 0) {
236 		perror("fork");
237 		exit(1);
238 	}
239 
240 	if (pid == 0) {
241 		if (e2fsck_global_ctx && e2fsck_global_ctx->progress_fd)
242 			close(e2fsck_global_ctx->progress_fd);
243 		if (daemon(0, 0) < 0) {
244 			perror("daemon");
245 			exit(1);
246 		}
247 		/*
248 		 * Grab the output from our parent
249 		 */
250 		close(fds[1]);
251 		while (do_read(fds[0]) > 0)
252 			;
253 		close(fds[0]);
254 
255 		/* OK, now let's try to open the output file */
256 		fd = -1;
257 		while (1) {
258 			if (fd < 0 && s0)
259 				fd = open(s0, O_WRONLY|O_CREAT|O_TRUNC, 0644);
260 			if (fd < 0 && s1)
261 				fd = open(s1, O_WRONLY|O_CREAT|O_TRUNC, 0644);
262 			if (fd < 0 && s2)
263 				fd = open(s2, O_WRONLY|O_CREAT|O_TRUNC, 0644);
264 			if (fd >= 0)
265 				break;
266 			sleep(1);
267 		}
268 
269 		cp = outbuf;
270 		while (outbufsize > 0) {
271 			c = write(fd, cp, outbufsize);
272 			if (c < 0) {
273 				if ((errno == EAGAIN) || (errno == EINTR))
274 					continue;
275 				break;
276 			}
277 			outbufsize -= c;
278 			cp += c;
279 		}
280 		exit(0);
281 	}
282 
283 	close(fds[0]);
284 	ret = fdopen(fds[1], "w");
285 	if (!ret)
286 		close(fds[1]);
287 	return ret;
288 }
289 
290 #ifndef TEST_PROGRAM
set_up_log_file(e2fsck_t ctx,const char * key,const char * fn)291 static FILE *set_up_log_file(e2fsck_t ctx, const char *key, const char *fn)
292 {
293 	FILE *f = NULL;
294 	struct string s, s1, s2;
295 	char *s0 = 0, *log_dir = 0, *log_fn = 0;
296 	int log_dir_wait = 0;
297 
298 	s.s = s1.s = s2.s = 0;
299 
300 	profile_get_boolean(ctx->profile, "options", "log_dir_wait", 0, 0,
301 			    &log_dir_wait);
302 	if (fn)
303 		log_fn = string_copy(ctx, fn, 0);
304 	else
305 		profile_get_string(ctx->profile, "options", key,
306 				   0, 0, &log_fn);
307 	profile_get_string(ctx->profile, "options", "log_dir", 0, 0, &log_dir);
308 
309 	if (!log_fn || !log_fn[0])
310 		goto out;
311 
312 	expand_logfn(ctx, log_fn, &s);
313 	if ((log_fn[0] == '/') || !log_dir || !log_dir[0])
314 		s0 = s.s;
315 
316 	if (log_dir && log_dir[0]) {
317 		alloc_string(&s1, strlen(log_dir) + strlen(s.s) + 2);
318 		append_string(&s1, log_dir, 0);
319 		append_string(&s1, "/", 1);
320 		append_string(&s1, s.s, 0);
321 	}
322 
323 	free(log_dir);
324 	profile_get_string(ctx->profile, "options", "log_dir_fallback", 0, 0,
325 			   &log_dir);
326 	if (log_dir && log_dir[0]) {
327 		alloc_string(&s2, strlen(log_dir) + strlen(s.s) + 2);
328 		append_string(&s2, log_dir, 0);
329 		append_string(&s2, "/", 1);
330 		append_string(&s2, s.s, 0);
331 		printf("%s\n", s2.s);
332 	}
333 
334 	if (s0)
335 		f = fopen(s0, "w");
336 	if (!f && s1.s)
337 		f = fopen(s1.s, "w");
338 	if (!f && s2.s)
339 		f = fopen(s2.s, "w");
340 	if (!f && log_dir_wait)
341 		f = save_output(s0, s1.s, s2.s);
342 
343 out:
344 	free(s.s);
345 	free(s1.s);
346 	free(s2.s);
347 	free(log_fn);
348 	free(log_dir);
349 	return f;
350 }
351 
set_up_logging(e2fsck_t ctx)352 void set_up_logging(e2fsck_t ctx)
353 {
354 	ctx->logf = set_up_log_file(ctx, "log_filename", ctx->log_fn);
355 	ctx->problem_logf = set_up_log_file(ctx, "problem_log_filename",
356 					    ctx->problem_log_fn);
357 }
358 #else
e2fsck_allocate_memory(e2fsck_t ctx,unsigned long size,const char * description)359 void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned long size,
360 			     const char *description)
361 {
362 	void *ret;
363 	char buf[256];
364 
365 	ret = malloc(size);
366 	if (!ret) {
367 		sprintf(buf, "Can't allocate %s\n", description);
368 		exit(1);
369 	}
370 	memset(ret, 0, size);
371 	return ret;
372 }
373 
e2fsck_allocate_context(e2fsck_t * ret)374 errcode_t e2fsck_allocate_context(e2fsck_t *ret)
375 {
376 	e2fsck_t	context;
377 	errcode_t	retval;
378 	char		*time_env;
379 
380 	context = malloc(sizeof(struct e2fsck_struct));
381 	if (!context)
382 		return ENOMEM;
383 
384 	memset(context, 0, sizeof(struct e2fsck_struct));
385 
386 	context->now = 1332006474;
387 
388 	context->filesystem_name = "/dev/sda3";
389 	context->device_name = "fslabel";
390 
391 	*ret = context;
392 	return 0;
393 }
394 
main(int argc,char ** argv)395 int main(int argc, char **argv)
396 {
397 	e2fsck_t	ctx;
398 	struct string	s;
399 
400 	putenv("TZ=EST+5:00");
401 	e2fsck_allocate_context(&ctx);
402 	expand_logfn(ctx, "e2fsck-%N.%h.%u.%D-%T", &s);
403 	printf("%s\n", s.s);
404 	free(s.s);
405 	expand_logfn(ctx, "e2fsck-%N.%h.%u.%Y%m%d-%H%M%S", &s);
406 	printf("%s\n", s.s);
407 	free(s.s);
408 
409 	return 0;
410 }
411 #endif
412