xref: /freebsd/contrib/sendmail/mailstats/mailstats.c (revision 13058a916175518dfbac6ce66b9b8e22ecf43155)
1 /*
2  * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
3  *	All rights reserved.
4  * Copyright (c) 1983 Eric P. Allman.  All rights reserved.
5  * Copyright (c) 1988, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * By using this file, you agree to the terms and conditions set
9  * forth in the LICENSE file which can be found at the top level of
10  * the sendmail distribution.
11  *
12  *
13  */
14 
15 #ifndef lint
16 static char copyright[] =
17 "@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\
18 	All rights reserved.\n\
19      Copyright (c) 1988, 1993\n\
20 	The Regents of the University of California.  All rights reserved.\n";
21 #endif /* ! lint */
22 
23 #ifndef lint
24 static char id[] = "@(#)$Id: mailstats.c,v 8.53.16.13 2001/05/07 22:06:38 gshapiro Exp $";
25 #endif /* ! lint */
26 
27 #include <unistd.h>
28 #include <stddef.h>
29 #include <stdlib.h>
30 #include <ctype.h>
31 #include <string.h>
32 #include <time.h>
33 #ifdef EX_OK
34 # undef EX_OK		/* unistd.h may have another use for this */
35 #endif /* EX_OK */
36 #include <sysexits.h>
37 
38 #include <sendmail/sendmail.h>
39 #include <sendmail/mailstats.h>
40 #include <sendmail/pathnames.h>
41 
42 
43 #define MNAMELEN	20	/* max length of mailer name */
44 
45 
46 int
47 main(argc, argv)
48 	int argc;
49 	char **argv;
50 {
51 	register int i;
52 	int mno;
53 	int save_errno;
54 	int ch, fd;
55 	char *sfile;
56 	char *cfile;
57 	FILE *cfp;
58 	bool mnames;
59 	bool progmode;
60 	long frmsgs = 0, frbytes = 0, tomsgs = 0, tobytes = 0, rejmsgs = 0;
61 	long dismsgs = 0;
62 	time_t now;
63 	char mtable[MAXMAILERS][MNAMELEN + 1];
64 	char sfilebuf[MAXLINE];
65 	char buf[MAXLINE];
66 	struct statistics stats;
67 	extern char *ctime();
68 	extern char *optarg;
69 	extern int optind;
70 
71 
72 	cfile = _PATH_SENDMAILCF;
73 	sfile = NULL;
74 	mnames = TRUE;
75 	progmode = FALSE;
76 	while ((ch = getopt(argc, argv, "C:f:op")) != -1)
77 	{
78 		switch (ch)
79 		{
80 		  case 'C':
81 			cfile = optarg;
82 			break;
83 
84 		  case 'f':
85 			sfile = optarg;
86 			break;
87 
88 		  case 'o':
89 			mnames = FALSE;
90 			break;
91 
92 		  case 'p':
93 			progmode = TRUE;
94 			break;
95 
96 		  case '?':
97 		  default:
98   usage:
99 			(void) fputs("usage: mailstats [-C cffile] [-f stfile] [-o] [-p]\n",
100 				     stderr);
101 			exit(EX_USAGE);
102 		}
103 	}
104 	argc -= optind;
105 	argv += optind;
106 
107 	if (argc != 0)
108 		goto usage;
109 
110 	if ((cfp = fopen(cfile, "r")) == NULL)
111 	{
112 		save_errno = errno;
113 		fprintf(stderr, "mailstats: ");
114 		errno = save_errno;
115 		perror(cfile);
116 		exit(EX_NOINPUT);
117 	}
118 
119 	mno = 0;
120 	(void) strlcpy(mtable[mno++], "prog", MNAMELEN + 1);
121 	(void) strlcpy(mtable[mno++], "*file*", MNAMELEN + 1);
122 	(void) strlcpy(mtable[mno++], "*include*", MNAMELEN + 1);
123 
124 	while (fgets(buf, sizeof(buf), cfp) != NULL)
125 	{
126 		register char *b;
127 		char *s;
128 		register char *m;
129 
130 		b = buf;
131 		switch (*b++)
132 		{
133 		  case 'M':		/* mailer definition */
134 			break;
135 
136 		  case 'O':		/* option -- see if .st file */
137 			if (strncasecmp(b, " StatusFile", 11) == 0 &&
138 			    !(isascii(b[11]) && isalnum(b[11])))
139 			{
140 				/* new form -- find value */
141 				b = strchr(b, '=');
142 				if (b == NULL)
143 					continue;
144 				while (isascii(*++b) && isspace(*b))
145 					continue;
146 			}
147 			else if (*b++ != 'S')
148 			{
149 				/* something else boring */
150 				continue;
151 			}
152 
153 			/* this is the S or StatusFile option -- save it */
154 			if (strlcpy(sfilebuf, b, sizeof sfilebuf) >=
155 			    sizeof sfilebuf)
156 			{
157 				fprintf(stderr,
158 					"StatusFile filename too long: %.30s...\n",
159 					b);
160 				exit(EX_CONFIG);
161 			}
162 			b = strchr(sfilebuf, '#');
163 			if (b == NULL)
164 				b = strchr(sfilebuf, '\n');
165 			if (b == NULL)
166 				b = &sfilebuf[strlen(sfilebuf)];
167 			while (isascii(*--b) && isspace(*b))
168 				continue;
169 			*++b = '\0';
170 			if (sfile == NULL)
171 				sfile = sfilebuf;
172 
173 		  default:
174 			continue;
175 		}
176 
177 		if (mno >= MAXMAILERS)
178 		{
179 			fprintf(stderr,
180 				"Too many mailers defined, %d max.\n",
181 				MAXMAILERS);
182 			exit(EX_SOFTWARE);
183 		}
184 		m = mtable[mno];
185 		s = m + MNAMELEN;		/* is [MNAMELEN + 1] */
186 		while (*b != ',' && !(isascii(*b) && isspace(*b)) &&
187 		       *b != '\0' && m < s)
188 			*m++ = *b++;
189 		*m = '\0';
190 		for (i = 0; i < mno; i++)
191 		{
192 			if (strcmp(mtable[i], mtable[mno]) == 0)
193 				break;
194 		}
195 		if (i == mno)
196 			mno++;
197 	}
198 	(void) fclose(cfp);
199 	for (; mno < MAXMAILERS; mno++)
200 		mtable[mno][0]='\0';
201 
202 	if (sfile == NULL)
203 	{
204 		fprintf(stderr, "mailstats: no statistics file located\n");
205 		exit (EX_OSFILE);
206 	}
207 
208 	fd = open(sfile, O_RDONLY);
209 	if ((fd < 0) || (i = read(fd, &stats, sizeof stats)) < 0)
210 	{
211 		save_errno = errno;
212 		(void) fputs("mailstats: ", stderr);
213 		errno = save_errno;
214 		perror(sfile);
215 		exit(EX_NOINPUT);
216 	}
217 	if (i == 0)
218 	{
219 		(void) sleep(1);
220 		if ((i = read(fd, &stats, sizeof stats)) < 0)
221 		{
222 			save_errno = errno;
223 			(void) fputs("mailstats: ", stderr);
224 			errno = save_errno;
225 			perror(sfile);
226 			exit(EX_NOINPUT);
227 		}
228 		else if (i == 0)
229 		{
230 			memset((ARBPTR_T) &stats, '\0', sizeof stats);
231 			(void) time(&stats.stat_itime);
232 		}
233 	}
234 	if (i != 0)
235 	{
236 		if (stats.stat_magic != STAT_MAGIC)
237 		{
238 			fprintf(stderr,
239 				"mailstats: incorrect magic number in %s\n",
240 				sfile);
241 			exit(EX_OSERR);
242 		}
243 		else if (stats.stat_version != STAT_VERSION)
244 		{
245 			fprintf(stderr,
246 				"mailstats version (%d) incompatible with %s version (%d)\n",
247 				STAT_VERSION, sfile, stats.stat_version);
248 			exit(EX_OSERR);
249 		}
250 		else if (i != sizeof stats || stats.stat_size != sizeof(stats))
251 		{
252 			(void) fputs("mailstats: file size changed.\n", stderr);
253 			exit(EX_OSERR);
254 		}
255 	}
256 
257 	if (progmode)
258 	{
259 		(void) time(&now);
260 		printf("%ld %ld\n", (long) stats.stat_itime, (long) now);
261 	}
262 	else
263 	{
264 		printf("Statistics from %s", ctime(&stats.stat_itime));
265 		printf(" M   msgsfr  bytes_from   msgsto    bytes_to  msgsrej msgsdis%s\n",
266 			mnames ? "  Mailer" : "");
267 	}
268 	for (i = 0; i < MAXMAILERS; i++)
269 	{
270 		if (stats.stat_nf[i] || stats.stat_nt[i] ||
271 		    stats.stat_nr[i] || stats.stat_nd[i])
272 		{
273 			char *format;
274 
275 			if (progmode)
276 				format = "%2d %8ld %10ld %8ld %10ld   %6ld  %6ld";
277 			else
278 				format = "%2d %8ld %10ldK %8ld %10ldK   %6ld  %6ld";
279 			printf(format, i,
280 			    stats.stat_nf[i], stats.stat_bf[i],
281 			    stats.stat_nt[i], stats.stat_bt[i],
282 			    stats.stat_nr[i], stats.stat_nd[i]);
283 			if (mnames)
284 				printf("  %s", mtable[i]);
285 			printf("\n");
286 			frmsgs += stats.stat_nf[i];
287 			frbytes += stats.stat_bf[i];
288 			tomsgs += stats.stat_nt[i];
289 			tobytes += stats.stat_bt[i];
290 			rejmsgs += stats.stat_nr[i];
291 			dismsgs += stats.stat_nd[i];
292 		}
293 	}
294 	if (progmode)
295 	{
296 		printf(" T %8ld %10ld %8ld %10ld   %6ld  %6ld\n",
297 		       frmsgs, frbytes, tomsgs, tobytes, rejmsgs, dismsgs);
298 		printf(" C %8ld %8ld %6ld\n",
299 		       stats.stat_cf, stats.stat_ct, stats.stat_cr);
300 		(void) close(fd);
301 		fd = open(sfile, O_RDWR | O_TRUNC);
302 		if (fd >= 0)
303 			(void) close(fd);
304 	}
305 	else
306 	{
307 		printf("=============================================================\n");
308 		printf(" T %8ld %10ldK %8ld %10ldK   %6ld  %6ld\n",
309 			frmsgs, frbytes, tomsgs, tobytes, rejmsgs, dismsgs);
310 		printf(" C %8ld %10s  %8ld %10s    %6ld\n",
311 		       stats.stat_cf, "", stats.stat_ct, "", stats.stat_cr);
312 	}
313 	exit(EX_OK);
314 	/* NOTREACHED */
315 	return EX_OK;
316 }
317