xref: /openbsd-src/usr.sbin/lpd/proc.c (revision af27b3cce1070d60f0e791a83d790160efdc2be8)
1*af27b3ccSclaudio /*	$OpenBSD: proc.c,v 1.7 2024/11/21 13:34:51 claudio Exp $	*/
23b188dabSeric 
33b188dabSeric /*
43b188dabSeric  * Copyright (c) 2017 Eric Faurot <eric@openbsd.org>
53b188dabSeric  *
63b188dabSeric  * Permission to use, copy, modify, and distribute this software for any
73b188dabSeric  * purpose with or without fee is hereby granted, provided that the above
83b188dabSeric  * copyright notice and this permission notice appear in all copies.
93b188dabSeric  *
103b188dabSeric  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
113b188dabSeric  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
123b188dabSeric  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
133b188dabSeric  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
143b188dabSeric  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
153b188dabSeric  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
163b188dabSeric  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
173b188dabSeric  */
183b188dabSeric 
193b188dabSeric #include <sys/queue.h>
203b188dabSeric #include <sys/socket.h>
213b188dabSeric 
223b188dabSeric #include <errno.h>
233b188dabSeric #include <event.h>
243b188dabSeric #include <imsg.h>
253b188dabSeric #include <stdlib.h>
263b188dabSeric #include <string.h>
273b188dabSeric #include <unistd.h>
283b188dabSeric 
293b188dabSeric #include "log.h"
303b188dabSeric #include "proc.h"
313b188dabSeric 
323b188dabSeric struct imsgproc {
333b188dabSeric 	TAILQ_ENTRY(imsgproc) tqe;
343b188dabSeric 	int		 type;
353b188dabSeric 	int		 instance;
363b188dabSeric 	char		*title;
373b188dabSeric 	pid_t		 pid;
383b188dabSeric 	void		*arg;
393b188dabSeric 	void		(*cb)(struct imsgproc *, struct imsg *, void *);
403b188dabSeric 	struct imsgbuf	 imsgbuf;
413b188dabSeric 	short		 events;
423b188dabSeric 	struct event	 ev;
433b188dabSeric 
443b188dabSeric 	struct {
453b188dabSeric 		const uint8_t	*pos;
463b188dabSeric 		const uint8_t	*end;
473b188dabSeric 	} m_in;
483b188dabSeric 
493b188dabSeric 	struct m_out {
503b188dabSeric 		char		*buf;
513b188dabSeric 		size_t		 alloc;
523b188dabSeric 		size_t		 pos;
533b188dabSeric 		uint32_t	 type;
543b188dabSeric 		uint32_t	 peerid;
553b188dabSeric 		pid_t		 pid;
563b188dabSeric 		int		 fd;
573b188dabSeric 	} m_out;
583b188dabSeric };
593b188dabSeric 
603b188dabSeric static struct imsgproc *proc_new(int);
613b188dabSeric static void proc_setsock(struct imsgproc *, int);
623b188dabSeric static void proc_callback(struct imsgproc *, struct imsg *);
633b188dabSeric static void proc_dispatch(int, short, void *);
643b188dabSeric static void proc_event_add(struct imsgproc *);
653b188dabSeric 
663b188dabSeric static TAILQ_HEAD(, imsgproc) procs = TAILQ_HEAD_INITIALIZER(procs);
673b188dabSeric 
683b188dabSeric pid_t
693b188dabSeric proc_getpid(struct imsgproc *p)
703b188dabSeric {
713b188dabSeric 	return p->pid;
723b188dabSeric }
733b188dabSeric 
743b188dabSeric int
753b188dabSeric proc_gettype(struct imsgproc *p)
763b188dabSeric {
773b188dabSeric 	return p->type;
783b188dabSeric }
793b188dabSeric 
803b188dabSeric int
813b188dabSeric proc_getinstance(struct imsgproc *p)
823b188dabSeric {
833b188dabSeric 	return p->instance;
843b188dabSeric }
853b188dabSeric 
863b188dabSeric const char *
873b188dabSeric proc_gettitle(struct imsgproc *p)
883b188dabSeric {
893b188dabSeric 	return p->title;
903b188dabSeric }
913b188dabSeric 
923b188dabSeric struct imsgproc *
933b188dabSeric proc_bypid(pid_t pid)
943b188dabSeric {
953b188dabSeric 	struct imsgproc *p;
963b188dabSeric 
973b188dabSeric 	TAILQ_FOREACH(p, &procs, tqe)
983b188dabSeric 		if (pid == p->pid)
993b188dabSeric 			return p;
1003b188dabSeric 
1013b188dabSeric 	return NULL;
1023b188dabSeric }
1033b188dabSeric 
1043b188dabSeric struct imsgproc *
1053b188dabSeric proc_exec(int type, char **argv)
1063b188dabSeric {
1073b188dabSeric 	struct imsgproc *p;
1083b188dabSeric 	int sp[2];
1093b188dabSeric 	pid_t pid;
1103b188dabSeric 
1113b188dabSeric 	p = proc_new(type);
1123b188dabSeric 	if (p == NULL) {
1133b188dabSeric 		log_warn("%s: proc_new", __func__);
1143b188dabSeric 		return NULL;
1153b188dabSeric 	}
1163b188dabSeric 
1173b188dabSeric 	if (socketpair(AF_UNIX, SOCK_STREAM|SOCK_NONBLOCK, PF_UNSPEC, sp) == -1) {
1183b188dabSeric 		log_warn("%s: socketpair", __func__);
1193b188dabSeric 		proc_free(p);
1203b188dabSeric 		return NULL;
1213b188dabSeric 	}
1223b188dabSeric 
1233b188dabSeric 	switch (pid = fork()) {
1243b188dabSeric 	case -1:
1253b188dabSeric 		log_warn("%s: fork", __func__);
1263b188dabSeric 		close(sp[0]);
1273b188dabSeric 		close(sp[1]);
1283b188dabSeric 		proc_free(p);
1293b188dabSeric 		return NULL;
1303b188dabSeric 	case 0:
1313b188dabSeric 		break;
1323b188dabSeric 	default:
1333b188dabSeric 		close(sp[0]);
1343b188dabSeric 		p->pid = pid;
1353b188dabSeric 		proc_setsock(p, sp[1]);
1363b188dabSeric 		return p;
1373b188dabSeric 	}
1383b188dabSeric 
1393b188dabSeric 	if (dup2(sp[0], 3) == -1)
1403b188dabSeric 		fatal("%s: dup2", __func__);
1413b188dabSeric 
1423b188dabSeric 	if (closefrom(4) == -1)
1433b188dabSeric 		fatal("%s: closefrom", __func__);
1443b188dabSeric 
1453b188dabSeric 	execvp(argv[0], argv);
1463b188dabSeric 	fatal("%s: execvp: %s", __func__, argv[0]);
1473b188dabSeric }
1483b188dabSeric 
1493b188dabSeric struct imsgproc *
1503b188dabSeric proc_attach(int type, int fd)
1513b188dabSeric {
1523b188dabSeric 	struct imsgproc *p;
1533b188dabSeric 
1543b188dabSeric 	p = proc_new(type);
1553b188dabSeric 	if (p == NULL)
1563b188dabSeric 		return NULL;
1573b188dabSeric 
1583b188dabSeric 	proc_setsock(p, fd);
1593b188dabSeric 	return p;
1603b188dabSeric }
1613b188dabSeric 
1623b188dabSeric void
1633b188dabSeric proc_settitle(struct imsgproc *p, const char *title)
1643b188dabSeric {
1653b188dabSeric 	free(p->title);
1663b188dabSeric 	if (title) {
1673b188dabSeric 		p->title = strdup(title);
1683b188dabSeric 		if (p->title == NULL)
1693b188dabSeric 			log_warn("%s: strdup", __func__);
1703b188dabSeric 	}
1713b188dabSeric 	else
1723b188dabSeric 		p->title = NULL;
1733b188dabSeric }
1743b188dabSeric 
1753b188dabSeric void
1763b188dabSeric proc_setpid(struct imsgproc *p, pid_t pid)
1773b188dabSeric {
1783b188dabSeric 	p->pid = pid;
1793b188dabSeric }
1803b188dabSeric 
1813b188dabSeric void
1823b188dabSeric proc_setcallback(struct imsgproc *p,
1833b188dabSeric     void(*cb)(struct imsgproc *, struct imsg *, void *), void *arg)
1843b188dabSeric {
1853b188dabSeric 	p->cb = cb;
1863b188dabSeric 	p->arg = arg;
1873b188dabSeric }
1883b188dabSeric 
1893b188dabSeric void
1903b188dabSeric proc_enable(struct imsgproc *p)
1913b188dabSeric {
1923b188dabSeric 	proc_event_add(p);
1933b188dabSeric }
1943b188dabSeric 
1953b188dabSeric void
1963b188dabSeric proc_free(struct imsgproc *p)
1973b188dabSeric {
1983b188dabSeric 	if (p == NULL)
1993b188dabSeric 		return;
2003b188dabSeric 
2013b188dabSeric 	TAILQ_REMOVE(&procs, p, tqe);
2023b188dabSeric 
2033b188dabSeric 	if (event_initialized(&p->ev))
2043b188dabSeric 		event_del(&p->ev);
2053b188dabSeric 	close(p->imsgbuf.fd);
206dd7efffeSclaudio 	imsgbuf_clear(&p->imsgbuf);
2073b188dabSeric 	free(p->title);
2083b188dabSeric 	free(p);
2093b188dabSeric }
2103b188dabSeric 
2113b188dabSeric static struct imsgproc *
2123b188dabSeric proc_new(int type)
2133b188dabSeric {
2143b188dabSeric 	struct imsgproc *p;
2153b188dabSeric 
2163b188dabSeric 	p = calloc(1, sizeof(*p));
2173b188dabSeric 	if (p == NULL)
2183b188dabSeric 		return NULL;
2193b188dabSeric 
220*af27b3ccSclaudio 	if (imsgbuf_init(&p->imsgbuf, -1) == -1) {
221*af27b3ccSclaudio 		free(p);
222*af27b3ccSclaudio 		return NULL;
223*af27b3ccSclaudio 	}
224*af27b3ccSclaudio 	imsgbuf_allow_fdpass(&p->imsgbuf);
225*af27b3ccSclaudio 
2263b188dabSeric 	p->type = type;
2273b188dabSeric 	p->instance = -1;
2283b188dabSeric 	p->pid = -1;
2293b188dabSeric 
2303b188dabSeric 	TAILQ_INSERT_TAIL(&procs, p, tqe);
2313b188dabSeric 
2323b188dabSeric 	return p;
2333b188dabSeric }
2343b188dabSeric 
2353b188dabSeric static void
2363b188dabSeric proc_setsock(struct imsgproc *p, int sock)
2373b188dabSeric {
2383b188dabSeric 	p->imsgbuf.fd = sock;
2393b188dabSeric }
2403b188dabSeric 
2413b188dabSeric static void
2423b188dabSeric proc_event_add(struct imsgproc *p)
2433b188dabSeric {
2443b188dabSeric 	short	events;
2453b188dabSeric 
2463b188dabSeric 	events = EV_READ;
247*af27b3ccSclaudio 	if (imsgbuf_queuelen(&p->imsgbuf) > 0)
2483b188dabSeric 		events |= EV_WRITE;
2493b188dabSeric 
2503b188dabSeric 	if (p->events)
2513b188dabSeric 		event_del(&p->ev);
2523b188dabSeric 
2533b188dabSeric 	p->events = events;
2543b188dabSeric 	if (events) {
2553b188dabSeric 		event_set(&p->ev, p->imsgbuf.fd, events, proc_dispatch, p);
2563b188dabSeric 		event_add(&p->ev, NULL);
2573b188dabSeric 	}
2583b188dabSeric }
2593b188dabSeric 
2603b188dabSeric static void
2613b188dabSeric proc_callback(struct imsgproc *p, struct imsg *imsg)
2623b188dabSeric {
2633b188dabSeric 	if (imsg != NULL) {
2643b188dabSeric 		p->m_in.pos = imsg->data;
2653b188dabSeric 		p->m_in.end = p->m_in.pos + (imsg->hdr.len - sizeof(imsg->hdr));
2663b188dabSeric 	}
2673b188dabSeric 	else {
2683b188dabSeric 		p->m_in.pos = NULL;
2693b188dabSeric 		p->m_in.end = NULL;
2703b188dabSeric 	}
2713b188dabSeric 
2723b188dabSeric 	p->cb(p, imsg, p->arg);
2733b188dabSeric }
2743b188dabSeric 
2753b188dabSeric static void
2763b188dabSeric proc_dispatch(int fd, short event, void *arg)
2773b188dabSeric {
2783b188dabSeric 	struct imsgproc	*p = arg;
2793b188dabSeric 	struct imsg	 imsg;
2803b188dabSeric 	ssize_t		 n;
2813b188dabSeric 
2823b188dabSeric 	p->events = 0;
2833b188dabSeric 
2843b188dabSeric 	if (event & EV_READ) {
285dd7efffeSclaudio 		n = imsgbuf_read(&p->imsgbuf);
2863b188dabSeric 		switch (n) {
2873b188dabSeric 		case -1:
288dd7efffeSclaudio 			log_warn("%s: imsgbuf_read", __func__);
2893b188dabSeric 			proc_callback(p, NULL);
2903b188dabSeric 			return;
2913b188dabSeric 		case 0:
2923b188dabSeric 			/* This pipe is dead. */
2933b188dabSeric 			proc_callback(p, NULL);
2943b188dabSeric 			return;
2953b188dabSeric 		default:
2963b188dabSeric 			break;
2973b188dabSeric 		}
2983b188dabSeric 	}
2993b188dabSeric 
3003b188dabSeric 	if (event & EV_WRITE) {
301dd7efffeSclaudio 		if (imsgbuf_write(&p->imsgbuf) == -1) {
302c1aa9554Sclaudio 			if (errno != EPIPE)
303dd7efffeSclaudio 				log_warn("%s: imsgbuf_write", __func__);
3043b188dabSeric 			proc_callback(p, NULL);
3053b188dabSeric 			return;
3063b188dabSeric 		}
3073b188dabSeric 	}
3083b188dabSeric 
3093b188dabSeric 	for (;;) {
3103b188dabSeric 		if ((n = imsg_get(&p->imsgbuf, &imsg)) == -1) {
3113b188dabSeric 			log_warn("%s: imsg_get", __func__);
3123b188dabSeric 			proc_callback(p, NULL);
3133b188dabSeric 			return;
3143b188dabSeric 		}
3153b188dabSeric 		if (n == 0)
3163b188dabSeric 			break;
3173b188dabSeric 
3183b188dabSeric 		proc_callback(p, &imsg);
3193b188dabSeric 		imsg_free(&imsg);
3203b188dabSeric 	}
3213b188dabSeric 
3223b188dabSeric 	proc_event_add(p);
3233b188dabSeric }
3243b188dabSeric 
3253b188dabSeric void
3263b188dabSeric m_compose(struct imsgproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd,
3273b188dabSeric     const void *data, size_t len)
3283b188dabSeric {
3293b188dabSeric 	if (imsg_compose(&p->imsgbuf, type, peerid, pid, fd, data, len) == -1)
3303b188dabSeric 		fatal("%s: imsg_compose", __func__);
3313b188dabSeric 
3323b188dabSeric 	proc_event_add(p);
3333b188dabSeric }
3343b188dabSeric 
3353b188dabSeric void
3363b188dabSeric m_create(struct imsgproc *p, uint32_t type, uint32_t peerid, pid_t pid, int fd)
3373b188dabSeric {
3383b188dabSeric 	p->m_out.pos = 0;
3393b188dabSeric 	p->m_out.type = type;
3403b188dabSeric 	p->m_out.peerid = peerid;
3413b188dabSeric 	p->m_out.pid = pid;
3423b188dabSeric 	p->m_out.fd = fd;
3433b188dabSeric }
3443b188dabSeric 
3453b188dabSeric void
3463b188dabSeric m_close(struct imsgproc *p)
3473b188dabSeric {
3483b188dabSeric 	if (imsg_compose(&p->imsgbuf, p->m_out.type, p->m_out.peerid,
3493b188dabSeric 	    p->m_out.pid, p->m_out.fd, p->m_out.buf, p->m_out.pos) == -1)
3503b188dabSeric 		fatal("%s: imsg_compose", __func__);
3513b188dabSeric 
3523b188dabSeric 	proc_event_add(p);
3533b188dabSeric }
3543b188dabSeric 
3553b188dabSeric void
3563b188dabSeric m_add(struct imsgproc *p, const void *data, size_t len)
3573b188dabSeric {
3583b188dabSeric 	size_t	 alloc;
3593b188dabSeric 	void	*tmp;
3603b188dabSeric 
3613b188dabSeric 	if (p->m_out.pos + len + IMSG_HEADER_SIZE > MAX_IMSGSIZE)
3623b188dabSeric 		fatalx("%s: message too large", __func__);
3633b188dabSeric 
3643b188dabSeric 	alloc = p->m_out.alloc ? p->m_out.alloc : 128;
3653b188dabSeric 	while (p->m_out.pos + len > alloc)
3663b188dabSeric 		alloc *= 2;
3673b188dabSeric 	if (alloc != p->m_out.alloc) {
3683b188dabSeric 		tmp = recallocarray(p->m_out.buf, p->m_out.alloc, alloc, 1);
3693b188dabSeric 		if (tmp == NULL)
3703b188dabSeric 			fatal("%s: reallocarray", __func__);
3713b188dabSeric 		p->m_out.alloc = alloc;
3723b188dabSeric 		p->m_out.buf = tmp;
3733b188dabSeric 	}
3743b188dabSeric 
3753b188dabSeric 	memmove(p->m_out.buf + p->m_out.pos, data, len);
3763b188dabSeric 	p->m_out.pos += len;
3773b188dabSeric }
3783b188dabSeric 
3793b188dabSeric void
3803b188dabSeric m_add_int(struct imsgproc *p, int v)
3813b188dabSeric {
3823b188dabSeric 	m_add(p, &v, sizeof(v));
3833b188dabSeric };
3843b188dabSeric 
3853b188dabSeric void
3863b188dabSeric m_add_u32(struct imsgproc *p, uint32_t v)
3873b188dabSeric {
3883b188dabSeric 	m_add(p, &v, sizeof(v));
3893b188dabSeric };
3903b188dabSeric 
3913b188dabSeric void
3923b188dabSeric m_add_u64(struct imsgproc *p, uint64_t v)
3933b188dabSeric {
3943b188dabSeric 	m_add(p, &v, sizeof(v));
3953b188dabSeric }
3963b188dabSeric 
3973b188dabSeric void
3983b188dabSeric m_add_size(struct imsgproc *p, size_t v)
3993b188dabSeric {
4003b188dabSeric 	m_add(p, &v, sizeof(v));
4013b188dabSeric }
4023b188dabSeric 
4033b188dabSeric void
4043b188dabSeric m_add_time(struct imsgproc *p, time_t v)
4053b188dabSeric {
4063b188dabSeric 	m_add(p, &v, sizeof(v));
4073b188dabSeric }
4083b188dabSeric 
4093b188dabSeric void
4103b188dabSeric m_add_string(struct imsgproc *p, const char *str)
4113b188dabSeric {
4129d855d3dSeric 	if (str) {
4139d855d3dSeric 		m_add(p, "s", 1);
4143b188dabSeric 		m_add(p, str, strlen(str) + 1);
4153b188dabSeric 	}
4169d855d3dSeric 	else
4179d855d3dSeric 		m_add(p, "\0", 1);
4189d855d3dSeric }
4193b188dabSeric 
4203b188dabSeric void
4213b188dabSeric m_add_sockaddr(struct imsgproc *p, const struct sockaddr *sa)
4223b188dabSeric {
4233b188dabSeric 	m_add_size(p, sa->sa_len);
4243b188dabSeric 	m_add(p, sa, sa->sa_len);
4253b188dabSeric }
4263b188dabSeric 
4273b188dabSeric void
4283b188dabSeric m_end(struct imsgproc *p)
4293b188dabSeric {
4303b188dabSeric 	if (p->m_in.pos != p->m_in.end)
4313b188dabSeric 		fatal("%s: %zi bytes left", __func__,
4323b188dabSeric 		    p->m_in.end - p->m_in.pos);
4333b188dabSeric }
4343b188dabSeric 
4353b188dabSeric int
4363b188dabSeric m_is_eom(struct imsgproc *p)
4373b188dabSeric {
4383b188dabSeric 	return (p->m_in.pos == p->m_in.end);
4393b188dabSeric }
4403b188dabSeric 
4413b188dabSeric void
4423b188dabSeric m_get(struct imsgproc *p, void *dst, size_t sz)
4433b188dabSeric {
4443b188dabSeric 	if (sz > MAX_IMSGSIZE ||
4453b188dabSeric 	    p->m_in.end - p->m_in.pos < (ssize_t)sz )
4463b188dabSeric 		fatalx("%s: %zu bytes requested, %zi left", __func__, sz,
4473b188dabSeric 		    p->m_in.end - p->m_in.pos);
4483b188dabSeric 
4493b188dabSeric 	memmove(dst, p->m_in.pos, sz);
4503b188dabSeric 	p->m_in.pos += sz;
4513b188dabSeric }
4523b188dabSeric 
4533b188dabSeric void
4543b188dabSeric m_get_int(struct imsgproc *p, int *dst)
4553b188dabSeric {
4563b188dabSeric 	m_get(p, dst, sizeof(*dst));
4573b188dabSeric }
4583b188dabSeric 
4593b188dabSeric void
4603b188dabSeric m_get_u32(struct imsgproc *p, uint32_t *dst)
4613b188dabSeric {
4623b188dabSeric 	m_get(p, dst, sizeof(*dst));
4633b188dabSeric }
4643b188dabSeric 
4653b188dabSeric void
4663b188dabSeric m_get_u64(struct imsgproc *p, uint64_t *dst)
4673b188dabSeric {
4683b188dabSeric 	m_get(p, dst, sizeof(*dst));
4693b188dabSeric }
4703b188dabSeric 
4713b188dabSeric void
4723b188dabSeric m_get_size(struct imsgproc *p, size_t *dst)
4733b188dabSeric {
4743b188dabSeric 	m_get(p, dst, sizeof(*dst));
4753b188dabSeric }
4763b188dabSeric 
4773b188dabSeric void
4783b188dabSeric m_get_time(struct imsgproc *p, time_t *dst)
4793b188dabSeric {
4803b188dabSeric 	m_get(p, dst, sizeof(*dst));
4813b188dabSeric }
4823b188dabSeric 
4833b188dabSeric void
4843b188dabSeric m_get_string(struct imsgproc *p, const char **dst)
4853b188dabSeric {
4869d855d3dSeric 	char *end, c;
4873b188dabSeric 
4883b188dabSeric 	if (p->m_in.pos >= p->m_in.end)
4893b188dabSeric 		fatalx("%s: no data left", __func__);
4903b188dabSeric 
4919d855d3dSeric 	c = *p->m_in.pos++;
4929d855d3dSeric 	if (c == '\0') {
4939d855d3dSeric 		*dst = NULL;
4949d855d3dSeric 		return;
4959d855d3dSeric 	}
4969d855d3dSeric 
4979d855d3dSeric 	if (p->m_in.pos >= p->m_in.end)
4989d855d3dSeric 		fatalx("%s: no data left", __func__);
4993b188dabSeric 	end = memchr(p->m_in.pos, 0, p->m_in.end - p->m_in.pos);
5003b188dabSeric 	if (end == NULL)
5013b188dabSeric 		fatalx("%s: unterminated string", __func__);
5023b188dabSeric 
5033b188dabSeric 	*dst = p->m_in.pos;
5043b188dabSeric 	p->m_in.pos = end + 1;
5053b188dabSeric }
5063b188dabSeric 
5073b188dabSeric void
5083b188dabSeric m_get_sockaddr(struct imsgproc *p, struct sockaddr *dst)
5093b188dabSeric {
5103b188dabSeric 	size_t len;
5113b188dabSeric 
5123b188dabSeric 	m_get_size(p, &len);
5133b188dabSeric 	m_get(p, dst, len);
5143b188dabSeric }
515