xref: /illumos-gate/usr/src/cmd/logadm/err.c (revision 3ce5372277f4657ad0e52d36c979527c4ca22de2)
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_ptr;
38 static const char *Myname;
39 static int Exitcode;
40 static FILE *Errorfile;
41 
42 /*
43  * err_init -- initialize the error handling routine
44  *
45  */
46 void
47 err_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 
57 static const char *File;
58 static int Line;
59 
60 /*
61  * err_fileline -- record the filename/line number for err(EF_FILE, ...)
62  */
63 void
64 err_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*/
76 void
77 err(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*/
158 void
159 out(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  */
174 void
175 err_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  */
193 void
194 err_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  */
210 void
211 err_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  */
255 void *
256 err_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  */
271 void *
272 err_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  */
287 char *
288 err_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*/
310 void
311 err_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  */
320 void
321 err_exitcode(int exitcode)
322 {
323 	Exitcode = exitcode;
324 }
325