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