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