xref: /illumos-gate/usr/src/cmd/logger/logger.c (revision f48205be)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
27 /*	  All Rights Reserved  	*/
28 
29 /*
30  * University Copyright- Copyright (c) 1982, 1986, 1988
31  * The Regents of the University of California
32  * All Rights Reserved
33  *
34  * University Acknowledgment- Portions of this document are derived from
35  * software developed by the University of California, Berkeley, and its
36  * contributors.
37  */
38 
39 #pragma ident	"%Z%%M%	%I%	%E% SMI"
40 
41 #include <sys/types.h>
42 #include <unistd.h>
43 #include <stdio.h>
44 #include <syslog.h>
45 #include <ctype.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #include <locale.h>
49 #include <limits.h>
50 #include <pwd.h>
51 #include <errno.h>
52 
53 #define	LOG_MARK	(LOG_NFACILITIES << 3)	/* mark "facility" */
54 #define	LOGGER_BUFLEN	1024
55 
56 struct code {
57 	char	*c_name;
58 	int	c_val;
59 };
60 
61 static struct code	PriNames[] = {
62 	"panic",	LOG_EMERG,
63 	"emerg",	LOG_EMERG,
64 	"alert",	LOG_ALERT,
65 	"crit",		LOG_CRIT,
66 	"err",		LOG_ERR,
67 	"error",	LOG_ERR,
68 	"warn",		LOG_WARNING,
69 	"warning", 	LOG_WARNING,
70 	"notice",	LOG_NOTICE,
71 	"info",		LOG_INFO,
72 	"debug",	LOG_DEBUG,
73 	NULL,		-1
74 };
75 
76 static struct code	FacNames[] = {
77 	"kern",		LOG_KERN,
78 	"user",		LOG_USER,
79 	"mail",		LOG_MAIL,
80 	"daemon",	LOG_DAEMON,
81 	"auth",		LOG_AUTH,
82 	"security",	LOG_AUTH,
83 	"mark",		LOG_MARK,
84 	"syslog",	LOG_SYSLOG,
85 	"lpr",		LOG_LPR,
86 	"news",		LOG_NEWS,
87 	"uucp",		LOG_UUCP,
88 	"cron",		LOG_CRON,
89 	"audit",	LOG_AUDIT,
90 	"local0",	LOG_LOCAL0,
91 	"local1",	LOG_LOCAL1,
92 	"local2",	LOG_LOCAL2,
93 	"local3",	LOG_LOCAL3,
94 	"local4",	LOG_LOCAL4,
95 	"local5",	LOG_LOCAL5,
96 	"local6",	LOG_LOCAL6,
97 	"local7",	LOG_LOCAL7,
98 	NULL,		-1
99 };
100 
101 static int	pencode(register char *);
102 static int	decode(char *, struct code *);
103 static void	bailout(char *, char *);
104 static void	usage(void);
105 
106 /*
107  *  LOGGER -- read and log utility
108  *
109  *	This routine reads from an input and arranges to write the
110  *	result on the system log, along with a useful tag.
111  */
112 
113 int
114 main(int argc, char **argv)
115 {
116 	char tmp[23];
117 	char *tag = NULL;
118 	char *infile = NULL;
119 	char *buf = NULL;
120 	size_t buflen;
121 	int pri = LOG_NOTICE;
122 	int logflags = 0;
123 	int opt;
124 	int pid_len = 0;
125 	struct passwd *pw;
126 	uid_t u;
127 	char fmt_uid[16];
128 	char *p, *endp;
129 	size_t len;
130 	ptrdiff_t offset = 0;
131 	int status = 0;
132 
133 	(void) setlocale(LC_ALL, "");
134 #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
135 #define	TEXT_DOMAIN "SYS_TEST"	/* Use this only if it weren't */
136 #endif
137 	(void) textdomain(TEXT_DOMAIN);
138 	/* initialize */
139 
140 	while ((opt = getopt(argc, argv, "it:p:f:")) != EOF)
141 		switch (opt) {
142 
143 		    case 't':		/* tag */
144 			tag = optarg;
145 			break;
146 
147 		    case 'p':		/* priority */
148 			pri = pencode(optarg);
149 			break;
150 
151 		    case 'i':		/* log process id also */
152 			logflags |= LOG_PID;
153 			pid_len = sprintf(tmp, "%ld", (long)getpid());
154 			pid_len = (pid_len <= 0) ? 0 : pid_len +2;
155 			break;
156 
157 		    case 'f':		/* file to log */
158 			if (strcmp(optarg, "-") == 0)
159 				break;
160 			infile = optarg;
161 			if (freopen(infile, "r", stdin) == NULL) {
162 				(void) fprintf(stderr, gettext("logger: "));
163 				perror(infile);
164 				exit(1);
165 			}
166 			break;
167 
168 		    default:
169 			usage();
170 		}
171 
172 		argc -= optind;
173 		argv = &argv[optind];
174 
175 	if ((tag == NULL) && ((tag = getlogin()) == NULL)) {
176 		u = getuid();
177 		if ((pw = getpwuid(u)) == NULL) {
178 			(void) sprintf(fmt_uid, "%u", u);
179 			tag = fmt_uid;
180 		} else
181 			tag = pw->pw_name;
182 	}
183 
184 	/* setup for logging */
185 	openlog(tag, logflags, 0);
186 	(void) fclose(stdout);
187 
188 	/* log input line if appropriate */
189 	if (argc > 0) {
190 		/*
191 		 * Log arguments from command line
192 		 */
193 		int i;
194 
195 		len = 0;
196 		for (i = 0; i < argc; i++) {
197 			len += strlen(argv[i]) + 1;	/* add 1 for <space> */
198 		}
199 		if ((buf = malloc(len + 1)) == NULL) {
200 			perror("logger");
201 			exit(1);
202 		}
203 		buf[0] = '\0';
204 		for (i = 0; i < argc; i++) {
205 			if (i != 0) {
206 				(void) strcat(buf, " ");
207 			}
208 			(void) strcat(buf, argv[i]);
209 		}
210 #ifdef DEBUG
211 		(void) fprintf(stderr, "len=%d, buf >%s<\n", len, buf);
212 #endif
213 		syslog(pri, "%s", buf);
214 	} else {
215 		/*
216 		 * Log arguments from stdin (or input file).
217 		 * When reading from stdin, logger grows its buffer if
218 		 * needed, to handle long lines.
219 		 */
220 		if ((buf = malloc(LOGGER_BUFLEN)) == NULL) {
221 			perror("logger");
222 			exit(1);
223 		}
224 		buflen = LOGGER_BUFLEN;
225 		p = buf;
226 		endp = buf + buflen;
227 		offset = 0;
228 		while (fgets(p, endp - p, stdin) != NULL) {
229 			len = strlen(p);
230 			if (p[len - 1] == '\n') {
231 #ifdef DEBUG
232 				(void) fprintf(stderr,
233 				    "p-buf =%d, len=%d, buflen=%d, buf >%s<\n",
234 				    p-buf, len, buflen, buf);
235 #endif
236 				syslog(pri, "%s", buf);
237 				p = buf;
238 				offset = 0;
239 			} else if (len < endp - p - 1) {
240 				/* short read or line with no <newline> */
241 				p += len;
242 				offset += len;
243 #ifdef DEBUG
244 				(void) fprintf(stderr,
245 				    "p-buf=%d, len=%d, buflen=%d, buf >%s<\n",
246 				    p-buf, len, buflen, buf);
247 #endif
248 				continue;
249 			} else {
250 				/* line longer than buflen, so get larger buf */
251 				buflen += LOGGER_BUFLEN;
252 				offset += len;
253 #ifdef DEBUG
254 				(void) fprintf(stderr,
255 				    "Realloc endp-p=%d, len=%d, offset=%d, "
256 				    "buflen %d\n",
257 				    endp - p, len, offset, buflen);
258 #endif
259 				if ((buf = realloc(buf, buflen)) == NULL) {
260 					perror("logger");
261 					exit(1);
262 				}
263 				p = buf + offset;
264 				endp = buf + buflen;
265 			}
266 		}	/* while */
267 
268 		if (feof(stdin)) {
269 			if (p > buf) {
270 				/* the last line did not end with newline */
271 #ifdef DEBUG
272 				(void) fprintf(stderr,
273 				    "(2) p-buf=%d, len=%d, buflen=%d, "
274 				    "buf >%s<\n",
275 				    p-buf, len, buflen, buf);
276 #endif
277 				syslog(pri, "%s", buf);
278 			}
279 		} else {
280 			/*
281 			 * fgets() encountered an error.  Log unlogged data
282 			 * from earlier fgets() (if any).  Write null byte
283 			 * after last full read, in case the fgets() that
284 			 * encountered error removed it and failed to null
285 			 * terminate.
286 			 */
287 			perror("logger");
288 			if (p > buf) {
289 				*p = '\0';
290 				syslog(pri, "%s", buf);
291 			}
292 			status = 1;
293 		}
294 	}	/* else !(argc > 0) */
295 	free(buf);
296 	return (status);
297 }
298 
299 /*
300  *  Decode a symbolic name to a numeric value
301  */
302 
303 
304 static int
305 pencode(s)
306 register char *s;
307 {
308 	register char *p;
309 	int lev;
310 	int fac = 0;
311 
312 	for (p = s; *s && *s != '.'; s++);
313 	if (*s) {
314 		*s = '\0';
315 		fac = decode(p, FacNames);
316 		if (fac < 0)
317 			bailout("unknown facility name: ", p);
318 		*s++ = '.';
319 	} else
320 		s = p;
321 	lev = decode(s, PriNames);
322 	if (lev < 0)
323 		bailout("unknown priority name: ", s);
324 
325 	return ((lev & LOG_PRIMASK) | (fac & LOG_FACMASK));
326 }
327 
328 
329 static int
330 decode(name, codetab)
331 char *name;
332 struct code *codetab;
333 {
334 	register struct code *c;
335 
336 	if (isdigit(*name))
337 		return (atoi(name));
338 
339 	for (c = codetab; c->c_name; c++)
340 		if (strcasecmp(name, c->c_name) == 0)
341 			return (c->c_val);
342 
343 	return (-1);
344 }
345 
346 
347 static void
348 bailout(a, b)
349 char *a, *b;
350 {
351 	(void) fprintf(stderr, gettext("logger: %s%s\n"), a, b);
352 	exit(1);
353 }
354 
355 
356 static void
357 usage(void)
358 {
359 	(void) fprintf(stderr, gettext(
360 	    "Usage:\tlogger string\n"
361 	    "\tlogger [-i] [-f filename] [-p priority] [-t tag] "
362 		"[message] ...\n"));
363 	exit(1);
364 }
365