1*bf921b2aSclaudio /* $OpenBSD: smtpctl.c,v 1.176 2024/11/21 13:42:22 claudio Exp $ */ 2c4f9d530Sgilles 3c4f9d530Sgilles /* 47a7bc169Seric * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> 565c4fdfbSgilles * Copyright (c) 2006 Gilles Chehade <gilles@poolp.org> 6c4f9d530Sgilles * Copyright (c) 2006 Pierre-Yves Ritschard <pyr@openbsd.org> 7c4f9d530Sgilles * Copyright (c) 2005 Claudio Jeker <claudio@openbsd.org> 8c4f9d530Sgilles * Copyright (c) 2004, 2005 Esben Norby <norby@openbsd.org> 9c4f9d530Sgilles * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> 10c4f9d530Sgilles * 11c4f9d530Sgilles * Permission to use, copy, modify, and distribute this software for any 12c4f9d530Sgilles * purpose with or without fee is hereby granted, provided that the above 13c4f9d530Sgilles * copyright notice and this permission notice appear in all copies. 14c4f9d530Sgilles * 15c4f9d530Sgilles * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 16c4f9d530Sgilles * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 17c4f9d530Sgilles * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 18c4f9d530Sgilles * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 19c4f9d530Sgilles * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 20c4f9d530Sgilles * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 21c4f9d530Sgilles * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 22c4f9d530Sgilles */ 23c4f9d530Sgilles 24c4f9d530Sgilles #include <sys/un.h> 258351d18bSgilles #include <sys/stat.h> 26c4f9d530Sgilles 27c4f9d530Sgilles #include <err.h> 28ed5c65a8Sgilles #include <errno.h> 297a7bc169Seric #include <fts.h> 3034cb135fSchl #include <inttypes.h> 317b5d776dSgilles #include <pwd.h> 32c4f9d530Sgilles #include <stdlib.h> 33c4f9d530Sgilles #include <string.h> 34ccfb4053Seric #include <syslog.h> 350dcffd0dSop #include <time.h> 36c4f9d530Sgilles #include <unistd.h> 37a8bde366Seric #include <vis.h> 38c4f9d530Sgilles 39c4f9d530Sgilles #include "smtpd.h" 40c4f9d530Sgilles #include "parser.h" 4138f0b910Seric #include "log.h" 42c4f9d530Sgilles 43299c4efeSeric #define PATH_GZCAT "/usr/bin/gzcat" 447a7bc169Seric #define PATH_CAT "/bin/cat" 45d5e710d9Sgilles #define PATH_QUEUE "/queue" 467b682ccfSeric #define PATH_ENCRYPT "/usr/bin/encrypt" 47d5e710d9Sgilles 480fcb81a3Seric int srv_connect(void); 498351d18bSgilles int srv_connected(void); 500fcb81a3Seric 51be925435Sgilles void usage(void); 529e4f546eSgilles static void show_queue_envelope(struct envelope *, int); 53d2241734Schl static void getflag(uint *, int, char *, char *, size_t); 54d5e710d9Sgilles static void display(const char *); 557a7bc169Seric static int str_to_trace(const char *); 567a7bc169Seric static int str_to_profile(const char *); 577a7bc169Seric static void show_offline_envelope(uint64_t); 587a7bc169Seric static int is_gzip_fp(FILE *); 597a7bc169Seric static int is_encrypted_fp(FILE *); 607a7bc169Seric static int is_encrypted_buffer(const char *); 617a7bc169Seric static int is_gzip_buffer(const char *); 628351d18bSgilles static FILE *offline_file(void); 633511367dSsunil static void sendmail_compat(int, char **); 64c4f9d530Sgilles 65aee31bfdSeric extern int spfwalk(int, struct parameter *); 664636661aSsunil 67f607a12cSgilles extern char *__progname; 687a7bc169Seric int sendmail; 697a7bc169Seric struct smtpd *env; 707a7bc169Seric struct imsgbuf *ibuf; 717a7bc169Seric struct imsg imsg; 727a7bc169Seric char *rdata; 737a7bc169Seric size_t rlen; 744fe02f32Seric time_t now; 754fe02f32Seric 7665c4fdfbSgilles struct queue_backend queue_backend_null; 777a7bc169Seric struct queue_backend queue_backend_proc; 7865c4fdfbSgilles struct queue_backend queue_backend_ram; 7965c4fdfbSgilles 80c4f9d530Sgilles __dead void 81c4f9d530Sgilles usage(void) 82c4f9d530Sgilles { 83f607a12cSgilles if (sendmail) 844fe02f32Seric fprintf(stderr, "usage: %s [-tv] [-f from] [-F name] to ...\n", 8583d9e0c8Sjacekm __progname); 86f607a12cSgilles else 874fe02f32Seric fprintf(stderr, "usage: %s command [argument ...]\n", 884fe02f32Seric __progname); 89c4f9d530Sgilles exit(1); 90c4f9d530Sgilles } 91c4f9d530Sgilles 927a7bc169Seric void stat_increment(const char *k, size_t v) 93cca8e8d2Schl { 947a7bc169Seric } 95cca8e8d2Schl 967a7bc169Seric void stat_decrement(const char *k, size_t v) 977a7bc169Seric { 98cca8e8d2Schl } 99cca8e8d2Schl 1000fcb81a3Seric int 1017a7bc169Seric srv_connect(void) 102c4f9d530Sgilles { 103ac61da4aSgilles struct sockaddr_un s_un; 1045d09b982Seric int ctl_sock, saved_errno; 105c4f9d530Sgilles 106d68200d3Sjacekm /* connect to smtpd control socket */ 107c4f9d530Sgilles if ((ctl_sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 108c4f9d530Sgilles err(1, "socket"); 109c4f9d530Sgilles 110ac61da4aSgilles memset(&s_un, 0, sizeof(s_un)); 111ac61da4aSgilles s_un.sun_family = AF_UNIX; 112ac61da4aSgilles (void)strlcpy(s_un.sun_path, SMTPD_SOCKET, sizeof(s_un.sun_path)); 113ac61da4aSgilles if (connect(ctl_sock, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { 1145d09b982Seric saved_errno = errno; 1155d09b982Seric close(ctl_sock); 1165d09b982Seric errno = saved_errno; 1175d09b982Seric return (0); 11825a5298cSjacekm } 119c4f9d530Sgilles 120118c16f3Sgilles ibuf = xcalloc(1, sizeof(struct imsgbuf)); 121*bf921b2aSclaudio if (imsgbuf_init(ibuf, ctl_sock) == -1) 122*bf921b2aSclaudio err(1, "imsgbuf_init"); 123*bf921b2aSclaudio imsgbuf_allow_fdpass(ibuf); 124c4f9d530Sgilles 1255d09b982Seric return (1); 1265d09b982Seric } 1275d09b982Seric 1288351d18bSgilles int 1298351d18bSgilles srv_connected(void) 1308351d18bSgilles { 1318351d18bSgilles return ibuf != NULL ? 1 : 0; 1328351d18bSgilles } 1338351d18bSgilles 1348351d18bSgilles FILE * 1358351d18bSgilles offline_file(void) 1368351d18bSgilles { 1378351d18bSgilles char path[PATH_MAX]; 1388351d18bSgilles int fd; 1398351d18bSgilles FILE *fp; 1408351d18bSgilles 1418351d18bSgilles if (!bsnprintf(path, sizeof(path), "%s%s/%lld.XXXXXXXXXX", PATH_SPOOL, 142e7954c7dSop PATH_OFFLINE, (long long)time(NULL))) 1438351d18bSgilles err(EX_UNAVAILABLE, "snprintf"); 1448351d18bSgilles 1458351d18bSgilles if ((fd = mkstemp(path)) == -1 || (fp = fdopen(fd, "w+")) == NULL) { 1468351d18bSgilles if (fd != -1) 1478351d18bSgilles unlink(path); 1488351d18bSgilles err(EX_UNAVAILABLE, "cannot create temporary file %s", path); 1498351d18bSgilles } 1508351d18bSgilles 1518351d18bSgilles if (fchmod(fd, 0600) == -1) { 1528351d18bSgilles unlink(path); 1538351d18bSgilles err(EX_SOFTWARE, "fchmod"); 1548351d18bSgilles } 1558351d18bSgilles 1568351d18bSgilles return fp; 1578351d18bSgilles } 1588351d18bSgilles 1598351d18bSgilles 1605d09b982Seric static void 1617a7bc169Seric srv_flush(void) 1625d09b982Seric { 163dd7efffeSclaudio if (imsgbuf_flush(ibuf) == -1) 1645d09b982Seric err(1, "write error"); 1655d09b982Seric } 1665d09b982Seric 1675d09b982Seric static void 1687a7bc169Seric srv_send(int msg, const void *data, size_t len) 1697a7bc169Seric { 1707a7bc169Seric if (ibuf == NULL && !srv_connect()) 1717a7bc169Seric errx(1, "smtpd doesn't seem to be running"); 1727a7bc169Seric imsg_compose(ibuf, msg, IMSG_VERSION, 0, -1, data, len); 1737a7bc169Seric } 1747a7bc169Seric 1757a7bc169Seric static void 1767a7bc169Seric srv_recv(int type) 1775d09b982Seric { 1785d09b982Seric ssize_t n; 1795d09b982Seric 1807a7bc169Seric srv_flush(); 1817a7bc169Seric 1825d09b982Seric while (1) { 1837a7bc169Seric if ((n = imsg_get(ibuf, &imsg)) == -1) 1845d09b982Seric errx(1, "imsg_get error"); 1857a7bc169Seric if (n) { 1867a7bc169Seric if (imsg.hdr.type == IMSG_CTL_FAIL && 1877a7bc169Seric imsg.hdr.peerid != 0 && 1887a7bc169Seric imsg.hdr.peerid != IMSG_VERSION) 1897a7bc169Seric errx(1, "incompatible smtpctl and smtpd"); 1907a7bc169Seric if (type != -1 && type != (int)imsg.hdr.type) 1917a7bc169Seric errx(1, "bad message type"); 1927a7bc169Seric rdata = imsg.data; 1937a7bc169Seric rlen = imsg.hdr.len - sizeof(imsg.hdr); 1947a7bc169Seric break; 1957a7bc169Seric } 1965d09b982Seric 19716b0c81bSclaudio if ((n = imsgbuf_read(ibuf)) == -1) 198ef2e27a1Sclaudio err(1, "read error"); 1995d09b982Seric if (n == 0) 2005d09b982Seric errx(1, "pipe closed"); 2015d09b982Seric } 2025d09b982Seric } 2035d09b982Seric 2047a7bc169Seric static void 2057a7bc169Seric srv_read(void *dst, size_t sz) 2065d09b982Seric { 2077a7bc169Seric if (sz == 0) 2087a7bc169Seric return; 2097a7bc169Seric if (rlen < sz) 2107a7bc169Seric errx(1, "message too short"); 2117a7bc169Seric if (dst) 2127a7bc169Seric memmove(dst, rdata, sz); 2137a7bc169Seric rlen -= sz; 2147a7bc169Seric rdata += sz; 2157a7bc169Seric } 2165d09b982Seric 2177a7bc169Seric static void 2184fca4105Seric srv_get_int(int *i) 2194fca4105Seric { 2204fca4105Seric srv_read(i, sizeof(*i)); 2214fca4105Seric } 2224fca4105Seric 2234fca4105Seric static void 2244fca4105Seric srv_get_time(time_t *t) 2254fca4105Seric { 2264fca4105Seric srv_read(t, sizeof(*t)); 2274fca4105Seric } 2284fca4105Seric 2294fca4105Seric static void 2304fca4105Seric srv_get_evpid(uint64_t *evpid) 2314fca4105Seric { 2324fca4105Seric srv_read(evpid, sizeof(*evpid)); 2334fca4105Seric } 2344fca4105Seric 2354fca4105Seric static void 23681a33c5dSeric srv_get_string(const char **s) 23781a33c5dSeric { 23881a33c5dSeric const char *end; 23981a33c5dSeric size_t len; 24081a33c5dSeric 24181a33c5dSeric if (rlen == 0) 24281a33c5dSeric errx(1, "message too short"); 24381a33c5dSeric 2445b1f635fSeric rlen -= 1; 2455b1f635fSeric if (*rdata++ == '\0') { 2465b1f635fSeric *s = NULL; 2475b1f635fSeric return; 2485b1f635fSeric } 2495b1f635fSeric 2505b1f635fSeric if (rlen == 0) 2515b1f635fSeric errx(1, "bogus string"); 2525b1f635fSeric 25381a33c5dSeric end = memchr(rdata, 0, rlen); 25481a33c5dSeric if (end == NULL) 25581a33c5dSeric errx(1, "unterminated string"); 25681a33c5dSeric 25781a33c5dSeric len = end + 1 - rdata; 25881a33c5dSeric 25981a33c5dSeric *s = rdata; 26081a33c5dSeric rlen -= len; 26181a33c5dSeric rdata += len; 26281a33c5dSeric } 26381a33c5dSeric 26481a33c5dSeric static void 2654fca4105Seric srv_get_envelope(struct envelope *evp) 2664fca4105Seric { 2674fca4105Seric uint64_t evpid; 26881a33c5dSeric const char *str; 2694fca4105Seric 2704fca4105Seric srv_get_evpid(&evpid); 27181a33c5dSeric srv_get_string(&str); 2724fca4105Seric 27381a33c5dSeric envelope_load_buffer(evp, str, strlen(str)); 2744fca4105Seric evp->id = evpid; 2754fca4105Seric } 2764fca4105Seric 2774fca4105Seric static void 2787a7bc169Seric srv_end(void) 2797a7bc169Seric { 2807a7bc169Seric if (rlen) 2817a7bc169Seric errx(1, "bogus data"); 2827a7bc169Seric imsg_free(&imsg); 2837a7bc169Seric } 2847a7bc169Seric 2857a7bc169Seric static int 2860fcb81a3Seric srv_check_result(int verbose_) 2877a7bc169Seric { 2887a7bc169Seric srv_recv(-1); 2897a7bc169Seric srv_end(); 2907a7bc169Seric 2917a7bc169Seric switch (imsg.hdr.type) { 2927a7bc169Seric case IMSG_CTL_OK: 2930fcb81a3Seric if (verbose_) 2947a7bc169Seric printf("command succeeded\n"); 2957a7bc169Seric return (0); 2967a7bc169Seric case IMSG_CTL_FAIL: 2970fcb81a3Seric if (verbose_) { 2987a7bc169Seric if (rlen) 2997a7bc169Seric printf("command failed: %s\n", rdata); 3007a7bc169Seric else 3017a7bc169Seric printf("command failed\n"); 3020fcb81a3Seric } 3037a7bc169Seric return (1); 3047a7bc169Seric default: 3057a7bc169Seric errx(1, "wrong message in response: %u", imsg.hdr.type); 3067a7bc169Seric } 3077a7bc169Seric return (0); 3087a7bc169Seric } 3097a7bc169Seric 3107a7bc169Seric static int 3117a7bc169Seric srv_iter_messages(uint32_t *res) 3127a7bc169Seric { 3137a7bc169Seric static uint32_t *msgids = NULL, from = 0; 3147a7bc169Seric static size_t n, curr; 3157a7bc169Seric static int done = 0; 3167a7bc169Seric 3177a7bc169Seric if (done) 3187a7bc169Seric return (0); 3197a7bc169Seric 3207a7bc169Seric if (msgids == NULL) { 3217a7bc169Seric srv_send(IMSG_CTL_LIST_MESSAGES, &from, sizeof(from)); 3227a7bc169Seric srv_recv(IMSG_CTL_LIST_MESSAGES); 3237a7bc169Seric if (rlen == 0) { 3247a7bc169Seric srv_end(); 3257a7bc169Seric done = 1; 3267a7bc169Seric return (0); 3277a7bc169Seric } 3287a7bc169Seric msgids = malloc(rlen); 3297a7bc169Seric n = rlen / sizeof(*msgids); 3307a7bc169Seric srv_read(msgids, rlen); 3317a7bc169Seric srv_end(); 3327a7bc169Seric 3337a7bc169Seric curr = 0; 3347a7bc169Seric from = msgids[n - 1] + 1; 3357a7bc169Seric if (from == 0) 3367a7bc169Seric done = 1; 3377a7bc169Seric } 3387a7bc169Seric 3397a7bc169Seric *res = msgids[curr++]; 3407a7bc169Seric if (curr == n) { 3417a7bc169Seric free(msgids); 3427a7bc169Seric msgids = NULL; 3437a7bc169Seric } 3447a7bc169Seric 3457a7bc169Seric return (1); 3467a7bc169Seric } 3477a7bc169Seric 3487a7bc169Seric static int 3497a7bc169Seric srv_iter_envelopes(uint32_t msgid, struct envelope *evp) 3507a7bc169Seric { 3517a7bc169Seric static uint32_t currmsgid = 0; 3527a7bc169Seric static uint64_t from = 0; 3537a7bc169Seric static int done = 0, need_send = 1, found; 3544fca4105Seric int flags; 3554fca4105Seric time_t nexttry; 3567a7bc169Seric 3577a7bc169Seric if (currmsgid != msgid) { 3587a7bc169Seric if (currmsgid != 0 && !done) 3597a7bc169Seric errx(1, "must finish current iteration first"); 3607a7bc169Seric currmsgid = msgid; 3617a7bc169Seric from = msgid_to_evpid(msgid); 3627a7bc169Seric done = 0; 3637a7bc169Seric found = 0; 3647a7bc169Seric need_send = 1; 3657a7bc169Seric } 3667a7bc169Seric 3677a7bc169Seric if (done) 3687a7bc169Seric return (0); 3697a7bc169Seric 3707a7bc169Seric again: 3717a7bc169Seric if (need_send) { 3727a7bc169Seric found = 0; 3737a7bc169Seric srv_send(IMSG_CTL_LIST_ENVELOPES, &from, sizeof(from)); 3747a7bc169Seric } 3757a7bc169Seric need_send = 0; 3767a7bc169Seric 3777a7bc169Seric srv_recv(IMSG_CTL_LIST_ENVELOPES); 3787a7bc169Seric if (rlen == 0) { 3797a7bc169Seric srv_end(); 3807a7bc169Seric if (!found || evpid_to_msgid(from) != msgid) { 3817a7bc169Seric done = 1; 3827a7bc169Seric return (0); 3837a7bc169Seric } 3847a7bc169Seric need_send = 1; 3857a7bc169Seric goto again; 3867a7bc169Seric } 3877a7bc169Seric 3884fca4105Seric srv_get_int(&flags); 3894fca4105Seric srv_get_time(&nexttry); 3904fca4105Seric srv_get_envelope(evp); 3917a7bc169Seric srv_end(); 3924fca4105Seric 3934fca4105Seric evp->flags |= flags; 3944fca4105Seric evp->nexttry = nexttry; 3954fca4105Seric 3967a7bc169Seric from = evp->id + 1; 3977a7bc169Seric found++; 3987a7bc169Seric return (1); 3997a7bc169Seric } 4007a7bc169Seric 4017a7bc169Seric static int 4020fcb81a3Seric srv_iter_evpids(uint32_t msgid, uint64_t *evpid, int *offset) 4030fcb81a3Seric { 404483e8451Sgilles static uint64_t *evpids = NULL, *tmp; 405f90bac70Seric static int n, tmpalloc, alloc = 0; 4060fcb81a3Seric struct envelope evp; 4070fcb81a3Seric 4080fcb81a3Seric if (*offset == 0) { 4090fcb81a3Seric n = 0; 4100fcb81a3Seric while (srv_iter_envelopes(msgid, &evp)) { 4110fcb81a3Seric if (n == alloc) { 412f90bac70Seric tmpalloc = alloc ? (alloc * 2) : 128; 413f90bac70Seric tmp = recallocarray(evpids, alloc, tmpalloc, 414362966cbSespie sizeof(*evpids)); 415483e8451Sgilles if (tmp == NULL) 416f90bac70Seric err(1, "recallocarray"); 417483e8451Sgilles evpids = tmp; 418f90bac70Seric alloc = tmpalloc; 4190fcb81a3Seric } 4200fcb81a3Seric evpids[n++] = evp.id; 4210fcb81a3Seric } 4220fcb81a3Seric } 4230fcb81a3Seric 4240fcb81a3Seric if (*offset >= n) 4250fcb81a3Seric return (0); 4260fcb81a3Seric *evpid = evpids[*offset]; 4270fcb81a3Seric *offset += 1; 4280fcb81a3Seric return (1); 4290fcb81a3Seric } 4300fcb81a3Seric 4310fcb81a3Seric static void 4320fcb81a3Seric srv_foreach_envelope(struct parameter *argv, int ctl, size_t *total, size_t *ok) 4330fcb81a3Seric { 4340fcb81a3Seric uint32_t msgid; 4350fcb81a3Seric uint64_t evpid; 4360fcb81a3Seric int i; 4370fcb81a3Seric 4380fcb81a3Seric *total = 0; 4390fcb81a3Seric *ok = 0; 4400fcb81a3Seric 4410fcb81a3Seric if (argv == NULL) { 4420fcb81a3Seric while (srv_iter_messages(&msgid)) { 4430fcb81a3Seric i = 0; 4440fcb81a3Seric while (srv_iter_evpids(msgid, &evpid, &i)) { 4450fcb81a3Seric *total += 1; 4460fcb81a3Seric srv_send(ctl, &evpid, sizeof(evpid)); 4470fcb81a3Seric if (srv_check_result(0) == 0) 4480fcb81a3Seric *ok += 1; 4490fcb81a3Seric } 4500fcb81a3Seric } 4510fcb81a3Seric } else if (argv->type == P_MSGID) { 4520fcb81a3Seric i = 0; 4530fcb81a3Seric while (srv_iter_evpids(argv->u.u_msgid, &evpid, &i)) { 4540fcb81a3Seric srv_send(ctl, &evpid, sizeof(evpid)); 4550fcb81a3Seric if (srv_check_result(0) == 0) 4560fcb81a3Seric *ok += 1; 4570fcb81a3Seric } 4580fcb81a3Seric } else { 4590fcb81a3Seric *total += 1; 4600fcb81a3Seric srv_send(ctl, &argv->u.u_evpid, sizeof(evpid)); 4610fcb81a3Seric if (srv_check_result(0) == 0) 4620fcb81a3Seric *ok += 1; 4630fcb81a3Seric } 4640fcb81a3Seric } 4650fcb81a3Seric 4660fcb81a3Seric static void 4670fcb81a3Seric srv_show_cmd(int cmd, const void *data, size_t len) 4680fcb81a3Seric { 4690fcb81a3Seric int done = 0; 4700fcb81a3Seric 4710fcb81a3Seric srv_send(cmd, data, len); 4720fcb81a3Seric 4730fcb81a3Seric do { 4740fcb81a3Seric srv_recv(cmd); 4750fcb81a3Seric if (rlen) { 4760fcb81a3Seric printf("%s\n", rdata); 4770fcb81a3Seric srv_read(NULL, rlen); 4780fcb81a3Seric } 4790fcb81a3Seric else 4800fcb81a3Seric done = 1; 4810fcb81a3Seric srv_end(); 4820fcb81a3Seric } while (!done); 4830fcb81a3Seric } 4840fcb81a3Seric 485aee31bfdSeric static void 486aee31bfdSeric droppriv(void) 487aee31bfdSeric { 488aee31bfdSeric struct passwd *pw; 489aee31bfdSeric 490aee31bfdSeric if (geteuid()) 491aee31bfdSeric return; 492aee31bfdSeric 493aee31bfdSeric if ((pw = getpwnam(SMTPD_USER)) == NULL) 494aee31bfdSeric errx(1, "unknown user " SMTPD_USER); 495aee31bfdSeric 496aee31bfdSeric if ((setgroups(1, &pw->pw_gid) || 497aee31bfdSeric setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 498aee31bfdSeric setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))) 499aee31bfdSeric err(1, "cannot drop privileges"); 500aee31bfdSeric } 501aee31bfdSeric 502aee31bfdSeric static int 503aee31bfdSeric do_permission_denied(int argc, struct parameter *argv) 504aee31bfdSeric { 505aee31bfdSeric errx(1, "need root privileges"); 506aee31bfdSeric } 507aee31bfdSeric 5080fcb81a3Seric static int 5097a7bc169Seric do_log_brief(int argc, struct parameter *argv) 5107a7bc169Seric { 5117a7bc169Seric int v = 0; 5127a7bc169Seric 5137a7bc169Seric srv_send(IMSG_CTL_VERBOSE, &v, sizeof(v)); 5140fcb81a3Seric return srv_check_result(1); 5157a7bc169Seric } 5167a7bc169Seric 5177a7bc169Seric static int 5187a7bc169Seric do_log_verbose(int argc, struct parameter *argv) 5197a7bc169Seric { 5207a7bc169Seric int v = TRACE_DEBUG; 5217a7bc169Seric 5227a7bc169Seric srv_send(IMSG_CTL_VERBOSE, &v, sizeof(v)); 5230fcb81a3Seric return srv_check_result(1); 5247a7bc169Seric } 5257a7bc169Seric 5267a7bc169Seric static int 5277a7bc169Seric do_monitor(int argc, struct parameter *argv) 5287a7bc169Seric { 5297a7bc169Seric struct stat_digest last, digest; 5307a7bc169Seric size_t count; 5317a7bc169Seric 532c1392a69Seric memset(&last, 0, sizeof(last)); 5337a7bc169Seric count = 0; 5347a7bc169Seric 5357a7bc169Seric while (1) { 536aa1d5973Seric srv_send(IMSG_CTL_GET_DIGEST, NULL, 0); 537aa1d5973Seric srv_recv(IMSG_CTL_GET_DIGEST); 5387a7bc169Seric srv_read(&digest, sizeof(digest)); 5397a7bc169Seric srv_end(); 5407a7bc169Seric 5417a7bc169Seric if (count % 25 == 0) { 5427a7bc169Seric if (count != 0) 5437a7bc169Seric printf("\n"); 5447a7bc169Seric printf("--- client --- " 5457a7bc169Seric "-- envelope -- " 5467a7bc169Seric "---- relay/delivery --- " 5477a7bc169Seric "------- misc -------\n" 5487a7bc169Seric "curr conn disc " 5497a7bc169Seric "curr enq deq " 5507a7bc169Seric "ok tmpfail prmfail loop " 5517a7bc169Seric "expire remove bounce\n"); 5527a7bc169Seric } 5537a7bc169Seric printf("%4zu %4zu %4zu " 5547a7bc169Seric "%4zu %4zu %4zu " 5557a7bc169Seric "%4zu %4zu %4zu %4zu " 5567a7bc169Seric "%4zu %4zu %4zu\n", 5577a7bc169Seric digest.clt_connect - digest.clt_disconnect, 5587a7bc169Seric digest.clt_connect - last.clt_connect, 5597a7bc169Seric digest.clt_disconnect - last.clt_disconnect, 5607a7bc169Seric 5617a7bc169Seric digest.evp_enqueued - digest.evp_dequeued, 5627a7bc169Seric digest.evp_enqueued - last.evp_enqueued, 5637a7bc169Seric digest.evp_dequeued - last.evp_dequeued, 5647a7bc169Seric 5657a7bc169Seric digest.dlv_ok - last.dlv_ok, 5667a7bc169Seric digest.dlv_tempfail - last.dlv_tempfail, 5677a7bc169Seric digest.dlv_permfail - last.dlv_permfail, 5687a7bc169Seric digest.dlv_loop - last.dlv_loop, 5697a7bc169Seric 5707a7bc169Seric digest.evp_expired - last.evp_expired, 5717a7bc169Seric digest.evp_removed - last.evp_removed, 5727a7bc169Seric digest.evp_bounce - last.evp_bounce); 5737a7bc169Seric 5747a7bc169Seric last = digest; 5757a7bc169Seric count++; 5767a7bc169Seric sleep(1); 5777a7bc169Seric } 5787a7bc169Seric 5797a7bc169Seric return (0); 5807a7bc169Seric } 5817a7bc169Seric 5827a7bc169Seric static int 58335e161d3Seric do_pause_envelope(int argc, struct parameter *argv) 58435e161d3Seric { 5850fcb81a3Seric size_t total, ok; 58635e161d3Seric 5870fcb81a3Seric srv_foreach_envelope(argv, IMSG_CTL_PAUSE_EVP, &total, &ok); 5880fcb81a3Seric printf("%zu envelope%s paused\n", ok, (ok > 1) ? "s" : ""); 58935e161d3Seric 59035e161d3Seric return (0); 59135e161d3Seric } 59235e161d3Seric 59335e161d3Seric static int 5947a7bc169Seric do_pause_mda(int argc, struct parameter *argv) 5957a7bc169Seric { 5967a7bc169Seric srv_send(IMSG_CTL_PAUSE_MDA, NULL, 0); 5970fcb81a3Seric return srv_check_result(1); 5987a7bc169Seric } 5997a7bc169Seric 6007a7bc169Seric static int 6017a7bc169Seric do_pause_mta(int argc, struct parameter *argv) 6027a7bc169Seric { 6037a7bc169Seric srv_send(IMSG_CTL_PAUSE_MTA, NULL, 0); 6040fcb81a3Seric return srv_check_result(1); 6057a7bc169Seric } 6067a7bc169Seric 6077a7bc169Seric static int 6087a7bc169Seric do_pause_smtp(int argc, struct parameter *argv) 6097a7bc169Seric { 6107a7bc169Seric srv_send(IMSG_CTL_PAUSE_SMTP, NULL, 0); 6110fcb81a3Seric return srv_check_result(1); 6127a7bc169Seric } 6137a7bc169Seric 6147a7bc169Seric static int 6157a7bc169Seric do_profile(int argc, struct parameter *argv) 6167a7bc169Seric { 6177a7bc169Seric int v; 6187a7bc169Seric 6197a7bc169Seric v = str_to_profile(argv[0].u.u_str); 6207a7bc169Seric 621e267740eSeric srv_send(IMSG_CTL_PROFILE_ENABLE, &v, sizeof(v)); 6220fcb81a3Seric return srv_check_result(1); 6237a7bc169Seric } 6247a7bc169Seric 6257a7bc169Seric static int 6267a7bc169Seric do_remove(int argc, struct parameter *argv) 6277a7bc169Seric { 6280fcb81a3Seric size_t total, ok; 6297a7bc169Seric 6300fcb81a3Seric srv_foreach_envelope(argv, IMSG_CTL_REMOVE, &total, &ok); 6310fcb81a3Seric printf("%zu envelope%s removed\n", ok, (ok > 1) ? "s" : ""); 6327a7bc169Seric 6337a7bc169Seric return (0); 6347a7bc169Seric } 6357a7bc169Seric 6367a7bc169Seric static int 63735e161d3Seric do_resume_envelope(int argc, struct parameter *argv) 63835e161d3Seric { 6390fcb81a3Seric size_t total, ok; 64035e161d3Seric 6410fcb81a3Seric srv_foreach_envelope(argv, IMSG_CTL_RESUME_EVP, &total, &ok); 6420fcb81a3Seric printf("%zu envelope%s resumed\n", ok, (ok > 1) ? "s" : ""); 64335e161d3Seric 64435e161d3Seric return (0); 64535e161d3Seric } 64635e161d3Seric 64735e161d3Seric static int 6487a7bc169Seric do_resume_mda(int argc, struct parameter *argv) 6497a7bc169Seric { 6507a7bc169Seric srv_send(IMSG_CTL_RESUME_MDA, NULL, 0); 6510fcb81a3Seric return srv_check_result(1); 6527a7bc169Seric } 6537a7bc169Seric 6547a7bc169Seric static int 6557a7bc169Seric do_resume_mta(int argc, struct parameter *argv) 6567a7bc169Seric { 6577a7bc169Seric srv_send(IMSG_CTL_RESUME_MTA, NULL, 0); 6580fcb81a3Seric return srv_check_result(1); 6597a7bc169Seric } 6607a7bc169Seric 6617a7bc169Seric static int 662c5acbec8Seric do_resume_route(int argc, struct parameter *argv) 663c5acbec8Seric { 664c5acbec8Seric uint64_t v; 665c5acbec8Seric 666c5acbec8Seric if (argc == 0) 667c5acbec8Seric v = 0; 668c5acbec8Seric else 669c5acbec8Seric v = argv[0].u.u_routeid; 670c5acbec8Seric 671c5acbec8Seric srv_send(IMSG_CTL_RESUME_ROUTE, &v, sizeof(v)); 6720fcb81a3Seric return srv_check_result(1); 673c5acbec8Seric } 674c5acbec8Seric 675c5acbec8Seric static int 6767a7bc169Seric do_resume_smtp(int argc, struct parameter *argv) 6777a7bc169Seric { 6787a7bc169Seric srv_send(IMSG_CTL_RESUME_SMTP, NULL, 0); 6790fcb81a3Seric return srv_check_result(1); 6807a7bc169Seric } 6817a7bc169Seric 6827a7bc169Seric static int 6837a7bc169Seric do_schedule(int argc, struct parameter *argv) 6847a7bc169Seric { 6850fcb81a3Seric size_t total, ok; 6867a7bc169Seric 6870fcb81a3Seric srv_foreach_envelope(argv, IMSG_CTL_SCHEDULE, &total, &ok); 6880fcb81a3Seric printf("%zu envelope%s scheduled\n", ok, (ok > 1) ? "s" : ""); 6897a7bc169Seric 6907a7bc169Seric return (0); 6917a7bc169Seric } 6927a7bc169Seric 6937a7bc169Seric static int 6947a7bc169Seric do_show_envelope(int argc, struct parameter *argv) 6957a7bc169Seric { 696953aae25Sderaadt char buf[PATH_MAX]; 6977a7bc169Seric 6987a7bc169Seric if (!bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/%016" PRIx64, 6997a7bc169Seric PATH_SPOOL, 7007a7bc169Seric PATH_QUEUE, 7017a7bc169Seric (evpid_to_msgid(argv[0].u.u_evpid) & 0xff000000) >> 24, 7027a7bc169Seric evpid_to_msgid(argv[0].u.u_evpid), 7037a7bc169Seric argv[0].u.u_evpid)) 7047a7bc169Seric errx(1, "unable to retrieve envelope"); 7057a7bc169Seric 7067a7bc169Seric display(buf); 7077a7bc169Seric 7087a7bc169Seric return (0); 7097a7bc169Seric } 7107a7bc169Seric 7117a7bc169Seric static int 712c5acbec8Seric do_show_hoststats(int argc, struct parameter *argv) 713c5acbec8Seric { 7140fcb81a3Seric srv_show_cmd(IMSG_CTL_MTA_SHOW_HOSTSTATS, NULL, 0); 715c5acbec8Seric 716c5acbec8Seric return (0); 717c5acbec8Seric } 718c5acbec8Seric 719c5acbec8Seric static int 7207a7bc169Seric do_show_message(int argc, struct parameter *argv) 7217a7bc169Seric { 722953aae25Sderaadt char buf[PATH_MAX]; 7237a7bc169Seric uint32_t msgid; 7247a7bc169Seric 7257a7bc169Seric if (argv[0].type == P_EVPID) 7267a7bc169Seric msgid = evpid_to_msgid(argv[0].u.u_evpid); 7277a7bc169Seric else 7287a7bc169Seric msgid = argv[0].u.u_msgid; 7297a7bc169Seric 7307a7bc169Seric if (!bsnprintf(buf, sizeof(buf), "%s%s/%02x/%08x/message", 7317a7bc169Seric PATH_SPOOL, 7327a7bc169Seric PATH_QUEUE, 7337a7bc169Seric (msgid & 0xff000000) >> 24, 7347a7bc169Seric msgid)) 7357a7bc169Seric errx(1, "unable to retrieve message"); 7367a7bc169Seric 7377a7bc169Seric display(buf); 7387a7bc169Seric 7397a7bc169Seric return (0); 7407a7bc169Seric } 7417a7bc169Seric 7427a7bc169Seric static int 7437a7bc169Seric do_show_queue(int argc, struct parameter *argv) 7447a7bc169Seric { 7457a7bc169Seric struct envelope evp; 7467a7bc169Seric uint32_t msgid; 7477a7bc169Seric FTS *fts; 7487a7bc169Seric FTSENT *ftse; 7497a7bc169Seric char *qpath[] = {"/queue", NULL}; 7507a7bc169Seric char *tmp; 7517a7bc169Seric uint64_t evpid; 7527a7bc169Seric 7537a7bc169Seric now = time(NULL); 7547a7bc169Seric 7557a7bc169Seric if (!srv_connect()) { 7567a7bc169Seric queue_init("fs", 0); 757a94ff62fSgilles if (chroot(PATH_SPOOL) == -1 || chdir("/") == -1) 7587a7bc169Seric err(1, "%s", PATH_SPOOL); 7597a7bc169Seric fts = fts_open(qpath, FTS_PHYSICAL|FTS_NOCHDIR, NULL); 7607a7bc169Seric if (fts == NULL) 7617a7bc169Seric err(1, "%s/queue", PATH_SPOOL); 7627a7bc169Seric 7637a7bc169Seric while ((ftse = fts_read(fts)) != NULL) { 7647a7bc169Seric switch (ftse->fts_info) { 7657a7bc169Seric case FTS_DP: 7667a7bc169Seric case FTS_DNR: 7677a7bc169Seric break; 7687a7bc169Seric case FTS_F: 7697a7bc169Seric tmp = NULL; 7707a7bc169Seric evpid = strtoull(ftse->fts_name, &tmp, 16); 7717a7bc169Seric if (tmp && *tmp != '\0') 7727a7bc169Seric break; 7737a7bc169Seric show_offline_envelope(evpid); 7747a7bc169Seric } 7757a7bc169Seric } 7767a7bc169Seric 7777a7bc169Seric fts_close(fts); 7787a7bc169Seric return (0); 7797a7bc169Seric } 7807a7bc169Seric 7817a7bc169Seric if (argc == 0) { 7827a7bc169Seric msgid = 0; 7837a7bc169Seric while (srv_iter_messages(&msgid)) 7847a7bc169Seric while (srv_iter_envelopes(msgid, &evp)) 7857a7bc169Seric show_queue_envelope(&evp, 1); 7867a7bc169Seric } else if (argv[0].type == P_MSGID) { 7877a7bc169Seric while (srv_iter_envelopes(argv[0].u.u_msgid, &evp)) 7887a7bc169Seric show_queue_envelope(&evp, 1); 7897a7bc169Seric } 7907a7bc169Seric 7917a7bc169Seric return (0); 7927a7bc169Seric } 7937a7bc169Seric 7947a7bc169Seric static int 795f9a337f4Seric do_show_hosts(int argc, struct parameter *argv) 796f9a337f4Seric { 797f9a337f4Seric srv_show_cmd(IMSG_CTL_MTA_SHOW_HOSTS, NULL, 0); 798f9a337f4Seric 799f9a337f4Seric return (0); 800f9a337f4Seric } 801f9a337f4Seric 802f9a337f4Seric static int 803f9a337f4Seric do_show_relays(int argc, struct parameter *argv) 804f9a337f4Seric { 805f9a337f4Seric srv_show_cmd(IMSG_CTL_MTA_SHOW_RELAYS, NULL, 0); 806f9a337f4Seric 807f9a337f4Seric return (0); 808f9a337f4Seric } 809f9a337f4Seric 810f9a337f4Seric static int 811c5acbec8Seric do_show_routes(int argc, struct parameter *argv) 812c5acbec8Seric { 8130fcb81a3Seric srv_show_cmd(IMSG_CTL_MTA_SHOW_ROUTES, NULL, 0); 814c5acbec8Seric 815c5acbec8Seric return (0); 816c5acbec8Seric } 817c5acbec8Seric 818c5acbec8Seric static int 8197a7bc169Seric do_show_stats(int argc, struct parameter *argv) 8207a7bc169Seric { 8217a7bc169Seric struct stat_kv kv; 8227a7bc169Seric time_t duration; 8237a7bc169Seric 824c1392a69Seric memset(&kv, 0, sizeof kv); 8257a7bc169Seric 8267a7bc169Seric while (1) { 827aa1d5973Seric srv_send(IMSG_CTL_GET_STATS, &kv, sizeof kv); 828aa1d5973Seric srv_recv(IMSG_CTL_GET_STATS); 8297a7bc169Seric srv_read(&kv, sizeof(kv)); 8307a7bc169Seric srv_end(); 8317a7bc169Seric 8327a7bc169Seric if (kv.iter == NULL) 8337a7bc169Seric break; 8347a7bc169Seric 8357a7bc169Seric if (strcmp(kv.key, "uptime") == 0) { 8367a7bc169Seric duration = time(NULL) - kv.val.u.counter; 8377a7bc169Seric printf("uptime=%lld\n", (long long)duration); 8387a7bc169Seric printf("uptime.human=%s\n", 8397a7bc169Seric duration_to_text(duration)); 8407a7bc169Seric } 8417a7bc169Seric else { 8427a7bc169Seric switch (kv.val.type) { 8437a7bc169Seric case STAT_COUNTER: 8447a7bc169Seric printf("%s=%zd\n", 8457a7bc169Seric kv.key, kv.val.u.counter); 8467a7bc169Seric break; 8477a7bc169Seric case STAT_TIMESTAMP: 8487a7bc169Seric printf("%s=%" PRId64 "\n", 8497a7bc169Seric kv.key, (int64_t)kv.val.u.timestamp); 8507a7bc169Seric break; 8517a7bc169Seric case STAT_TIMEVAL: 8527a7bc169Seric printf("%s=%lld.%lld\n", 8537a7bc169Seric kv.key, (long long)kv.val.u.tv.tv_sec, 8547a7bc169Seric (long long)kv.val.u.tv.tv_usec); 8557a7bc169Seric break; 8567a7bc169Seric case STAT_TIMESPEC: 857f70d44e6Seric printf("%s=%lld.%06ld\n", 8587a7bc169Seric kv.key, 8597a7bc169Seric (long long)kv.val.u.ts.tv_sec * 1000000 + 8607a7bc169Seric kv.val.u.ts.tv_nsec / 1000000, 8617a7bc169Seric kv.val.u.ts.tv_nsec % 1000000); 8627a7bc169Seric break; 8637a7bc169Seric } 8647a7bc169Seric } 8657a7bc169Seric } 8667a7bc169Seric 8677a7bc169Seric return (0); 8687a7bc169Seric } 8697a7bc169Seric 8707a7bc169Seric static int 871c37e9483Seric do_show_status(int argc, struct parameter *argv) 872c37e9483Seric { 873c37e9483Seric uint32_t sc_flags; 874c37e9483Seric 875c37e9483Seric srv_send(IMSG_CTL_SHOW_STATUS, NULL, 0); 876c37e9483Seric srv_recv(IMSG_CTL_SHOW_STATUS); 877c37e9483Seric srv_read(&sc_flags, sizeof(sc_flags)); 878c37e9483Seric srv_end(); 879c37e9483Seric printf("MDA %s\n", 880c37e9483Seric (sc_flags & SMTPD_MDA_PAUSED) ? "paused" : "running"); 881c37e9483Seric printf("MTA %s\n", 882c37e9483Seric (sc_flags & SMTPD_MTA_PAUSED) ? "paused" : "running"); 883c37e9483Seric printf("SMTP %s\n", 884c37e9483Seric (sc_flags & SMTPD_SMTP_PAUSED) ? "paused" : "running"); 885c37e9483Seric return (0); 886c37e9483Seric } 887c37e9483Seric 888c37e9483Seric static int 8897a7bc169Seric do_trace(int argc, struct parameter *argv) 8907a7bc169Seric { 8917a7bc169Seric int v; 8927a7bc169Seric 8937a7bc169Seric v = str_to_trace(argv[0].u.u_str); 8947a7bc169Seric 895aa1d5973Seric srv_send(IMSG_CTL_TRACE_ENABLE, &v, sizeof(v)); 8960fcb81a3Seric return srv_check_result(1); 8977a7bc169Seric } 8987a7bc169Seric 8997a7bc169Seric static int 9007a7bc169Seric do_unprofile(int argc, struct parameter *argv) 9017a7bc169Seric { 9027a7bc169Seric int v; 9037a7bc169Seric 9047a7bc169Seric v = str_to_profile(argv[0].u.u_str); 9057a7bc169Seric 906aa1d5973Seric srv_send(IMSG_CTL_PROFILE_DISABLE, &v, sizeof(v)); 9070fcb81a3Seric return srv_check_result(1); 9087a7bc169Seric } 9097a7bc169Seric 9107a7bc169Seric static int 9117a7bc169Seric do_untrace(int argc, struct parameter *argv) 9127a7bc169Seric { 9137a7bc169Seric int v; 9147a7bc169Seric 9157a7bc169Seric v = str_to_trace(argv[0].u.u_str); 9167a7bc169Seric 917aa1d5973Seric srv_send(IMSG_CTL_TRACE_DISABLE, &v, sizeof(v)); 9180fcb81a3Seric return srv_check_result(1); 9197a7bc169Seric } 9207a7bc169Seric 9217a7bc169Seric static int 9227a7bc169Seric do_update_table(int argc, struct parameter *argv) 9237a7bc169Seric { 9247a7bc169Seric const char *name = argv[0].u.u_str; 9257a7bc169Seric 926aa1d5973Seric srv_send(IMSG_CTL_UPDATE_TABLE, name, strlen(name) + 1); 9270fcb81a3Seric return srv_check_result(1); 9287a7bc169Seric } 9297a7bc169Seric 9307b682ccfSeric static int 9317b682ccfSeric do_encrypt(int argc, struct parameter *argv) 9327b682ccfSeric { 9337b682ccfSeric const char *p = NULL; 9347b682ccfSeric 935aee31bfdSeric droppriv(); 936aee31bfdSeric 9377b682ccfSeric if (argv) 9387b682ccfSeric p = argv[0].u.u_str; 939dd673463Sgilles execl(PATH_ENCRYPT, "encrypt", "--", p, (char *)NULL); 9407b682ccfSeric errx(1, "execl"); 9417b682ccfSeric } 9427b682ccfSeric 9435b6a9ce9Seric static int 9445b6a9ce9Seric do_block_mta(int argc, struct parameter *argv) 9455b6a9ce9Seric { 9465b6a9ce9Seric struct ibuf *m; 9475b6a9ce9Seric 9485b6a9ce9Seric if (ibuf == NULL && !srv_connect()) 9495b6a9ce9Seric errx(1, "smtpd doesn't seem to be running"); 9505b6a9ce9Seric m = imsg_create(ibuf, IMSG_CTL_MTA_BLOCK, IMSG_VERSION, 0, 9515b6a9ce9Seric sizeof(argv[0].u.u_ss) + strlen(argv[1].u.u_str) + 1); 9525b6a9ce9Seric if (imsg_add(m, &argv[0].u.u_ss, sizeof(argv[0].u.u_ss)) == -1) 9535b6a9ce9Seric errx(1, "imsg_add"); 9545b6a9ce9Seric if (imsg_add(m, argv[1].u.u_str, strlen(argv[1].u.u_str) + 1) == -1) 9555b6a9ce9Seric errx(1, "imsg_add"); 9565b6a9ce9Seric imsg_close(ibuf, m); 9575b6a9ce9Seric 9585b6a9ce9Seric return srv_check_result(1); 9595b6a9ce9Seric } 9605b6a9ce9Seric 9615b6a9ce9Seric static int 9625b6a9ce9Seric do_unblock_mta(int argc, struct parameter *argv) 9635b6a9ce9Seric { 9645b6a9ce9Seric struct ibuf *m; 9655b6a9ce9Seric 9665b6a9ce9Seric if (ibuf == NULL && !srv_connect()) 9675b6a9ce9Seric errx(1, "smtpd doesn't seem to be running"); 9685b6a9ce9Seric 9695b6a9ce9Seric m = imsg_create(ibuf, IMSG_CTL_MTA_UNBLOCK, IMSG_VERSION, 0, 9705b6a9ce9Seric sizeof(argv[0].u.u_ss) + strlen(argv[1].u.u_str) + 1); 9715b6a9ce9Seric if (imsg_add(m, &argv[0].u.u_ss, sizeof(argv[0].u.u_ss)) == -1) 9725b6a9ce9Seric errx(1, "imsg_add"); 9735b6a9ce9Seric if (imsg_add(m, argv[1].u.u_str, strlen(argv[1].u.u_str) + 1) == -1) 9745b6a9ce9Seric errx(1, "imsg_add"); 9755b6a9ce9Seric imsg_close(ibuf, m); 9765b6a9ce9Seric 9775b6a9ce9Seric return srv_check_result(1); 9785b6a9ce9Seric } 9795b6a9ce9Seric 9805b6a9ce9Seric static int 9815b6a9ce9Seric do_show_mta_block(int argc, struct parameter *argv) 9825b6a9ce9Seric { 9835b6a9ce9Seric srv_show_cmd(IMSG_CTL_MTA_SHOW_BLOCK, NULL, 0); 9845b6a9ce9Seric 9855b6a9ce9Seric return (0); 9865b6a9ce9Seric } 9875b6a9ce9Seric 988a9835440Ssunil static int 989a9835440Ssunil do_discover(int argc, struct parameter *argv) 990a9835440Ssunil { 991a9835440Ssunil uint64_t evpid; 992a9835440Ssunil uint32_t msgid; 993a9835440Ssunil size_t n_evp; 994a9835440Ssunil 995a9835440Ssunil if (ibuf == NULL && !srv_connect()) 996a9835440Ssunil errx(1, "smtpd doesn't seem to be running"); 997a9835440Ssunil 998a9835440Ssunil if (argv[0].type == P_EVPID) { 999a9835440Ssunil evpid = argv[0].u.u_evpid; 1000a9835440Ssunil srv_send(IMSG_CTL_DISCOVER_EVPID, &evpid, sizeof evpid); 1001a9835440Ssunil srv_recv(IMSG_CTL_DISCOVER_EVPID); 1002a9835440Ssunil } else { 1003a9835440Ssunil msgid = argv[0].u.u_msgid; 1004a9835440Ssunil srv_send(IMSG_CTL_DISCOVER_MSGID, &msgid, sizeof msgid); 1005a9835440Ssunil srv_recv(IMSG_CTL_DISCOVER_MSGID); 1006a9835440Ssunil } 1007a9835440Ssunil 1008a9835440Ssunil if (rlen == 0) { 1009a9835440Ssunil srv_end(); 1010a9835440Ssunil return (0); 1011a9835440Ssunil } else { 1012a9835440Ssunil srv_read(&n_evp, sizeof n_evp); 1013a9835440Ssunil srv_end(); 1014a9835440Ssunil } 1015a9835440Ssunil 1016a9835440Ssunil printf("%zu envelope%s discovered\n", n_evp, (n_evp != 1) ? "s" : ""); 1017a9835440Ssunil return (0); 1018a9835440Ssunil } 1019a9835440Ssunil 1020d05af802Ssunil static int 1021aee31bfdSeric do_spf_walk(int argc, struct parameter *argv) 1022aee31bfdSeric { 1023aee31bfdSeric droppriv(); 1024aee31bfdSeric 1025aee31bfdSeric return spfwalk(argc, argv); 1026aee31bfdSeric } 1027aee31bfdSeric 1028aee31bfdSeric #define cmd_install_priv(s, f) \ 1029aee31bfdSeric cmd_install((s), privileged ? (f) : do_permission_denied) 1030aee31bfdSeric 10317a7bc169Seric int 10327a7bc169Seric main(int argc, char **argv) 10337a7bc169Seric { 10348351d18bSgilles gid_t gid; 1035aee31bfdSeric int privileged; 10367a7bc169Seric char *argv_mailq[] = { "show", "queue", NULL }; 10377a7bc169Seric 1038ff01b044Seric log_init(1, LOG_MAIL); 1039ff01b044Seric 10403511367dSsunil sendmail_compat(argc, argv); 1041aee31bfdSeric privileged = geteuid() == 0; 10425d09b982Seric 10433511367dSsunil gid = getgid(); 10448351d18bSgilles if (setresgid(gid, gid, gid) == -1) 10458351d18bSgilles err(1, "setresgid"); 10468351d18bSgilles 1047aee31bfdSeric /* Privileged commands */ 1048aee31bfdSeric cmd_install_priv("discover <evpid>", do_discover); 1049aee31bfdSeric cmd_install_priv("discover <msgid>", do_discover); 1050aee31bfdSeric cmd_install_priv("pause mta from <addr> for <str>", do_block_mta); 1051aee31bfdSeric cmd_install_priv("resume mta from <addr> for <str>", do_unblock_mta); 1052aee31bfdSeric cmd_install_priv("show mta paused", do_show_mta_block); 1053aee31bfdSeric cmd_install_priv("log brief", do_log_brief); 1054aee31bfdSeric cmd_install_priv("log verbose", do_log_verbose); 1055aee31bfdSeric cmd_install_priv("monitor", do_monitor); 1056aee31bfdSeric cmd_install_priv("pause envelope <evpid>", do_pause_envelope); 1057aee31bfdSeric cmd_install_priv("pause envelope <msgid>", do_pause_envelope); 1058aee31bfdSeric cmd_install_priv("pause envelope all", do_pause_envelope); 1059aee31bfdSeric cmd_install_priv("pause mda", do_pause_mda); 1060aee31bfdSeric cmd_install_priv("pause mta", do_pause_mta); 1061aee31bfdSeric cmd_install_priv("pause smtp", do_pause_smtp); 1062aee31bfdSeric cmd_install_priv("profile <str>", do_profile); 1063aee31bfdSeric cmd_install_priv("remove <evpid>", do_remove); 1064aee31bfdSeric cmd_install_priv("remove <msgid>", do_remove); 1065aee31bfdSeric cmd_install_priv("remove all", do_remove); 1066aee31bfdSeric cmd_install_priv("resume envelope <evpid>", do_resume_envelope); 1067aee31bfdSeric cmd_install_priv("resume envelope <msgid>", do_resume_envelope); 1068aee31bfdSeric cmd_install_priv("resume envelope all", do_resume_envelope); 1069aee31bfdSeric cmd_install_priv("resume mda", do_resume_mda); 1070aee31bfdSeric cmd_install_priv("resume mta", do_resume_mta); 1071aee31bfdSeric cmd_install_priv("resume route <routeid>", do_resume_route); 1072aee31bfdSeric cmd_install_priv("resume smtp", do_resume_smtp); 1073aee31bfdSeric cmd_install_priv("schedule <msgid>", do_schedule); 1074aee31bfdSeric cmd_install_priv("schedule <evpid>", do_schedule); 1075aee31bfdSeric cmd_install_priv("schedule all", do_schedule); 1076aee31bfdSeric cmd_install_priv("show envelope <evpid>", do_show_envelope); 1077aee31bfdSeric cmd_install_priv("show hoststats", do_show_hoststats); 1078aee31bfdSeric cmd_install_priv("show message <msgid>", do_show_message); 1079aee31bfdSeric cmd_install_priv("show message <evpid>", do_show_message); 1080aee31bfdSeric cmd_install_priv("show queue", do_show_queue); 1081aee31bfdSeric cmd_install_priv("show queue <msgid>", do_show_queue); 1082aee31bfdSeric cmd_install_priv("show hosts", do_show_hosts); 1083aee31bfdSeric cmd_install_priv("show relays", do_show_relays); 1084aee31bfdSeric cmd_install_priv("show routes", do_show_routes); 1085aee31bfdSeric cmd_install_priv("show stats", do_show_stats); 1086aee31bfdSeric cmd_install_priv("show status", do_show_status); 1087aee31bfdSeric cmd_install_priv("trace <str>", do_trace); 1088aee31bfdSeric cmd_install_priv("unprofile <str>", do_unprofile); 1089aee31bfdSeric cmd_install_priv("untrace <str>", do_untrace); 1090aee31bfdSeric cmd_install_priv("update table <str>", do_update_table); 1091aee31bfdSeric 1092aee31bfdSeric /* Unprivileged commands */ 10937b682ccfSeric cmd_install("encrypt", do_encrypt); 10947b682ccfSeric cmd_install("encrypt <str>", do_encrypt); 1095aee31bfdSeric cmd_install("spf walk", do_spf_walk); 10967a7bc169Seric 10975d09b982Seric if (strcmp(__progname, "mailq") == 0) 10987a7bc169Seric return cmd_run(2, argv_mailq); 10997a7bc169Seric if (strcmp(__progname, "smtpctl") == 0) 11007a7bc169Seric return cmd_run(argc - 1, argv + 1); 11017a7bc169Seric 11025d09b982Seric errx(1, "unsupported mode"); 11034fe02f32Seric return (0); 11043511367dSsunil } 1105f607a12cSgilles 11063511367dSsunil void 11073511367dSsunil sendmail_compat(int argc, char **argv) 11083511367dSsunil { 11093511367dSsunil FILE *offlinefp = NULL; 11103511367dSsunil gid_t gid; 11115b9e028dSgilles int i, r; 11123511367dSsunil 11133511367dSsunil if (strcmp(__progname, "sendmail") == 0 || 11143511367dSsunil strcmp(__progname, "send-mail") == 0) { 11153511367dSsunil /* 11163511367dSsunil * determine whether we are called with flags 11173511367dSsunil * that should invoke makemap/newaliases. 11183511367dSsunil */ 11193511367dSsunil for (i = 1; i < argc; i++) 11205ba55e18Ssunil if (strncmp(argv[i], "-bi", 3) == 0) 11210228dab0Smillert exit(makemap(P_SENDMAIL, argc, argv)); 11223511367dSsunil 11233511367dSsunil if (!srv_connect()) 11243511367dSsunil offlinefp = offline_file(); 11253511367dSsunil 11263511367dSsunil gid = getgid(); 11273511367dSsunil if (setresgid(gid, gid, gid) == -1) 11283511367dSsunil err(1, "setresgid"); 11293511367dSsunil 11303511367dSsunil /* we'll reduce further down the road */ 11313511367dSsunil if (pledge("stdio rpath wpath cpath tmppath flock " 11323511367dSsunil "dns getpw recvfd", NULL) == -1) 11333511367dSsunil err(1, "pledge"); 11343511367dSsunil 11353511367dSsunil sendmail = 1; 11363511367dSsunil exit(enqueue(argc, argv, offlinefp)); 11375ba55e18Ssunil } else if (strcmp(__progname, "makemap") == 0) 11385ba55e18Ssunil exit(makemap(P_MAKEMAP, argc, argv)); 11395b9e028dSgilles else if (strcmp(__progname, "newaliases") == 0) { 11405b9e028dSgilles r = makemap(P_NEWALIASES, argc, argv); 11415b9e028dSgilles /* 11425b9e028dSgilles * if server is available, notify of table update. 11435b9e028dSgilles * only makes sense for static tables AND if server is up. 11445b9e028dSgilles */ 11455b9e028dSgilles if (srv_connect()) { 11465b9e028dSgilles srv_send(IMSG_CTL_UPDATE_TABLE, "aliases", strlen("aliases") + 1); 11475b9e028dSgilles srv_check_result(0); 11485b9e028dSgilles } 11495b9e028dSgilles exit(r); 11505b9e028dSgilles } 115138f0b910Seric } 115238f0b910Seric 115338f0b910Seric static void 11544fe02f32Seric show_queue_envelope(struct envelope *e, int online) 115538f0b910Seric { 11564fe02f32Seric const char *src = "?", *agent = "?"; 1157a8bde366Seric char status[128], runstate[128], errline[LINE_MAX]; 115838f0b910Seric 115938f0b910Seric status[0] = '\0'; 116038f0b910Seric 11617a7bc169Seric getflag(&e->flags, EF_BOUNCE, "bounce", status, sizeof(status)); 11627a7bc169Seric getflag(&e->flags, EF_AUTHENTICATED, "auth", status, sizeof(status)); 11637a7bc169Seric getflag(&e->flags, EF_INTERNAL, "internal", status, sizeof(status)); 116435e161d3Seric getflag(&e->flags, EF_SUSPEND, "suspend", status, sizeof(status)); 11656dc81a07Seric getflag(&e->flags, EF_HOLD, "hold", status, sizeof(status)); 116638f0b910Seric 11674fe02f32Seric if (online) { 116865c4fdfbSgilles if (e->flags & EF_PENDING) 11695c9fb78eSeric (void)snprintf(runstate, sizeof runstate, "pending|%zd", 11704fe02f32Seric (ssize_t)(e->nexttry - now)); 117165c4fdfbSgilles else if (e->flags & EF_INFLIGHT) 11724c5b19cfSsunil (void)snprintf(runstate, sizeof runstate, 11734c5b19cfSsunil "inflight|%zd", (ssize_t)(now - e->lasttry)); 11744fe02f32Seric else 117586d491f8Sgilles (void)snprintf(runstate, sizeof runstate, "invalid|"); 117665c4fdfbSgilles e->flags &= ~(EF_PENDING|EF_INFLIGHT); 11774fe02f32Seric } 11784fe02f32Seric else 117986d491f8Sgilles (void)strlcpy(runstate, "offline|", sizeof runstate); 11804fe02f32Seric 118138f0b910Seric if (e->flags) 118238f0b910Seric errx(1, "%016" PRIx64 ": unexpected flags 0x%04x", e->id, 118338f0b910Seric e->flags); 118438f0b910Seric 118538f0b910Seric if (status[0]) 118638f0b910Seric status[strlen(status) - 1] = '\0'; 118738f0b910Seric 11884fe02f32Seric if (e->type == D_MDA) 11894fe02f32Seric agent = "mda"; 11904fe02f32Seric else if (e->type == D_MTA) 11914fe02f32Seric agent = "mta"; 11924fe02f32Seric else if (e->type == D_BOUNCE) 11934fe02f32Seric agent = "bounce"; 119438f0b910Seric 1195468bf152Seric if (e->ss.ss_family == AF_LOCAL) 119656c4d9e6Seric src = "local"; 1197468bf152Seric else if (e->ss.ss_family == AF_INET) 119856c4d9e6Seric src = "inet4"; 1199468bf152Seric else if (e->ss.ss_family == AF_INET6) 120056c4d9e6Seric src = "inet6"; 1201468bf152Seric 1202a8bde366Seric strnvis(errline, e->errorline, sizeof(errline), 0); 1203a8bde366Seric 12044fe02f32Seric printf("%016"PRIx64 12054fe02f32Seric "|%s|%s|%s|%s@%s|%s@%s|%s@%s" 12064fe02f32Seric "|%zu|%zu|%zu|%zu|%s|%s\n", 12074fe02f32Seric 120838f0b910Seric e->id, 12094fe02f32Seric 1210468bf152Seric src, 12114fe02f32Seric agent, 121238f0b910Seric status, 121338f0b910Seric e->sender.user, e->sender.domain, 12144fe02f32Seric e->rcpt.user, e->rcpt.domain, 121538f0b910Seric e->dest.user, e->dest.domain, 121638f0b910Seric 12174fe02f32Seric (size_t) e->creation, 1218a8e22235Sgilles (size_t) (e->creation + e->ttl), 12194fe02f32Seric (size_t) e->lasttry, 12204fe02f32Seric (size_t) e->retry, 12214fe02f32Seric runstate, 1222a8bde366Seric errline); 122338f0b910Seric } 122438f0b910Seric 122538f0b910Seric static void 1226d2241734Schl getflag(uint *bitmap, int bit, char *bitstr, char *buf, size_t len) 122738f0b910Seric { 122838f0b910Seric if (*bitmap & bit) { 122938f0b910Seric *bitmap &= ~bit; 123086d491f8Sgilles (void)strlcat(buf, bitstr, len); 123186d491f8Sgilles (void)strlcat(buf, ",", len); 123238f0b910Seric } 123338f0b910Seric } 1234d5e710d9Sgilles 1235d5e710d9Sgilles static void 12367a7bc169Seric show_offline_envelope(uint64_t evpid) 12377a7bc169Seric { 12387a7bc169Seric FILE *fp = NULL; 1239953aae25Sderaadt char pathname[PATH_MAX]; 12407a7bc169Seric size_t plen; 12417a7bc169Seric char *p; 12427a7bc169Seric size_t buflen; 12437a7bc169Seric char buffer[sizeof(struct envelope)]; 12447a7bc169Seric 12457a7bc169Seric struct envelope evp; 12467a7bc169Seric 12477a7bc169Seric if (!bsnprintf(pathname, sizeof pathname, 12487a7bc169Seric "/queue/%02x/%08x/%016"PRIx64, 12497a7bc169Seric (evpid_to_msgid(evpid) & 0xff000000) >> 24, 12507a7bc169Seric evpid_to_msgid(evpid), evpid)) 12517a7bc169Seric goto end; 12527a7bc169Seric fp = fopen(pathname, "r"); 12537a7bc169Seric if (fp == NULL) 12547a7bc169Seric goto end; 12557a7bc169Seric 12560fcb81a3Seric buflen = fread(buffer, 1, sizeof (buffer) - 1, fp); 12577a7bc169Seric p = buffer; 12587a7bc169Seric plen = buflen; 12590fcb81a3Seric buffer[buflen] = '\0'; 12607a7bc169Seric 12617a7bc169Seric if (is_encrypted_buffer(p)) { 12627a7bc169Seric warnx("offline encrypted queue is not supported yet"); 12637a7bc169Seric goto end; 12647a7bc169Seric } 12657a7bc169Seric 12667a7bc169Seric if (is_gzip_buffer(p)) { 12677a7bc169Seric warnx("offline compressed queue is not supported yet"); 12687a7bc169Seric goto end; 12697a7bc169Seric } 12707a7bc169Seric 12717a7bc169Seric if (!envelope_load_buffer(&evp, p, plen)) 12727a7bc169Seric goto end; 12737a7bc169Seric evp.id = evpid; 12747a7bc169Seric show_queue_envelope(&evp, 0); 12757a7bc169Seric 12767a7bc169Seric end: 12777a7bc169Seric if (fp) 12787a7bc169Seric fclose(fp); 12797a7bc169Seric } 12807a7bc169Seric 12817a7bc169Seric static void 1282d5e710d9Sgilles display(const char *s) 1283d5e710d9Sgilles { 12847a7bc169Seric FILE *fp; 12857a7bc169Seric char *key; 12867a7bc169Seric int gzipped; 12877a7bc169Seric char *gzcat_argv0 = strrchr(PATH_GZCAT, '/') + 1; 1288d5e710d9Sgilles 12897a7bc169Seric if ((fp = fopen(s, "r")) == NULL) 12907a7bc169Seric err(1, "fopen"); 12917a7bc169Seric 12927a7bc169Seric if (is_encrypted_fp(fp)) { 12937a7bc169Seric int i; 1294230bac0bSgilles FILE *ofp = NULL; 12957a7bc169Seric 1296729f2a4cSmartijn if ((ofp = tmpfile()) == NULL) 1297729f2a4cSmartijn err(1, "tmpfile"); 12987a7bc169Seric 12997a7bc169Seric for (i = 0; i < 3; i++) { 13007a7bc169Seric key = getpass("key> "); 13017a7bc169Seric if (crypto_setup(key, strlen(key))) 13027a7bc169Seric break; 13037a7bc169Seric } 13047a7bc169Seric if (i == 3) 13057a7bc169Seric errx(1, "crypto-setup: invalid key"); 13067a7bc169Seric 13077a7bc169Seric if (!crypto_decrypt_file(fp, ofp)) { 13087a7bc169Seric printf("object is encrypted: %s\n", key); 13097a7bc169Seric exit(1); 1310d5e710d9Sgilles } 1311d5e710d9Sgilles 13127a7bc169Seric fclose(fp); 13137a7bc169Seric fp = ofp; 13140fcb81a3Seric fseek(fp, 0, SEEK_SET); 13157a7bc169Seric } 13167a7bc169Seric gzipped = is_gzip_fp(fp); 13177a7bc169Seric 13180fcb81a3Seric lseek(fileno(fp), 0, SEEK_SET); 13197a7bc169Seric (void)dup2(fileno(fp), STDIN_FILENO); 13207a7bc169Seric if (gzipped) 132153408464Skrw execl(PATH_GZCAT, gzcat_argv0, (char *)NULL); 13227a7bc169Seric else 132353408464Skrw execl(PATH_CAT, "cat", (char *)NULL); 13247a7bc169Seric err(1, "execl"); 13257a7bc169Seric } 13267a7bc169Seric 13277a7bc169Seric static int 13287a7bc169Seric str_to_trace(const char *str) 1329d5e710d9Sgilles { 13307a7bc169Seric if (!strcmp(str, "imsg")) 133165c4fdfbSgilles return TRACE_IMSG; 13327a7bc169Seric if (!strcmp(str, "io")) 133365c4fdfbSgilles return TRACE_IO; 13347a7bc169Seric if (!strcmp(str, "smtp")) 133565c4fdfbSgilles return TRACE_SMTP; 1336041b2d9aSeric if (!strcmp(str, "filters")) 1337041b2d9aSeric return TRACE_FILTERS; 13387a7bc169Seric if (!strcmp(str, "mta")) 133965c4fdfbSgilles return TRACE_MTA; 13407a7bc169Seric if (!strcmp(str, "bounce")) 134165c4fdfbSgilles return TRACE_BOUNCE; 13427a7bc169Seric if (!strcmp(str, "scheduler")) 134365c4fdfbSgilles return TRACE_SCHEDULER; 13447a7bc169Seric if (!strcmp(str, "lookup")) 13450cf935dfSgilles return TRACE_LOOKUP; 13467a7bc169Seric if (!strcmp(str, "stat")) 134765c4fdfbSgilles return TRACE_STAT; 13487a7bc169Seric if (!strcmp(str, "rules")) 134965c4fdfbSgilles return TRACE_RULES; 13507a7bc169Seric if (!strcmp(str, "mproc")) 1351299c4efeSeric return TRACE_MPROC; 13527a7bc169Seric if (!strcmp(str, "expand")) 135359a46edcSgilles return TRACE_EXPAND; 13547a7bc169Seric if (!strcmp(str, "all")) 1355299c4efeSeric return ~TRACE_DEBUG; 13567a7bc169Seric errx(1, "invalid trace keyword: %s", str); 13577a7bc169Seric return (0); 135865c4fdfbSgilles } 135965c4fdfbSgilles 13607a7bc169Seric static int 13617a7bc169Seric str_to_profile(const char *str) 136265c4fdfbSgilles { 13637a7bc169Seric if (!strcmp(str, "imsg")) 136465c4fdfbSgilles return PROFILE_IMSG; 13657a7bc169Seric if (!strcmp(str, "queue")) 136665c4fdfbSgilles return PROFILE_QUEUE; 13677a7bc169Seric errx(1, "invalid profile keyword: %s", str); 13687a7bc169Seric return (0); 136965c4fdfbSgilles } 137065c4fdfbSgilles 13717a7bc169Seric static int 13727a7bc169Seric is_gzip_buffer(const char *buffer) 13737a7bc169Seric { 13747a7bc169Seric uint16_t magic; 13757a7bc169Seric 13767a7bc169Seric memcpy(&magic, buffer, sizeof magic); 13777a7bc169Seric #define GZIP_MAGIC 0x8b1f 13787a7bc169Seric return (magic == GZIP_MAGIC); 13797a7bc169Seric } 13807a7bc169Seric 13817a7bc169Seric static int 13827a7bc169Seric is_gzip_fp(FILE *fp) 13837a7bc169Seric { 13847a7bc169Seric uint8_t magic[2]; 13857a7bc169Seric int ret = 0; 13867a7bc169Seric 13877a7bc169Seric if (fread(&magic, 1, sizeof magic, fp) != sizeof magic) 13887a7bc169Seric goto end; 13897a7bc169Seric 13907a7bc169Seric ret = is_gzip_buffer((const char *)&magic); 13917a7bc169Seric end: 13920fcb81a3Seric fseek(fp, 0, SEEK_SET); 13937a7bc169Seric return ret; 13947a7bc169Seric } 13957a7bc169Seric 13967a7bc169Seric 13977a7bc169Seric /* XXX */ 13987a7bc169Seric /* 13997a7bc169Seric * queue supports transparent encryption. 14007a7bc169Seric * encrypted chunks are prefixed with an API version byte 14017a7bc169Seric * which we ensure is unambiguous with gzipped / plain 14027a7bc169Seric * objects. 14037a7bc169Seric */ 14047a7bc169Seric 14057a7bc169Seric static int 14067a7bc169Seric is_encrypted_buffer(const char *buffer) 14077a7bc169Seric { 14087a7bc169Seric uint8_t magic; 14097a7bc169Seric 14107a7bc169Seric magic = *buffer; 14117a7bc169Seric #define ENCRYPTION_MAGIC 0x1 14127a7bc169Seric return (magic == ENCRYPTION_MAGIC); 14137a7bc169Seric } 14147a7bc169Seric 14157a7bc169Seric static int 14167a7bc169Seric is_encrypted_fp(FILE *fp) 14177a7bc169Seric { 14187a7bc169Seric uint8_t magic; 14197a7bc169Seric int ret = 0; 14207a7bc169Seric 14217a7bc169Seric if (fread(&magic, 1, sizeof magic, fp) != sizeof magic) 14227a7bc169Seric goto end; 14237a7bc169Seric 14247a7bc169Seric ret = is_encrypted_buffer((const char *)&magic); 14257a7bc169Seric end: 14260fcb81a3Seric fseek(fp, 0, SEEK_SET); 14277a7bc169Seric return ret; 142865c4fdfbSgilles } 1429