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.
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.
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]
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 * logadm/kw.c -- manage keywords table
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.
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.
37 #pragma ident "%Z%%M% %I% %E% SMI"
42 #include <sys/types.h>
44 #include <sys/utsname.h>
45 #include <sys/systeminfo.h>
55 /* forward declarations for functions used internally by this module */
56 static void kw_printer(const char *lhs
, void *rhs
, void *arg
);
59 * absurdly long length to hold sprintf of a %d,
60 * or strftime() expansion of a *single* percent sequence
64 static struct lut
*Keywords
; /* lookup table for keywords */
66 extern time_t Now
; /* time used for keyword expansions */
69 * kw_init -- initialize keywords based on given filename
72 kw_init(struct fn
*fnp
, struct fn
*nfnp
)
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];
85 static char zonename
[ZONENAME_MAX
];
86 static zoneid_t zoneid
;
87 static int initialized
;
90 /* make a copy of the string for $file */
93 fullpath
= STRDUP(fn_s(fnp
));
94 Keywords
= lut_add(Keywords
, "file", fullpath
);
96 /* make a copy of the string for $nfile */
101 Keywords
= lut_add(Keywords
, "nfile", "");
103 nfullpath
= STRDUP(fn_s(nfnp
));
104 Keywords
= lut_add(Keywords
, "nfile", nfullpath
);
107 /* make a copy of the string for $dirname/$basename */
110 splitpath
= STRDUP(fn_s(fnp
));
112 if ((ptr
= strrchr(splitpath
, '/')) == NULL
) {
113 Keywords
= lut_add(Keywords
, "basename", splitpath
);
114 Keywords
= lut_add(Keywords
, "dirname", ".");
117 Keywords
= lut_add(Keywords
, "basename", ptr
);
118 Keywords
= lut_add(Keywords
, "dirname", splitpath
);
122 return; /* rest of the keywords don't change */
124 (void) snprintf(secs
, MAXDIGITS
, "%d", (int)Now
);
125 Keywords
= lut_add(Keywords
, "secs", secs
);
128 err(EF_SYS
, "uname");
130 Keywords
= lut_add(Keywords
, "nodename", un
.nodename
);
131 Keywords
= lut_add(Keywords
, "release", un
.release
);
132 Keywords
= lut_add(Keywords
, "machine", un
.machine
);
134 if (sysinfo(SI_ARCHITECTURE
, isa
, sizeof (isa
)) == -1)
135 err(EF_WARN
|EF_SYS
, "sysinfo(SI_ARCHITECTURE) failed.");
137 Keywords
= lut_add(Keywords
, "isa", isa
);
139 if (sysinfo(SI_PLATFORM
, platform
, sizeof (platform
)) == -1)
140 err(EF_WARN
|EF_SYS
, "sysinfo(SI_PLATFORM) failed.");
142 Keywords
= lut_add(Keywords
, "platform", platform
);
144 if (sysinfo(SI_SRPC_DOMAIN
, domain
, sizeof (domain
)) == -1)
145 err(EF_WARN
|EF_SYS
, "sysinfo(SI_SRPC_DOMAIN) failed.");
147 Keywords
= lut_add(Keywords
, "domain", domain
);
149 if ((home
= getenv("HOME")) != NULL
)
150 Keywords
= lut_add(Keywords
, "home", STRDUP(home
));
152 if ((user
= getenv("USER")) != NULL
)
153 Keywords
= lut_add(Keywords
, "user", STRDUP(user
));
155 if ((logname
= getenv("LOGNAME")) != NULL
)
156 Keywords
= lut_add(Keywords
, "logname", STRDUP(logname
));
158 zoneid
= getzoneid();
159 if ((getzonenamebyid(zoneid
, zonename
, sizeof (zonename
))) == -1)
160 err(EF_WARN
|EF_SYS
, "getzonenamebyid() failed.");
162 Keywords
= lut_add(Keywords
, "zonename", STRDUP(zonename
));
167 /* helper function for kw_print() */
169 kw_printer(const char *lhs
, void *rhs
, void *arg
)
171 FILE *stream
= (FILE *)arg
;
173 (void) fprintf(stream
, "%20.20s %s\n", lhs
, (char *)rhs
);
177 * kw_print -- spew the entire keywords table to stream
179 * this routine is used to dump the keywords table for debugging.
182 kw_print(FILE *stream
)
184 lut_walk(Keywords
, kw_printer
, stream
);
188 * kw_expand -- expand src into dst with given n value for $n (or $N)
190 * n == -1 means expand src into a reglob
191 * if gz is true, include ".gz" extension
193 * returns true if template contains $n or $N (implying rotation of files)
196 kw_expand(struct fn
*src
, struct fn
*dst
, int n
, boolean_t gz
)
200 boolean_t hasn
= B_FALSE
;
201 struct fn
*kw
= fn_new(NULL
);
203 struct tm
*gmt_tm
= localtime(&Now
);
205 while ((c
= fn_getc(src
)) != '\0')
214 /* when building an re, escape with a backslash */
220 /* when building an re, change '?' to a single dot */
225 /* when building an re, change '*' to ".*" */
231 /* '$' marks the start of a keyword */
232 switch (c
= fn_getc(src
)) {
234 /* double '$' stands for a single '$' */
241 * $# expands to nothing, but forces an end
242 * of keyword, allow juxtaposition of a
243 * keyword with lower-case characters
248 if (c
== 'N' || !islower(fn_peekc(src
))) {
250 * we've found $n or $N, if we're
251 * building an re, build one that
252 * matches a number, otherwise
253 * expand the keyword to the n
254 * passed in to this function
258 fn_puts(dst
, "([0-9]+)$0");
262 (c
== 'n') ? n
: n
+ 1);
269 /* gather up the keyword name */
272 while (islower(fn_peekc(src
)))
273 fn_putc(kw
, fn_getc(src
));
276 if ((ptr
= (char *)lut_lookup(Keywords
,
277 fn_s(kw
))) == NULL
) {
278 /* nope, copy it unexpanded */
289 * % sequence for strftime(), if we're building
290 * an re, we take our best guess at the re for
291 * this sequence, otherwise we pass it to strftime()
295 * the regex for a percent sequence is
296 * usually just ".*" unless it is one
297 * of the common cases we know about
298 * that are numeric. in those cases, we
299 * tighten up the regex to just match digits.
301 * while it is gross that we embed knowledge
302 * of strftime() sequences here, they are
303 * specified in a standard so aren't
304 * expected to change often, and it *really*
305 * cuts down on the possibility that we'll
306 * expire a file that isn't an old log file.
308 if ((c
= fn_getc(src
)) == 'E' || c
== 'O') {
329 /* pure numeric cases */
330 fn_puts(dst
, "[0-9]+");
335 /* possible space then num */
336 fn_puts(dst
, " *[0-9]+");
338 case 'D': /* %m/%d/%y */
341 "[0-9]+/[0-9]+/[0-9]+");
343 case 'R': /* %H:%M */
344 fn_puts(dst
, "[0-9]+:[0-9]+");
346 case 'T': /* %H:%M:%S */
348 "[0-9]+:[0-9]+:[0-9]+");
356 /* copy % sequence to tbuf */
358 tbuf
[1] = fn_getc(src
);
359 if (tbuf
[1] == 'E' || tbuf
[1] == 'O') {
360 /* "extended" sequence */
361 tbuf
[2] = fn_getc(src
);
366 if (strftime(buf
, MAXDIGITS
, tbuf
, gmt_tm
) == 0)
374 /* nothing special, just copy it */
380 fn_puts(dst
, "(\\.gz){0,1}");
394 * test main for kw module, usage: a.out fname [template...]
397 main(int argc
, char *argv
[])
400 struct fn
*src
= fn_new(NULL
);
401 struct fn
*dst
= fn_new(NULL
);
404 setbuf(stdout
, NULL
);
409 err(0, "first arg must be fname");
411 kw_init(fn_new(argv
[1]), NULL
);
415 for (i
= 2; i
< argc
; i
++) {
418 for (n
= -1; n
< 2; n
++) {
419 fn_renew(src
, argv
[i
]);
421 printf("expand<%s> n %d hasn %d ",
422 argv
[i
], n
, kw_expand(src
, dst
, n
, B_FALSE
));
423 printf("result <%s>\n", fn_s(dst
));
432 #endif /* TESTMODULE */