/* 
   Unix SMB/Netbios implementation.
   Version 3.0
   program to send control messages to Samba processes
   Copyright (C) Andrew Tridgell 1994-1998
   
   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.
*/

#define NO_SYSLOG

#include "includes.h"

static struct {
	char *name;
	int value;
} msg_types[] = {
	{"debug", MSG_DEBUG},
	{"force-election", MSG_FORCE_ELECTION},
	{"ping", MSG_PING},
	{"profile", MSG_PROFILE},
	{"debuglevel", MSG_REQ_DEBUGLEVEL},
	{NULL, -1}
};

static void usage(BOOL doexit)
{
	int i;
	if (doexit) {
		printf("Usage: smbcontrol -i\n");
		printf("       smbcontrol <destination> <message-type> <parameters>\n\n");
	} else {
		printf("<destination> <message-type> <parameters>\n\n");
	}
	printf("\t<destination> is one of \"nmbd\", \"smbd\" or a process ID\n");
	printf("\t<message-type> is one of: ");
	for (i=0; msg_types[i].name; i++) 
	    printf("%s%s", i?", ":"",msg_types[i].name);
	printf("\n");
	if (doexit) exit(1);
}

static int pong_count;
static BOOL got_level;
static BOOL pong_registered = False;
static BOOL debuglevel_registered = False;


/****************************************************************************
a useful function for testing the message system
****************************************************************************/
void pong_function(int msg_type, pid_t src, void *buf, size_t len)
{
	pong_count++;
	printf("PONG\n");
}

/****************************************************************************
Prints out the current Debug level returned by MSG_DEBUGLEVEL
****************************************************************************/
void debuglevel_function(int msg_type, pid_t src, void *buf, size_t len)
{
        int level;
        memcpy(&level, buf, sizeof(int));

	printf("Current debug level is %d\n",level);
	got_level = True;
}

/****************************************************************************
send a message to a named destination
****************************************************************************/
static BOOL send_message(char *dest, int msg_type, void *buf, int len)
{
	pid_t pid;

	/* "smbd" is the only broadcast operation */
	if (strequal(dest,"smbd")) {
		return message_send_all(msg_type, buf, len);
	} else if (strequal(dest,"nmbd")) {
		pid = pidfile_pid(dest);
		if (pid == 0) {
			fprintf(stderr,"Can't find pid for nmbd\n");
			return False;
		}
	} else {
		pid = atoi(dest);
		if (pid == 0) {
			fprintf(stderr,"Not a valid pid\n");
			return False;
		}		
	} 

	return message_send_pid(pid, msg_type, buf, len);
}

/****************************************************************************
evaluate a message type string
****************************************************************************/
static int parse_type(char *mtype)
{
	int i;
	for (i=0;msg_types[i].name;i++) {
		if (strequal(mtype, msg_types[i].name)) return msg_types[i].value;
	}
	return -1;
}


/****************************************************************************
do command
****************************************************************************/
static BOOL do_command(char *dest, char *msg_name, char *params)
{
	int i, n, v;
	int mtype;

	mtype = parse_type(msg_name);
	if (mtype == -1) {
		fprintf(stderr,"Couldn't resolve message type: %s\n", msg_name);
		return(False);
	}

	switch (mtype) {
	case MSG_DEBUG:
		if (!params) {
			fprintf(stderr,"MSG_DEBUG needs a parameter\n");
			return(False);
		}
		v = atoi(params);
		send_message(dest, MSG_DEBUG, &v, sizeof(int));
		break;

	case MSG_PROFILE:
		if (!params) {
			fprintf(stderr,"MSG_PROFILE needs a parameter\n");
			return(False);
		}
		if (strequal(params, "on")) {
			v = 2;
		} else if (strequal(params, "off")) {
			v = 0;
		} else if (strequal(params, "count")) {
			v = 1;
		} else {
		    fprintf(stderr,
			"MSG_PROFILE parameter must be on, off, or count\n");
		    return(False);
		}
		send_message(dest, MSG_PROFILE, &v, sizeof(int));
		break;

	case MSG_FORCE_ELECTION:
		if (!strequal(dest, "nmbd")) {
			fprintf(stderr,"force-election can only be sent to nmbd\n");
			return(False);
		}
		send_message(dest, MSG_FORCE_ELECTION, NULL, 0);
		break;

	case MSG_REQ_DEBUGLEVEL:
		if (!debuglevel_registered) {
		    message_register(MSG_DEBUGLEVEL, debuglevel_function);
		    debuglevel_registered = True;
		}
		if (strequal(dest, "nmbd") || strequal(dest, "smbd")) {
		    fprintf(stderr,"debuglevel can only be sent to a PID\n");
		    return(False);
		}
		got_level = False;
		send_message(dest, MSG_REQ_DEBUGLEVEL, NULL, 0);
		while (!got_level) message_dispatch();
		break;

	case MSG_PING:
		if (!pong_registered) {
		    message_register(MSG_PONG, pong_function);
		    pong_registered = True;
		}
		if (!params) {
			fprintf(stderr,"MSG_PING needs a parameter\n");
			return(False);
		}
		n = atoi(params);
		pong_count = 0;
		for (i=0;i<n;i++) {
			send_message(dest, MSG_PING, NULL, 0);
		}
		while (pong_count < n) message_dispatch();
		break;

	}
	
	return (True);
}

 int main(int argc, char *argv[])
{
	int opt;
	char temp[255];
	extern int optind;
	pstring servicesf = CONFIGFILE;
	BOOL interactive = False;

	TimeInit();
	setup_logging(argv[0],True);
	
	charset_initialise();
	lp_load(servicesf,False,False,False);

	message_init();

	if (argc < 2) usage(True);

	while ((opt = getopt(argc, argv,"i")) != EOF) {
		switch (opt) {
		case 'i':
			interactive = True;
			break;
		default:
			printf("Unknown option %c (%d)\n", (char)opt, opt);
			usage(True);
		}
	}

	argc -= optind;
	argv = &argv[optind];

	if (!interactive) {
		if (argc < 2) usage(True);
		return (do_command(argv[0],argv[1],argc > 2 ? argv[2] : 0));
	}

	while (True) {
		char *myargv[3];
		int myargc;

		printf("smbcontrol> ");
		if (!gets(temp)) break;
		myargc = 0;
		while ((myargc < 3) && 
		       (myargv[myargc] = strtok(myargc?NULL:temp," \t"))) {
			myargc++;
		}
		if (!myargc) break;
		if (myargc < 2)
			usage(False);
		else if (!do_command(myargv[0],myargv[1],myargc > 2 ? myargv[2] : 0))
			usage(False);
	}
	return(0);
}