/* Unix SMB/CIFS implementation. Send messages to other Samba daemons Copyright (C) Tim Potter 2003 Copyright (C) Andrew Tridgell 1994-1998 Copyright (C) Martin Pool 2001-2002 Copyright (C) Simo Sorce 2002 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "includes.h" /* Default timeout value when waiting for replies (in seconds) */ #define DEFAULT_TIMEOUT 10 static int timeout = DEFAULT_TIMEOUT; static int num_replies; /* Used by message callback fns */ /* Send a message to a destination pid. Zero means broadcast smbd. */ static BOOL send_message(pid_t pid, int msg_type, const void *buf, int len, BOOL duplicates) { TDB_CONTEXT *tdb; BOOL ret; int n_sent = 0; if (!message_init()) return False; if (pid != 0) return message_send_pid(pid, msg_type, buf, len, duplicates); tdb = tdb_open_log(lock_path("connections.tdb"), 0, TDB_DEFAULT, O_RDWR, 0); if (!tdb) { fprintf(stderr,"Failed to open connections database" ": %s\n", strerror(errno)); return False; } ret = message_send_all(tdb,msg_type, buf, len, duplicates, &n_sent); DEBUG(10,("smbcontrol/send_message: broadcast message to " "%d processes\n", n_sent)); tdb_close(tdb); return ret; } /* Wait for one or more reply messages */ static void wait_replies(BOOL multiple_replies) { time_t start_time = time(NULL); /* Wait around a bit. This is pretty disgusting - we have to busy-wait here as there is no nicer way to do it. */ do { message_dispatch(); if (num_replies > 0 && !multiple_replies) break; sleep(1); } while (timeout - (time(NULL) - start_time) > 0); } /* Message handler callback that displays a string on stdout */ static void print_string_cb(int msg_type, pid_t pid, void *buf, size_t len) { printf("%.*s", (int)len, (const char *)buf); num_replies++; } /* Send no message. Useful for testing. */ static BOOL do_noop(const pid_t pid, const int argc, const char **argv) { if (argc != 1) { fprintf(stderr, "Usage: smbcontrol noop\n"); return False; } /* Move along, nothing to see here */ return True; } /* Send a debug string */ static BOOL do_debug(const pid_t pid, const int argc, const char **argv) { if (argc != 2) { fprintf(stderr, "Usage: smbcontrol debug " "\n"); return False; } return send_message( pid, MSG_DEBUG, argv[1], strlen(argv[1]) + 1, False); } /* Force a browser election */ static BOOL do_election(const pid_t pid, const int argc, const char **argv) { if (argc != 1) { fprintf(stderr, "Usage: smbcontrol force-election\n"); return False; } return send_message( pid, MSG_FORCE_ELECTION, NULL, 0, False); } /* Ping a samba daemon process */ static void pong_cb(int msg_type, pid_t pid, void *buf, size_t len) { printf("PONG from pid %u\n", (unsigned int)pid); num_replies++; } static BOOL do_ping(const pid_t pid, const int argc, const char **argv) { if (argc != 1) { fprintf(stderr, "Usage: smbcontrol ping\n"); return False; } /* Send a message and register our interest in a reply */ if (!send_message(pid, MSG_PING, NULL, 0, False)) return False; message_register(MSG_PONG, pong_cb); wait_replies(pid == 0); /* No replies were received within the timeout period */ if (num_replies == 0) printf("No replies received\n"); message_deregister(MSG_PONG); return num_replies; } /* Set profiling options */ static BOOL do_profile(const pid_t pid, const int argc, const char **argv) { int v; if (argc != 2) { fprintf(stderr, "Usage: smbcontrol profile " "\n"); return False; } if (strcmp(argv[1], "off") == 0) { v = 0; } else if (strcmp(argv[1], "count") == 0) { v = 1; } else if (strcmp(argv[1], "on") == 0) { v = 2; } else if (strcmp(argv[1], "flush") == 0) { v = 3; } else { fprintf(stderr, "Unknown profile command '%s'\n", argv[1]); return False; } return send_message(pid, MSG_PROFILE, &v, sizeof(int), False); } /* Return the profiling level */ static void profilelevel_cb(int msg_type, pid_t pid, void *buf, size_t len) { int level; const char *s; num_replies++; if (len != sizeof(int)) { fprintf(stderr, "invalid message length %d returned\n", len); return; } memcpy(&level, buf, sizeof(int)); switch (level) { case 0: s = "not enabled"; break; case 1: s = "off"; break; case 3: s = "count only"; break; case 7: s = "count and time"; break; default: s = "BOGUS"; break; } printf("Profiling %s on pid %u\n",s,(unsigned int)pid); } static void profilelevel_rqst(int msg_type, pid_t pid, void *buf, size_t len) { int v = 0; /* Send back a dummy reply */ send_message(pid, MSG_PROFILELEVEL, &v, sizeof(int), False); } static BOOL do_profilelevel(const pid_t pid, const int argc, const char **argv) { if (argc != 1) { fprintf(stderr, "Usage: smbcontrol profilelevel\n"); return False; } /* Send a message and register our interest in a reply */ if (!send_message(pid, MSG_REQ_PROFILELEVEL, NULL, 0, False)) return False; message_register(MSG_PROFILELEVEL, profilelevel_cb); message_register(MSG_REQ_PROFILELEVEL, profilelevel_rqst); wait_replies(pid == 0); /* No replies were received within the timeout period */ if (num_replies == 0) printf("No replies received\n"); message_deregister(MSG_PROFILE); return num_replies; } /* Display debug level settings */ static BOOL do_debuglevel(const pid_t pid, const int argc, const char **argv) { if (argc != 1) { fprintf(stderr, "Usage: smbcontrol debuglevel\n"); return False; } /* Send a message and register our interest in a reply */ if (!send_message(pid, MSG_REQ_DEBUGLEVEL, NULL, 0, False)) return False; message_register(MSG_DEBUGLEVEL, print_string_cb); wait_replies(pid == 0); /* No replies were received within the timeout period */ if (num_replies == 0) printf("No replies received\n"); message_deregister(MSG_DEBUGLEVEL); return num_replies; } /* Send a print notify message */ static BOOL do_printnotify(const pid_t pid, const int argc, const char **argv) { const char *cmd; /* Check for subcommand */ if (argc == 1) { fprintf(stderr, "Must specify subcommand:\n"); fprintf(stderr, "\tqueuepause \n"); fprintf(stderr, "\tqueueresume \n"); fprintf(stderr, "\tjobpause \n"); fprintf(stderr, "\tjobresume \n"); fprintf(stderr, "\tjobdelete \n"); fprintf(stderr, "\tprinter \n"); return False; } cmd = argv[1]; if (strcmp(cmd, "queuepause") == 0) { if (argc != 3) { fprintf(stderr, "Usage: smbcontrol printnotify" " queuepause \n"); return False; } notify_printer_status_byname(argv[2], PRINTER_STATUS_PAUSED); goto send; } else if (strcmp(cmd, "queueresume") == 0) { if (argc != 3) { fprintf(stderr, "Usage: smbcontrol printnotify" " queuereume \n"); return False; } notify_printer_status_byname(argv[2], PRINTER_STATUS_OK); goto send; } else if (strcmp(cmd, "jobpause") == 0) { int jobid; if (argc != 4) { fprintf(stderr, "Usage: smbcontrol printnotify" " jobpause \n"); return False; } jobid = atoi(argv[3]); notify_job_status_byname( argv[2], jobid, JOB_STATUS_PAUSED, SPOOLSS_NOTIFY_MSG_UNIX_JOBID); goto send; } else if (strcmp(cmd, "jobresume") == 0) { int jobid; if (argc != 4) { fprintf(stderr, "Usage: smbcontrol printnotify" " jobpause \n"); return False; } jobid = atoi(argv[3]); notify_job_status_byname( argv[2], jobid, JOB_STATUS_QUEUED, SPOOLSS_NOTIFY_MSG_UNIX_JOBID); goto send; } else if (strcmp(cmd, "jobdelete") == 0) { int jobid; if (argc != 4) { fprintf(stderr, "Usage: smbcontrol printnotify" " jobpause \n"); return False; } jobid = atoi(argv[3]); notify_job_status_byname( argv[2], jobid, JOB_STATUS_DELETING, SPOOLSS_NOTIFY_MSG_UNIX_JOBID); notify_job_status_byname( argv[2], jobid, JOB_STATUS_DELETING| JOB_STATUS_DELETED, SPOOLSS_NOTIFY_MSG_UNIX_JOBID); goto send; } else if (strcmp(cmd, "printer") == 0) { uint32 attribute; if (argc != 5) { fprintf(stderr, "Usage: smbcontrol printnotify " "printer " "\n"); return False; } if (strcmp(argv[3], "comment") == 0) { attribute = PRINTER_NOTIFY_COMMENT; } else if (strcmp(argv[3], "port") == 0) { attribute = PRINTER_NOTIFY_PORT_NAME; } else if (strcmp(argv[3], "driver") == 0) { attribute = PRINTER_NOTIFY_DRIVER_NAME; } else { fprintf(stderr, "Invalid printer command '%s'\n", argv[3]); return False; } notify_printer_byname(argv[2], attribute, argv[4]); goto send; } fprintf(stderr, "Invalid subcommand '%s'\n", cmd); return False; send: print_notify_send_messages(0); return True; } /* Close a share */ static BOOL do_closeshare(const pid_t pid, const int argc, const char **argv) { if (argc != 2) { fprintf(stderr, "Usage: smbcontrol close-share " "\n"); return False; } return send_message( pid, MSG_SMB_FORCE_TDIS, argv[1], strlen(argv[1]) + 1, False); } /* Force a SAM synchronisation */ static BOOL do_samsync(const pid_t pid, const int argc, const char **argv) { if (argc != 1) { fprintf(stderr, "Usage: smbcontrol samsync\n"); return False; } return send_message( pid, MSG_SMB_SAM_SYNC, NULL, 0, False); } /* Force a SAM replication */ static BOOL do_samrepl(const pid_t pid, const int argc, const char **argv) { if (argc != 1) { fprintf(stderr, "Usage: smbcontrol samrepl\n"); return False; } return send_message( pid, MSG_SMB_SAM_REPL, NULL, 0, False); } /* Display talloc pool usage */ static BOOL do_poolusage(const pid_t pid, const int argc, const char **argv) { if (argc != 1) { fprintf(stderr, "Usage: smbcontrol pool-usage\n"); return False; } /* Send a message and register our interest in a reply */ if (!send_message(pid, MSG_REQ_POOL_USAGE, NULL, 0, False)) return False; message_register(MSG_POOL_USAGE, print_string_cb); wait_replies(pid == 0); /* No replies were received within the timeout period */ if (num_replies == 0) printf("No replies received\n"); message_deregister(MSG_POOL_USAGE); return num_replies; } /* Perform a dmalloc mark */ static BOOL do_dmalloc_mark(const pid_t pid, const int argc, const char **argv) { if (argc != 1) { fprintf(stderr, "Usage: smbcontrol dmalloc-mark\n"); return False; } return send_message( pid, MSG_REQ_DMALLOC_MARK, NULL, 0, False); } /* Perform a dmalloc changed */ static BOOL do_dmalloc_changed(const pid_t pid, const int argc, const char **argv) { if (argc != 1) { fprintf(stderr, "Usage: smbcontrol " "dmalloc-log-changed\n"); return False; } return send_message( pid, MSG_REQ_DMALLOC_LOG_CHANGED, NULL, 0, False); } /* Shutdown a server process */ static BOOL do_shutdown(const pid_t pid, const int argc, const char **argv) { if (argc != 1) { fprintf(stderr, "Usage: smbcontrol shutdown\n"); return False; } return send_message(pid, MSG_SHUTDOWN, NULL, 0, False); } /* Notify a driver upgrade */ static BOOL do_drvupgrade(const pid_t pid, const int argc, const char **argv) { if (argc != 2) { fprintf(stderr, "Usage: smbcontrol drvupgrade " "\n"); return False; } return send_message( pid, MSG_DEBUG, argv[1], strlen(argv[1]) + 1, False); } static BOOL do_reload_config(const pid_t pid, const int argc, const char **argv) { if (argc != 1) { fprintf(stderr, "Usage: smbcontrol reload-config\n"); return False; } return send_message(pid, MSG_SMB_CONF_UPDATED, NULL, 0, False); } /* A list of message type supported */ static const struct { const char *name; /* Option name */ BOOL (*fn)(const pid_t pid, const int argc, const char **argv); const char *help; /* Short help text */ } msg_types[] = { { "debug", do_debug, "Set debuglevel" }, { "force-election", do_election, "Force a browse election" }, { "ping", do_ping, "Elicit a response" }, { "profile", do_profile, "" }, { "profilelevel", do_profilelevel, "" }, { "debuglevel", do_debuglevel, "Display current debuglevels" }, { "printnotify", do_printnotify, "Send a print notify message" }, { "close-share", do_closeshare, "Forcibly disconnect a share" }, { "samsync", do_samsync, "Initiate SAM synchronisation" }, { "samrepl", do_samrepl, "Initiate SAM replication" }, { "pool-usage", do_poolusage, "Display talloc memory usage" }, { "dmalloc-mark", do_dmalloc_mark, "" }, { "dmalloc-log-changed", do_dmalloc_changed, "" }, { "shutdown", do_shutdown, "Shut down daemon" }, { "drvupgrade", do_drvupgrade, "Notify a printer driver has changed" }, { "reload-config", do_reload_config, "Force smbd or winbindd to reload config file"}, { "noop", do_noop, "Do nothing" }, { NULL } }; /* Display usage information */ static void usage(poptContext *pc) { int i; poptPrintHelp(*pc, stderr, 0); fprintf(stderr, "\n"); fprintf(stderr, " is one of \"nmbd\", \"smbd\" or a " "process ID\n"); fprintf(stderr, "\n"); fprintf(stderr, " is one of:\n"); for (i = 0; msg_types[i].name; i++) fprintf(stderr, "\t%-30s%s\n", msg_types[i].name, msg_types[i].help); fprintf(stderr, "\n"); exit(1); } /* Return the pid number for a string destination */ static pid_t parse_dest(const char *dest) { pid_t pid; /* Zero is a special return value for broadcast smbd */ if (strequal(dest, "smbd")) return 0; /* Try self - useful for testing */ if (strequal(dest, "self")) return sys_getpid(); /* Check for numeric pid number */ if ((pid = atoi(dest)) != 0) return pid; /* Look up other destinations in pidfile directory */ if ((pid = pidfile_pid(dest)) != 0) return pid; fprintf(stderr,"Can't find pid for destination '%s'\n", dest); return -1; } /* Execute smbcontrol command */ static BOOL do_command(int argc, const char **argv) { const char *dest = argv[0], *command = argv[1]; pid_t pid; int i; /* Check destination */ if ((pid = parse_dest(dest)) == -1) return False; /* Check command */ for (i = 0; msg_types[i].name; i++) { if (strequal(command, msg_types[i].name)) return msg_types[i].fn(pid, argc - 1, argv + 1); } fprintf(stderr, "smbcontrol: unknown command '%s'\n", command); return False; } /* Main program */ int main(int argc, const char **argv) { poptContext pc; int opt; static struct poptOption wbinfo_options[] = { { "timeout", 't', POPT_ARG_INT, &timeout, 't', "Set timeout value in seconds", "TIMEOUT" }, { "configfile", 's', POPT_ARG_STRING, NULL, 's', "Use alternative configuration file", "CONFIGFILE" }, POPT_TABLEEND }; struct poptOption options[] = { { NULL, 0, POPT_ARG_INCLUDE_TABLE, wbinfo_options, 0, "Options" }, POPT_AUTOHELP POPT_COMMON_VERSION POPT_TABLEEND }; setup_logging(argv[0],True); /* Parse command line arguments using popt */ pc = poptGetContext( "smbcontrol", argc, (const char **)argv, options, 0); poptSetOtherOptionHelp(pc, "[OPTION...] " ""); if (argc == 1) usage(&pc); while ((opt = poptGetNextOpt(pc)) != -1) { switch(opt) { case 't': /* --timeout */ argc -= 2; break; case 's': /* --configfile */ pstrcpy(dyn_CONFIGFILE, optarg); argc -= 2; break; default: fprintf(stderr, "Invalid option\n"); poptPrintHelp(pc, stderr, 0); break; } } /* We should now have the remaining command line arguments in argv. The argc parameter should have been decremented to the correct value in the above switch statement. */ argv = (const char **)poptGetArgs(pc); argc--; /* Don't forget about argv[0] */ if (argc == 1) usage(&pc); lp_load(dyn_CONFIGFILE,False,False,False); /* Need to invert sense of return code -- samba * routines mostly return True==1 for success, but * shell needs 0. */ return !do_command(argc, argv); }