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