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," "," ",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