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