GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/snmpd/control.c Lines: 0 317 0.0 %
Date: 2017-11-07 Branches: 0 221 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: control.c,v 1.42 2017/04/21 13:50:23 jca Exp $	*/
2
3
/*
4
 * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
5
 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
6
 *
7
 * Permission to use, copy, modify, and distribute this software for any
8
 * purpose with or without fee is hereby granted, provided that the above
9
 * copyright notice and this permission notice appear in all copies.
10
 *
11
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
 */
19
20
#include <sys/queue.h>
21
#include <sys/stat.h>
22
#include <sys/socket.h>
23
#include <sys/un.h>
24
#include <sys/tree.h>
25
26
#include <net/if.h>
27
28
#include <errno.h>
29
#include <event.h>
30
#include <fcntl.h>
31
#include <stdlib.h>
32
#include <string.h>
33
#include <unistd.h>
34
#include <signal.h>
35
36
#include "snmpd.h"
37
38
#define	CONTROL_BACKLOG	5
39
40
struct ctl_connlist ctl_conns;
41
42
static int agentx_sessionid = 1;
43
44
void	 control_accept(int, short, void *);
45
void	 control_close(struct ctl_conn *, const char *, struct imsg *);
46
void	 control_dispatch_imsg(int, short, void *);
47
void	 control_dispatch_agentx(int, short, void *);
48
void	 control_imsg_forward(struct imsg *);
49
void	 control_event_add(struct ctl_conn *, int, int, struct timeval *);
50
ssize_t	 imsg_read_nofd(struct imsgbuf *);
51
52
int
53
control_init(struct privsep *ps, struct control_sock *cs)
54
{
55
	struct snmpd		*env = ps->ps_env;
56
	struct sockaddr_un	 sun;
57
	int			 fd;
58
	mode_t			 old_umask, mode;
59
60
	if (cs->cs_name == NULL)
61
		return (0);
62
63
	if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1) {
64
		log_warn("%s: socket", __func__);
65
		return (-1);
66
	}
67
68
	sun.sun_family = AF_UNIX;
69
	if (strlcpy(sun.sun_path, cs->cs_name,
70
	    sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) {
71
		log_warn("%s: %s name too long", __func__, cs->cs_name);
72
		close(fd);
73
		return (-1);
74
	}
75
76
	if (unlink(cs->cs_name) == -1)
77
		if (errno != ENOENT) {
78
			log_warn("%s: unlink %s", __func__, cs->cs_name);
79
			close(fd);
80
			return (-1);
81
		}
82
83
	if (cs->cs_restricted || cs->cs_agentx) {
84
		old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
85
		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
86
	} else {
87
		old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
88
		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
89
	}
90
91
	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
92
		log_warn("%s: bind: %s", __func__, cs->cs_name);
93
		close(fd);
94
		(void)umask(old_umask);
95
		return (-1);
96
	}
97
	(void)umask(old_umask);
98
99
	if (chmod(cs->cs_name, mode) == -1) {
100
		log_warn("%s: chmod", __func__);
101
		close(fd);
102
		(void)unlink(cs->cs_name);
103
		return (-1);
104
	}
105
106
	cs->cs_fd = fd;
107
	cs->cs_env = env;
108
109
	return (0);
110
}
111
112
int
113
control_listen(struct control_sock *cs)
114
{
115
	if (cs->cs_name == NULL)
116
		return (0);
117
118
	if (listen(cs->cs_fd, CONTROL_BACKLOG) == -1) {
119
		log_warn("%s: listen", __func__);
120
		return (-1);
121
	}
122
123
	event_set(&cs->cs_ev, cs->cs_fd, EV_READ,
124
	    control_accept, cs);
125
	event_add(&cs->cs_ev, NULL);
126
	evtimer_set(&cs->cs_evt, control_accept, cs);
127
128
	return (0);
129
}
130
131
void
132
control_cleanup(struct control_sock *cs)
133
{
134
	if (cs->cs_name == NULL)
135
		return;
136
	event_del(&cs->cs_ev);
137
	event_del(&cs->cs_evt);
138
}
139
140
/* ARGSUSED */
141
void
142
control_accept(int listenfd, short event, void *arg)
143
{
144
	struct control_sock	*cs = arg;
145
	int			 connfd;
146
	socklen_t		 len;
147
	struct sockaddr_un	 sun;
148
	struct ctl_conn		*c;
149
150
	event_add(&cs->cs_ev, NULL);
151
	if ((event & EV_TIMEOUT))
152
		return;
153
154
	len = sizeof(sun);
155
	if ((connfd = accept4(listenfd,
156
	    (struct sockaddr *)&sun, &len, SOCK_NONBLOCK)) == -1) {
157
		/*
158
		 * Pause accept if we are out of file descriptors, or
159
		 * libevent will haunt us here too.
160
		 */
161
		if (errno == ENFILE || errno == EMFILE) {
162
			struct timeval evtpause = { 1, 0 };
163
164
			event_del(&cs->cs_ev);
165
			evtimer_add(&cs->cs_evt, &evtpause);
166
		} else if (errno != EWOULDBLOCK && errno != EINTR &&
167
		    errno != ECONNABORTED)
168
			log_warn("%s: accept", __func__);
169
		return;
170
	}
171
172
	if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
173
		close(connfd);
174
		log_warn("%s: calloc", __func__);
175
		return;
176
	}
177
178
	imsg_init(&c->iev.ibuf, connfd);
179
	if (cs->cs_agentx) {
180
		c->handle = snmp_agentx_alloc(c->iev.ibuf.fd);
181
		if (c->handle == NULL) {
182
			free(c);
183
			log_warn("%s: agentx", __func__);
184
			return;
185
		}
186
		c->flags |= CTL_CONN_LOCKED;
187
		c->iev.handler = control_dispatch_agentx;
188
		TAILQ_INIT(&c->oids);
189
	} else
190
		c->iev.handler = control_dispatch_imsg;
191
	c->iev.events = EV_READ;
192
	c->iev.data = c;
193
	c->cs = cs;
194
	event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
195
	    c->iev.handler, c->iev.data);
196
	event_add(&c->iev.ev, NULL);
197
198
	TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
199
}
200
201
void
202
control_close(struct ctl_conn *c, const char *msg, struct imsg *imsg)
203
{
204
	struct control_sock *cs = c->cs;
205
206
	if (imsg) {
207
		log_debug("%s: fd %d: %s, imsg %d datalen %zu", __func__,
208
		    c->iev.ibuf.fd, msg, imsg->hdr.type, IMSG_DATA_SIZE(imsg));
209
		imsg_free(imsg);
210
	} else
211
		log_debug("%s: fd %d: %s", __func__, c->iev.ibuf.fd, msg);
212
213
	msgbuf_clear(&c->iev.ibuf.w);
214
	TAILQ_REMOVE(&ctl_conns, c, entry);
215
216
	event_del(&c->iev.ev);
217
	close(c->iev.ibuf.fd);
218
219
	/* Some file descriptors are available again. */
220
	if (evtimer_pending(&cs->cs_evt, NULL)) {
221
		evtimer_del(&cs->cs_evt);
222
		event_add(&cs->cs_ev, NULL);
223
	}
224
225
	free(c);
226
}
227
228
/* ARGSUSED */
229
void
230
control_dispatch_imsg(int fd, short event, void *arg)
231
{
232
	struct ctl_conn		*c = arg;
233
	struct control_sock	*cs = c->cs;
234
	struct snmpd		*env = cs->cs_env;
235
	struct imsg		 imsg;
236
	int			 n, v, i;
237
238
	if (event & EV_READ) {
239
		if (((n = imsg_read_nofd(&c->iev.ibuf)) == -1 &&
240
		    errno != EAGAIN) || n == 0) {
241
			control_close(c, "could not read imsg", NULL);
242
			return;
243
		}
244
	}
245
	if (event & EV_WRITE) {
246
		if (msgbuf_write(&c->iev.ibuf.w) <= 0 && errno != EAGAIN) {
247
			control_close(c, "could not write imsg", NULL);
248
			return;
249
		}
250
	}
251
252
	for (;;) {
253
		if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
254
			control_close(c, "could not get imsg", NULL);
255
			return;
256
		}
257
258
		if (n == 0)
259
			break;
260
261
		if (cs->cs_restricted || (c->flags & CTL_CONN_LOCKED)) {
262
			switch (imsg.hdr.type) {
263
			case IMSG_SNMP_AGENTX:
264
			case IMSG_SNMP_ELEMENT:
265
			case IMSG_SNMP_END:
266
			case IMSG_SNMP_LOCK:
267
				break;
268
			default:
269
				control_close(c,
270
				    "client requested restricted command",
271
				    &imsg);
272
				return;
273
			}
274
		}
275
276
		control_imsg_forward(&imsg);
277
278
		switch (imsg.hdr.type) {
279
		case IMSG_CTL_NOTIFY:
280
			if (IMSG_DATA_SIZE(&imsg))
281
				return control_close(c, "invalid size", &imsg);
282
283
			if (c->flags & CTL_CONN_NOTIFY) {
284
				log_debug("%s: "
285
				    "client requested notify more than once",
286
				    __func__);
287
				imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
288
				    0, 0, -1, NULL, 0);
289
				break;
290
			}
291
			c->flags |= CTL_CONN_NOTIFY;
292
			break;
293
294
		case IMSG_SNMP_LOCK:
295
			if (IMSG_DATA_SIZE(&imsg))
296
				return control_close(c, "invalid size", &imsg);
297
298
			/* enable restricted control mode */
299
			c->flags |= CTL_CONN_LOCKED;
300
			break;
301
302
		case IMSG_SNMP_AGENTX:
303
			if (IMSG_DATA_SIZE(&imsg))
304
				return control_close(c, "invalid size", &imsg);
305
306
			/* rendezvous with the client */
307
			imsg_compose_event(&c->iev, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
308
			if (imsg_flush(&c->iev.ibuf) == -1) {
309
				control_close(c,
310
				    "could not rendezvous with agentx client",
311
				    &imsg);
312
				return;
313
			}
314
315
			/* enable AgentX socket */
316
			c->handle = snmp_agentx_alloc(c->iev.ibuf.fd);
317
			if (c->handle == NULL) {
318
				control_close(c,
319
				    "could not allocate agentx socket",
320
				    &imsg);
321
				return;
322
			}
323
			/* disable IMSG notifications */
324
			c->flags &= ~CTL_CONN_NOTIFY;
325
			c->flags |= CTL_CONN_LOCKED;
326
			c->iev.handler = control_dispatch_agentx;
327
			break;
328
329
		case IMSG_CTL_VERBOSE:
330
			if (IMSG_DATA_SIZE(&imsg) != sizeof(v))
331
				return control_close(c, "invalid size", &imsg);
332
333
			memcpy(&v, imsg.data, sizeof(v));
334
			log_setverbose(v);
335
336
			for (i = 0; i < PROC_MAX; i++) {
337
				if (privsep_process == PROC_CONTROL)
338
					continue;
339
				proc_forward_imsg(&env->sc_ps, &imsg, i, -1);
340
			}
341
			break;
342
		case IMSG_CTL_RELOAD:
343
			if (IMSG_DATA_SIZE(&imsg))
344
				return control_close(c, "invalid size", &imsg);
345
			proc_forward_imsg(&env->sc_ps, &imsg, PROC_PARENT, -1);
346
			break;
347
		default:
348
			control_close(c, "invalid type", &imsg);
349
			return;
350
		}
351
352
		imsg_free(&imsg);
353
	}
354
355
	imsg_event_add(&c->iev);
356
}
357
358
static void
359
purge_registered_oids(struct oidlist *oids)
360
{
361
	struct oid	*oid;
362
363
	while ((oid = TAILQ_FIRST(oids)) != NULL) {
364
		if (!(oid->o_flags & OID_REGISTERED))
365
			fatalx("attempting to unregister a static mib");
366
		smi_delete(oid);
367
		TAILQ_REMOVE(oids, oid, o_list);
368
	}
369
}
370
371
/* ARGSUSED */
372
void
373
control_dispatch_agentx(int fd, short event, void *arg)
374
{
375
	struct ctl_conn			*c = arg;
376
	struct agentx_handle		*h = c->handle;
377
	struct agentx_pdu		*pdu;
378
	struct timeval			 tv;
379
	struct agentx_open_timeout	 to;
380
	struct ber_oid			 oid;
381
	struct agentx_close_request_data clhdr;
382
	int				 closing = 0;
383
	int				 evflags = 0;
384
	int				 timer = 0;
385
	int				 error = AGENTX_ERR_NONE;
386
	int				 idx = 0, vcpylen, dlen, uptime;
387
	char				*descr, *varcpy;
388
389
	varcpy = descr = NULL;
390
	if (h->timeout != 0)
391
		tv.tv_sec = h->timeout;
392
	else
393
		tv.tv_sec = AGENTX_DEFAULT_TIMEOUT;
394
	tv.tv_usec = 0;
395
396
	if (event & EV_TIMEOUT) {
397
		log_info("subagent session '%i' timed out after %i seconds",
398
		    h->sessionid, h->timeout);
399
		goto teardown;
400
	}
401
402
	if (event & EV_WRITE) {
403
		if (snmp_agentx_send(h, NULL) == -1) {
404
			if (errno != EAGAIN)
405
				goto teardown;
406
407
			/* short write */
408
			evflags |= EV_WRITE;
409
			timer = 1;
410
		}
411
	}
412
413
	if (event & EV_READ) {
414
		if ((pdu = snmp_agentx_recv(h)) == NULL) {
415
			if (h->error) {
416
				error = h->error;
417
				goto respond;
418
			}
419
			if (errno != EAGAIN)
420
				goto teardown;
421
422
			/* short read */
423
			timer = 1;
424
			goto done;
425
		}
426
427
		switch (pdu->hdr->type) {
428
		case AGENTX_OPEN:
429
			if (snmp_agentx_read_raw(pdu, &to, sizeof(to)) == -1 ||
430
			    snmp_agentx_read_oid(pdu,
431
			    (struct snmp_oid *)&oid) == -1 ||
432
			    (descr =
433
			    snmp_agentx_read_octetstr(pdu, &dlen)) == NULL) {
434
				error = AGENTX_ERR_PARSE_ERROR;
435
				break;
436
			}
437
438
			log_info("opening AgentX socket for '%.*s'",
439
			    dlen, descr);
440
441
			h->sessionid = pdu->hdr->sessionid =
442
			    agentx_sessionid++;
443
			if (to.timeout != 0)
444
				h->timeout = to.timeout;
445
			else
446
				h->timeout = AGENTX_DEFAULT_TIMEOUT;
447
			break;
448
449
		case AGENTX_CLOSE:
450
			if (snmp_agentx_read_raw(pdu,
451
			    &clhdr, sizeof(clhdr)) == -1) {
452
				error = AGENTX_ERR_PARSE_ERROR;
453
				break;
454
			}
455
			closing = 1;
456
			break;
457
458
		case AGENTX_NOTIFY:
459
			error = trap_agentx(h, pdu, &idx, &varcpy, &vcpylen);
460
			break;
461
462
		case AGENTX_PING:
463
			/* no processing, just an empty response */
464
			break;
465
466
		case AGENTX_REGISTER: {
467
			struct agentx_register_hdr	 rhdr;
468
			struct oidlist			 oids;
469
			struct oid			*miboid;
470
			uint32_t			 ubound = 0;
471
472
			TAILQ_INIT(&oids);
473
474
			if (snmp_agentx_read_raw(pdu,
475
			    &rhdr, sizeof(rhdr)) == -1 ||
476
			    snmp_agentx_read_oid(pdu,
477
			    (struct snmp_oid *)&oid) == -1) {
478
				error = AGENTX_ERR_PARSE_ERROR;
479
				break;
480
			}
481
482
			do {
483
				if ((miboid = calloc(1, sizeof(*miboid))) == NULL) {
484
					purge_registered_oids(&oids);
485
					error = AGENTX_ERR_PARSE_ERROR;
486
					goto dodone;
487
				}
488
				bcopy(&oid, &miboid->o_id, sizeof(oid));
489
				miboid->o_flags = OID_RD|OID_WR|OID_REGISTERED;
490
				miboid->o_session = c;
491
				if (smi_insert(miboid) == -1) {
492
					purge_registered_oids(&oids);
493
					error = AGENTX_ERR_DUPLICATE_REGISTRATION;
494
					goto dodone;
495
				}
496
				TAILQ_INSERT_TAIL(&oids, miboid, o_list);
497
			} while (++oid.bo_id[rhdr.subrange] <= ubound);
498
499
			while ((miboid = TAILQ_FIRST(&oids)) != NULL) {
500
				TAILQ_REMOVE(&oids, miboid, o_list);
501
				TAILQ_INSERT_TAIL(&c->oids, miboid, o_list);
502
			}
503
 dodone:
504
			break;
505
		}
506
507
		case AGENTX_UNREGISTER: {
508
			struct agentx_unregister_hdr	 uhdr;
509
			struct oid			*miboid;
510
			uint32_t			 ubound = 0;
511
512
			if (snmp_agentx_read_raw(pdu,
513
			    &uhdr, sizeof(uhdr)) == -1 ||
514
			    snmp_agentx_read_oid(pdu,
515
			    (struct snmp_oid *)&oid) == -1) {
516
				error = AGENTX_ERR_PARSE_ERROR;
517
				break;
518
			}
519
520
			do {
521
				if ((miboid = smi_find((struct oid *)&oid)) == NULL) {
522
					log_warnx("attempting to remove unregistered MIB");
523
					continue;
524
				}
525
				if (miboid->o_session != c) {
526
					log_warnx("attempting to remove MIB registered by other session");
527
					continue;
528
				}
529
				smi_delete(miboid);
530
			} while (++oid.bo_id[uhdr.subrange] <= ubound);
531
			break;
532
		}
533
534
		case AGENTX_RESPONSE: {
535
			struct snmp_message		*msg = pdu->request->cookie;
536
			struct agentx_response_data	 resp;
537
			struct agentx_varbind_hdr	 vbhdr;
538
			struct ber_element		**elm, **iter;
539
540
			if (snmp_agentx_read_response(pdu, &resp) == -1) {
541
				msg->sm_error = SNMP_ERROR_GENERR;
542
				goto dispatch;
543
			}
544
545
			switch (resp.error) {
546
			case AGENTX_ERR_NONE:
547
				break;
548
549
			/* per RFC, resp.error may be an SNMP error value */
550
			case SNMP_ERROR_TOOBIG:
551
			case SNMP_ERROR_NOSUCHNAME:
552
			case SNMP_ERROR_BADVALUE:
553
			case SNMP_ERROR_READONLY:
554
			case SNMP_ERROR_GENERR:
555
			case SNMP_ERROR_NOACCESS:
556
			case SNMP_ERROR_WRONGTYPE:
557
			case SNMP_ERROR_WRONGLENGTH:
558
			case SNMP_ERROR_WRONGENC:
559
			case SNMP_ERROR_WRONGVALUE:
560
			case SNMP_ERROR_NOCREATION:
561
			case SNMP_ERROR_INCONVALUE:
562
			case SNMP_ERROR_RESUNAVAIL:
563
			case SNMP_ERROR_COMMITFAILED:
564
			case SNMP_ERROR_UNDOFAILED:
565
			case SNMP_ERROR_AUTHERROR:
566
			case SNMP_ERROR_NOTWRITABLE:
567
			case SNMP_ERROR_INCONNAME:
568
				msg->sm_error = resp.error;
569
				msg->sm_errorindex = resp.index;
570
				break;
571
572
			case AGENTX_ERR_INDEX_WRONG_TYPE:
573
			case AGENTX_ERR_UNSUPPORTED_CONTEXT:
574
			case AGENTX_ERR_PARSE_ERROR:
575
			case AGENTX_ERR_REQUEST_DENIED:
576
			case AGENTX_ERR_PROCESSING_ERROR:
577
			default:
578
				msg->sm_error = SNMP_ERROR_GENERR;
579
				msg->sm_errorindex = resp.index;
580
				break;
581
			}
582
583
			iter = elm = &msg->sm_varbindresp;
584
585
			while (pdu->datalen > sizeof(struct agentx_hdr)) {
586
				if (snmp_agentx_read_raw(pdu, &vbhdr, sizeof(vbhdr)) == -1 ||
587
				    varbind_convert(pdu, &vbhdr, elm, iter)
588
				    != AGENTX_ERR_NONE) {
589
					msg->sm_error = SNMP_ERROR_GENERR;
590
					msg->sm_errorindex = msg->sm_i;
591
					goto dispatch;
592
				}
593
			}
594
 dispatch:
595
			snmpe_dispatchmsg(msg);
596
			break;
597
		}
598
599
		/* unimplemented, but parse and accept for now */
600
		case AGENTX_ADD_AGENT_CAPS:
601
		case AGENTX_REMOVE_AGENT_CAPS:
602
			break;
603
604
		/* unimplemented */
605
		case AGENTX_GET:
606
		case AGENTX_GET_NEXT:
607
		case AGENTX_GET_BULK:
608
		case AGENTX_TEST_SET:
609
		case AGENTX_COMMIT_SET:
610
		case AGENTX_UNDO_SET:
611
		case AGENTX_CLEANUP_SET:
612
		case AGENTX_INDEX_ALLOCATE:
613
		case AGENTX_INDEX_DEALLOCATE:
614
			error = AGENTX_ERR_REQUEST_DENIED;
615
			break;
616
617
		/* NB: by RFC, this should precede all other checks. */
618
		default:
619
			log_info("unknown AgentX type '%i'", pdu->hdr->type);
620
			error = AGENTX_ERR_PARSE_ERROR;
621
			break;
622
		}
623
 respond:
624
		if (pdu)
625
			snmp_agentx_pdu_free(pdu);
626
627
		uptime = smi_getticks();
628
		if ((pdu = snmp_agentx_response_pdu(uptime, error, idx)) == NULL) {
629
			log_debug("failed to generate response");
630
			free(varcpy);
631
			control_event_add(c, fd, EV_WRITE, NULL);	/* XXX -- EV_WRITE? */
632
			return;
633
		}
634
635
		if (varcpy) {
636
			snmp_agentx_raw(pdu, varcpy, vcpylen); /* XXX */
637
			free(varcpy);
638
			varcpy = NULL;
639
		}
640
		snmp_agentx_send(h, pdu);
641
642
		/* Request processed, now write out response */
643
		evflags |= EV_WRITE;
644
	}
645
646
	if (closing)
647
		goto teardown;
648
 done:
649
	control_event_add(c, fd, evflags, timer ? &tv : NULL);
650
	return;
651
652
 teardown:
653
	log_debug("subagent session '%i' destroyed", h->sessionid);
654
	snmp_agentx_free(h);
655
	purge_registered_oids(&c->oids);
656
	free(varcpy);
657
	control_close(c, "agentx teardown", NULL);
658
}
659
660
void
661
control_imsg_forward(struct imsg *imsg)
662
{
663
	struct ctl_conn *c;
664
665
	TAILQ_FOREACH(c, &ctl_conns, entry)
666
		if (c->flags & CTL_CONN_NOTIFY)
667
			imsg_compose_event(&c->iev, imsg->hdr.type,
668
			    0, imsg->hdr.pid, -1, imsg->data,
669
			    imsg->hdr.len - IMSG_HEADER_SIZE);
670
}
671
672
void
673
control_event_add(struct ctl_conn *c, int fd, int wflag, struct timeval *tv)
674
{
675
	event_del(&c->iev.ev);
676
	event_set(&c->iev.ev, fd, EV_READ|wflag, control_dispatch_agentx, c);
677
	event_add(&c->iev.ev, tv);
678
}
679
680
/* This should go into libutil, from smtpd/mproc.c */
681
ssize_t
682
imsg_read_nofd(struct imsgbuf *ibuf)
683
{
684
	ssize_t	 n;
685
	char	*buf;
686
	size_t	 len;
687
688
	buf = ibuf->r.buf + ibuf->r.wpos;
689
	len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
690
691
	while ((n = recv(ibuf->fd, buf, len, 0)) == -1) {
692
		if (errno != EINTR)
693
			return (n);
694
	}
695
696
        ibuf->r.wpos += n;
697
        return (n);
698
}