/*
* Copyright (c) 2004, 2005 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.
*/
#include "sm/generic.h"
SM_RCSID("@(#)$Id: qmgrctl.c,v 1.11 2005/11/28 06:08:33 ca Exp $")
#include "sm/common.h"
#include "sm/assert.h"
#include "sm/error.h"
#include "sm/memops.h"
#include "sm/io.h"
#include "sm/ctype.h"
#include "sm/fcntl.h"
#include "sm/mta.h"
#include "sm/rcb.h"
#include "sm/unixsock.h"
#include "sm/reccom.h"
#include "sm/util.h"
#include "sm/sysexits.h"
#define SM_RCBSIZE 1024
#define SM_MAXRCBSIZE (64 * 1024)
static int Verbose = 0;
/*
** USAGE -- show usage
**
** Parameters:
** prg -- program name
**
** Returns:
** does not return
*/
static void
usage(const char *prg)
{
sm_io_fprintf(smioerr,
"usage: %s [options] socket\n"
"interact with qmgr to receive status information [default]\n"
"or request some actions.\n"
"socket must be the path name of the control_socket of qmgr\n"
"options\n"
"-d n set QMGR debug level\n"
/*
"-R rcpt-id remove recipient from queue\n"
*/
"-r reload QMGR maps\n"
"-s rcpt-id schedule recipient for delivery as soon as possible\n"
/*
"-T ta-id remove entire transaction from queue\n"
*/
"-V increase verbosity\n"
"-x c[.l] set QMGR debug level for category c to l\n"
, prg);
exit(EX_USAGE);
}
/*
** QPRTBUF -- print content of buffer (unprintable char as hex)
**
** Parameters:
** fp -- file for output
** buf -- buffer to print
** len -- len of buffer
**
** Returns:
** nothing
*/
static void
qprtbuf(sm_file_T *fp, const uchar *buf, size_t len)
{
size_t i;
int c;
for (i = 0; i < len; i++)
{
c = (int) (buf[i] & 0xff);
if (ISPRINT(c) || c == '\n' || c == '\r')
sm_io_putc(fp, c);
else
sm_io_fprintf(fp, " %02x ", c);
}
}
/*
** QSENDRCB -- send an rcb
**
** Parameters:
** rcb -- RCB
** fd -- fd
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
qsendrcb(sm_rcb_P rcb, rcb_fd_T fd)
{
int res;
sm_ret_T ret;
ret = sm_rcb_open_snd(rcb);
do
{
res = sm_rcb_snd(fd, rcb);
} while (res > 0);
ret = sm_rcb_close_snd(rcb);
if (res != 0)
{
sm_io_fprintf(smioerr,
"write_fd failed=%d, len=%d, errno=%d\n",
res, (int) sm_rcb_getlen(rcb), errno);
ret = sm_error_perm(SM_EM_Q, EIO);
}
return ret;
}
/*
** QRCVRCB -- rcv rcb
**
** Parameters:
** rcb -- RCB
** fd -- fd
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
qrcvrcb(sm_rcb_P rcb, rcb_fd_T fd)
{
sm_ret_T ret;
uint32_t rt, val, len;
uchar buf[SM_MAXRCBSIZE];
ret = sm_rcb_open_rcv(rcb);
val = 0;
len = 0;
do
{
ret = sm_rcb_rcv(fd, rcb, 16);
} while (ret > 0);
if (ret < 0)
return ret;
ret = sm_rcb_close_rcv(rcb);
ret = sm_rcb_open_dec(rcb);
if (sm_is_err(ret))
return ret;
/* total length of record */
ret = sm_rcb_getuint32(rcb, &len);
if (sm_is_err(ret))
goto error;
if (len > sm_rcb_getlen(rcb))
{
ret = sm_error_perm(SM_EM_Q, SM_E_RCB2LONG);
goto error;
}
/* protocol header: version */
ret = sm_rcb_get3uint32(rcb, &len, &rt, &val);
if (sm_is_err(ret))
goto error;
if (len != 4 || rt != RT_PROT_VER || val != PROT_VER_RT)
{
ret = sm_error_perm(SM_EM_Q, SM_E_PR_V_MISM);
goto error;
}
do
{
ret = sm_rcb_get2uint32(rcb, &len, &rt);
if (sm_is_err(ret))
goto error;
switch (rt)
{
case 0:
ret = sm_rcb_getuint32(rcb, &val);
break;
case RT_Q2CTL_INFO:
if (len < sizeof(buf) - 1)
{
sm_memzero(buf, sizeof(buf));
ret = sm_rcb_getn(rcb, buf, len);
if (sm_is_err(ret))
break;
qprtbuf(smioerr, buf, len);
sm_io_putc(smioerr, '\n');
}
break;
default:
goto error;
break;
}
} while (!SM_RCB_ISEOB(rcb));
ret = sm_rcb_close_dec(rcb);
return ret;
error:
(void) sm_rcb_close_decn(rcb);
return ret;
}
/*
** QQUERY -- send and receive rcb
**
** Parameters:
** prg -- name of program
** sockname -- name of socket
** cmd -- command to send
**
** Returns:
** usual sm_error code
*/
static sm_ret_T
qquery(const char *prg, const char *sockname, int cmd, uint32_t v, uint32_t v2, rcpt_id_P rcpt_id, sessta_id_P ta_ss_id)
{
int fd;
bool reply;
sm_rcb_P rcb;
sm_ret_T ret;
rcb = NULL;
reply = false;
ret = unix_client_connect(sockname, &fd);
if (sm_is_err(ret))
{
sm_io_fprintf(smioerr, "%s: cannot connect to %s, error=%#X\n"
, prg, sockname, ret);
return ret;
}
if (!is_valid_fd(fd))
return sm_error_perm(SM_EM_Q, EINVAL);
rcb = sm_rcb_new(NULL, SM_RCBSIZE, SM_MAXRCBSIZE);
if (rcb == NULL)
{
ret = sm_error_temp(SM_EM_Q, ENOMEM);
goto done;
}
switch (cmd)
{
case 0:
ret = sm_rcb_putrec(rcb, RCB_PUTR_DFLT, 0, -1,
SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
SM_RCBV_INT, RT_CTL2Q_INFO, 0,
SM_RCBV_END);
reply = true;
break;
case RT_CTL2Q_DBG:
ret = sm_rcb_putrec(rcb, RCB_PUTR_DFLT, 0, -1,
SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
SM_RCBV_INT, cmd, v,
SM_RCBV_END);
break;
case RT_CTL2Q_DBG_C:
ret = sm_rcb_putrec(rcb, RCB_PUTR_DFLT, 0, -1,
SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
SM_RCBV_INT2, cmd, v, v2,
SM_RCBV_END);
break;
case RT_CTL2Q_S_RCPT:
case RT_CTL2Q_D_RCPT:
ret = sm_rcb_putrec(rcb, RCB_PUTR_DFLT, 0, -1,
SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
SM_RCBV_BUF, cmd, rcpt_id, SMTP_RCPTID_SIZE,
SM_RCBV_END);
break;
case RT_CTL2Q_D_TA:
ret = sm_rcb_putrec(rcb, RCB_PUTR_DFLT, 0, -1,
SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
SM_RCBV_BUF, cmd, ta_ss_id, SMTP_STID_SIZE,
SM_RCBV_END);
break;
#if 0
default:
ret = sm_rcb_putrec(rcb, RCB_PUTR_DFLT, 0, -1,
SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT,
SM_RCBV_STR, rt, str,
SM_RCBV_END);
#endif
}
ret = qsendrcb(rcb, fd);
if (sm_is_err(ret))
goto done;
if (reply)
{
ret = qrcvrcb(rcb, fd);
if (sm_is_err(ret) && Verbose > 3)
sm_io_fprintf(smioerr, "clt: rcvrcb=%#x\n", ret);
}
done:
if (rcb != NULL)
sm_rcb_free(rcb);
if (is_valid_fd(fd))
close(fd);
return ret;
}
int
main(int argc, char *argv[])
{
int c, cmd;
uint32_t v, v2;
ulong first, last, level;
sm_ret_T ret;
char *sockname, *prg, *cptr;
rcpt_id_T rcpt_id;
sessta_id_T ta_ss_id;
prg = argv[0];
if (getuid() == 0 || geteuid() == 0)
{
sm_io_fprintf(smioerr, SM_DONTRUNASROOT "\n", prg);
exit(EX_USAGE);
}
opterr = 0;
Verbose = 0;
cmd = 0;
sockname = NULL;
rcpt_id[0] = '\0';
ta_ss_id[0] = '\0';
v = v2 = 0;
while ((c = getopt(argc, argv, "d:R:rs:T:Vx:")) != -1)
{
switch (c)
{
case 'd':
v = (uint) strtoul(optarg, NULL, 0);
break;
case 'R':
cmd = RT_CTL2Q_D_RCPT;
if (strlen(optarg) != SMTP_RCPTID_LEN - 1)
usage(prg);
if (strlcpy(rcpt_id, optarg, sizeof(rcpt_id))
>= sizeof(rcpt_id))
usage(prg);
break;
case 'r':
cmd = RT_CTL2Q_R_MAP;
break;
case 's':
cmd = RT_CTL2Q_S_RCPT;
if (strlen(optarg) != SMTP_RCPTID_LEN - 1)
{
sm_io_fprintf(smioerr,
"%s: expected_len=%d, got=%d\n", prg
, SMTP_RCPTID_LEN - 1, strlen(optarg));
usage(prg);
}
if (strlcpy(rcpt_id, optarg, sizeof(rcpt_id))
>= sizeof(rcpt_id))
{
sm_io_fprintf(smioerr,
"%s: expected_len=%d, copied=%d\n", prg
, sizeof(rcpt_id), strlen(optarg));
usage(prg);
}
break;
case 'T':
cmd = RT_CTL2Q_D_TA;
if (strlen(optarg) != SMTP_STID_SIZE - 1)
usage(prg);
if (strlcpy(ta_ss_id, optarg, sizeof(ta_ss_id))
>= sizeof(ta_ss_id))
usage(prg);
break;
case 'V':
++Verbose;
break;
case 'x':
cptr = optarg;
ret = sm_parse_ct_lvl(&cptr, &first, &last, &level);
if (sm_is_err(ret) || first != last)
usage(prg);
v = first;
v2 = last;
break;
default:
usage(prg);
}
}
argc -= optind;
argv += optind;
if (argc <= 0)
usage(prg);
sockname = argv[0];
qquery(prg, sockname, cmd, v, v2, rcpt_id, ta_ss_id);
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1