Whamcloud - gitweb
Merge branch 'maint' into next
[tools/e2fsprogs.git] / e2fsck / logfile.c
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
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
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
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
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
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  */
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
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
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
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
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
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