1/*
2   Unix SMB/CIFS implementation.
3   web status page
4   Copyright (C) Andrew Tridgell 1997-1998
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2 of the License, or
9   (at your option) any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19*/
20
21#include "includes.h"
22#include "web/swat_proto.h"
23
24#define PIDMAP		struct PidMap
25
26/* how long to wait for start/stops to take effect */
27#define SLEEP_TIME 3
28
29PIDMAP {
30	PIDMAP	*next, *prev;
31	pid_t	pid;
32	char	*machine;
33};
34
35static PIDMAP	*pidmap;
36static int	PID_or_Machine;		/* 0 = show PID, else show Machine name */
37
38static pid_t smbd_pid;
39
40/* from 2nd call on, remove old list */
41static void initPid2Machine (void)
42{
43	/* show machine name rather PID on table "Open Files"? */
44	if (PID_or_Machine) {
45		PIDMAP *p;
46
47		for (p = pidmap; p != NULL; ) {
48			DLIST_REMOVE(pidmap, p);
49			SAFE_FREE(p->machine);
50			SAFE_FREE(p);
51		}
52
53		pidmap = NULL;
54	}
55}
56
57/* add new PID <-> Machine name mapping */
58static void addPid2Machine (pid_t pid, char *machine)
59{
60	/* show machine name rather PID on table "Open Files"? */
61	if (PID_or_Machine) {
62		PIDMAP *newmap;
63
64		if ((newmap = SMB_MALLOC_P(PIDMAP)) == NULL) {
65			/* XXX need error message for this?
66			   if malloc fails, PID is always shown */
67			return;
68		}
69
70		newmap->pid = pid;
71		newmap->machine = SMB_STRDUP(machine);
72
73		DLIST_ADD(pidmap, newmap);
74	}
75}
76
77/* lookup PID <-> Machine name mapping */
78static char *mapPid2Machine (pid_t pid)
79{
80	static char pidbuf [64];
81	PIDMAP *map;
82
83	/* show machine name rather PID on table "Open Files"? */
84	if (PID_or_Machine) {
85		for (map = pidmap; map != NULL; map = map->next) {
86			if (pid == map->pid) {
87				if (map->machine == NULL)	/* no machine name */
88					break;			/* show PID */
89
90				return map->machine;
91			}
92		}
93	}
94
95	/* PID not in list or machine name NULL? return pid as string */
96	snprintf (pidbuf, sizeof (pidbuf) - 1, "%lu", (unsigned long)pid);
97	return pidbuf;
98}
99
100static char *tstring(time_t t)
101{
102	static pstring buf;
103	pstrcpy(buf, asctime(LocalTime(&t)));
104	all_string_sub(buf," ","&nbsp;",sizeof(buf));
105	return buf;
106}
107
108static void print_share_mode(share_mode_entry *e, char *fname)
109{
110	char           *utf8_fname;
111
112	printf("<tr><td>%s</td>",_(mapPid2Machine(e->pid)));
113	printf("<td>");
114	switch ((e->share_mode>>4)&0xF) {
115	case DENY_NONE: printf("DENY_NONE"); break;
116	case DENY_ALL:  printf("DENY_ALL   "); break;
117	case DENY_DOS:  printf("DENY_DOS   "); break;
118	case DENY_READ: printf("DENY_READ  "); break;
119	case DENY_WRITE:printf("DENY_WRITE "); break;
120	}
121	printf("</td>");
122
123	printf("<td>");
124	switch (e->share_mode&0xF) {
125	case 0: printf("%s", _("RDONLY     ")); break;
126	case 1: printf("%s", _("WRONLY     ")); break;
127	case 2: printf("%s", _("RDWR       ")); break;
128	}
129	printf("</td>");
130
131	printf("<td>");
132	if((e->op_type &
133	    (EXCLUSIVE_OPLOCK|BATCH_OPLOCK)) ==
134	   (EXCLUSIVE_OPLOCK|BATCH_OPLOCK))
135		printf("EXCLUSIVE+BATCH ");
136	else if (e->op_type & EXCLUSIVE_OPLOCK)
137		printf("EXCLUSIVE       ");
138	else if (e->op_type & BATCH_OPLOCK)
139		printf("BATCH           ");
140	else if (e->op_type & LEVEL_II_OPLOCK)
141		printf("LEVEL_II        ");
142	else
143		printf("NONE            ");
144	printf("</td>");
145
146	push_utf8_allocate(&utf8_fname, fname);
147	printf("<td>%s</td><td>%s</td></tr>\n",
148	       utf8_fname,tstring(e->time.tv_sec));
149	SAFE_FREE(utf8_fname);
150}
151
152
153/* kill off any connections chosen by the user */
154static int traverse_fn1(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void* state)
155{
156	struct connections_data crec;
157
158	if (dbuf.dsize != sizeof(crec))
159		return 0;
160
161	memcpy(&crec, dbuf.dptr, sizeof(crec));
162
163	if (crec.cnum == -1 && process_exists(crec.pid)) {
164		char buf[30];
165		slprintf(buf,sizeof(buf)-1,"kill_%d", (int)crec.pid);
166		if (cgi_variable(buf)) {
167			kill_pid(crec.pid);
168			sleep(SLEEP_TIME);
169		}
170	}
171	return 0;
172}
173
174/* traversal fn for showing machine connections */
175static int traverse_fn2(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void* state)
176{
177	struct connections_data crec;
178
179	if (dbuf.dsize != sizeof(crec))
180		return 0;
181
182	memcpy(&crec, dbuf.dptr, sizeof(crec));
183
184	if (crec.cnum == -1 || !process_exists(crec.pid) || (crec.pid == smbd_pid))
185		return 0;
186
187	addPid2Machine (crec.pid, crec.machine);
188
189	printf("<tr><td>%d</td><td>%s</td><td>%s</td><td>%s</td>\n",
190	       (int)crec.pid,
191	       crec.machine,crec.addr,
192	       tstring(crec.start));
193	if (geteuid() == 0) {
194		printf("<td><input type=submit value=\"X\" name=\"kill_%d\"></td>\n",
195		       (int)crec.pid);
196	}
197	printf("</tr>\n");
198
199	return 0;
200}
201
202/* traversal fn for showing share connections */
203static int traverse_fn3(TDB_CONTEXT *tdb, TDB_DATA kbuf, TDB_DATA dbuf, void* state)
204{
205	struct connections_data crec;
206
207	if (dbuf.dsize != sizeof(crec))
208		return 0;
209
210	memcpy(&crec, dbuf.dptr, sizeof(crec));
211
212	if (crec.cnum == -1 || !process_exists(crec.pid))
213		return 0;
214
215	printf("<tr><td>%s</td><td>%s</td><td>%s</td><td>%d</td><td>%s</td><td>%s</td></tr>\n",
216	       crec.name,uidtoname(crec.uid),
217	       gidtoname(crec.gid),(int)crec.pid,
218	       crec.machine,
219	       tstring(crec.start));
220	return 0;
221}
222
223
224/* show the current server status */
225void status_page(void)
226{
227	const char *v;
228	int autorefresh=0;
229	int refresh_interval=30;
230	TDB_CONTEXT *tdb;
231	int nr_running=0;
232	BOOL waitup = False;
233
234	smbd_pid = pidfile_pid("smbd");
235
236	if (cgi_variable("smbd_restart") || cgi_variable("all_restart")) {
237		stop_smbd();
238		start_smbd();
239		waitup=True;
240	}
241
242	if (cgi_variable("smbd_start") || cgi_variable("all_start")) {
243		start_smbd();
244		waitup=True;
245	}
246
247	if (cgi_variable("smbd_stop") || cgi_variable("all_stop")) {
248		stop_smbd();
249		waitup=True;
250	}
251
252	if (cgi_variable("nmbd_restart") || cgi_variable("all_restart")) {
253		stop_nmbd();
254		start_nmbd();
255		waitup=True;
256	}
257	if (cgi_variable("nmbd_start") || cgi_variable("all_start")) {
258		start_nmbd();
259		waitup=True;
260	}
261
262	if (cgi_variable("nmbd_stop")|| cgi_variable("all_stop")) {
263		stop_nmbd();
264		waitup=True;
265	}
266
267#ifdef WITH_WINBIND
268	if (cgi_variable("winbindd_restart") || cgi_variable("all_restart")) {
269		stop_winbindd();
270		start_winbindd();
271		waitup=True;
272	}
273
274	if (cgi_variable("winbindd_start") || cgi_variable("all_start")) {
275		start_winbindd();
276		waitup=True;
277	}
278
279	if (cgi_variable("winbindd_stop") || cgi_variable("all_stop")) {
280		stop_winbindd();
281		waitup=True;
282	}
283#endif
284	/* wait for daemons to start/stop */
285	if (waitup)
286		sleep(SLEEP_TIME);
287
288	if (cgi_variable("autorefresh")) {
289		autorefresh = 1;
290	} else if (cgi_variable("norefresh")) {
291		autorefresh = 0;
292	} else if (cgi_variable("refresh")) {
293		autorefresh = 1;
294	}
295
296	if ((v=cgi_variable("refresh_interval"))) {
297		refresh_interval = atoi(v);
298	}
299
300	if (cgi_variable("show_client_in_col_1")) {
301		PID_or_Machine = 1;
302	}
303
304	if (cgi_variable("show_pid_in_col_1")) {
305		PID_or_Machine = 0;
306	}
307
308	tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_DEFAULT, O_RDONLY, 0);
309	if (tdb) tdb_traverse(tdb, traverse_fn1, NULL);
310
311	initPid2Machine ();
312
313	printf("<H2>%s</H2>\n", _("Server Status"));
314
315	printf("<FORM method=post>\n");
316
317	if (!autorefresh) {
318		printf("<input type=submit value=\"%s\" name=\"autorefresh\">\n", _("Auto Refresh"));
319		printf("<br>%s", _("Refresh Interval: "));
320		printf("<input type=text size=2 name=\"refresh_interval\" value=\"%d\">\n",
321		       refresh_interval);
322	} else {
323		printf("<input type=submit value=\"%s\" name=\"norefresh\">\n", _("Stop Refreshing"));
324		printf("<br>%s%d\n", _("Refresh Interval: "), refresh_interval);
325		printf("<input type=hidden name=\"refresh\" value=\"1\">\n");
326	}
327
328	printf("<p>\n");
329
330	if (!tdb) {
331		/* open failure either means no connections have been
332                   made */
333	}
334
335
336	printf("<table>\n");
337
338	printf("<tr><td>%s</td><td>%s</td></tr>", _("version:"), SAMBA_VERSION_STRING);
339
340	fflush(stdout);
341	printf("<tr><td>%s</td><td>%s</td>\n", _("smbd:"), smbd_running()?_("running"):_("not running"));
342	if (geteuid() == 0) {
343	    if (smbd_running()) {
344		nr_running++;
345		printf("<td><input type=submit name=\"smbd_stop\" value=\"%s\"></td>\n", _("Stop smbd"));
346	    } else {
347		printf("<td><input type=submit name=\"smbd_start\" value=\"%s\"></td>\n", _("Start smbd"));
348	    }
349	    printf("<td><input type=submit name=\"smbd_restart\" value=\"%s\"></td>\n", _("Restart smbd"));
350	}
351	printf("</tr>\n");
352
353	fflush(stdout);
354	printf("<tr><td>%s</td><td>%s</td>\n", _("nmbd:"), nmbd_running()?_("running"):_("not running"));
355	if (geteuid() == 0) {
356	    if (nmbd_running()) {
357		nr_running++;
358		printf("<td><input type=submit name=\"nmbd_stop\" value=\"%s\"></td>\n", _("Stop nmbd"));
359	    } else {
360		printf("<td><input type=submit name=\"nmbd_start\" value=\"%s\"></td>\n", _("Start nmbd"));
361	    }
362	    printf("<td><input type=submit name=\"nmbd_restart\" value=\"%s\"></td>\n", _("Restart nmbd"));
363	}
364	printf("</tr>\n");
365
366#ifdef WITH_WINBIND
367	fflush(stdout);
368	printf("<tr><td>%s</td><td>%s</td>\n", _("winbindd:"), winbindd_running()?_("running"):_("not running"));
369	if (geteuid() == 0) {
370	    if (winbindd_running()) {
371		nr_running++;
372		printf("<td><input type=submit name=\"winbindd_stop\" value=\"%s\"></td>\n", _("Stop winbindd"));
373	    } else {
374		printf("<td><input type=submit name=\"winbindd_start\" value=\"%s\"></td>\n", _("Start winbindd"));
375	    }
376	    printf("<td><input type=submit name=\"winbindd_restart\" value=\"%s\"></td>\n", _("Restart winbindd"));
377	}
378	printf("</tr>\n");
379#endif
380
381	if (geteuid() == 0) {
382	    printf("<tr><td></td><td></td>\n");
383	    if (nr_running >= 1) {
384	        /* stop, restart all */
385		printf("<td><input type=submit name=\"all_stop\" value=\"%s\"></td>\n", _("Stop All"));
386		printf("<td><input type=submit name=\"all_restart\" value=\"%s\"></td>\n", _("Restart All"));
387	    }
388	    else if (nr_running == 0) {
389	    	/* start all */
390		printf("<td><input type=submit name=\"all_start\" value=\"%s\"></td>\n", _("Start All"));
391	    }
392	    printf("</tr>\n");
393	}
394	printf("</table>\n");
395	fflush(stdout);
396
397	printf("<p><h3>%s</h3>\n", _("Active Connections"));
398	printf("<table border=1>\n");
399	printf("<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th>\n", _("PID"), _("Client"), _("IP address"), _("Date"));
400	if (geteuid() == 0) {
401		printf("<th>%s</th>\n", _("Kill"));
402	}
403	printf("</tr>\n");
404
405	if (tdb) tdb_traverse(tdb, traverse_fn2, NULL);
406
407	printf("</table><p>\n");
408
409	printf("<p><h3>%s</h3>\n", _("Active Shares"));
410	printf("<table border=1>\n");
411	printf("<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>\n\n",
412		_("Share"), _("User"), _("Group"), _("PID"), _("Client"), _("Date"));
413
414	if (tdb) tdb_traverse(tdb, traverse_fn3, NULL);
415
416	printf("</table><p>\n");
417
418	printf("<h3>%s</h3>\n", _("Open Files"));
419	printf("<table border=1>\n");
420	printf("<tr><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th><th>%s</th></tr>\n", _("PID"), _("Sharing"), _("R/W"), _("Oplock"), _("File"), _("Date"));
421
422	locking_init(1);
423	share_mode_forall(print_share_mode);
424	locking_end();
425	printf("</table>\n");
426
427	if (tdb) tdb_close(tdb);
428
429	printf("<br><input type=submit name=\"show_client_in_col_1\" value=\"%s\">\n", _("Show Client in col 1"));
430	printf("<input type=submit name=\"show_pid_in_col_1\" value=\"%s\">\n", _("Show PID in col 1"));
431
432	printf("</FORM>\n");
433
434	if (autorefresh) {
435		/* this little JavaScript allows for automatic refresh
436                   of the page. There are other methods but this seems
437                   to be the best alternative */
438		printf("<script language=\"JavaScript\">\n");
439		printf("<!--\nsetTimeout('window.location.replace(\"%s/status?refresh_interval=%d&refresh=1\")', %d)\n",
440		       cgi_baseurl(),
441		       refresh_interval,
442		       refresh_interval*1000);
443		printf("//-->\n</script>\n");
444	}
445}
446