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