]>
Commit | Line | Data |
---|---|---|
b0e91c89 TT |
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 | |
182acd17 AD |
16 | #include <sys/types.h> |
17 | #include <sys/stat.h> | |
18 | #include <fcntl.h> | |
b0e91c89 TT |
19 | |
20 | #include "e2fsck.h" | |
21 | #include <pwd.h> | |
22 | ||
a4f95cca TT |
23 | extern e2fsck_t e2fsck_global_ctx; /* Try your very best not to use this! */ |
24 | ||
b0e91c89 TT |
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 = len; | |
36 | s->end = 0; | |
37 | } | |
38 | ||
39 | static void append_string(struct string *s, const char *a, int len) | |
40 | { | |
6dd83548 NZ |
41 | int needlen; |
42 | ||
b0e91c89 TT |
43 | if (!len) |
44 | len = strlen(a); | |
45 | ||
6dd83548 NZ |
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); | |
b0e91c89 TT |
53 | |
54 | if (n) { | |
55 | s->s = n; | |
6dd83548 | 56 | s->len = needlen; |
b0e91c89 | 57 | } else { |
6dd83548 NZ |
58 | /* Don't append if we ran out of memory */ |
59 | return; | |
b0e91c89 TT |
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 | |
25ff7725 | 140 | #ifdef HAVE_GETPWUID_R |
b0e91c89 | 141 | getpwuid_r(getuid(), &pw_struct, buf, sizeof(buf), &pw); |
25ff7725 TT |
142 | #else |
143 | pw = getpwuid(getuid()); | |
144 | #endif | |
b0e91c89 TT |
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 | ||
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 | ||
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 | */ | |
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) { | |
a4f95cca TT |
238 | if (e2fsck_global_ctx && e2fsck_global_ctx->progress_fd) |
239 | close(e2fsck_global_ctx->progress_fd); | |
b0e91c89 TT |
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 | |
d2dd606f | 288 | static FILE *set_up_log_file(e2fsck_t ctx, const char *key, const char *fn) |
b0e91c89 | 289 | { |
d2dd606f | 290 | FILE *f = NULL; |
b0e91c89 TT |
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); | |
d2dd606f TT |
299 | if (fn) |
300 | log_fn = string_copy(ctx, fn, 0); | |
b0e91c89 | 301 | else |
d2dd606f | 302 | profile_get_string(ctx->profile, "options", key, |
b0e91c89 TT |
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) | |
d2dd606f TT |
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); | |
b0e91c89 TT |
339 | |
340 | out: | |
341 | free(s.s); | |
342 | free(s1.s); | |
343 | free(s2.s); | |
344 | free(log_fn); | |
345 | free(log_dir); | |
d2dd606f TT |
346 | return f; |
347 | } | |
348 | ||
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); | |
b0e91c89 TT |
354 | } |
355 | #else | |
356 | void *e2fsck_allocate_memory(e2fsck_t ctx, unsigned int 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 | ||
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 | ||
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 |