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