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_logging(e2fsck_t ctx)288 void set_up_logging(e2fsck_t ctx)
289 {
290 struct string s, s1, s2;
291 char *s0 = 0, *log_dir = 0, *log_fn = 0;
292 int log_dir_wait = 0;
293
294 s.s = s1.s = s2.s = 0;
295
296 profile_get_boolean(ctx->profile, "options", "log_dir_wait", 0, 0,
297 &log_dir_wait);
298 if (ctx->log_fn)
299 log_fn = string_copy(ctx, ctx->log_fn, 0);
300 else
301 profile_get_string(ctx->profile, "options", "log_filename",
302 0, 0, &log_fn);
303 profile_get_string(ctx->profile, "options", "log_dir", 0, 0, &log_dir);
304
305 if (!log_fn || !log_fn[0])
306 goto out;
307
308 expand_logfn(ctx, log_fn, &s);
309 if ((log_fn[0] == '/') || !log_dir || !log_dir[0])
310 s0 = s.s;
311
312 if (log_dir && log_dir[0]) {
313 alloc_string(&s1, strlen(log_dir) + strlen(s.s) + 2);
314 append_string(&s1, log_dir, 0);
315 append_string(&s1, "/", 1);
316 append_string(&s1, s.s, 0);
317 }
318
319 free(log_dir);
320 profile_get_string(ctx->profile, "options", "log_dir_fallback", 0, 0,
321 &log_dir);
322 if (log_dir && log_dir[0]) {
323 alloc_string(&s2, strlen(log_dir) + strlen(s.s) + 2);
324 append_string(&s2, log_dir, 0);
325 append_string(&s2, "/", 1);
326 append_string(&s2, s.s, 0);
327 printf("%s\n", s2.s);
328 }
329
330 if (s0)
331 ctx->logf = fopen(s0, "w");
332 if (!ctx->logf && s1.s)
333 ctx->logf = fopen(s1.s, "w");
334 if (!ctx->logf && s2.s)
335 ctx->logf = fopen(s2.s, "w");
336 if (!ctx->logf && log_dir_wait)
337 ctx->logf = save_output(s0, s1.s, s2.s);
338
339 out:
340 free(s.s);
341 free(s1.s);
342 free(s2.s);
343 free(log_fn);
344 free(log_dir);
345 return;
346 }
347 #else
e2fsck_allocate_memory(e2fsck_t ctx,unsigned int size,const char * description)348 void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int size,
349 const char *description)
350 {
351 void *ret;
352 char buf[256];
353
354 ret = malloc(size);
355 if (!ret) {
356 sprintf(buf, "Can't allocate %s\n", description);
357 exit(1);
358 }
359 memset(ret, 0, size);
360 return ret;
361 }
362
e2fsck_allocate_context(e2fsck_t * ret)363 errcode_t e2fsck_allocate_context(e2fsck_t *ret)
364 {
365 e2fsck_t context;
366 errcode_t retval;
367 char *time_env;
368
369 context = malloc(sizeof(struct e2fsck_struct));
370 if (!context)
371 return ENOMEM;
372
373 memset(context, 0, sizeof(struct e2fsck_struct));
374
375 context->now = 1332006474;
376
377 context->filesystem_name = "/dev/sda3";
378 context->device_name = "fslabel";
379
380 *ret = context;
381 return 0;
382 }
383
main(int argc,char ** argv)384 int main(int argc, char **argv)
385 {
386 e2fsck_t ctx;
387 struct string s;
388
389 putenv("TZ=EST+5:00");
390 e2fsck_allocate_context(&ctx);
391 expand_logfn(ctx, "e2fsck-%N.%h.%u.%D-%T", &s);
392 printf("%s\n", s.s);
393 free(s.s);
394 expand_logfn(ctx, "e2fsck-%N.%h.%u.%Y%m%d-%H%M%S", &s);
395 printf("%s\n", s.s);
396 free(s.s);
397
398 return 0;
399 }
400 #endif
401