1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 * logadm/kw.c -- manage keywords table 27 * 28 * this module expands things like $file.$n in "templates". 29 * calling kw_init() sets the current "filename" used for 30 * $file, $dirname, and $basename. 31 * 32 * any time-based expansions, like $secs, or all the strftime() 33 * percent sequences, are based on the exact same point in time. 34 * so calling kw_expand() on something like "file-%T" will return 35 * the same thing when called multiple times during the same logadm run. 36 */ 37 38 #pragma ident "%Z%%M% %I% %E% SMI" 39 40 #include <stdio.h> 41 #include <libintl.h> 42 #include <stdlib.h> 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 #include <sys/utsname.h> 46 #include <sys/systeminfo.h> 47 #include <strings.h> 48 #include <time.h> 49 #include <ctype.h> 50 #include "err.h" 51 #include "lut.h" 52 #include "fn.h" 53 #include "kw.h" 54 55 /* forward declarations for functions used internally by this module */ 56 static void kw_printer(const char *lhs, void *rhs, void *arg); 57 58 /* 59 * absurdly long length to hold sprintf of a %d, 60 * or strftime() expansion of a *single* percent sequence 61 */ 62 #define MAXDIGITS 100 63 64 static struct lut *Keywords; /* lookup table for keywords */ 65 66 extern time_t Now; /* time used for keyword expansions */ 67 68 /* 69 * kw_init -- initialize keywords based on given filename 70 */ 71 void 72 kw_init(struct fn *fnp, struct fn *nfnp) 73 { 74 static char *fullpath; 75 static char *nfullpath; 76 static char *splitpath; 77 static char secs[MAXDIGITS]; 78 static struct utsname un; 79 static char platform[SYS_NMLN]; 80 static char isa[SYS_NMLN]; 81 static char domain[256]; 82 static char *home; 83 static char *user; 84 static char *logname; 85 static int initialized; 86 char *ptr; 87 88 /* make a copy of the string for $file */ 89 if (fullpath) 90 FREE(fullpath); 91 fullpath = STRDUP(fn_s(fnp)); 92 Keywords = lut_add(Keywords, "file", fullpath); 93 94 /* make a copy of the string for $nfile */ 95 if (nfullpath) 96 FREE(nfullpath); 97 if (nfnp == NULL) { 98 nfullpath = NULL; 99 Keywords = lut_add(Keywords, "nfile", ""); 100 } else { 101 nfullpath = STRDUP(fn_s(nfnp)); 102 Keywords = lut_add(Keywords, "nfile", nfullpath); 103 } 104 105 /* make a copy of the string for $dirname/$basename */ 106 if (splitpath) 107 FREE(splitpath); 108 splitpath = STRDUP(fn_s(fnp)); 109 110 if ((ptr = strrchr(splitpath, '/')) == NULL) { 111 Keywords = lut_add(Keywords, "basename", splitpath); 112 Keywords = lut_add(Keywords, "dirname", "."); 113 } else { 114 *ptr++ = '\0'; 115 Keywords = lut_add(Keywords, "basename", ptr); 116 Keywords = lut_add(Keywords, "dirname", splitpath); 117 } 118 119 if (initialized) 120 return; /* rest of the keywords don't change */ 121 122 (void) snprintf(secs, MAXDIGITS, "%d", (int)Now); 123 Keywords = lut_add(Keywords, "secs", secs); 124 125 if (uname(&un) < 0) 126 err(EF_SYS, "uname"); 127 128 Keywords = lut_add(Keywords, "nodename", un.nodename); 129 Keywords = lut_add(Keywords, "release", un.release); 130 Keywords = lut_add(Keywords, "machine", un.machine); 131 132 if (sysinfo(SI_ARCHITECTURE, isa, sizeof (isa)) == -1) 133 err(EF_WARN|EF_SYS, "sysinfo(SI_ARCHITECTURE) failed."); 134 else 135 Keywords = lut_add(Keywords, "isa", isa); 136 137 if (sysinfo(SI_PLATFORM, platform, sizeof (platform)) == -1) 138 err(EF_WARN|EF_SYS, "sysinfo(SI_PLATFORM) failed."); 139 else 140 Keywords = lut_add(Keywords, "platform", platform); 141 142 if (sysinfo(SI_SRPC_DOMAIN, domain, sizeof (domain)) == -1) 143 err(EF_WARN|EF_SYS, "sysinfo(SI_SRPC_DOMAIN) failed."); 144 else 145 Keywords = lut_add(Keywords, "domain", domain); 146 147 if ((home = getenv("HOME")) != NULL) 148 Keywords = lut_add(Keywords, "home", STRDUP(home)); 149 150 if ((user = getenv("USER")) != NULL) 151 Keywords = lut_add(Keywords, "user", STRDUP(user)); 152 153 if ((logname = getenv("LOGNAME")) != NULL) 154 Keywords = lut_add(Keywords, "logname", STRDUP(logname)); 155 156 initialized = 1; 157 } 158 159 /* helper function for kw_print() */ 160 static void 161 kw_printer(const char *lhs, void *rhs, void *arg) 162 { 163 FILE *stream = (FILE *)arg; 164 165 (void) fprintf(stream, "%20.20s %s\n", lhs, (char *)rhs); 166 } 167 168 /* 169 * kw_print -- spew the entire keywords table to stream 170 * 171 * this routine is used to dump the keywords table for debugging. 172 */ 173 void 174 kw_print(FILE *stream) 175 { 176 lut_walk(Keywords, kw_printer, stream); 177 } 178 179 /* 180 * kw_expand -- expand src into dst with given n value for $n (or $N) 181 * 182 * n == -1 means expand src into a reglob 183 * if gz is true, include ".gz" extension 184 * 185 * returns true if template contains $n or $N (implying rotation of files) 186 */ 187 boolean_t 188 kw_expand(struct fn *src, struct fn *dst, int n, boolean_t gz) 189 { 190 int c; 191 char buf[MAXDIGITS]; 192 boolean_t hasn = B_FALSE; 193 struct fn *kw = fn_new(NULL); 194 char *ptr; 195 struct tm *gmt_tm = gmtime(&Now); 196 197 while ((c = fn_getc(src)) != '\0') 198 switch (c) { 199 case '.': 200 case '(': 201 case ')': 202 case '^': 203 case '+': 204 case '{': 205 case '}': 206 /* when building an re, escape with a backslash */ 207 if (n < 0) 208 fn_putc(dst, '\\'); 209 fn_putc(dst, c); 210 break; 211 case '?': 212 /* when building an re, change '?' to a single dot */ 213 if (n < 0) 214 fn_putc(dst, '.'); 215 break; 216 case '*': 217 /* when building an re, change '*' to ".*" */ 218 if (n < 0) 219 fn_putc(dst, '.'); 220 fn_putc(dst, '*'); 221 break; 222 case '$': 223 /* '$' marks the start of a keyword */ 224 switch (c = fn_getc(src)) { 225 case '$': 226 /* double '$' stands for a single '$' */ 227 if (n < 0) 228 fn_putc(dst, '\\'); 229 fn_putc(dst, '$'); 230 break; 231 case '#': 232 /* 233 * $# expands to nothing, but forces an end 234 * of keyword, allow juxtaposition of a 235 * keyword with lower-case characters 236 */ 237 break; 238 case 'n': 239 case 'N': 240 if (c == 'N' || !islower(fn_peekc(src))) { 241 /* 242 * we've found $n or $N, if we're 243 * building an re, build one that 244 * matches a number, otherwise 245 * expand the keyword to the n 246 * passed in to this function 247 */ 248 hasn = B_TRUE; 249 if (n < 0) 250 fn_puts(dst, "([0-9]+)$0"); 251 else { 252 (void) snprintf(buf, 253 MAXDIGITS, "%d", 254 (c == 'n') ? n : n + 1); 255 fn_puts(dst, buf); 256 } 257 break; 258 } 259 /*FALLTHROUGH*/ 260 default: 261 /* gather up the keyword name */ 262 fn_renew(kw, NULL); 263 fn_putc(kw, c); 264 while (islower(fn_peekc(src))) 265 fn_putc(kw, fn_getc(src)); 266 267 /* lookup keyword */ 268 if ((ptr = (char *)lut_lookup(Keywords, 269 fn_s(kw))) == NULL) { 270 /* nope, copy it unexpanded */ 271 if (n < 0) 272 fn_putc(dst, '\\'); 273 fn_putc(dst, '$'); 274 fn_putfn(dst, kw); 275 } else 276 fn_puts(dst, ptr); 277 } 278 break; 279 case '%': 280 /* 281 * % sequence for strftime(), if we're building 282 * an re, we take our best guess at the re for 283 * this sequence, otherwise we pass it to strftime() 284 */ 285 if (n < 0) { 286 /* 287 * the regex for a percent sequence is 288 * usually just ".*" unless it is one 289 * of the common cases we know about 290 * that are numeric. in those cases, we 291 * tighten up the regex to just match digits. 292 * 293 * while it is gross that we embed knowledge 294 * of strftime() sequences here, they are 295 * specified in a standard so aren't 296 * expected to change often, and it *really* 297 * cuts down on the possibility that we'll 298 * expire a file that isn't an old log file. 299 */ 300 if ((c = fn_getc(src)) == 'E' || c == 'O') { 301 c = fn_getc(src); 302 fn_puts(dst, ".*"); 303 } else 304 switch (c) { 305 case 'd': 306 case 'g': 307 case 'G': 308 case 'H': 309 case 'I': 310 case 'j': 311 case 'm': 312 case 'M': 313 case 'S': 314 case 'u': 315 case 'U': 316 case 'V': 317 case 'w': 318 case 'W': 319 case 'y': 320 case 'Y': 321 /* pure numeric cases */ 322 fn_puts(dst, "[0-9]+"); 323 break; 324 case 'e': 325 case 'k': 326 case 'l': 327 /* possible space then num */ 328 fn_puts(dst, " *[0-9]+"); 329 break; 330 case 'D': /* %m/%d/%y */ 331 /* adds slashes! */ 332 fn_puts(dst, 333 "[0-9]+/[0-9]+/[0-9]+"); 334 break; 335 case 'R': /* %H:%M */ 336 fn_puts(dst, "[0-9]+:[0-9]+"); 337 break; 338 case 'T': /* %H:%M:%S */ 339 fn_puts(dst, 340 "[0-9]+:[0-9]+:[0-9]+"); 341 break; 342 default: 343 fn_puts(dst, ".*"); 344 } 345 } else { 346 char tbuf[4]; 347 348 /* copy % sequence to tbuf */ 349 tbuf[0] = '%'; 350 tbuf[1] = fn_getc(src); 351 if (tbuf[1] == 'E' || tbuf[1] == 'O') { 352 /* "extended" sequence */ 353 tbuf[2] = fn_getc(src); 354 tbuf[3] = '\0'; 355 } else 356 tbuf[2] = '\0'; 357 358 if (strftime(buf, MAXDIGITS, tbuf, gmt_tm) == 0) 359 /* just copy %x */ 360 fn_puts(dst, tbuf); 361 else 362 fn_puts(dst, buf); 363 } 364 break; 365 default: 366 /* nothing special, just copy it */ 367 fn_putc(dst, c); 368 } 369 370 if (gz) { 371 if (n < 0) 372 fn_puts(dst, "(\\.gz){0,1}"); 373 else 374 fn_puts(dst, ".gz"); 375 } 376 377 fn_free(kw); 378 return (hasn); 379 } 380 381 #ifdef TESTMODULE 382 383 time_t Now; 384 385 /* 386 * test main for kw module, usage: a.out fname [template...] 387 */ 388 main(int argc, char *argv[]) 389 { 390 int i; 391 struct fn *src = fn_new(NULL); 392 struct fn *dst = fn_new(NULL); 393 394 err_init(argv[0]); 395 setbuf(stdout, NULL); 396 397 Now = time(0); 398 399 if (argc < 2) 400 err(0, "first arg must be fname"); 401 402 kw_init(fn_new(argv[1]), NULL); 403 404 kw_print(stdout); 405 406 for (i = 2; i < argc; i++) { 407 int n; 408 409 for (n = -1; n < 2; n++) { 410 fn_renew(src, argv[i]); 411 fn_renew(dst, NULL); 412 printf("expand<%s> n %d hasn %d ", 413 argv[i], n, kw_expand(src, dst, n, B_FALSE)); 414 printf("result <%s>\n", fn_s(dst)); 415 } 416 } 417 418 err_done(0); 419 } 420 421 #endif /* TESTMODULE */ 422