Remove duplicate "tallocdump" message from tdb messaging system. The
[samba.git] / source / 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    Copyright (C) Simo Sorce 2002
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 const struct {
28         const 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         {"printnotify", MSG_PRINTER_NOTIFY2 },
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         {"shutdown", MSG_SHUTDOWN },
45         {"drvupgrade", MSG_PRINTER_DRVUPGRADE},
46         {NULL, -1}
47 };
48
49 time_t timeout_start;
50
51 #define MAX_WAIT        10
52
53 /* we need these because we link to printing*.o */
54
55 void become_root(void) {}
56 void unbecome_root(void) {}
57
58
59 static void usage(BOOL doexit)
60 {
61         int i;
62         if (doexit) {
63                 printf("Usage: smbcontrol -i -s configfile\n");
64                 printf("       smbcontrol <destination> <message-type> <parameters>\n\n");
65         } else {
66                 printf("<destination> <message-type> <parameters>\n\n");
67         }
68         printf("\t<destination> is one of \"nmbd\", \"smbd\" or a process ID\n");
69         printf("\t<message-type> is one of:\n");
70         for (i=0; msg_types[i].name; i++) 
71             printf("\t\t%s\n", msg_types[i].name);
72         printf("\n");
73         if (doexit) exit(1);
74 }
75
76 static int pong_count;
77 static BOOL got_level;
78 static BOOL got_pool;
79 static BOOL pong_registered = False;
80 static BOOL debuglevel_registered = False;
81 static BOOL poolusage_registered = False;
82 static BOOL profilelevel_registered = False;
83
84
85 /**
86  * Wait for replies for up to @p *max_secs seconds, or until @p
87  * max_replies are received.  max_replies may be NULL in which case it
88  * is ignored.
89  *
90  * @note This is a pretty lame timeout; all it means is that after
91  * max_secs we won't look for any more messages.
92  **/
93 static void wait_for_replies(int max_secs, int *max_replies)
94 {
95         time_t timeout_end = time(NULL) + max_secs;
96
97         while ((!max_replies || (*max_replies)-- > 0)
98                &&  (time(NULL) < timeout_end)) {
99                 message_dispatch();
100         }
101 }
102
103
104 /****************************************************************************
105 a useful function for testing the message system
106 ****************************************************************************/
107 void pong_function(int msg_type, pid_t src, void *buf, size_t len)
108 {
109         pong_count++;
110         printf("PONG from PID %u\n",(unsigned int)src);
111 }
112
113 /****************************************************************************
114  Prints out the current talloc list.
115 ****************************************************************************/
116 void tallocdump_function(int msg_type, pid_t src, void *buf, size_t len)
117 {
118         char *info = (char *)buf;
119
120         printf("Current talloc contexts for process %u\n", (unsigned int)src );
121         if (len == 0)
122                 printf("None returned\n");
123         else
124                 printf(info);
125         printf("\n");
126         got_pool = True;
127 }
128
129 /****************************************************************************
130 Prints out the current Debug level returned by MSG_DEBUGLEVEL
131 ****************************************************************************/
132 void debuglevel_function(int msg_type, pid_t src, void *buf, size_t len)
133 {
134         const char *levels = (char *)buf;
135
136         printf("Current debug levels of PID %u are:\n",(unsigned int)src);
137         printf("%s\n", levels);
138         
139         got_level = True;
140 }
141
142 /****************************************************************************
143 Prints out the current Profile level returned by MSG_PROFILELEVEL
144 ****************************************************************************/
145 void profilelevel_function(int msg_type, pid_t src, void *buf, size_t len)
146 {
147         int level;
148         const char *s=NULL;
149         memcpy(&level, buf, sizeof(int));
150
151         if (level) {
152             switch (level) {
153             case 1:
154                 s = "off";
155                 break;
156             case 3:
157                 s = "count only";
158                 break;
159             case 7:
160                 s = "count and time";
161                 break;
162             default:
163                     s = "BOGUS";
164                     break;
165             }
166             printf("Profiling %s on PID %u\n",s,(unsigned int)src);
167         } else {
168             printf("Profiling not available on PID %u\n",(unsigned int)src);
169         }
170         got_level = True;
171 }
172
173 /**
174  * Handle reply from POOL_USAGE.
175  **/
176 static void pool_usage_cb(int msg_type, pid_t src_pid, void *buf, size_t len)
177 {
178         printf("Got POOL_USAGE reply from pid %u:\n%.*s",
179                (unsigned int) src_pid, (int) len, (const char *) buf);
180 }
181
182
183 /**
184  * Send a message to a named destination
185  *
186  * @return False if an error occurred.
187  **/
188 static BOOL send_message(char *dest, int msg_type, void *buf, int len, BOOL duplicates)
189 {
190         pid_t pid;
191         /* "smbd" is the only broadcast operation */
192         if (strequal(dest,"smbd")) {
193                 TDB_CONTEXT *tdb;
194                 BOOL ret;
195                 int n_sent = 0;
196
197                 tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_DEFAULT, O_RDWR, 0);
198                 if (!tdb) {
199                         fprintf(stderr,"Failed to open connections database in send_message.\n");
200                         return False;
201                 }
202
203                 ret = message_send_all(tdb,msg_type, buf, len, duplicates,
204                                        &n_sent);
205                 DEBUG(10,("smbcontrol/send_message: broadcast message to "
206                           "%d processes\n", n_sent));
207                 tdb_close(tdb);
208
209                 return ret;
210         } else if (strequal(dest,"nmbd")) {
211                 pid = pidfile_pid(dest);
212                 if (pid == 0) {
213                         fprintf(stderr,"Can't find pid for nmbd\n");
214                         return False;
215                 }
216         } else if (strequal(dest,"self")) {
217                 pid = sys_getpid();
218         } else {
219                 pid = atoi(dest);
220                 if (pid == 0) {
221                         fprintf(stderr,"Not a valid pid\n");
222                         return False;
223                 }               
224         } 
225
226         DEBUG(10,("smbcontrol/send_message: send message to pid%d\n", pid));
227         return message_send_pid(pid, msg_type, buf, len, duplicates);
228 }
229
230 /****************************************************************************
231 evaluate a message type string
232 ****************************************************************************/
233 static int parse_type(char *mtype)
234 {
235         int i;
236         for (i=0;msg_types[i].name;i++) {
237                 if (strequal(mtype, msg_types[i].name)) return msg_types[i].value;
238         }
239         return -1;
240 }
241
242
243 static void register_all(void)
244 {
245         message_register(MSG_POOL_USAGE, pool_usage_cb);
246 }
247
248 /* This guy is here so we can link printing/notify.c to the smbcontrol
249    binary without having to pull in tons of other crap. */
250
251 TDB_CONTEXT *conn_tdb_ctx(void)
252 {
253         static TDB_CONTEXT *tdb;
254
255         if (tdb)
256                 return tdb;
257
258         tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_DEFAULT, O_RDONLY, 0);
259
260         if (!tdb)
261                 DEBUG(3, ("Failed to open connections database in send_spoolss_notify2_msg\n"));
262
263         return tdb;
264 }
265
266 /****************************************************************************
267 do command
268 ****************************************************************************/
269 static BOOL do_command(char *dest, char *msg_name, int iparams, char **params)
270 {
271         int i, n, v;
272         int mtype;
273         BOOL retval=False;
274         BOOL check_notify_msgs = False;
275
276         mtype = parse_type(msg_name);
277         if (mtype == -1) {
278                 fprintf(stderr,"Couldn't resolve message type: %s\n", msg_name);
279                 return(False);
280         }
281
282         switch (mtype) {
283         case MSG_DEBUG: {
284                 char *buf, *b;
285                 char **p;
286                 int dim = 0;
287
288                 if (!params || !params[0]) {
289                         fprintf(stderr,"MSG_DEBUG needs a parameter\n");
290                         return(False);
291                 }
292
293                 /* first pass retrieve total lenght */
294                 for (p = params; p && *p ; p++)
295                         dim += (strnlen(*p, 1024) +1); /* lenght + space */
296                 b = buf = malloc(dim);
297                 if (!buf) {
298                         fprintf(stderr, "Out of memory!");
299                         return(False);
300                 }
301                 /* now build a single string with all parameters */
302                 for(p = params; p && *p; p++) {
303                         int l = strnlen(*p, 1024);
304                         strncpy(b, *p, l);
305                         b[l] = ' ';
306                         b = b + l + 1;
307                 }
308                 b[-1] = '\0';
309
310                 send_message(dest, MSG_DEBUG, buf, dim, False);
311
312                 free(buf);
313   
314                 break;
315         }
316
317         case MSG_PROFILE:
318                 if (!params || !params[0]) {
319                         fprintf(stderr,"MSG_PROFILE needs a parameter\n");
320                         return(False);
321                 }
322                 if (strequal(params[0], "off")) {
323                         v = 0;
324                 } else if (strequal(params[0], "count")) {
325                         v = 1;
326                 } else if (strequal(params[0], "on")) {
327                         v = 2;
328                 } else if (strequal(params[0], "flush")) {
329                         v = 3;
330                 } else {
331                     fprintf(stderr,
332                         "MSG_PROFILE parameter must be off, count, on, or flush\n");
333                     return(False);
334                 }
335                 send_message(dest, MSG_PROFILE, &v, sizeof(int), False);
336                 break;
337
338         case MSG_FORCE_ELECTION:
339                 if (!strequal(dest, "nmbd")) {
340                         fprintf(stderr,"force-election can only be sent to nmbd\n");
341                         return(False);
342                 }
343                 send_message(dest, MSG_FORCE_ELECTION, NULL, 0, False);
344                 break;
345
346         case MSG_REQ_PROFILELEVEL:
347                 if (!profilelevel_registered) {
348                     message_register(MSG_PROFILELEVEL, profilelevel_function);
349                     profilelevel_registered = True;
350                 }
351                 got_level = False;
352                 retval = send_message(dest, MSG_REQ_PROFILELEVEL, NULL, 0, True);
353                 if (retval) {
354                         timeout_start = time(NULL);
355                         while (!got_level) {
356                                 message_dispatch();
357                                 if ((time(NULL) - timeout_start) > MAX_WAIT) {
358                                         fprintf(stderr,"profilelevel timeout\n");
359                                         break;
360                                 }
361                         }
362                 }
363                 break;
364
365         case MSG_REQ_DEBUGLEVEL:
366                 if (!debuglevel_registered) {
367                     message_register(MSG_DEBUGLEVEL, debuglevel_function);
368                     debuglevel_registered = True;
369                 }
370                 got_level = False;
371                 retval = send_message(dest, MSG_REQ_DEBUGLEVEL, NULL, 0, True);
372                 if (retval) {
373                         timeout_start = time(NULL);
374                         while (!got_level) {
375                                 message_dispatch();
376                                 if ((time(NULL) - timeout_start) > MAX_WAIT) {
377                                         fprintf(stderr,"debuglevel timeout\n");
378                                         break;
379                                 }
380                         }
381                 }
382                 break;
383
384                 /* Send a notification message to a printer */
385
386         case MSG_PRINTER_NOTIFY2: {
387                 char *cmd;
388
389                 /* Read subcommand */
390
391                 if (!params || !params[0]) {
392                         fprintf(stderr, "Must specify subcommand:\n");
393                         fprintf(stderr, "\tqueuepause <printername>\n");
394                         fprintf(stderr, "\tqueueresume <printername>\n");
395                         fprintf(stderr, "\tjobpause <printername> <unix jobid>\n");
396                         fprintf(stderr, "\tjobresume <printername> <unix jobid>\n");
397                         fprintf(stderr, "\tjobdelete <printername> <unix jobid>\n");
398                         fprintf(stderr, "\tprinter <printername> <comment|port|driver> <new value>\n");
399                         return False;
400                 }
401
402                 cmd = params[0];
403
404                 check_notify_msgs = True;
405
406                 /* Pause a print queue */
407
408                 if (strequal(cmd, "queuepause")) {
409
410                         if (!params[1]) {
411                                 fprintf(stderr, "queuepause command requires a printer name\n");
412                                 return False;
413                         }
414
415                         notify_printer_status_byname(params[1], PRINTER_STATUS_PAUSED);
416                         break;
417                 }
418
419                 /* Resume a print queue */
420
421                 if (strequal(cmd, "queueresume")) {
422
423                         if (!params[1]) {
424                                 fprintf(stderr, "queueresume command requires a printer name\n");
425                                 return False;
426                         }
427
428                         notify_printer_status_byname(params[1], PRINTER_STATUS_OK);
429                         break;
430                 }
431
432                 /* Pause a print job */
433
434                 if (strequal(cmd, "jobpause")) {
435                         int jobid;
436
437                         if (!params[1] || !params[2]) {
438                                 fprintf(stderr, "jobpause command requires a printer name and a jobid\n");
439                                 return False;
440                         }
441
442                         jobid = atoi(params[2]);
443
444                         notify_job_status_byname(
445                                 params[1], jobid, JOB_STATUS_PAUSED, 
446                                 SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
447                         break;
448                 }
449
450                 /* Resume a print job */
451
452                 if (strequal(cmd, "jobresume")) {
453                         int jobid;
454
455                         if (!params[1] || !params[2]) {
456                                 fprintf(stderr, "jobresume command requires a printer name and a jobid\n");
457                                 return False;
458                         }
459
460                         jobid = atoi(params[2]);
461
462                         notify_job_status_byname(
463                                 params[1], jobid, JOB_STATUS_QUEUED,
464                                 SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
465                         break;
466                 }
467
468                 /* Delete a print job */
469
470                 if (strequal(cmd, "jobdelete")) {
471                         int jobid;
472
473                         if (!params[1] || !params[2]) {
474                                 fprintf(stderr, "jobdelete command requires a printer name and a jobid\n");
475                                 return False;
476                         }
477
478                         jobid = atoi(params[2]);
479
480                         notify_job_status_byname(
481                                 params[1], jobid, JOB_STATUS_DELETING,
482                                 SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
483
484                         notify_job_status_byname(
485                                 params[1], jobid, JOB_STATUS_DELETING|
486                                 JOB_STATUS_DELETED,
487                                 SPOOLSS_NOTIFY_MSG_UNIX_JOBID);
488                 }
489                 
490                 /* printer change notify */
491                 
492                 if (strequal(cmd, "printer")) {
493                         int attribute = -1;
494                         
495                         if (!params[1] || !params[2] || !params[3]) {
496                                 fprintf(stderr, "printer command requires an and attribute name and value!\n");
497                                 fprintf(stderr, "supported attributes:\n");
498                                 fprintf(stderr, "\tcomment:\n");
499                                 fprintf(stderr, "\tport:\n");
500                                 fprintf(stderr, "\tdriver:\n");
501                                 return False;
502                         }
503                         if ( strequal(params[2], "comment") )
504                                 attribute = PRINTER_NOTIFY_COMMENT;
505                         else if ( strequal(params[2], "port") )
506                                 attribute = PRINTER_NOTIFY_PORT_NAME;
507                         else if ( strequal(params[2], "driver") )
508                                 attribute = PRINTER_NOTIFY_DRIVER_NAME;
509                         
510                         if ( attribute == -1 ) {
511                                 fprintf(stderr, "bad attribute!\n");
512                                 return False;
513                         }
514                         
515                         notify_printer_byname( params[1], attribute, params[3]);
516                         
517                         break;
518                 }
519                 
520                 break;
521           }
522
523
524         case MSG_SMB_FORCE_TDIS:
525                 if (!strequal(dest, "smbd")) {
526                         fprintf(stderr,"close-share can only be sent to smbd\n");
527                         return(False);
528                 }
529                 if (!params || !params[0]) {
530                         fprintf(stderr, "close-share needs a share name or '*'\n");
531                         return (False);
532                 }
533                 retval = send_message(dest, MSG_SMB_FORCE_TDIS, params[0],
534                                       strlen(params[0]) + 1, False);
535                 break;
536
537         case MSG_SMB_SAM_SYNC:
538                 if (!strequal(dest, "smbd")) {
539                         fprintf(stderr, "samsync can only be sent to smbd\n");
540                         return False;
541                 }
542
543                 if (params) {
544                         fprintf(stderr, "samsync does not take any parameters\n");
545                         return False;
546                 }
547
548                 retval = send_message(dest, MSG_SMB_SAM_SYNC, NULL, 0, False);
549
550                 break;
551
552         case MSG_SMB_SAM_REPL: {
553                 uint32 seqnum;
554
555                 if (!strequal(dest, "smbd")) {
556                         fprintf(stderr, "sam repl can only be sent to smbd\n");
557                         return False;
558                 }
559
560                 if (!params || !params[0]) {
561                         fprintf(stderr, "SAM_REPL needs a parameter\n");
562                         return False;
563                 }
564
565                 seqnum = atoi(params[0]);
566
567                 retval = send_message(dest, MSG_SMB_SAM_SYNC, 
568                                       (char *)&seqnum, sizeof(uint32), False); 
569
570                 break;
571         }
572
573         case MSG_PING:
574                 if (!pong_registered) {
575                     message_register(MSG_PONG, pong_function);
576                     pong_registered = True;
577                 }
578                 if (!params || !params[0]) {
579                         fprintf(stderr,"MSG_PING needs a parameter\n");
580                         return(False);
581                 }
582                 n = atoi(params[0]);
583                 pong_count = 0;
584                 for (i=0;i<n;i++) {
585                         if (iparams > 1)
586                                 retval = send_message(dest, MSG_PING, params[1], strlen(params[1]) + 1, True);
587                         else
588                                 retval = send_message(dest, MSG_PING, NULL, 0, True);
589                         if (retval == False)
590                                 return False;
591                 }
592                 wait_for_replies(MAX_WAIT, &n);
593                 if (n > 0) {
594                         fprintf(stderr,"PING timeout\n");
595                 }
596                 break;
597
598         case MSG_REQ_POOL_USAGE:
599                 if (!send_message(dest, MSG_REQ_POOL_USAGE, NULL, 0, True))
600                         return False;
601                 wait_for_replies(MAX_WAIT, NULL);
602                 
603                 break;
604
605         case MSG_REQ_DMALLOC_LOG_CHANGED:
606         case MSG_REQ_DMALLOC_MARK:
607                 if (!send_message(dest, mtype, NULL, 0, False))
608                         return False;
609                 break;
610
611         case MSG_SHUTDOWN:
612                 if (!send_message(dest, MSG_SHUTDOWN, NULL, 0, False))
613                         return False;
614                 break;
615         case MSG_PRINTER_DRVUPGRADE:
616                 if (!params || !params[0]) {
617                         fprintf(stderr,"drvupgrade needs a parameter\n");
618                         return(False);
619                 }
620
621                 if (!send_message(dest, MSG_PRINTER_DRVUPGRADE, params[0], 0, False))
622                         return False;
623                 break;
624         }
625
626         /* check if we have any pending print notify messages */
627
628         if ( check_notify_msgs )
629                 print_notify_send_messages(0);
630                 
631         return (True);
632 }
633
634  int main(int argc, char *argv[])
635 {
636         int opt;
637         char temp[255];
638         extern int optind;
639         BOOL interactive = False;
640
641         AllowDebugChange = False;
642         DEBUGLEVEL = 0;
643
644         setup_logging(argv[0],True);
645         
646         if (argc < 2) usage(True);
647
648         while ((opt = getopt(argc, argv,"is:")) != EOF) {
649                 switch (opt) {
650                 case 'i':
651                         interactive = True;
652                         break;
653                 case 's':
654                         pstrcpy(dyn_CONFIGFILE, optarg);
655                         break;
656                 default:
657                         printf("Unknown option %c (%d)\n", (char)opt, opt);
658                         usage(True);
659                 }
660         }
661
662         lp_load(dyn_CONFIGFILE,False,False,False);
663
664         if (!message_init()) exit(1);
665
666         argc -= optind;
667         argv = &argv[optind];
668
669         register_all();
670
671         if (!interactive) {
672                 if (argc < 2) usage(True);
673                 /* Need to invert sense of return code -- samba
674                  * routines mostly return True==1 for success, but
675                  * shell needs 0. */ 
676                 return ! do_command(argv[0],argv[1], argc-2, argc > 2 ? &argv[2] : 0);
677         }
678
679         while (True) {
680                 char *myargv[4];
681                 int myargc;
682
683                 printf("smbcontrol> ");
684                 if (!fgets(temp, sizeof(temp)-1, stdin)) break;
685                 myargc = 0;
686                 while ((myargc < 4) && 
687                        (myargv[myargc] = strtok(myargc?NULL:temp," \t\n"))) {
688                         myargc++;
689                 }
690                 if (!myargc) break;
691                 if (strequal(myargv[0],"q")) break;
692                 if (myargc < 2)
693                         usage(False);
694                 else if (!do_command(myargv[0],myargv[1],myargc-2,myargc > 2 ? &myargv[2] : 0))
695                         usage(False);
696         }
697         return(0);
698 }
699