/* * 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; }