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