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