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 (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
23 *
24 * logadm/err.c -- some basic error routines
25 *
26 */
27
28#include <stdio.h>
29#include <unistd.h>
30#include <libintl.h>
31#include <stdlib.h>
32#include <stdarg.h>
33#include <string.h>
34#include <errno.h>
35#include "err.h"
36
37jmp_buf	*Err_env_ptr;
38static const char *Myname;
39static int Exitcode;
40static FILE *Errorfile;
41
42/*
43 * err_init -- initialize the error handling routine
44 *
45 */
46void
47err_init(const char *myname)
48{
49	char *ptr;
50
51	if ((ptr = strrchr(myname, '/')) == NULL)
52		Myname = myname;
53	else
54		Myname = ptr + 1;
55}
56
57static const char *File;
58static int Line;
59
60/*
61 * err_fileline -- record the filename/line number for err(EF_FILE, ...)
62 */
63void
64err_fileline(const char *file, int line)
65{
66	File = file;
67	Line = line;
68}
69
70/*
71 * err -- print an error message and return, exit or longjmp based on flags
72 *
73 * this routine calls gettext() to translate the fmt string.
74 */
75/*PRINTFLIKE2*/
76void
77err(int flags, const char *fmt, ...)
78{
79	va_list ap;
80	int safe_errno = errno;
81	char *errno_msg = NULL;
82	int as_is = 0;
83	int jump = 0;
84	int warning = 0;
85	int fileline = 0;
86	char *prefix = "Error: ";
87	const char *intlfmt;
88
89	va_start(ap, fmt);
90	intlfmt = gettext(fmt);
91
92	if (flags & EF_WARN) {
93		warning = 1;
94		prefix = "Warning: ";
95	}
96	if (flags & EF_FILE) {
97		fileline = 1;
98		Exitcode++;
99	}
100	if (flags & EF_SYS)
101		errno_msg = strerror(safe_errno);
102	if (flags & EF_JMP)
103		jump = 1;
104	if (flags & EF_RAW)
105		as_is = 1;
106
107	/* print a copy to stderr */
108	if (!as_is) {
109		if (Myname != NULL) {
110			(void) fprintf(stderr, "%s: ", Myname);
111			if (Errorfile)
112				(void) fprintf(Errorfile, "%s: ", Myname);
113		}
114		if (fileline && File) {
115			(void) fprintf(stderr, "%s line %d: ", File, Line);
116			if (Errorfile)
117				(void) fprintf(Errorfile,
118				    "%s line %d: ", File, Line);
119		}
120		(void) fputs(gettext(prefix), stderr);
121		if (Errorfile)
122			(void) fputs(gettext(prefix), Errorfile);
123	}
124	(void) vfprintf(stderr, intlfmt, ap);
125	if (Errorfile)
126		(void) vfprintf(Errorfile, intlfmt, ap);
127	if (errno_msg != NULL) {
128		(void) fprintf(stderr, ": %s", errno_msg);
129		if (Errorfile)
130			(void) fprintf(Errorfile, ": %s", errno_msg);
131	}
132	if (!as_is) {
133		(void) fprintf(stderr, "\n");
134		if (Errorfile)
135			(void) fprintf(Errorfile, "\n");
136	}
137	(void) fflush(stderr);
138	if (Errorfile)
139		(void) fflush(Errorfile);
140
141	va_end(ap);
142
143	if (jump)
144		longjmp(*Err_env_ptr, 1);
145
146	if (!warning && !fileline) {
147		err_done(1);
148		/*NOTREACHED*/
149	}
150}
151
152/*
153 * out -- print a message and return
154 *
155 * this routine calls gettext() to translate the fmt string.
156 */
157/*PRINTFLIKE1*/
158void
159out(const char *fmt, ...)
160{
161	va_list ap;
162
163	va_start(ap, fmt);
164
165	(void) vfprintf(stdout, gettext(fmt), ap);
166
167	va_end(ap);
168}
169
170#define	CHUNKSIZE 8192		/* for copying stderr */
171/*
172 * err_fromfd -- copy data from fd to stderr
173 */
174void
175err_fromfd(int fd)
176{
177	char buf[CHUNKSIZE];
178	int count;
179
180	while ((count = read(fd, buf, CHUNKSIZE)) > 0) {
181		(void) fwrite(buf, 1, count, stderr);
182		if (Errorfile)
183			(void) fwrite(buf, 1, count, Errorfile);
184	}
185	(void) fflush(stderr);
186	if (Errorfile)
187		(void) fflush(Errorfile);
188}
189
190/*
191 * err_done -- exit the program
192 */
193void
194err_done(int exitcode)
195{
196	/* send error mail if applicable */
197	err_mailto(NULL);
198
199	if (exitcode)
200		exit(exitcode);
201	else
202		exit(Exitcode);
203	/*NOTREACHED*/
204}
205
206#define	MAXLINE	8192	/* for tmp file line buffer */
207/*
208 * err_mailto -- arrange for error output to be mailed to someone
209 */
210void
211err_mailto(const char *recipient)
212{
213	static const char *lastrecipient;
214	static char mailcmd[] = "/bin/mailx -s 'logadm error output'";
215	char *cmd;
216	int len;
217	FILE *pfp;
218	char line[MAXLINE];
219
220	if (lastrecipient != NULL) {
221		if (recipient != NULL &&
222		    strcmp(recipient, lastrecipient) == 0)
223			return;		/* keep going, same recipient */
224
225		/* stop saving output for lastrecipient and send message */
226		if (ftell(Errorfile)) {
227			rewind(Errorfile);
228			len = strlen(lastrecipient) + strlen(mailcmd) + 2;
229			cmd = MALLOC(len);
230			(void) snprintf(cmd, len, "%s %s",
231			    mailcmd, lastrecipient);
232			if ((pfp = popen(cmd, "w")) == NULL)
233				err(EF_SYS, "popen to mailx");
234			while (fgets(line, MAXLINE, Errorfile) != NULL)
235				(void) fputs(line, pfp);
236			(void) pclose(pfp);
237		}
238		(void) fclose(Errorfile);
239		Errorfile = NULL;
240	}
241
242	if (recipient != NULL) {
243		/* start saving error output for this recipient */
244		if ((Errorfile = tmpfile()) == NULL)
245			err(EF_SYS, "tmpfile");
246	}
247	lastrecipient = recipient;
248}
249
250/*
251 * err_malloc -- a malloc() with checks
252 *
253 * this routine is typically called via the MALLOC() macro in err.h
254 */
255void *
256err_malloc(int nbytes, const char *fname, int line)
257{
258	void *retval = malloc(nbytes);
259
260	if (retval == NULL)
261		err(0, "%s:%d: out of memory", fname, line);
262
263	return (retval);
264}
265
266/*
267 * err_realloc -- a realloc() with checks
268 *
269 * this routine is typically called via the REALLOC() macro in err.h
270 */
271void *
272err_realloc(void *ptr, int nbytes, const char *fname, int line)
273{
274	void *retval = realloc(ptr, nbytes);
275
276	if (retval == NULL)
277		err(0, "%s:%d: out of memory", fname, line);
278
279	return (retval);
280}
281
282/*
283 * err_strdup -- a strdup() with checks
284 *
285 * this routine is typically called via the STRDUP() macro in err.h
286 */
287char *
288err_strdup(const char *ptr, const char *fname, int line)
289{
290	char *retval = NULL;
291
292	if (ptr != NULL) {
293		retval = strdup(ptr);
294		if (retval == NULL)
295			err(0, "%s:%d: out of memory", fname, line);
296	} else
297		err(0, "%s:%d: could not strdup", fname, line);
298
299
300	return (retval);
301
302}
303
304/*
305 * err_free -- a free() with checks
306 *
307 * this routine is typically called via the FREE() macro in err.h
308 */
309/*ARGSUSED1*/
310void
311err_free(void *ptr, const char *fname, int line)
312{
313	/* nothing to check in this version */
314	free(ptr);
315}
316
317/*
318 * err_exitcode -- set an error exit code for when done(0) is called
319 */
320void
321err_exitcode(int exitcode)
322{
323	Exitcode = exitcode;
324}
325