1 /*
2  *   Copyright 1996 - 2005 Michiel Boland.
3  *   All rights reserved.
4  *
5  *   Redistribution and use in source and binary forms, with or
6  *   without modification, are permitted provided that the following
7  *   conditions are met:
8  *
9  *   1. Redistributions of source code must retain the above
10  *      copyright notice, this list of conditions and the following
11  *      disclaimer.
12  *
13  *   2. Redistributions in binary form must reproduce the above
14  *      copyright notice, this list of conditions and the following
15  *      disclaimer in the documentation and/or other materials
16  *      provided with the distribution.
17  *
18  *   3. The name of the author may not be used to endorse or promote
19  *      products derived from this software without specific prior
20  *      written permission.
21  *
22  *   THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
23  *   EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
24  *   THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
25  *   PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR
26  *   BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27  *   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
28  *   TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
29  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
30  *   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31  *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
32  *   IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
33  *   THE POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 /* As */
37 
38 static const char rcsid[] = "$Id: dump.c,v 1.60.2.2 2005/03/22 18:46:16 boland Exp $";
39 
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <sys/time.h>
43 #include <sys/resource.h>
44 #include <sys/stat.h>
45 #include <netinet/in.h>
46 #include <arpa/inet.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <unistd.h>
51 #include <fcntl.h>
52 #include "mathopd.h"
53 
dump_connections(FILE * f)54 static void dump_connections(FILE *f)
55 {
56 	int i, n_reading, n_writing, n_waiting, n_forked;
57 	size_t n;
58 	struct connection *cn;
59 
60 	n_reading = 0;
61 	n_writing = 0;
62 	n_waiting = 0;
63 	n_forked = 0;
64 	i = -1;
65 	fprintf(f, "Connections:\n");
66 	for (n = 0; n < tuning.num_connections; n++) {
67 		cn = connection_array + n;
68 		if (++i == 50) {
69 			putc('\n', f);
70 			i = 0;
71 		}
72 		switch (cn->connection_state) {
73 		case HC_FREE:
74 			putc('.', f);
75 			break;
76 		case HC_READING:
77 			putc('r', f);
78 			++n_reading;
79 			break;
80 		case HC_WRITING:
81 			putc('W', f);
82 			++n_writing;
83 			break;
84 		case HC_WAITING:
85 			putc('-', f);
86 			++n_waiting;
87 			break;
88 		case HC_FORKED:
89 			putc('F', f);
90 			++n_forked;
91 			break;
92 		default:
93 			putc('?', f);
94 			break;
95 		}
96 		cn = cn->next;
97 	}
98 	fprintf(f, "\nReading: %d, Writing: %d, Waiting: %d, Forked: %d\n", n_reading, n_writing, n_waiting, n_forked);
99 }
100 
tvadd(struct timeval * t1,struct timeval * t2,struct timeval * r)101 static void tvadd(struct timeval *t1, struct timeval *t2, struct timeval *r)
102 {
103 	long u;
104 
105 	r->tv_sec = t1->tv_sec + t2->tv_sec;
106 	u = t1->tv_usec + t2->tv_usec;
107 	if (u >= 1000000) {
108 		u -= 1000000;
109 		++r->tv_sec;
110 	}
111 	r->tv_usec = u;
112 }
113 
fdump(FILE * f)114 static void fdump(FILE *f)
115 {
116 	struct rusage ru;
117 	struct timeval t;
118 
119 	fprintf(f, "Uptime: %d seconds\n", (int) (current_time - startuptime));
120 	fprintf(f, "Active connections: %d out of %lu\n", stats.nconnections, tuning.num_connections);
121 	fprintf(f, "Max simultaneous connections since last dump: %d\n", stats.maxconnections);
122 	fprintf(f, "Forked child processes: %lu\n", stats.forked_children);
123 	fprintf(f, "Exited child processes: %lu\n", stats.exited_children);
124 	fprintf(f, "Requests executed: %lu\n", stats.nrequests);
125 	fprintf(f, "Accepted connections: %lu\n", stats.accepted_connections);
126 	fprintf(f, "Pipelined requests: %lu\n", stats.pipelined_requests);
127 	fprintf(f, "\n");
128 	getrusage(RUSAGE_SELF, &ru);
129 	tvadd(&ru.ru_utime, &ru.ru_stime, &t);
130 	fprintf(f, "CPU time used by this process: %8ld.%02ld\n", t.tv_sec, t.tv_usec / 10000);
131 	getrusage(RUSAGE_CHILDREN, &ru);
132 	tvadd(&ru.ru_utime, &ru.ru_stime, &t);
133 	fprintf(f, "                     children: %8ld.%02ld\n\n", t.tv_sec, t.tv_usec / 10000);
134 	stats.maxconnections = stats.nconnections;
135 	dump_connections(f);
136 	fprintf(f, "*** End of dump\n");
137 }
138 
process_dump(struct request * r)139 int process_dump(struct request *r)
140 {
141 	FILE *f;
142 	int fd, fd2;
143 	char name[32];
144 
145 	if (r->method != M_GET && r->method != M_HEAD) {
146 		r->status = 405;
147 		return 0;
148 	}
149 	if (r->path_args[0]) {
150 		r->error_file = r->c->error_404_file;
151 		r->status = 404;
152 		return 1;
153 	}
154 	strcpy(name, "/tmp/mathop-dump.XXXXXXXX");
155 	fd = mkstemp(name);
156 	if (fd == -1) {
157 		r->status = 500;
158 		return 0;
159 	}
160 	fcntl(fd, F_SETFD, FD_CLOEXEC);
161 	if (remove(name) == -1) {
162 		log_d("cannot remove temporary file %s", name);
163 		lerror("remove");
164 		close(fd);
165 		r->status = 500;
166 		return 0;
167 	}
168 	fd2 = dup(fd);
169 	if (fd2 == -1) {
170 		lerror("dup");
171 		close(fd);
172 		r->status = 500;
173 		return 0;
174 	}
175 	fcntl(fd2, F_SETFD, FD_CLOEXEC);
176 	f = fdopen(fd2, "a+");
177 	if (f == 0) {
178 		log_d("dump: failed to associate stream with descriptor %d", fd2);
179 		close(fd2);
180 		close(fd);
181 		r->status = 500;
182 		return 0;
183 	}
184 	fdump(f);
185 	if (fclose(f) == EOF) {
186 		lerror("fclose");
187 		close(fd2);
188 		close(fd);
189 		r->status = 500;
190 		return 0;
191 	}
192 	if (fstat(fd, &r->finfo) == -1) {
193 		lerror("fstat");
194 		close(fd);
195 		r->status = 500;
196 		return 0;
197 	}
198 	r->content_length = r->finfo.st_size;
199 	r->last_modified = r->finfo.st_mtime;
200 	if (r->method == M_GET) {
201 		lseek(fd, 0, SEEK_SET);
202 		r->cn->file_offset = 0;
203 		r->cn->rfd = fd;
204 	} else
205 		close(fd);
206 	r->content_type = "text/plain";
207 	r->num_content = 0;
208 	r->status = 200;
209 	return 0;
210 }
211 
internal_dump(void)212 void internal_dump(void)
213 {
214 	FILE *f;
215 	int fd;
216 	char name[40];
217 	struct timeval tv;
218 
219 	sprintf(name, "/tmp/mathopd-%d-dump.XXXXXXX", my_pid);
220 	fd = mkstemp(name);
221 	if (fd == -1) {
222 		lerror("mkstemp");
223 		return;
224 	}
225 	f = fdopen(fd, "a");
226 	if (f == 0) {
227 		lerror("fdopen");
228 		close(fd);
229 		return;
230 	}
231 	gettimeofday(&tv, 0);
232 	fprintf(f, "*** Dump performed at %ld.%06ld\n", tv.tv_sec, tv.tv_usec);
233 	fdump(f);
234 	fclose(f);
235 }
236