Add two more memory-debug smbcontrol messages: these ones should
[ira/wip.git] / source3 / utils / smbcontrol.c
1 /* 
2    Unix SMB/Netbios implementation.
3    Version 3.0
4    program to send control messages to Samba processes
5    Copyright (C) Andrew Tridgell 1994-1998
6    Copyright (C) 2001, 2002 by Martin Pool
7    
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12    
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17    
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "includes.h"
24
25 extern BOOL AllowDebugChange;
26
27 static struct {
28         char *name;
29         int value;
30 } msg_types[] = {
31         {"debug", MSG_DEBUG},
32         {"force-election", MSG_FORCE_ELECTION},
33         {"ping", MSG_PING},
34         {"profile", MSG_PROFILE},
35         {"profilelevel", MSG_REQ_PROFILELEVEL},
36         {"debuglevel", MSG_REQ_DEBUGLEVEL},
37         {"printer-notify", MSG_PRINTER_NOTIFY},
38         {"close-share", MSG_SMB_FORCE_TDIS},
39         {"samsync", MSG_SMB_SAM_SYNC},
40         {"samrepl", MSG_SMB_SAM_REPL},
41         {"pool-usage", MSG_REQ_POOL_USAGE },
42         {"dmalloc-mark", MSG_REQ_DMALLOC_MARK },
43         {"dmalloc-log-changed", MSG_REQ_DMALLOC_LOG_CHANGED },
44         {NULL, -1}
45 };
46
47 time_t timeout_start;
48
49 #define MAX_WAIT        10
50
51 static void usage(BOOL doexit)
52 {
53         int i;
54         if (doexit) {
55                 printf("Usage: smbcontrol -i -s configfile\n");
56                 printf("       smbcontrol <destination> <message-type> <parameters>\n\n");
57         } else {
58                 printf("<destination> <message-type> <parameters>\n\n");
59         }
60         printf("\t<destination> is one of \"nmbd\", \"smbd\" or a process ID\n");
61         printf("\t<message-type> is one of: ");
62         for (i=0; msg_types[i].name; i++) 
63             printf("%s%s", i?", ":"",msg_types[i].name);
64         printf("\n");
65         if (doexit) exit(1);
66 }
67
68 static int pong_count;
69 static BOOL got_level;
70 static BOOL pong_registered = False;
71 static BOOL debuglevel_registered = False;
72 static BOOL profilelevel_registered = False;
73
74
75 /**
76  * Wait for replies for up to @p *max_secs seconds, or until @p
77  * max_replies are received.  max_replies may be NULL in which case it
78  * is ignored.
79  *
80  * @note This is a pretty lame timeout; all it means is that after
81  * max_secs we won't look for any more messages.
82  **/
83 static void wait_for_replies(int max_secs, int *max_replies)
84 {
85         time_t timeout_end = time(NULL) + max_secs;
86
87         while ((!max_replies || (*max_replies)-- > 0)
88                &&  (time(NULL) < timeout_end)) {
89                 message_dispatch();
90         }
91 }
92
93
94 /****************************************************************************
95 a useful function for testing the message system
96 ****************************************************************************/
97 void pong_function(int msg_type, pid_t src, void *buf, size_t len)
98 {
99         pong_count++;
100         printf("PONG from PID %u\n",(unsigned int)src);
101 }
102
103 /****************************************************************************
104 Prints out the current Debug level returned by MSG_DEBUGLEVEL
105 ****************************************************************************/
106 void debuglevel_function(int msg_type, pid_t src, void *buf, size_t len)
107 {
108         int i;
109         int debuglevel_class[DBGC_LAST];
110
111         memcpy(debuglevel_class, buf, len);
112
113         printf("Current debug level of PID %u is %d ",(unsigned int)src, debuglevel_class[0]);
114         for (i=1;i<DBGC_LAST;i++)
115                 if (debuglevel_class[i])
116                         printf("%s:%d ", debug_classname_from_index(i), debuglevel_class[i]);
117         printf("\n");
118
119         got_level = True;
120 }
121
122 /****************************************************************************
123 Prints out the current Profile level returned by MSG_PROFILELEVEL
124 ****************************************************************************/
125 void profilelevel_function(int msg_type, pid_t src, void *buf, size_t len)
126 {
127         int level;
128         char *s=NULL;
129         memcpy(&level, buf, sizeof(int));
130
131         if (level) {
132             switch (level) {
133             case 1:
134                 s = "off";
135                 break;
136             case 3:
137                 s = "count only";
138                 break;
139             case 7:
140                 s = "count and time";
141                 break;
142             }
143             printf("Profiling %s on PID %u\n",s,(unsigned int)src);
144         } else {
145             printf("Profiling not available on PID %u\n",(unsigned int)src);
146         }
147         got_level = True;
148 }
149
150 /**
151  * Handle reply from POOL_USAGE.
152  **/
153 static void pool_usage_cb(int msg_type, pid_t src_pid, void *buf, size_t len)
154 {
155         printf("Got POOL_USAGE reply from pid%u:\n%.*s",
156                (unsigned int) src_pid, (int) len, (const char *) buf);
157 }
158
159
160 /**
161  * Send a message to a named destination
162  *
163  * @return False if an error occurred.
164  **/
165 static BOOL send_message(char *dest, int msg_type, void *buf, int len, BOOL duplicates)
166 {
167         pid_t pid;
168         /* "smbd" is the only broadcast operation */
169         if (strequal(dest,"smbd")) {
170                 TDB_CONTEXT *tdb;
171                 BOOL ret;
172                 int n_sent = 0;
173
174                 tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_DEFAULT, O_RDWR, 0);
175                 if (!tdb) {
176                         fprintf(stderr,"Failed to open connections database in send_message.\n");
177                         return False;
178                 }
179
180                 ret = message_send_all(tdb,msg_type, buf, len, duplicates,
181                                        &n_sent);
182                 DEBUG(10,("smbcontrol/send_message: broadcast message to "
183                           "%d processes\n", n_sent));
184                 tdb_close(tdb);
185
186                 return ret;
187         } else if (strequal(dest,"nmbd")) {
188                 pid = pidfile_pid(dest);
189                 if (pid == 0) {
190                         fprintf(stderr,"Can't find pid for nmbd\n");
191                         return False;
192                 }
193         } else if (strequal(dest,"self")) {
194                 pid = getpid();
195         } else {
196                 pid = atoi(dest);
197                 if (pid == 0) {
198                         fprintf(stderr,"Not a valid pid\n");
199                         return False;
200                 }               
201         } 
202
203         DEBUG(10,("smbcontrol/send_message: send message to pid%d\n", pid));
204         return message_send_pid(pid, msg_type, buf, len, duplicates);
205 }
206
207 /****************************************************************************
208 evaluate a message type string
209 ****************************************************************************/
210 static int parse_type(char *mtype)
211 {
212         int i;
213         for (i=0;msg_types[i].name;i++) {
214                 if (strequal(mtype, msg_types[i].name)) return msg_types[i].value;
215         }
216         return -1;
217 }
218
219
220 static void register_all(void)
221 {
222         message_register(MSG_POOL_USAGE, pool_usage_cb);
223 }
224
225
226 /****************************************************************************
227 do command
228 ****************************************************************************/
229 static BOOL do_command(char *dest, char *msg_name, int iparams, char **params)
230 {
231         int i, n, v;
232         int mtype;
233         BOOL retval=False;
234
235         mtype = parse_type(msg_name);
236         if (mtype == -1) {
237                 fprintf(stderr,"Couldn't resolve message type: %s\n", msg_name);
238                 return(False);
239         }
240
241         switch (mtype) {
242         case MSG_DEBUG: {
243                 struct debuglevel_message dm;
244
245                 if (!params || !params[0]) {
246                         fprintf(stderr,"MSG_DEBUG needs a parameter\n");
247                         return(False);
248                 }
249
250                 ZERO_STRUCT(dm);
251                 if (!debug_parse_params(params, dm.debuglevel_class, dm.debuglevel_class_isset)) {
252                         fprintf(stderr, "MSG_DEBUG error. Expected <class name>:level\n");
253                         return(False);
254                 } else
255                         send_message(dest, MSG_DEBUG, &dm, sizeof(dm), False);
256                 break;
257         }
258
259         case MSG_PROFILE:
260                 if (!params || !params[0]) {
261                         fprintf(stderr,"MSG_PROFILE needs a parameter\n");
262                         return(False);
263                 }
264                 if (strequal(params[0], "off")) {
265                         v = 0;
266                 } else if (strequal(params[0], "count")) {
267                         v = 1;
268                 } else if (strequal(params[0], "on")) {
269                         v = 2;
270                 } else if (strequal(params[0], "flush")) {
271                         v = 3;
272                 } else {
273                     fprintf(stderr,
274                         "MSG_PROFILE parameter must be off, count, on, or flush\n");
275                     return(False);
276                 }
277                 send_message(dest, MSG_PROFILE, &v, sizeof(int), False);
278                 break;
279
280         case MSG_FORCE_ELECTION:
281                 if (!strequal(dest, "nmbd")) {
282                         fprintf(stderr,"force-election can only be sent to nmbd\n");
283                         return(False);
284                 }
285                 send_message(dest, MSG_FORCE_ELECTION, NULL, 0, False);
286                 break;
287
288         case MSG_REQ_PROFILELEVEL:
289                 if (!profilelevel_registered) {
290                     message_register(MSG_PROFILELEVEL, profilelevel_function);
291                     profilelevel_registered = True;
292                 }
293                 got_level = False;
294                 retval = send_message(dest, MSG_REQ_PROFILELEVEL, NULL, 0, True);
295                 if (retval) {
296                         timeout_start = time(NULL);
297                         while (!got_level) {
298                                 message_dispatch();
299                                 if ((time(NULL) - timeout_start) > MAX_WAIT) {
300                                         fprintf(stderr,"profilelevel timeout\n");
301                                         break;
302                                 }
303                         }
304                 }
305                 break;
306
307         case MSG_REQ_DEBUGLEVEL:
308                 if (!debuglevel_registered) {
309                     message_register(MSG_DEBUGLEVEL, debuglevel_function);
310                     debuglevel_registered = True;
311                 }
312                 got_level = False;
313                 retval = send_message(dest, MSG_REQ_DEBUGLEVEL, NULL, 0, True);
314                 if (retval) {
315                         timeout_start = time(NULL);
316                         while (!got_level) {
317                                 message_dispatch();
318                                 if ((time(NULL) - timeout_start) > MAX_WAIT) {
319                                         fprintf(stderr,"debuglevel timeout\n");
320                                         break;
321                                 }
322                         }
323                 }
324                 break;
325
326         case MSG_PRINTER_NOTIFY:
327                 if (!strequal(dest, "smbd")) {
328                         fprintf(stderr,"printer-notify can only be sent to smbd\n");
329                         return(False);
330                 }
331                 if (!params || !params[0]) {
332                         fprintf(stderr, "printer-notify needs a printer name\n");
333                         return (False);
334                 }
335                 retval = send_message(dest, MSG_PRINTER_NOTIFY, params[0],
336                                       strlen(params[0]) + 1, False);
337                 break;
338
339         case MSG_SMB_FORCE_TDIS:
340                 if (!strequal(dest, "smbd")) {
341                         fprintf(stderr,"close-share can only be sent to smbd\n");
342                         return(False);
343                 }
344                 if (!params || !params[0]) {
345                         fprintf(stderr, "close-share needs a share name or '*'\n");
346                         return (False);
347                 }
348                 retval = send_message(dest, MSG_SMB_FORCE_TDIS, params[0],
349                                       strlen(params[0]) + 1, False);
350                 break;
351
352         case MSG_SMB_SAM_SYNC:
353                 if (!strequal(dest, "smbd")) {
354                         fprintf(stderr, "samsync can only be sent to smbd\n");
355                         return False;
356                 }
357
358                 if (params) {
359                         fprintf(stderr, "samsync does not take any parameters\n");
360                         return False;
361                 }
362
363                 retval = send_message(dest, MSG_SMB_SAM_SYNC, NULL, 0, False);
364
365                 break;
366
367         case MSG_SMB_SAM_REPL: {
368                 uint32 seqnum;
369
370                 if (!strequal(dest, "smbd")) {
371                         fprintf(stderr, "sam repl can only be sent to smbd\n");
372                         return False;
373                 }
374
375                 if (!params || !params[0]) {
376                         fprintf(stderr, "SAM_REPL needs a parameter\n");
377                         return False;
378                 }
379
380                 seqnum = atoi(params[0]);
381
382                 retval = send_message(dest, MSG_SMB_SAM_SYNC, 
383                                       (char *)&seqnum, sizeof(uint32), False); 
384
385                 break;
386         }
387
388         case MSG_PING:
389                 if (!pong_registered) {
390                     message_register(MSG_PONG, pong_function);
391                     pong_registered = True;
392                 }
393                 if (!params || !params[0]) {
394                         fprintf(stderr,"MSG_PING needs a parameter\n");
395                         return(False);
396                 }
397                 n = atoi(params[0]);
398                 pong_count = 0;
399                 for (i=0;i<n;i++) {
400                         if (iparams > 1)
401                                 retval = send_message(dest, MSG_PING, params[1], strlen(params[1]) + 1, True);
402                         else
403                                 retval = send_message(dest, MSG_PING, NULL, 0, True);
404                         if (retval == False)
405                                 return False;
406                 }
407                 wait_for_replies(MAX_WAIT, &n);
408                 if (n > 0) {
409                         fprintf(stderr,"PING timeout\n");
410                 }
411                 break;
412
413         case MSG_REQ_POOL_USAGE:
414                 if (!send_message(dest, MSG_REQ_POOL_USAGE, NULL, 0, True))
415                         return False;
416                 wait_for_replies(MAX_WAIT, NULL);
417                 
418                 break;
419
420         case MSG_REQ_DMALLOC_LOG_CHANGED:
421         case MSG_REQ_DMALLOC_MARK:
422                 if (!send_message(dest, mtype, NULL, 0, False))
423                         return False;
424                 break;
425         }
426
427         return (True);
428 }
429
430  int main(int argc, char *argv[])
431 {
432         int opt;
433         char temp[255];
434         extern int optind;
435         BOOL interactive = False;
436
437         AllowDebugChange = False;
438         DEBUGLEVEL = 0;
439
440         setup_logging(argv[0],True);
441         
442         if (argc < 2) usage(True);
443
444         while ((opt = getopt(argc, argv,"is:")) != EOF) {
445                 switch (opt) {
446                 case 'i':
447                         interactive = True;
448                         break;
449                 case 's':
450                         pstrcpy(dyn_CONFIGFILE, optarg);
451                         break;
452                 default:
453                         printf("Unknown option %c (%d)\n", (char)opt, opt);
454                         usage(True);
455                 }
456         }
457
458         lp_load(dyn_CONFIGFILE,False,False,False);
459
460         if (!message_init()) exit(1);
461
462         argc -= optind;
463         argv = &argv[optind];
464
465         register_all();
466
467         if (!interactive) {
468                 if (argc < 2) usage(True);
469                 /* Need to invert sense of return code -- samba
470                  * routines mostly return True==1 for success, but
471                  * shell needs 0. */ 
472                 return ! do_command(argv[0],argv[1], argc-2, argc > 2 ? &argv[2] : 0);
473         }
474
475         while (True) {
476                 char *myargv[4];
477                 int myargc;
478
479                 printf("smbcontrol> ");
480                 if (!fgets(temp, sizeof(temp)-1, stdin)) break;
481                 myargc = 0;
482                 while ((myargc < 4) && 
483                        (myargv[myargc] = strtok(myargc?NULL:temp," \t\n"))) {
484                         myargc++;
485                 }
486                 if (!myargc) break;
487                 if (strequal(myargv[0],"q")) break;
488                 if (myargc < 2)
489                         usage(False);
490                 else if (!do_command(myargv[0],myargv[1],myargc-2,myargc > 2 ? &myargv[2] : 0))
491                         usage(False);
492         }
493         return(0);
494 }
495