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