/*
* Copyright (c) 1998-2004, 2006 Sendmail, Inc. and its suppliers.
* All rights reserved.
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the sendmail distribution.
*
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sendmail.h>
/* values for cmd_code */
struct cmd
{
};
{
{ "help", CMDHELP },
{ "restart", CMDRESTART },
{ "shutdown", CMDSHUTDOWN },
{ "status", CMDSTATUS },
{ "memdump", CMDMEMDUMP },
{ "mstat", CMDMSTAT },
};
static void controltimeout __P((int));
/*
**
** Creates and opens a named socket for external control over
** the sendmail daemon.
**
** Parameters:
** none.
**
** Returns:
** 0 if successful, -1 otherwise
*/
int
{
# if NETUNIX
int save_errno;
int rval;
return 0;
{
return -1;
}
/* if not safe, don't create */
if (rval != 0)
{
return -1;
}
if (ControlSocket < 0)
return -1;
{
clrcontrol();
return -1;
}
(void) unlink(ControlSocketName);
sizeof(controladdr.sun_path));
sizeof(controladdr)) < 0)
{
save_errno = errno;
clrcontrol();
errno = save_errno;
return -1;
}
if (geteuid() == 0)
{
uid_t u = 0;
if (RunAsUid != 0)
u = RunAsUid;
else if (TrustedUid != 0)
u = TrustedUid;
if (u != 0 &&
{
save_errno = errno;
"ownership change on %s to uid %d failed: %s",
ControlSocketName, (int) u,
message("050 ownership change on %s to uid %d failed: %s",
ControlSocketName, (int) u,
closecontrolsocket(true);
errno = save_errno;
return -1;
}
}
{
save_errno = errno;
closecontrolsocket(true);
errno = save_errno;
return -1;
}
{
save_errno = errno;
closecontrolsocket(true);
errno = save_errno;
return -1;
}
# endif /* NETUNIX */
return 0;
}
/*
** CLOSECONTROLSOCKET -- close the daemon control named socket
**
** Close a named socket.
**
** Parameters:
** fullclose -- if set, close the socket and remove it;
** otherwise, just remove it
**
** Returns:
** none.
*/
void
bool fullclose;
{
# if NETUNIX
if (ControlSocket >= 0)
{
int rval;
if (fullclose)
{
(void) close(ControlSocket);
ControlSocket = -1;
}
/* if not safe, don't unlink */
if (rval != 0)
return;
if (unlink(ControlSocketName) < 0)
{
"Could not remove control socket: %s",
return;
}
}
# endif /* NETUNIX */
return;
}
/*
** CLRCONTROL -- reset the control connection
**
** Parameters:
** none.
**
** Returns:
** none.
**
** Side Effects:
** releases any resources used by the control interface.
*/
void
{
# if NETUNIX
if (ControlSocket >= 0)
(void) close(ControlSocket);
ControlSocket = -1;
# endif /* NETUNIX */
}
/*
** CONTROL_COMMAND -- read and process command from named socket
**
** Read and process the command from the opened socket.
** Exits when done since it is running in a forked child.
**
** Parameters:
** sock -- the opened socket from getrequests()
** e -- the current envelope
**
** Returns:
** none.
*/
/* ARGSUSED0 */
static void
int timeout;
{
/*
** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD
** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
** DOING.
*/
}
void
int sock;
ENVELOPE *e;
{
char *cmd;
char *p;
struct cmd *c;
sm_setproctitle(false, e, "control cmd read");
if (TimeOuts.to_control > 0)
{
/* handle possible input timeout */
if (setjmp(CtxControlTimeout) != 0)
{
if (LogLevel > 2)
"timeout waiting for input during control command");
}
}
SM_IO_RDWR, NULL);
if (s == NULL)
{
errno = save_errno;
}
{
(void) sm_io_close(s, SM_TIME_DEFAULT);
}
(void) sm_io_flush(s, SM_TIME_DEFAULT);
/* clean up end of line */
/* break off command */
continue;
while (*p != '\0' &&
*cmd++ = *p++;
*cmd = '\0';
/* throw away leading whitespace */
p++;
/* decode command */
{
break;
}
switch (c->cmd_code)
{
case CMDHELP: /* get help */
oldout = OutChannel;
OutChannel = s;
help("control", e);
OutChannel = oldout;
break;
case CMDRESTART: /* restart the daemon */
break;
case CMDSHUTDOWN: /* kill the daemon */
break;
case CMDSTATUS: /* daemon status */
{
int qgrp;
long bsize;
long free;
/* XXX need to deal with different partitions */
if (!ISVALIDQGRP(qgrp))
qgrp = 0;
/*
** Prevent overflow and don't lose
** precision (if bsize == 512)
*/
if (free > 0)
((double) bsize / 1024));
(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
"%d/%d/%ld/%d\r\n",
}
proc_list_display(s, "");
break;
case CMDMSTAT: /* daemon status, extended, tagged format */
(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
"C:%d\r\nM:%d\r\nL:%d\r\n",
getla());
printnqe(s, "Q:");
disk_status(s, "D:");
proc_list_display(s, "P:");
break;
case CMDMEMDUMP: /* daemon memory dump, to find memory leaks */
# if SM_HEAP_CHECK
/* dump the heap, if we are checking for memory leaks */
{
}
else
{
(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
"Memory dump unavailable.\r\n");
(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
"To fix, run sendmail with -dsm_check_heap.4\r\n");
}
# else /* SM_HEAP_CHECK */
(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
"Memory dump unavailable.\r\n");
(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
"To fix, rebuild with -DSM_HEAP_CHECK\r\n");
# endif /* SM_HEAP_CHECK */
break;
case CMDERROR: /* unknown command */
(void) sm_io_fprintf(s, SM_TIME_DEFAULT,
"Bad command (%s)\r\n", cmdbuf);
break;
}
(void) sm_io_close(s, SM_TIME_DEFAULT);
}