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