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 = len; 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 } 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) { 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 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 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 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 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